Goの公式dockerイメージとは?
- これの事 : golang Repository | Docker Hub Registry - Repositories of Docker Images
- アナウンス記事 : Docker Hub Official Repos: Announcing Language Stacks | Docker Blog
- 「各言語の実行環境が整ったコンテナ を動かすためのイメージ」を公式に公開したぜ、という話
- ということはつまり、 Goアプリの実行・テスト・プロファイリング・ビルド といった作業を「公式のお墨付き且つ使い捨ての環境」で行うことが出来る という事。素敵
- 言語のバージョンも揃っているので、バージョン間の挙動差異を確認したい時にサクっと環境をGETすることが出来る
使い方の例
実行環境として使う (Start a Go instance in your app)
Goで作成したcliツールを公式イメージのコンテナで動かすケースを試してみる(拙作 で試してみる)
事前準備
- 対象goアプリをgit cloneする(ローカルにあればそこにcd)
$ git clone https://github.com/goldeneggg/ipcl.git
$ cd ipcl
- 公式記事にあるサンプルの形式でDockerfileを用意する
-
FROM
で指定するベースイメージは、tagに-onbuild
というsuffixが付いているものを選択- このイメージには、
ONBUILD
処理が複数定義されている
- このイメージには、
- buildする際、この
ONBUILD
で定義されている処理も動く-
ONBUILD
で処理を定義した場合、 下流の/派生先の Dockerfileのcontextで処理が実行される- 派生先Dockerfileの
FROM
句 直後に処理を埋め込むイメージ
- 派生先Dockerfileの
- 公式のサンプルではコンテナ上に
app
という名前で自・Goアプリがインストールされるけど、ONBUILD
を使う事によりbuildを実行したディレクトリの内容次第でこのapp
の実体が変わる。という仕組み- つまり、Dockerfile自体に手を加えること無く使い回しが効く
-
-
$ vi Dockerfile
FROM golang:1.3.1-onbuild
# 対象goアプリ/ツールがコンテナ上では app という名前でインストールされるので、その名前を指定して実行
## 実行時引数が無いケース
# CMD ["app"]
## 実行時引数があるケース
CMD ["app", "192.168.56.24/0"]
- このDockerfileを(任意のtagを付けて)buildする
- build時に行われる処理の流れ
-
golang/1.3.1/onbuild イメージの親(FROMで指定されてる)イメージの golang/1.3.1 がpull&buildされる
- Dockerfileはこれ。goバイナリのダウンロード・
GOPATH
等の環境変数設定・buildで使うwrapperスクリプト go-wrapper のコピー・などなどが実行される
- Dockerfileはこれ。goバイナリのダウンロード・
-
golang/1.3.1/onbuild イメージがpull&buildされる。
ONBUILD
により下記処理がビルド時に実行される- カレントディレクトリのファイル群をコンテナの
/go/src/app
にコピー -
go-wrapper download
の実行 -
go-wrapper install
の実行
- カレントディレクトリのファイル群をコンテナの
-
golang/1.3.1/onbuild イメージの親(FROMで指定されてる)イメージの golang/1.3.1 がpull&buildされる
- build時に行われる処理の流れ
$ docker build -t my-ipcl .
:
(ONBUILD ここから)
# Executing 3 build triggers
Step onbuild-0 : COPY . /go/src/app
---> 370cb70d6c73
Step onbuild-1 : RUN go-wrapper download
---> Running in c6751d9cb63b
+ exec go get -v -d
github.com/goldeneggg/ipcl (download)
github.com/jessevdk/go-flags (download)
---> bc940f23da94
Step onbuild-2 : RUN go-wrapper install
---> Running in 04625b77395b
+ exec go install -v
github.com/goldeneggg/ipcl/parser
github.com/goldeneggg/ipcl/writer
github.com/jessevdk/go-flags
app
---> b6115dc12578
---> b6115dc12578
(ONBUILD ここまで)
:
goアプリの実行
- 実行すると、CMDで指定したコマンド(と引数)の結果が表示される
$ docker run -it --rm --name my-running-ipcl my-ipcl
source_cidr : 192.168.56.0/24
network : 192.168.56.0
mask : 255.255.255.0
host_num : 254
min_address : 192.168.56.1
max_address : 192.168.56.254
broadcast : 192.168.56.255
-
app
に渡す引数を動的に指定したい場合は、DockerfileのCMD
をENTRYPOINT
にすれば良い
FROM golang:1.3.1-onbuild
# 実行時引数は docker run 時に指定する
ENTRYPOINT ["app"]
$ docker build -t my-ipcl .
$ docker run -it --rm --name my-running-ipcl my-ipcl 192.168.56.0/24 # <- 実行時引数指定
source_cidr : 192.168.56.0/24
network : 192.168.56.0
mask : 255.255.255.0
host_num : 254
min_address : 192.168.56.1
max_address : 192.168.56.254
broadcast : 192.168.56.255
# 引数が無い場合Usageを表示する想定だが、その通り動くか? -> OK
$ docker run -it --rm --name my-running-ipcl my-golang-ipcl
Target CIDR(or CIDR list file) is not assigned
Usage:
ipcl [OPTIONS] <CIDR TEXT | -f <FILE>>
Application Options:
-f, --file= Filepath listed target CIDR
-c, --csv= Output format is csv
-t, --tsv= Output format is tsv
-v, --version Print version
Help Options:
-h, --help Show this help message
コンパイル環境として使う
自作Goアプリをコンテナ上でビルドしたい場合
事前準備
- 対象goアプリをgit cloneする(ローカルにあればそこにcd)
$ git clone https://github.com/goldeneggg/ipcl.git
$ cd ipcl
※公式記事のサンプル通りでは動かない
- 公式記事にあるサンプルの形式で
go build
やmake
してみる-
-v
や-w
のpathは$GOPATH/src/<対象appのpackage path>
に置き換える-
GOPATH
は/go
-
-
-
ちょっとハマった...
- 外部ライブラリに依存している場合、事前に
go get
するなりして依存解決しておかないとbuildエラーになる - makeコマンドが無い(公式Dockerfileではapt-get installしているはずなのだが)、github上のDockerfileとdockerhubに公開されてるイメージに差分があるのかしら。。。
- 外部ライブラリに依存している場合、事前に
$ docker run --rm -v "$(pwd)":/go/src/github.com/goldeneggg/ipcl -w /go/src/github.com/goldeneggg/ipcl golang:1.3.1 go build -v
main.go:11:2: cannot find package "github.com/jessevdk/go-flags" in any of:
/usr/src/go/src/pkg/github.com/jessevdk/go-flags (from $GOROOT)
/go/src/github.com/jessevdk/go-flags (from $GOPATH)
$ docker run --rm -v "$(pwd)":/go/src/github.com/goldeneggg/ipcl -w /go/src/github.com/goldeneggg/ipcl golang:1.3.1 make
exec: "make": executable file not found in $PATH
- 対策として、
-
go build
する前に、依存パッケージのgo get
を行うようにする - makeが動かなかった公式の
golang:1.3.1
用Dockerfileをローカルでbuildして使う- 折角公開されているイメージをローカルでbuildし直して使う、というのは本来やるべきではないと思うが
-
$ wget https://raw.githubusercontent.com/docker-library/golang/master/1.3.1/go-wrapper
$ wget https://raw.githubusercontent.com/docker-library/golang/master/1.3.1/Dockerfile
$ docker build -t golang:1.3.1 .
通常コンパイル (Compile your app inside the Docker container)
- 普通に
go build
する場合-
-v
で カレントディレクトリ(=git cloneしたディレクトリ) と コンテナのGOPATH配下(/go
配下) をマウント -
-w
でコンテナ側の作業ディレクトリを同じGOPATH配下にしており、ビルドの成果物はここに出力される =マウントしているサーバー側のカレントディレクトリに置かれる
-
$ docker run --rm -v "$(pwd)":/go/src/github.com/goldeneggg/ipcl -w /go/src/github.com/goldeneggg/ipcl golang:1.3.1 bash -c 'go get -d ./... && go build -v'
github.com/goldeneggg/ipcl/parser
github.com/goldeneggg/ipcl/writer
github.com/goldeneggg/ipcl
- コンパイルで生成された実行ファイルをサーバー側で確認
$ ls ipcl
ipcl
- Makefileを用意していて
make
を実行する場合
$ docker run --rm -v "$(pwd)":/go/src/github.com/goldeneggg/ipcl -w /go/src/github.com/goldeneggg/ipcl golang:1.3.1 make
go get github.com/jessevdk/go-flags
go test ./parser
ok github.com/goldeneggg/ipcl/parser 0.004s
go build -o /ipcl
クロスコンパイル (Cross-compile your app inside the Docker container)
- 通常コンパイルと異なるのは下記の2点
- 使用するオフィシャルイメージはtagに
-cross
というsuffixの付いたもの- 通常コンパイル同様、ローカルでbuildして使う
- これもベースイメージをローカルでbuildしたからであって、ローカルでbuildし直して使う のは本来やるべきではない
- 通常コンパイル同様、ローカルでbuildして使う
- docker runの際に
-e
でプラットフォーム環境変数を指定するGOOS
GOARCH
- 使用するオフィシャルイメージはtagに
- ベースとなっている
golang:1.3.1
イメージをローカルでbuildしたので、golang:1.3.1-cross
もローカルでbuildする- Dockerfileはこれ。go自体をクロスプラットフォームでbuildする処理を行っている
$ wget https://raw.githubusercontent.com/docker-library/golang/master/1.3.1/cross/Dockerfile
$ docker build -t golang:1.3.1-cross .
- クロスコンパイル実行 (
go build
で)-
-e
でプラットフォーム環境変数指定。例としてwindowsを- windowsを指定しているので実行ファイルは
.exe
形式で生成される
- windowsを指定しているので実行ファイルは
-
$ docker run --rm -v "$(pwd)":/go/src/github.com/goldeneggg/ipcl -w /go/src/github.com/goldeneggg/ipcl -e GOOS=windows -e GOARCH=386 golang:1.3.1-cross bash -c 'go get -d ./... && go build -v'
github.com/goldeneggg/ipcl/parser
github.com/goldeneggg/ipcl/writer
github.com/jessevdk/go-flags
github.com/goldeneggg/ipcl
- コンパイルで生成された実行ファイルをサーバー側で確認
$ ls ipcl.exe
ipcl.exe
- こんな感じのスクリプトを用意しとけば、複数プラットフォーム向けのクロスコンパイル → 実行ファイル取得 までを 常に同じGo環境を使って一括実施する ことができる
$ vi cross_compiles.sh
#!/bin/sh
PKG_PATH=$1
P=`basename ${PKG_PATH}`
PKG_NAME=${P%.*}
OS_S=("linux" "windows")
ARCH_S=("386" "amd64")
for os in ${OS_S[@]}
do
for arch in ${ARCH_S[@]}
do
bin=${PKG_NAME}_${os}_${arch}
if [ ${os} = "windows" ]
then
bin=${bin}.exe
fi
docker run --rm -v "$(pwd)":/go/src/${PKG_PATH} -w /go/src/${PKG_PATH} -e GOOS=${os} -e GOARCH=${arch} golang:1.3.1-cross bash -c "go get -d ./... && go build -o ${bin} -v"
done
done
$ ./cross_compiles.sh github.com/goldeneggg/ipcl
github.com/goldeneggg/ipcl/parser
github.com/goldeneggg/ipcl/writer
github.com/jessevdk/go-flags
github.com/goldeneggg/ipcl
github.com/goldeneggg/ipcl/parser
github.com/goldeneggg/ipcl/writer
github.com/jessevdk/go-flags
github.com/goldeneggg/ipcl
github.com/goldeneggg/ipcl/parser
github.com/goldeneggg/ipcl/writer
github.com/jessevdk/go-flags
github.com/goldeneggg/ipcl
github.com/goldeneggg/ipcl/parser
github.com/goldeneggg/ipcl/writer
github.com/jessevdk/go-flags
github.com/goldeneggg/ipcl
$ ls ipcl*
ipcl_linux_386 ipcl_linux_amd64 ipcl_windows_386.exe ipcl_windows_amd64.exe
所感
- これまで言語の実行環境用イメージは自前でDockerfileを書いて用意していたが、「dockerの公式」という箔が付いたイメージが公開された事で、種々の作業について合意形成が行い易くなったと思う
- イメージの内容について理解する必要がない。勉強して理解したい人だけがやればいい
- イメージの内容を保証する必要がない
- オレオレ文化の抑止効果
- 個人・部署・会社を跨いだノウハウ共有のハードルが下がった
- とはいえ、公式イメージも随時修正されるもの(という想定)で考えておかねばならないわけで、現場で公式イメージを使用する運用を行うのであれば「どの / いつ時点の commitのイメージを使用したか?」はチーム内で管理・共有しておくべきである気がする
- 管理・共有が面倒であれば「社内では、ある時点で公式をforkして、docker buildし直したイメージを使うようにする」とか良しなにルールを決めるなり何なり