BazelでDockerイメージのビルドとレジストリへのプッシュをする


前回までのエントリに引き続き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でベースイメージを参照できる。

1
2
3
4
5
6
7
8
# 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を参照する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 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イメージがビルドできる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(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でクロスコンパイルオプションを追加している。

レジストリにプッシュする

レジストリへのプッシュは次のように定義する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 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のタスクを実行するとレジストリにプッシュされたことが確認できる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
(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

まとめ

コード