LoginSignup
8

More than 3 years have passed since last update.

posted at

GoのプロジェクトのDocker imageをDockerを使わずにBazelだけで作成する

はじめに

この記事ではGoのプロジェクトに対してBazelを用いることでDockerのimageを作成する方法をハンズオン形式で紹介する.

前回の記事で、Bazelによってbuildできるようにしたプロジェクトを、ここでは再利用する。
このプロジェクトにはprotobufが含まれているが、それは今回の話では本質ではなく、protobufを使用しないプロジェクトにおいても以下の解説は同じである。

BazelによるDocker imageの作成

WORKSPACE

Bazelを用いるプロジェクトではトップディレクトリーにWORKSPACEというファイルがある。前回、このファイルにおいてGoのための設定を行ったが、さらに、Docker imageを作成するためのruleを読み込むため、以下の記述を追加する。

WORKSPACE
# 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を作成するための記述となる。

greeter_server/BUILD.bazel
# 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 buildbazel 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_serverbazel 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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
8