skills/go-configuration/SKILL.md
Go 設定管理最佳實務:Viper 配置、環境變數優先級、Secrets 處理、設定驗證、 動態重載、多環境管理、12-Factor App 原則。 **適用場景**:使用 Viper、環境變數管理、Secrets 處理、設定驗證、動態重載設定、 多環境配置(dev/staging/prod)、Kubernetes ConfigMap/Secret 整合。
npx skillsauth add vincent119/ai-rules-kit go-configurationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
相關 Skills:本規範建議搭配
go-observability(日誌配置)與go-graceful-shutdown(動態重載)
環境變數 > 設定檔 > 預設值
go get github.com/spf13/viper
package config
import (
"fmt"
"github.com/spf13/viper"
)
type Config struct {
Server ServerConfig `mapstructure:"server"`
Database DatabaseConfig `mapstructure:"database"`
Redis RedisConfig `mapstructure:"redis"`
Log LogConfig `mapstructure:"log"`
}
type ServerConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
}
type DatabaseConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"` // 敏感資訊
Database string `mapstructure:"database"`
}
type RedisConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Password string `mapstructure:"password"`
}
type LogConfig struct {
Level string `mapstructure:"level"`
Format string `mapstructure:"format"` // json or console
}
func Load() (*Config, error) {
// 1. 設定檔路徑
viper.SetConfigName("config") // config.yaml
viper.SetConfigType("yaml")
viper.AddConfigPath(".") // 當前目錄
viper.AddConfigPath("./configs") // ./configs 目錄
viper.AddConfigPath("/etc/myapp") // 系統目錄
// 2. 預設值
setDefaults()
// 3. 讀取設定檔
if err := viper.ReadInConfig(); err != nil {
// 設定檔不存在不算錯誤(使用預設值 + 環境變數)
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return nil, fmt.Errorf("read config: %w", err)
}
}
// 4. 環境變數覆蓋
viper.AutomaticEnv()
viper.SetEnvPrefix("MYAPP") // 環境變數前綴:MYAPP_SERVER_PORT
// 5. 綁定到 Struct
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
return nil, fmt.Errorf("unmarshal config: %w", err)
}
// 6. 驗證設定
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("validate config: %w", err)
}
return &cfg, nil
}
func setDefaults() {
viper.SetDefault("server.host", "0.0.0.0")
viper.SetDefault("server.port", 8080)
viper.SetDefault("log.level", "info")
viper.SetDefault("log.format", "json")
}
server:
host: 0.0.0.0
port: 8080
database:
host: localhost
port: 5432
user: postgres
password: ${DB_PASSWORD} # 從環境變數讀取(Viper 不支援)
database: myapp
redis:
host: localhost
port: 6379
password: ""
log:
level: info
format: json
# 格式:<PREFIX>_<SECTION>_<KEY>
export MYAPP_SERVER_PORT=8080
export MYAPP_DATABASE_HOST=postgres.example.com
export MYAPP_DATABASE_PASSWORD=secret123
export MYAPP_LOG_LEVEL=debug
import "strings"
func Load() (*Config, error) {
// 自動替換 . 為 _
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
viper.SetEnvPrefix("MYAPP")
// 現在可以這樣使用:
// MYAPP_SERVER_PORT=9000 -> viper.GetInt("server.port")
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}
# .env
MYAPP_SERVER_PORT=8080
MYAPP_DATABASE_HOST=localhost
MYAPP_DATABASE_PASSWORD=dev_password
MYAPP_LOG_LEVEL=debug
import "github.com/joho/godotenv"
func init() {
// 開發環境載入 .env(生產環境不需要)
if os.Getenv("ENV") != "production" {
_ = godotenv.Load()
}
}
type DatabaseConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"` // 從環境變數讀取
}
func main() {
// 直接從環境變數讀取
dbPassword := os.Getenv("DB_PASSWORD")
if dbPassword == "" {
log.Fatal("DB_PASSWORD not set")
}
}
# k8s-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
type: Opaque
data:
db-password: cGFzc3dvcmQxMjM= # base64 編碼
# deployment.yaml
env:
- name: MYAPP_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: myapp-secrets
key: db-password
import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/secretsmanager"
)
func LoadSecrets() (string, error) {
sess := session.Must(session.NewSession())
svc := secretsmanager.New(sess)
input := &secretsmanager.GetSecretValueInput{
SecretId: aws.String("myapp/database/password"),
}
result, err := svc.GetSecretValue(input)
if err != nil {
return "", err
}
return *result.SecretString, nil
}
go get github.com/go-playground/validator/v10
import "github.com/go-playground/validator/v10"
type Config struct {
Server ServerConfig `mapstructure:"server" validate:"required"`
Database DatabaseConfig `mapstructure:"database" validate:"required"`
}
type ServerConfig struct {
Host string `mapstructure:"host" validate:"required,hostname|ip"`
Port int `mapstructure:"port" validate:"required,min=1,max=65535"`
}
type DatabaseConfig struct {
Host string `mapstructure:"host" validate:"required"`
Port int `mapstructure:"port" validate:"required,min=1,max=65535"`
User string `mapstructure:"user" validate:"required"`
Password string `mapstructure:"password" validate:"required,min=8"`
Database string `mapstructure:"database" validate:"required"`
}
func (c *Config) Validate() error {
validate := validator.New()
return validate.Struct(c)
}
func (c *Config) Validate() error {
// 使用 validator
validate := validator.New()
if err := validate.Struct(c); err != nil {
return err
}
// 自訂邏輯
if c.Database.Host == "localhost" && os.Getenv("ENV") == "production" {
return errors.New("production should not use localhost database")
}
if c.Log.Level != "debug" && c.Log.Level != "info" && c.Log.Level != "warn" && c.Log.Level != "error" {
return fmt.Errorf("invalid log level: %s", c.Log.Level)
}
return nil
}
import "github.com/fsnotify/fsnotify"
func WatchConfig(cfg *Config) {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Info("config file changed", zap.String("file", e.Name))
// 重新載入設定
var newCfg Config
if err := viper.Unmarshal(&newCfg); err != nil {
log.Error("failed to reload config", zap.Error(err))
return
}
if err := newCfg.Validate(); err != nil {
log.Error("invalid config after reload", zap.Error(err))
return
}
// 更新設定(需要 atomic 操作)
*cfg = newCfg
log.Info("config reloaded successfully")
})
}
import "sync/atomic"
type ConfigHolder struct {
cfg atomic.Value
}
func NewConfigHolder(cfg *Config) *ConfigHolder {
holder := &ConfigHolder{}
holder.cfg.Store(cfg)
return holder
}
func (h *ConfigHolder) Get() *Config {
return h.cfg.Load().(*Config)
}
func (h *ConfigHolder) Update(cfg *Config) {
h.cfg.Store(cfg)
}
// 使用範例
func main() {
cfg, _ := config.Load()
holder := NewConfigHolder(cfg)
// 監聽變更
go func() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
var newCfg Config
if err := viper.Unmarshal(&newCfg); err != nil {
log.Error("reload failed", zap.Error(err))
return
}
holder.Update(&newCfg)
})
}()
// 讀取設定(執行緒安全)
currentCfg := holder.Get()
}
func Load() (*Config, error) {
env := os.Getenv("ENV")
if env == "" {
env = "development"
}
configFile := fmt.Sprintf("config.%s.yaml", env)
viper.SetConfigName(configFile)
viper.AddConfigPath("./configs")
// config.development.yaml
// config.staging.yaml
// config.production.yaml
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
// ...
}
func Load() (*Config, error) {
// 1. 載入基礎設定
viper.SetConfigName("config.base")
viper.AddConfigPath("./configs")
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
// 2. 載入環境特定設定(覆蓋)
env := os.Getenv("ENV")
if env != "" {
viper.SetConfigName(fmt.Sprintf("config.%s", env))
if err := viper.MergeInConfig(); err != nil {
// 環境設定可選
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return nil, err
}
}
}
// 3. 環境變數覆蓋
viper.AutomaticEnv()
// ...
}
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
config.yaml: |
server:
host: 0.0.0.0
port: 8080
log:
level: info
format: json
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: myapp
image: myapp:latest
volumeMounts:
- name: config
mountPath: /etc/myapp
readOnly: true
env:
- name: MYAPP_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: myapp-secrets
key: db-password
volumes:
- name: config
configMap:
name: myapp-config
func Load() (*Config, error) {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("/etc/myapp") // Kubernetes 掛載路徑
viper.AddConfigPath(".") // 本地開發
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
// 環境變數覆蓋(包含 Secret)
viper.AutomaticEnv()
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}
基本設定
環境變數
MYAPP_).env 檔案(開發環境)Secrets
驗證
validator 驗證設定多環境
動態重載
viper.WatchConfig 監聽變更atomic.Value 安全更新Kubernetes
volumeMounts 路徑tools
基於 SLA/SLO 量化評估事故影響的計算模型與業務影響矩陣。適用於「SLA 影響」、「SLO 違反」、「影響評估」、「營收損失估算」、「Error Budget」、「可用性計算」、「事故成本評估」等量化事故業務影響的任務。強化 impact-assessor 的評估能力。注意:事故原因分析與改善規劃不在此技能範圍內。
research
根因分析(RCA)方法論詳細指南。提供 5 Whys、Fishbone 圖、Fault Tree Analysis、變更分析等結構化 RCA 技術,以及認知偏誤防範清單。適用於「根因分析」、「RCA」、「5 Whys」、「魚骨圖」、「Fault Tree」、「原因分析方法論」、「變更分析」等事故原因分析任務。強化 root-cause-investigator 的分析能力。注意:時間軸重建與改善規劃不在此技能範圍內。
testing
事故事後分析(Postmortem)完整流程。協調 7 個執行階段:資訊收集 → 時間軸重建 → 根因分析 → 影響評估 → 改善規劃 → 報告審查 → 整合報告,最終產出完整的 Postmortem 報告。適用於「寫事故報告」、「post-incident 分析」、「RCA 報告」、「事故時間軸整理」、「建立改善措施」等請求。注意:即時 Incident Response(on-call)、監控系統設定、告警配置不在此技能範圍內。
content-media
投影片版面模式庫。提供 20 種投影片類型的最佳版面配置、格線系統、色彩與字型設計 Token。適用於「投影片版面」、「Slide Layout」、「設計系統」、「格線」、「字型」、「色彩規範」等投影片視覺設計任務。強化 visual-designer 的設計能力。注意:PPT/Keynote 檔案直接輸出不在此技能範圍內。