大概在好幾個月前,我寫了一篇關於如何在 golang 設定環境變數的文章,怎麼優雅地設定環境變數是一件相當重要的事情,因此我寫了一個簡單的函式來做這件事。
當初的考量很簡單,如果有提供對應的 config
檔,就把裡頭的 key/value 用 os.Setenv
設定。之後在整個 App 當中,就可以直接使用 os.Getenv
來取值。
不過一旦要正式部署,難免會發現幾個問題:
- 設定檔不一定是用 yaml,也可能是用其他格式
- 有時希望透過外部服務(consul, etcd 等等)來注入環境變數,但目前的寫法是用
os.Getenv
- 可以從不同的路徑讀取設定檔
- 可以透過 command line 設定變數
- 更多意想不到的問題
在小型專案中,這樣其實解決了我的問題,但一旦需要更彈性的設定方式,光是這樣還不夠。
而以上這些問題其實在 golang 已經有一套成熟的解決方案,叫做 Viper。
VIPER
VIPER 是一個功能相當強大的環境變數設定套件。它支援了下列功能:
- 預設值
- 支援不同格式的設定檔(
json
,toml
,yaml
) - 可以監聽設定檔的變化(這樣就不用重開 server 了!)
- 可以從遠端伺服器拿取變數(例如
consul
或etcd
) - 可以使用
flag
來獲取變數 - 可以從
reader
來獲取變數,這代表你可以用不同的來源來設定變數。
基本使用
首先先用 go get 或 mod 下載 VIPER。
go get github.com/spf13/viper
func main() {
viper.SetDefault("AWS_ACCESS_TOKEN", "AWS123456789") // 設定預設值
viper.SetConfigName("config") // 指定 config 的檔名
viper.AddConfigPath("./config")
viper.ReadInConfig()
viper.AutomaticEnv()
}
SetDefault(key, value)
:設定預設值SetConfigName(name)
: 設定 config 的檔名,例如我們的設定檔叫做config.yml
就叫做config
,為什麼不用指定 type 呢?viper 會自動幫我們處理。AddConfigPath
:可以設定多個資料夾ReadInConfig
:要記得呼叫,才會真的讀取設定AutomaticEnv
:可以自動將ENV
的變數同步到 viper 當中
viper 還有提供許多函數方便設定。
取用變數
設定完變數後,我們可以用 viper.Get
來讀取。Get
回傳的是一個 interface{}
,但 viper 也提供許多型別轉換,例如 viper.GetString
、viper.GetDuration
、viper.GetStringMap
等等。
func main() {
fmt.Println(viper.GetString("AWS_ACCESS_TOKEN"))
fmt.Println(viper.GetUint32("YOUR_INT"))
fmt.Println(viper.GetTime("YOUR_TIME"))
}
總結
變數設定說難不難,說簡單不簡單,要考慮的事情比想像中的還多,而 VIPER
幫我們處理掉了很多常見的使用情景。
另外要特別提醒的一點是,在設定像是 access token 或是 secret key 的時候,一定要記得用 .gitignore
掉,避免 commit 時意外推送到 github 上,造成遺憾。