LoginSignup
9
7

More than 5 years have passed since last update.

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

Posted at

はじめに

この記事では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

9
7
0

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
  3. You can use dark theme
What you can do with signing up
9
7