ククログ

株式会社クリアコード > ククログ > Fluent BitのGolang製のプラグインのDockerfileを作った話

Fluent BitのGolang製のプラグインのDockerfileを作った話

はじめに

Fluent BitはFluentdファミリーを構成するソフトウェアの一つです。 Fluent BitはGo Pluginプロキシが提供されており、Golangにて共有ライブラリを作成することにより、プラグインとして振る舞わせることのできるインターフェースが提供されています。 この機能については、fluent-bit-go-s3とFluent BitのGo Pluginプロキシの話でも解説しました。

最近はDockerイメージ上での問題が報告される事が多くなって来たので、Dockerfileを眺めている事が多くなりました。

Golang製のツールをビルドするDockerfileの書き方

Golangはコンパイルをしなければ動作しない静的型付きの言語です。 また、Golangの実行バイナリは使用したライブラリを静的にリンクしています。1 このことが、libcすら何もないLinuxのユーザーランドでのGolang製の実行バイナリを実行することを可能にしています。

Golangのコードをビルドするのに必要なツールチェインが揃っているDockerイメージがDockerHubで提供されています。

例えば、GolangのGOPATHを指定してビルドするDockerfileの例は次のようになります。

FROM golang:1.12.7-stretch
ENV GOOS=linux
ENV GOARCH=amd64
ENV GOPATH=/go
ADD . /go/src/path/to/githubrepo
WORKDIR /go/src/path/to/githubrepo
RUN go build .

Golang製の共有オブジェクトをDockerイメージに載せるには

Golangから作成した共有オブジェクトは基本的に共有ライブラリへの依存が少ないため、Golangのコンパイラで作成された実行ファイルや共有オブジェクトは更に別のDockerfileへコピーして載せることが容易です。 また、最近のDockerではmulti stage buildという機能が入っているため、Dockerfileを多段階に組み合わせることなく一つのDockerfileで多段階のDockerイメージのビルドができるようになっています。

では、ここまでの背景知識を元に、筆者が作成しているfluent-bit-go-lokiプラグインの実例2を出しつつ解説します。

Golangの共有ライブラリをビルドするには前述のGolangツールチェインがインストールされているイメージを使います。また、fluent-bit-go-lokiは共有ライブラリをCGOを経由して作成するため、それに必要な環境変数も有効にしておきます。

FROM golang:1.12.7-stretch AS build-env
ENV CGO_ENABLED=1
ENV GOOS=linux
ENV GOARCH=amd64
ENV GOPATH=/go
ADD . /go/src/github.com/cosmo0920/fluent-bit-go-loki
WORKDIR /go/src/github.com/cosmo0920/fluent-bit-go-loki
RUN go build -buildmode=c-shared -o out_loki.so .

out_loki.soの作成に必要なCGO_ENABLED=1, GOOS=linux, GOARCH=amd64, GOPATH=/goの設定が完了しました。 後の三行はout_loki.soをビルドするのに必要なディレクトリ構造を整える行と、ワーキングディレクトリの設定と、実際にビルドを行う行です。

ここで、FROM golang:1.12.7-stretch AS build-envとして、このout_loki.soをビルドするイメージをbuild-envという別名をつけている事に注意してください。この名前はビルドパイプラインの後で使用する事になります。

前段のパイプラインの成果物のout_loki.soをただコピーしてfluent/fluent-bit:1.2イメージ3から派生させています。

作成したout_loki.soのシステムの共有ライブラリの依存関係を見ると、

% ldd out_loki.so
	linux-vdso.so.1 (0x00007ffc58381000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fd36a51d000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd36a35c000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fd36bc46000)

となるため、libc相当の共有ライブラリがシステムに居れば十分に動作させることができそうです。 ここでは、fluent/fluent-bit:1.2がgcr.io/distroless/ccイメージを利用しており、このイメージ内に必要なライブラリが揃っていると見做すことで問題なさそうです。

FROM fluent/fluent-bit:1.2
COPY --from=build-env /go/src/github.com/cosmo0920/fluent-bit-go-loki/out_loki.so /usr/lib/x86_64-linux-gnu/
# ...

これらと、fluent-bit-go-lokiプラグインの実行に必要なエントリポイントについてはfluent-bit-go-lokiプラグインのDockerfileを参照してください。

まとめ

このようなイメージの作成の仕方をする事でGolangの実行バイナリや共有オブジェクトのみを載せたサイズが軽量となるイメージを作成することができます。 Golang製のツールをDockerイメージに載せるこの方法は始めはなんてまどろっこしい方法を取るんだ!と思っていたら、実行ファイルとその実行に必要な共有オブジェクトのみにしておける利点がある事がわかり腑に落ちました。 Dockerfileを書く事ができればDocker上にデプロイするのは非常に簡単なのでDocker上でもガンガンFluentdファミリーでログを収集してみてください。

  1. https://golang.org/doc/faq#Why_is_my_trivial_program_such_a_large_binary

  2. LokiはGrafanaの新しく開発されたデータソースです。

  3. fluent-bitのイメージはlibc, libgcc程度しか入っていない軽量イメージから派生しています。