golangマルチステージビルド
イメージ構築にあたり最もチャレンジングなものの1つに、イメージ容量を小さくし続けるというものがあります。
Dockerfile 中の命令ごとにイメージにレイヤを追加するため、次のレイヤへと移る前に、不要なアーティファクトを忘れずクリーンアップし続ける必要があります。
本当に効率的な これまで Dockerfile を書くためには、以降のレイヤで必要になるアーティファクトのみを保持するために、シェル芸(shell tricks)を駆使する必要と、レイヤを維持するロジックを用いる必要がありました。
実際にとても一般的になったのは、開発用途に1つのファイル(アプリケーションの構築に必要な全てを含む)を用いることです。
そして、プロダクション向けに、そのアプリケーションを実行するために必要なものだけを含む、1つのレイヤへとスリムダウンすることです。こ
れが従来はずっと「構築パターン(builder pattern)」として参照されていました。しかし、2つの Dockerfile を持つのは、理想的ではありません。
Dockerfile.build と Dockerfile は、この構築パターンを遵守した例
# Dockerfile.build
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
COPY app.go .
RUN go get -d -v golang.org/x/net/html \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
この例では2つの RUN コマンドを Bash の && 演算子を使い1行にまとめ、イメージ中に追加レイヤが増えないのを防いでいます。ですが、これは失敗しがちでメンテナンスが大変です。これは、他のコマンドの連結が簡単ですが、場合によっては行の最後で \ 文字を使うのを忘れがちです。
# Dockerfile
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]
# build.sh
#!/bin/sh
echo Building alexellis2/href-counter:build
docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \
-t alexellis2/href-counter:build . -f Dockerfile.build
docker container create --name extract alexellis2/href-counter:build
docker container cp extract:/go/src/github.com/alexellis/href-counter/app ./app
docker container rm -f extract
echo Building alexellis2/href-counter:latest
docker build --no-cache -t alexellis2/href-counter:latest .
rm ./app
build.sh スクリプトを実行するにあたり、まずイメージを構築する必要があります。コンテナを作成し、そこからアーティファクトをコピーし、2つめに構築するイメージにコピーします。ローカルディスク上で両イメージがシステム上で場所を取るだけでなく、 app アーティファクトも同様に場所をとります。
マルチステージ・ビルドを使う
マルチステージ・ビルドでは、 Dockerfile の中で複数の FROM 命令文を使います。
各 FROM 命令は、異なるベースを使い、それを使って新しい構築ステージを始めます。あるステージから別のステージに対し、コピーするアーティファクトを選べるため、最終イメージで不要なすべてを残したままにできます。これがどのような挙動か確認するために、先ほどのセクションで使った Dockerfile をマルチステージ・ビルドに対応させましょう。
# Dockerfile
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
あなたが必要なのは1つの Dockerfile だけです。構築スクリプトを分ける必要はありません。 docker build を実行するだけです。
最終結果は、先ほどと同じ小さなプロダクション・イメージですが、複雑さは極めて減少しました。もうこれで中間イメージを作成する必要はありませんし、ローカルシステム上にアーティファクトを展開する必要も、もうありません。
2つめの FROM 命令は、 alpine:latest をベースとして新しい構築ステージを開始します。 COPY --from=0 行が、以前のステージで構築したアーティファクトを、この新しいイメージの中にコピーします。Go SDK や他の中間アーティファクトは残したままであり、最終イメージの中に保存しません。
デフォルトでは、ステージに名前がなく、ステージを 0 で始まる整数値で参照します。しかし、 FROM 命令の中で AS <名前> を追加することにより、ステージに対して名前を付けられます。先ほどの例を改善し、ステージに対して名前を付け、その名前を COPY 命令で使います。つまり、Dockerfile に記述する( FROM )命令の順番を入れ替えたとしても、 COPY 命令は壊れません。
FROM golang:1.7.3 AS builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
特定の構築ステージ後に停止
イメージの構築時、Dockerfile 含まれる各イメージを全て構築する必要はありません。特定のターゲット(target)構築ステージを指定できます。以下のコマンドは、以前の Dockerfile を使いますが、 builder という名前のステージで停止します。
$ docker build --target builder -t alexellis2/href-counter:latest .
いくつかの場合に、これが非常にパワフルになるでしょう。
特定の構築ステージをデバッグする用途
デバッグ用の目印として debug ステージを使うか、ツールを有効化することで、 production ステージをスリムにする用途
testing イメージを使い、アプリがテストデータを処理できるようにしますが、プロダクションが使う別のステージ構築時には実際のデータを使う用途
外部イメージを「ステージ」として使う
マルチステージ・ビルドを使う時、 Dockerfile でこれまで作成済みのステージからコピーするだけ、という制限はありません。
COPY --from 命令で別のイメージからコピーできるだけでなく、ローカルで利用可能なイメージとタグの利用や、Docker レジストリ上やタグ ID ですらも利用できます。それらからアーティファクトのコピーが必要であれば、Docker クライアントはイメージを取得します。構文は次の通りです。
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
以前のステージを新しいステージとして使う
以前のステージを残したまま、そこを``FROM`` 命令を使って参照できます。以下は例です。
FROM alpine:latest as builder
RUN apk --no-cache add build-base
FROM builder as build1
COPY source1.cpp source.cpp
RUN g++ -o /binary source.cpp
FROM builder as build2
COPY source2.cpp source.cpp
RUN g++ -o /binary source.cpp
この記事が気に入ったらサポートをしてみませんか?