Cloud Build のドキュメントを見ていたら、ビルドを高速化するのに kaniko を使うソリューションがあるよ、と書かれていたので、試してみる。
Kaniko キャッシュの使用 | Cloud Build | Google Cloud GitHub - GoogleContainerTools/kaniko: Build Container Images In Kubernetes
kaniko っていうのはすごいざっくりと理解したのは Docker なしに k8s 上で Docker イメージをビルドできるよ、というもの。 で、そのとき対象の Dockerfile に関して、レイヤーごとに全部キャッシュを取ってくれるので、差分があった部分以降をビルドするだけになり docker build よりも高速化できる、という話っぽい。通常 docker build するときも、レイヤーごとの情報を持っていて、変わった部分以降をビルドすることができる。できるが、そのキャッシュのバックエンドとか、マルチステージにしたときのビルドステップの進め方とかをいい感じにしたっていう話なんじゃあないかなあ。(それ以上の理解はできていない)
さて、ビルドする対象となる Dockerfile はこういう具合。 マルチステージで go で書かれたアプリケーションサーバのビルド、からのそのアプリケーションコンテナのイメージを作る。
FROM golang:1.12 as builder
ARG GITHUB_TOKEN
ARG VERSION
ENV GO111MODULE=on
WORKDIR /go/src/github.com/foo/bar
COPY go.mod go.sum ./
RUN echo "machine github.com login ${GITHUB_TOKEN}" > ~/.netrc
RUN go mod download
COPY . ./
RUN CGO_ENABLED=0 GOOS=linux go install -v -ldflags "-X main.version=${VERSION}" github.com/foo/bar/cmd/server
FROM alpine:latest
RUN apk add --update --no-cache ca-certificates tzdata
COPY --from=builder /go/bin/server /bin/server
CMD ["/bin/server"]
それでもとの cloudbuild.yaml はこうなっている。 変数をバケツリレーしていく。
steps:
- name: gcr.io/cloud-builders/docker
args:
- build
- -t
- $_IMAGE
- --build-arg
- GITHUB_TOKEN=$_GITHUB_TOKEN
- --build-arg
- VERSION=$_VERSION
- -f
- $_DOCKERFILE_PATH
- .
images:
- $_IMAGE
で、これを kaniko に対応させるとこうなる。
steps:
- name: 'gcr.io/kaniko-project/executor:latest'
args:
- --destination=$_IMAGE
- --dockerfile=$_DOCKERFILE_PATH
- --cache=true
- --cache-ttl=6h
- --build-arg=GITHUB_TOKEN=$_GITHUB_TOKEN
- --build-arg=VERSION=$_VERSION
その他 GCP 設定や Dockerfile などなどはそのままで実行することはできた。
できたが結果はちょっと微妙で、めっちゃ早くなったかというとそうでもない。。。
ビルド時、主に時間がかかるのが go mod download と go install (実際は go build が時間かかる)のふたつで、アプリケーションのコードを変えただけの場合は go mod のキャッシュが効いてくれるが、レイヤーを持ってくる(?)のにやっぱり時間がかかるので、プラスマイナスみて、ややマイナス、みたいな状態。。
具体的な時間としては kaniko を使う前の元々ので 5 分くらい。 kaniko してフルキャッシュ状態(再実行しただけ)で 1-2 分。 go mod のキャッシュが効いてて 2-4 分くらい。キャッシュがまったくなし = go.mod が変更された場合で 5-7 分くらい。ちゃんと測ってなくて数回ためした程度で、実はもっと早いかもしれない。そこは試す余地がある。
あとは単純にお財布で戦って、マシンサイズを上げて強い子にすると早くなるんじゃないかなあ~。
そもそも、コンテナイメージの作成を早くするのが目的なら CI 上でクロスコンパイルしたバイナリを Docker にいれちゃうのがよさそう。 というか CI 上でも golang:1.12 なイメージでビルドするならば別にクロスコンパイルでもないのか。
マルチステージな Dockerfile でアプリケーションのビルドもまるっと!と Cloud Build でビルドすると、余計なゴミが入りにくい、という良い点はありそう。
とはいえ、ワークスペースをアタッチしない、かつ go mod だけ共有してビルドしちゃえば Cloud Build でやるのとほぼ同じ環境になりそうな気がする。そうしてできたバイナリをコンテナ内に COPY して、コンテナレジストリに登録しちゃえばいいんじゃないかな。