はじめに
この記事ではGoのプロジェクトに対してBazelを用いることでDockerのimageを作成する方法をハンズオン形式で紹介する.
前回の記事で、Bazelによってbuildできるようにしたプロジェクトを、ここでは再利用する。
このプロジェクトにはprotobufが含まれているが、それは今回の話では本質ではなく、protobufを使用しないプロジェクトにおいても以下の解説は同じである。
BazelによるDocker imageの作成
WORKSPACE
Bazelを用いるプロジェクトではトップディレクトリーにWORKSPACEというファイルがある。前回、このファイルにおいてGoのための設定を行ったが、さらに、Docker imageを作成するためのruleを読み込むため、以下の記述を追加する。
# download Docker rules
http_archive(
name = "io_bazel_rules_docker",
sha256 = "29d109605e0d6f9c892584f07275b8c9260803bf0c6fcb7de2623b2bedc910bd",
strip_prefix = "rules_docker-0.5.1",
urls = ["https://github.com/bazelbuild/rules_docker/archive/v0.5.1.tar.gz"],
)
# load Docker
load(
"@io_bazel_rules_docker//container:container.bzl",
"container_pull",
container_repositories = "repositories",
)
container_repositories()
# load Go image
load(
"@io_bazel_rules_docker//go:image.bzl",
_go_image_repos = "repositories",
)
_go_image_repos()
BUILD
次に greeter_server/BUILD.bazel
の最後に、以下の設定を加える。これがDocker imageを作成するための記述となる。
# Docker
load("@io_bazel_rules_docker//go:image.bzl", "go_image")
go_image(
name = "greeter_server_image",
embed = [":go_default_library"],
importpath = "google.golang.org/grpc/examples/helloworld/greeter_server",
goarch = "amd64",
goos = "linux",
pure = "on",
)
BazelによるDocker imageの作成
bazel
を実行することでDocker imageが作成される。
$ bazel build //greeter_server:greeter_server_image
Mac / Windowsでのcross-compile
Linuxでは、問題ないが、MacやWindowsでは次のようなエラーが出る。Docker imageはLinuxなので、buildではcross-compileを指定する必要がある。
ERROR: /private/var/tmp/_bazel_stn/56853cb50709ed59d05b544de7cfcee8/external/org_golang_google_grpc/internal/channelz/BUILD.bazel:3:1: GoCompile external/org_golang_google_grpc/internal/channelz/linux_amd64_pure_stripped/go_default_library%/google.golang.org/grpc/internal/channelz.a failed (Exit 1)
GoCompile: missing strict dependencies:
/private/var/tmp/_bazel_stn/56853cb50709ed59d05b544de7cfcee8/sandbox/darwin-sandbox/50/execroot/__main__/external/org_golang_google_grpc/internal/channelz/types_linux.go: import of "golang.org/x/sys/unix"
Known dependencies are:
google.golang.org/grpc/connectivity
google.golang.org/grpc/credentials
google.golang.org/grpc/grpclog
Check that imports in Go sources match importpath attributes in deps.
Target //greeter_server:greeter_server_image failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 37.508s, Critical Path: 16.71s
INFO: 48 processes: 48 darwin-sandbox.
FAILED: Build did NOT complete successfully
そこで、 --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64
を指定して再度buildする。
$ bazel build --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64 //greeter_server:greeter_server_image
今度は、無事、imageが作成された。
INFO: Build options have changed, discarding analysis cache.
INFO: Analysed target //greeter_server:greeter_server_image (1 packages loaded).
INFO: Found 1 target...
Target //greeter_server:greeter_server_image up-to-date:
bazel-bin/greeter_server/greeter_server_image-layer.tar
INFO: Elapsed time: 27.162s, Critical Path: 20.21s
INFO: 74 processes: 74 darwin-sandbox.
INFO: Build completed successfully, 79 total actions
ここまでのステップで、Dockerを使っていないことに気づいただろうか。この時点では、Docker daemonを起動しておく必要はなく、Dockerの入っていないシステムでもimageの作成はBazelだけでできる。
Dockerでの実行
作成されたimageは bazel-bin/greeter_server/greeter_server_image-layer.tar
とtar形式になっている。このimageを実際に実行して動作を確認してみる。
imageを読み込むには、
$ docker import bazel-bin/greeter_server/greeter_server_image-layer.tar イメージ名:tag
でもできるが、先程のbazel build
をbazel run
に変えて、
$ bazel run --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64 //greeter_server:greeter_server_image
とすればよい。 docker ps
によってbazel/greeter_server:greeter_server_image
というimageが読み込まれgreeter_server
が実行されていることが分かる。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4dfd4b02596b bazel/greeter_server:greeter_server_image "/app/greeter_server…" 5 seconds ago Up 4 seconds tender_turing
また、走らせる必要はなく、imageを登録するだけなら、コマンドの最後に -- --norun
をつければよい。
おまけ: portの話
Dockerのimageを作成して、実行するという点ではここまでだが、greeter_server
にbazel run //greeter_client
で接続を試みると失敗する。
$ bazel run //greeter_client
...
2018/10/14 08:37:14 could not greet: rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: Error while dialing dial tcp [::1]:50051: connect: connection refused"
そう、ポートが開いていない。そこで、
$ docker run -p 50051:50051 bazel/greeter_server:greeter_server_image
とすることで、接続ができるようになる。
$ bazel run //greeter_client
...
2018/10/14 08:38:53 Greeting: Hello world
まとめ
Bazelを用いて、GoのプロジェクトのDocker imageを作成することができた。
ここに書かれていないことなども調べたいときは、こちらを参考にしてください。
https://github.com/bazelbuild/rules_docker#go_image
また、ここまでの作業を行った結果をGithubにあげておきました。
https://github.com/stn/grpc-helloworld-bazel/tree/docker