記一次對 Go 項目 Makefile 的重構(gòu)
如果你不了解 Makefile 的話,那么推薦看看阮一峰的文章「Make 命令教程[1]」。本文通過一個重構(gòu)的例子帶你寫出味道更好的 Makefile,讓我們開始吧!
假設(shè)有一個名為 foo 的項目,用 golang 開發(fā),在 docker 上部署,其 Makefile 如下:
APP?=?$(shell?basename?${CURDIR})
TAG?=?$(shell?git?log?--pretty=format:"%cd.%h"?--date=short?-1)
.PHONY:?build
build:
?go?build?-ldflags?"-X?'main.version=${TAG}'"?-o?./tmp/${APP}?.
.PHONY:?docker-config
docker-config:?env
?TAG=${TAG}?docker-compose?config
.PHONY:?docker-build
docker-build:?env
?TAG=${TAG}?docker-compose?build
.PHONY:?docker-push
docker-push:?env
?TAG=${TAG}?docker-compose?push
.PHONY:?docker-up
docker-up:?env
?TAG=${TAG}?docker-compose?up
.PHONY:?docker-down
docker-down:
?TAG=${TAG}?docker-compose?down
看上去很簡潔,唯一需要說明的是在操作 docker-compose 的時候,傳遞了一個名為 TAG 的環(huán)境變量,表示項目當前所屬的標簽,看一下對應(yīng)的 docker-compose.yml 文件:
version:?"3.0"
services:
??server:
????image:?docker.domain.com/foo:${TAG}
????build:
??????context:?.
??????dockerfile:?build/docker/Dockerfile
????ports:
??????-?"9090:9090"
??????-?"6060:6060"
此時出現(xiàn)了一個有待改進的地方:ports 信息重復,看一下對應(yīng)的 config.toml 文件:
[rpc]
port = 9090
[debug]
port = 6060
其中,rpc 端口 9090,debug 端口 6060 最初是在 config.toml 文件里配置的,但是在 docker-compose.yml 文件又重復了一次,假設(shè)要修改的話,就需要修改多個地方。
此時我們很容易想到的解決方案是把端口信息也通過環(huán)境變量傳遞,就像 TAG 變量那樣,為了獲取端口信息,我還專門寫了一個子命令 config:
package?cmd
import?(
?"fmt"
?"os"
?"github.com/spf13/cobra"
?"github.com/spf13/viper"
)
func?NewConfigCmd()?*cobra.Command?{
?configCmd?:=?&cobra.Command{
??Use:?"config?",
??Run:?config,
?}
?return?configCmd
}
func?config(cmd?*cobra.Command,?args?[]string)?{
?if?len(args)?!=?1?{
??_?=?cmd.Usage()
??os.Exit(1)
?}
?key?:=?args[0]
?value?:=?viper.Get(key)
?fmt.Println(value)
}
確定了解決方案,讓我們在看一下對應(yīng)的 docker-compose.yml 文件:
version:?"3.0"
services:
??server:
????image:?docker.domain.com/${APP}:${TAG}
????build:
??????context:?.
??????dockerfile:?build/docker/Dockerfile
????ports:
??????-?"${RPC_PORT}:${RPC_PORT}"
??????-?"${DEBUG_PORT}:${DEBUG_PORT}"
此時沒有硬編碼的配置信息了,讓我們再看看對應(yīng)的 Makefile 文件:
APP?=?$(shell?basename?${CURDIR})
TAG?=?$(shell?git?log?--pretty=format:"%cd.%h"?--date=short?-1)
RPC_PORT???=?$(shell?./tmp/${APP}?config?rpc.port)
DEBUG_PORT?=?$(shell?./tmp/${APP}?config?debug.port)
.PHONY:?build
build:
?go?build?-ldflags?"-X?'main.version=${TAG}'"?-o?./tmp/${APP}?.
.PHONY:?docker-config
docker-config:?env
?APP=${APP}?TAG=${TAG}?RPC_PORT={RPC_PORT}?DEBUG_PORT={DEBUG_PORT}?docker-compose?config
.PHONY:?docker-build
docker-build:?env
?APP=${APP}?TAG=${TAG}?RPC_PORT={RPC_PORT}?DEBUG_PORT={DEBUG_PORT}?docker-compose?build
.PHONY:?docker-push
docker-push:?env
?APP=${APP}?TAG=${TAG}?RPC_PORT={RPC_PORT}?DEBUG_PORT={DEBUG_PORT}?docker-compose?push
.PHONY:?docker-up
docker-up:?env
?APP=${APP}?TAG=${TAG}?RPC_PORT={RPC_PORT}?DEBUG_PORT={DEBUG_PORT}?docker-compose?up
.PHONY:?docker-down
docker-down:
?APP=${APP}?TAG=${TAG}?RPC_PORT={RPC_PORT}?DEBUG_PORT={DEBUG_PORT}?docker-compose?down
不得不說,長長的環(huán)境變量實在是太丑了,好在 docker-compose 支持 .env 文件[2],于是我們可以把環(huán)境變量寫入 .env 文件,然后讓 docker-compose 命令從其中取數(shù)據(jù):
APP?=?$(shell?basename?${CURDIR})
TAG?=?$(shell?git?log?--pretty=format:"%cd.%h"?--date=short?-1)
RPC_PORT???=?$(shell?./tmp/${APP}?config?rpc.port)
DEBUG_PORT?=?$(shell?./tmp/${APP}?config?debug.port)
.PHONY:?env
env:
?echo?"APP=${APP}"?>?.env;?\
??echo?"TAG=${TAG}"?>>?.env;?\
??echo?"RPC_PORT=${RPC_PORT}"?>>?.env;?\
??echo?"DEBUG_PORT=${DEBUG_PORT}"?>>?.env
.PHONY:?build
build:
?go?build?-ldflags?"-X?'main.version=${TAG}'"?-o?./tmp/${APP}?.
.PHONY:?docker-config
docker-config:?env
?docker-compose?config
.PHONY:?docker-build
docker-build:?env
?docker-compose?build
.PHONY:?docker-push
docker-push:?env
?docker-compose?push
.PHONY:?docker-up
docker-up:?env
?docker-compose?up
.PHONY:?docker-down
docker-down:
?docker-compose?down
在 Makefile 里,我們定義了一個 env 操作,并把它作為所有 docker-compose 操作的前置執(zhí)行操作,終于不用再寫長長的環(huán)境變量了,不過記得把 .env 寫到 .gitignore 里!
參考資料
Make 命令教程: https://www.ruanyifeng.com/blog/2015/02/make.html
[2]docker-compose 支持 .env 文件: https://docs.docker.com/compose/environment-variables/
推薦閱讀
