またビルド失敗しちゃった~…
問題文
問題文は以下の通りでした。
新入社員の障害太郎くんがGoの勉強をしようとしています。
どうやらDockerのマルチステージビルドを使って、Goのバイナリをコンテナ上で実行しようとしたらうまく立ち上がらないようです。
先輩のトラシュウさんに聞いたところ、「Dockerfileが間違っている」というメモを残して業務に戻ってしまいました。
先輩のトラシュウさんの代わりに原因を特定して修正してあげてください。
初期状態とゴール
初期状態
~/app/Dockerfile
を用いてdocker image build -t ictsc2020:0.1 .
したあとに、docker run -p 80:1323 [コンテナID]
をするとエラーが表示され、コンテナ上のバイナリが正常に実行できません。
終了状態
curl localhost
でWelcome to ICTSC2020!
が返ってくる。
また、問題の解決が永続化されている。
解説
前提:golangで書いたプログラムをgo build
すると、基本的には静的リンクになるがnet
パッケージを使用していた場合、自動的に動的リンクになる。(go1.4から)
この問題は、Dockerのマルチステージビルドをする際に、ビルドされたバイナリが動的リンクなせいでコンテナ間を移動させたあとに実行しようしてもうまく行かないという問題です。
開始時の~/app/Dockerfile
は以下のようになっています。
FROM golang:1.15.0 AS builder
ENV GO111MODULE=on
ENV GOPATH=
COPY ./server/main.go ./
RUN go mod init ictsc2020
RUN go build -o /app ./main.go
FROM alpine:3.12
COPY --from=builder /app .
EXPOSE 1323
ENTRYPOINT ["./app"]
解決方法はビルド時に静的リンクにしてあげるだけ。
具体的には、~/app/Dockerfile
の6行目のRUN go build -o /app ./main.go
をRUN CGO_ENABLED=0 go build -o /app ./main.go
に変更してから、コンテナをビルドし直して実行するだけです。
なので、変更後の~/app/Dockerfile
が下記のようになっていれば、docker image build -t ictsc2020:0.1 .
したあとに、docker run -p 80:1323 [コンテナID]
することでcurl localhost
でWelcome to ICTSC2020!
が返ってくるようになります。
FROM golang:1.15.0 AS builder
ENV GO111MODULE=on
ENV GOPATH=
COPY ./server/main.go ./
RUN go mod init ictsc2020
RUN CGO_ENABLED=0 go build -o /app ./main.go
FROM alpine:3.12
COPY --from=builder /app .
EXPOSE 1323
ENTRYPOINT ["./app"]
解説は以上です。
採点基準
- Dockerfileに正しい改善がなされている: 50%
- curl localhostをして、正しいレスポンスが返ってくる: 50%
講評
解答について
30チームが解答を提出してくれました。様々な解答や想定外解法があり楽しかったです。ありがとうございました。
この問題は、Go言語でビルドした際に特定のライブラリをimportすると動的リンクなバイナリが出力され、依存しているライブラリが実行環境に存在しないと実行が出来ない、という問題でした。
普段、ビルドして生成したバイナリを別環境に置いて実行することが少ない場合には気づきにくい問題だったかと思います。
この問題の解決方法としては、上記のようにビルド時にオプションを追加したり、環境変数を弄ることで静的リンクなバイナリにしてあげる方法が大多数のチームの解答で目立ちました。
しかし、1チームだけ「動的リンクのまま、ldd
コマンドを使用し依存しているライブラリを特定して、そのライブラリをbuilder
コンテナから実行用のコンテナにコピー・配置する」、という面白い想定外解法もありました。
この想定外解法では、今回の問題の本質のそれぞれのリンク方式によるバイナリの実行方法の違いというものを理解し、それに対する対応というのを出来ており想定解法とは違いましたが、解答を読んでいてとても面白かったです。
終わりに
普段意識されない人はあまり意識をしないプログラムのリンク方式ですが、実際の開発時に意外と詰まる可能性があるものだと思います。
特に、最近はコンテナやVMといった仮想化技術を用いて開発・運用を行うことが多くなっており、開発環境とビルド環境、実行環境などの違いがかなり起こりやすくなっていると思います。
今回の問題を通してプログラムのリンク方式を含めビルドの方法などに対して少し目を向けていただけたら嬉しいです。
解いてくれた・挑戦していただいた方々、ありがとうございました!