BazelでDockerイメージのビルドとレジストリへのプッシュをする
前回までのエントリに引き続きBazelのビルドをまとめていく。
- GoとKotlinのマルチプロジェクトをBazelでビルドする - 平日インプット週末アウトプットぶろぐ
- BazelでGoプロジェクトのビルド。Gazelleのgo_repositoryで外部ライブラリの依存とBazelのgo_testでテスト。 - 平日インプット週末アウトプットぶろぐ
- gRPCサーバを含むGoプロジェクトをBazelでビルドする - 平日インプット週末アウトプットぶろぐ
今回はDockerイメージのビルドとビルドしたイメージをレジストリにプッシュする方法をまとめる。
Bazelの魅力
具体的な方法に入る前にBazelの魅力を整理したい。
Bazelの魅力はマルチ言語のビルドをサポートしている点が挙げられる。GoプロジェクトであればGazelleがWORKSPACEとBUILDファイルの中間コードを補完してくれる。そしてコンテナ周辺のビルドタスクも定義できる。
コンテナ周辺のビルドタスクのサポートがBazelの最大の魅力だと感じる。マイクロサービスを取り入れたプロジェクトであればサービスをどのような単位で管理するだろうか。シンプルに考えればサービス1つに対してレポジトリを作るだろう。そのレポジトリにDockerfileを置けばビルド時にイメージのビルドとレジストリへのプッシュも行える。しかしいくつかの課題がある。レポジトリ間の依存である。ユーティリティ系のレポジトリやgRPCのprotoファイルなどレポジトリを分けることによって運用の手数が増えることになる。
その点Bazelは1つのレポジトリにマルチプロジェクトを構成しgRPCのprotoファイルを集約させることができる。そしてDockerイメージのビルドとプッシュも行えるとあれば一連の運用フローがBazelで完遂することができるのだ。
Goプロジェクトをdistrolessをベースイメージとしてビルドする
Goプロジェクトをdistrolessイメージでビルドしたい。
これまではalpineイメージをベースにRUNでglibc, openssl , ca-certificateなどをインストールしていたのだがdistrolessはこれらがセットアップされた軽量なイメージである。また調べる限りではcontainer_imageなどのDockerビルドの機能にRUN相当のオプションが指定できない。
distrolessのイメージを使わずともBazelにはgo_image
が用意されていてDockerイメージのビルドが行えるのだがentrypointやcmdが指定できない。これらの理由からベースイメージを変える方法の知見を残す意味でもdistrolessを利用している。
WORKSPACEにベースイメージとするdistroless/baseを定義する。container_pullを定義することで以降のビルド定義でdistroless_base_image
でベースイメージを参照できる。
# WORKSPACE container_pull( name = "distroless_base_image", registry = "gcr.io", repository = "distroless/base", digest = "sha256:628939ac8bf3f49571d05c6c76b8688cb4a851af6c7088e599388259875bde20" )
pkg/public_go/BUILD.bazelのcontainer_imageでdistroless_base_image
を参照する。
# pkg/public_go/BUILD.bazel container_image( name = "public_go_image", base = "@distroless_base_image//image", files = [":public_go"], cmd = [ "/public_go", "-greet", "Awesome", ], )
filesに指定している:public_go
はgo_binaryで定義されたタスクでGoバイナリを生成する。これによりイメージ内にGoバイナリが配置される。あとはcmdにアプリケーションを起動するコマンドを定義する。ここらへんの定義方法はDockerとほぼ同じである。
public_go_imageのタスクを実行するとDockerイメージがビルドできる。
(bazel-multiprojects) $ bazel run --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64 //pkg/public_go:public_go_image INFO: Build options have changed, discarding analysis cache. INFO: Analysed target //pkg/public_go:public_go_image (97 packages loaded). INFO: Found 1 target... Target //pkg/public_go:public_go_image up-to-date: bazel-bin/pkg/public_go/public_go_image-layer.tar INFO: Elapsed time: 5.632s, Critical Path: 1.53s INFO: 6 processes: 6 darwin-sandbox. INFO: Build completed successfully, 11 total actions INFO: Build completed successfully, 11 total actions Loaded image ID: sha256:5626914bed1706245748cf5a090fcf4afed675592d7a17ec0116907b49102758 Tagging 5626914bed1706245748cf5a090fcf4afed675592d7a17ec0116907b49102758 as bazel/pkg/public_go:public_go_image
--platforms=@io_bazel_rules_go//go/toolchain:linux_amd64
でクロスコンパイルオプションを追加している。
レジストリにプッシュする
レジストリへのプッシュは次のように定義する。
# pkg/public_go/BUILD.bazel container_push( name = "push_public_go_image", image = ":public_go_image", format = "Docker", registry = "index.docker.io", repository = "soushin/bazel-multiprojects-go", tag = "latest", )
imageでcontainer_imageのpublic_go_image
を参照する。
push_public_go_imageのタスクを実行するとレジストリにプッシュされたことが確認できる。
(bazel-multiprojects) $ bazel run //pkg/public_go:push_public_go_image INFO: Build options have changed, discarding analysis cache. INFO: Analysed target //pkg/public_go:push_public_go_image (0 packages loaded). INFO: Found 1 target... Target //pkg/public_go:push_public_go_image up-to-date: bazel-bin/pkg/public_go/push_public_go_image INFO: Elapsed time: 3.065s, Critical Path: 2.48s INFO: 5 processes: 5 darwin-sandbox. INFO: Build completed successfully, 7 total actions INFO: Build completed successfully, 7 total actions index.docker.io/soushin/bazel-multiprojects-go:latest was published with digest: sha256:3025afe23e007e6d48b7b661a3a2726f7bc32940bbb56feeb6a14acded613dc1
まとめ
- BazelでDockerイメージのビルドとレジストリへのプッシュする方法をまとめた。
- Bazelの魅力でまとめたとおりContainer周辺のタスクもビルド定義できるのでコードのビルド、テスト、コンテナのビルド、プッシュまでワンストップで行えるBazelはすごい。
- マルチプロジェクトでBazelをつかっていきたいので効率的なCIとの連携を考えていきたい。