本番環境用にDockerイメージを最適化する方法
著者はCode.orgを選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
実稼働環境では、 Docker を使用すると、コンテナー内でアプリケーションを簡単に作成、デプロイ、および実行できます。 コンテナーを使用すると、開発者はアプリケーションとそのすべてのコアの必需品と依存関係を1つのパッケージにまとめて、Dockerイメージに変換して複製することができます。 Dockerイメージは、Dockerfilesから構築されます。 Dockerfileは、イメージがどのように表示されるか、イメージが持つ基本オペレーティングシステム、およびイメージ内で実行されるコマンドを定義するファイルです。
大きなDockerイメージは、クラスターとクラウドプロバイダー間でイメージを構築して送信するのにかかる時間を長くする可能性があります。 たとえば、開発者の1人がビルドをトリガーするたびにプッシュするギガバイトサイズのイメージがある場合、ネットワーク上で作成するスループットはCI / CDプロセス中に加算され、アプリケーションの動作が遅くなり、最終的にリソースが消費されます。 。 このため、本番環境に適したDockerイメージには、必要最低限のものだけをインストールする必要があります。
Dockerイメージのサイズを小さくして、本番環境に最適化する方法はいくつかあります。 まず、これらのイメージは通常、アプリケーションを実行するためのビルドツールを必要としないため、追加する必要はまったくありません。 マルチステージビルドプロセスを使用すると、中間イメージを使用してコードをコンパイルおよびビルドし、依存関係をインストールし、すべてを可能な限り最小のサイズにパッケージ化してから、アプリケーションの最終バージョンをにコピーできます。ビルドツールなしの空のイメージ。 さらに、 AlpineLinuxのような小さなベースの画像を使用できます。 Alpineは、アプリケーションを実行するために必要なものだけを備えているため、本番環境に適したLinuxディストリビューションです。
このチュートリアルでは、いくつかの簡単な手順でDockerイメージを最適化し、Dockerイメージをより小さく、より速く、より本番環境に適したものにします。 サンプルGoAPI のイメージを、Ubuntuおよび言語固有のイメージから始めて、Alpineディストリビューションに移り、いくつかの異なるDockerコンテナーでビルドします。 また、マルチステージビルドを使用して、制作用にイメージを最適化します。 このチュートリアルの最終目標は、デフォルトのUbuntuイメージと最適化された対応するイメージの使用のサイズの違いを示し、マルチステージビルドの利点を示すことです。 このチュートリアルを読んだ後、これらの手法を独自のプロジェクトとCI/CDパイプラインに適用できるようになります。
注:このチュートリアルでは、例としてGoで記述されたAPIを使用します。 このシンプルなAPIにより、Dockerイメージを使用してGoマイクロサービスを最適化する方法を明確に理解できます。 このチュートリアルではGoAPIを使用していますが、このプロセスはほとんどすべてのプログラミング言語に適用できます。
前提条件
始める前に、次のものが必要になります。
-
root以外のユーザーアカウントを持つUbuntu18.04サーバー
sudo
特権。 ガイダンスについては、 Ubuntu18.04を使用した初期サーバーセットアップのチュートリアルに従ってください。 このチュートリアルはUbuntu18.04でテストされていますが、どのLinuxディストリビューションでも多くの手順を実行できます。 -
サーバーにDockerがインストールされています。 インストール手順については、 Ubuntu18.04にDockerをインストールして使用する方法のステップ1と2に従ってください。
ステップ1—サンプルGoAPIをダウンロードする
Dockerイメージを最適化する前に、Dockerイメージのビルド元となるサンプルAPIを最初にダウンロードする必要があります。 シンプルなGoAPIを使用すると、Dockerコンテナ内でアプリケーションを構築して実行するためのすべての主要な手順を紹介します。 このチュートリアルでは、 C++やJavaのようなコンパイル型言語であるため、Goを使用しますが、それらとは異なり、フットプリントが非常に小さくなっています。
サーバーで、サンプルのGoAPIのクローンを作成することから始めます。
- git clone https://github.com/do-community/mux-go-api.git
プロジェクトのクローンを作成すると、次の名前のディレクトリが作成されます。 mux-go-api
サーバー上で。 このディレクトリに移動します cd
:
- cd mux-go-api
これがプロジェクトのホームディレクトリになります。 このディレクトリからDockerイメージをビルドします。 内部には、Goで記述されたAPIのソースコードがあります。 api.go
ファイル。 このAPIは最小限であり、エンドポイントはわずかですが、このチュートリアルの目的で本番環境に対応したAPIをシミュレートするのに適しています。
サンプルのGoAPIをダウンロードしたので、ベースのUbuntu Dockerイメージを構築する準備が整いました。これに対して、後で最適化されたDockerイメージを比較できます。
ステップ2—ベースUbuntuイメージを構築する
最初のDockerイメージでは、ベースのUbuntuイメージから始めたときにどのように見えるかを確認すると便利です。 これにより、Ubuntuサーバーですでに実行しているソフトウェアと同様の環境にサンプルAPIがパッケージ化されます。 イメージ内に、アプリケーションの実行に必要なさまざまなパッケージとモジュールをインストールします。 ただし、このプロセスでは、ビルド時間とDockerfileのコードの可読性に影響を与えるかなり重いUbuntuイメージが作成されることがわかります。
まず、DockerにUbuntuイメージを作成し、Goをインストールして、サンプルAPIを実行するように指示するDockerfileを作成します。 クローンリポジトリのディレクトリにDockerfileを作成してください。 ホームディレクトリにクローンを作成した場合は、 $HOME/mux-go-api
.
と呼ばれる新しいファイルを作成します Dockerfile.ubuntu
. で開く nano
またはお気に入りのテキストエディタ:
- nano ~/mux-go-api/Dockerfile.ubuntu
このDockerfileでは、Ubuntuイメージを定義し、Golangをインストールします。 次に、必要な依存関係のインストールとバイナリのビルドに進みます。 次の内容をに追加します Dockerfile.ubuntu
:
FROM ubuntu:18.04
RUN apt-get update -y \
&& apt-get install -y git gcc make golang-1.10
ENV GOROOT /usr/lib/go-1.10
ENV PATH $GOROOT/bin:$PATH
ENV GOPATH /root/go
ENV APIPATH /root/go/src/api
WORKDIR $APIPATH
COPY . .
RUN \
go get -d -v \
&& go install -v \
&& go build
EXPOSE 3000
CMD ["./api"]
上から始めて、 FROM
コマンドは、イメージが持つ基本オペレーティングシステムを指定します。 そうして RUN
コマンドは、イメージの作成中にGo言語をインストールします。 ENV
Goコンパイラが正しく動作するために必要な特定の環境変数を設定します。 WORKDIR
コードをコピーするディレクトリを指定し、 COPY
コマンドは、ディレクトリからコードを取得します。 Dockerfile.ubuntu
であり、それを画像にコピーします。 最終 RUN
コマンドは、ソースコードがAPIをコンパイルして実行するために必要なGo依存関係をインストールします。
注: &&
一緒に文字列を作成する演算子 RUN
コマンドはDockerfilesを最適化する上で重要です。 RUN
コマンドは新しいレイヤーを作成し、新しいレイヤーごとに最終的な画像のサイズが大きくなります。
ファイルを保存して終了します。 今、あなたは実行することができます build
作成したDockerfileからDockerイメージを作成するコマンド:
- docker build -f Dockerfile.ubuntu -t ubuntu .
The build
コマンドはDockerfileからイメージを構築します。 The -f
フラグは、からビルドすることを指定します Dockerfile.ubuntu
ファイル、 -t
タグの略で、名前でタグ付けしていることを意味します ubuntu
. 最後のドットは、現在のコンテキストを表します。 Dockerfile.ubuntu
位置しています。
しばらく時間がかかりますので、お気軽にご利用ください。 ビルドが完了すると、APIを実行するためのUbuntuイメージの準備が整います。 ただし、画像の最終的なサイズは理想的ではない場合があります。 このAPIの数百MBを超えるものは、非常に大きな画像と見なされます。
次のコマンドを実行して、すべてのDockerイメージを一覧表示し、Ubuntuイメージのサイズを見つけます。
- docker images
作成した画像を示す出力が表示されます。
OutputREPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 61b2096f6871 33 seconds ago 636MB
. . .
出力で強調表示されているように、この画像のサイズは、基本的なGolangAPIの636MB であり、マシンごとにわずかに異なる場合があります。 複数のビルドにわたって、この大きなサイズは展開時間とネットワークスループットに大きく影響します。
このセクションでは、ステップ1でクローン化したAPIを実行するために必要なすべてのGoツールと依存関係を使用してUbuntuイメージを構築しました。 次のセクションでは、事前にビルドされた言語固有のDockerイメージを使用して、Dockerfileを簡素化し、ビルドプロセスを合理化します。
ステップ3—言語固有のベースイメージを構築する
ビルド済みイメージは、状況固有のツールを含めるためにユーザーが変更した通常のベースイメージです。 その後、ユーザーはこれらのイメージを Docker Hub イメージリポジトリにプッシュできるため、他のユーザーは独自のDockerファイルを作成する代わりに共有イメージを使用できます。 これは本番環境での一般的なプロセスであり、ほとんどすべてのユースケースでDockerHubにさまざまなビルド済みイメージを見つけることができます。 このステップでは、コンパイラと依存関係がすでにインストールされているGo固有のイメージを使用してサンプルAPIをビルドします。
アプリをビルドして実行するために必要なツールがすでに含まれているビルド済みのベースイメージを使用すると、ビルド時間を大幅に短縮できます。 必要なすべてのツールがプリインストールされているベースから開始しているため、これらをDockerfileに追加することをスキップして、見た目をすっきりさせ、最終的にビルド時間を短縮できます。
先に進み、別のDockerfileを作成して、名前を付けます Dockerfile.golang
. テキストエディタで開きます。
- nano ~/mux-go-api/Dockerfile.golang
このファイルには、Go固有の依存関係、ツール、およびコンパイラがすべてプリインストールされているため、前のファイルよりも大幅に簡潔になります。
ここで、次の行を追加します。
FROM golang:1.10
WORKDIR /go/src/api
COPY . .
RUN \
go get -d -v \
&& go install -v \
&& go build
EXPOSE 3000
CMD ["./api"]
上から始めて、あなたはそれを見つけるでしょう FROM
ステートメントは今です golang:1.10
. これは、Dockerが必要なすべてのGoツールがすでにインストールされているDockerHubからビルド済みのGoイメージをフェッチすることを意味します。
ここで、もう一度、Dockerイメージを次のコマンドでビルドします。
- docker build -f Dockerfile.golang -t golang .
次のコマンドを使用して、画像の最終的なサイズを確認します。
- docker images
これにより、次のような出力が得られます。
OutputREPOSITORY TAG IMAGE ID CREATED SIZE
golang latest eaee5f524da2 40 seconds ago 744MB
. . .
Dockerfile自体はより効率的で、ビルド時間は短くなりますが、実際には合計イメージサイズが大きくなります。 ビルド済みのGolangイメージは約744MBで、かなりの量です。
これは、Dockerイメージを構築するための推奨される方法です。 これは、コミュニティが指定された言語(この場合はGo)で使用する標準として承認したベースイメージを提供します。 ただし、イメージを本番環境で使用できるようにするには、実行中のアプリケーションが必要としない部分を切り取る必要があります。
ニーズがよくわからない場合は、これらの重い画像を使用しても問題ないことに注意してください。 使い捨てのコンテナとしてだけでなく、他のイメージを構築するためのベースとしてもお気軽にご利用ください。 ネットワークを介した画像の送信について考える必要がない開発またはテストの目的では、重い画像を使用することはまったく問題ありません。 ただし、展開を最適化する場合は、イメージをできるだけ小さくするために最善を尽くす必要があります。
言語固有のイメージをテストしたので、次のステップに進むことができます。このステップでは、軽量のAlpine Linuxディストリビューションをベースイメージとして使用して、Dockerイメージを軽量化します。
ステップ4—ベースアルパインイメージの構築
Dockerイメージを最適化する最も簡単な手順の1つは、小さいベースイメージを使用することです。 Alpine は、セキュリティとリソース効率のために設計された軽量Linuxディストリビューションです。 Alpine Dockerイメージは、 musllibcとBusyBoxを使用してコンパクトな状態を維持し、コンテナーでの実行に必要なのは8MB以下です。 サイズが小さいのは、バイナリパッケージが間引かれ分割されているためです。これにより、インストールするものをより細かく制御できるため、環境を可能な限り小さく効率的に保つことができます。
アルパインイメージを作成するプロセスは、ステップ2でUbuntuイメージを作成した方法と似ています。 まず、という新しいファイルを作成します Dockerfile.alpine
:
- nano ~/mux-go-api/Dockerfile.alpine
次に、このスニペットを追加します。
FROM alpine:3.8
RUN apk add --no-cache \
ca-certificates \
git \
gcc \
musl-dev \
openssl \
go
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
ENV APIPATH $GOPATH/src/api
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" "$APIPATH" && chmod -R 777 "$GOPATH"
WORKDIR $APIPATH
COPY . .
RUN \
go get -d -v \
&& go install -v \
&& go build
EXPOSE 3000
CMD ["./api"]
ここに追加します apk add
Alpineのパッケージマネージャーを使用してGoと必要なすべてのライブラリをインストールするコマンド。 Ubuntuイメージと同様に、環境変数も設定する必要があります。
先に進み、イメージを作成します。
- docker build -f Dockerfile.alpine -t alpine .
もう一度、画像サイズを確認します。
- docker images
次のような出力が表示されます。
OutputREPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest ee35a601158d 30 seconds ago 426MB
. . .
サイズは約426MBに縮小されました。
アルパインベース画像のサイズが小さいため、最終的な画像サイズは小さくなりましたが、さらに小さくするためにできることがいくつかあります。
次に、Go用に事前に作成されたAlpineイメージを使用してみてください。 これにより、Dockerfileが短くなり、最終的なイメージのサイズも小さくなります。 Go用にビルド済みのAlpineイメージは、ソースからコンパイルされたGoを使用してビルドされるため、そのフットプリントは大幅に小さくなります。
と呼ばれる新しいファイルを作成することから始めます Dockerfile.golang-alpine
:
- nano ~/mux-go-api/Dockerfile.golang-alpine
次の内容をファイルに追加します。
FROM golang:1.10-alpine3.8
RUN apk add --no-cache --update git
WORKDIR /go/src/api
COPY . .
RUN go get -d -v \
&& go install -v \
&& go build
EXPOSE 3000
CMD ["./api"]
間の唯一の違い Dockerfile.golang-alpine
と Dockerfile.alpine
は FROM
コマンドと最初の RUN
指図。 さて、 FROM
コマンドは golang
との画像 1.10-alpine3.8
タグ、および RUN
Gitをインストールするためのコマンドのみがあります。 あなたはGitが必要です go get
2番目に動作するコマンド RUN
下部のコマンド Dockerfile.golang-alpine
.
次のコマンドを使用してイメージをビルドします。
- docker build -f Dockerfile.golang-alpine -t golang-alpine .
画像のリストを取得します。
- docker images
次の出力が表示されます。
OutputREPOSITORY TAG IMAGE ID CREATED SIZE
golang-alpine latest 97103a8b912b 49 seconds ago 288MB
これで、画像サイズは約288MBになりました。
サイズを大幅に縮小できたとしても、画像を制作できるようにするためにできる最後のことが1つあります。 これは、マルチステージビルドと呼ばれます。 マルチステージビルドを使用することで、1つのイメージを使用してアプリケーションをビルドし、別のより軽いイメージを使用して、コンパイルされたアプリケーションを本番用にパッケージ化できます。このプロセスは次のステップで実行します。
ステップ5—マルチステージビルドでビルドツールを除外する
理想的には、本番環境で実行するイメージには、本番アプリケーションを実行するために冗長なビルドツールや依存関係がインストールされていない必要があります。 マルチステージビルドを使用して、これらを最終的なDockerイメージから削除できます。 これは、バイナリ、つまりコンパイルされたGoアプリケーションを中間コンテナーでビルドし、それを不要な依存関係のない空のコンテナーにコピーすることで機能します。
と呼ばれる別のファイルを作成することから始めます Dockerfile.multistage
:
- nano ~/mux-go-api/Dockerfile.multistage
ここで追加するものはおなじみです。 とまったく同じコードを追加することから始めます Dockerfile.golang-alpine
. ただし、今回は、最初の画像からバイナリをコピーする2番目の画像も追加します。
FROM golang:1.10-alpine3.8 AS multistage
RUN apk add --no-cache --update git
WORKDIR /go/src/api
COPY . .
RUN go get -d -v \
&& go install -v \
&& go build
##
FROM alpine:3.8
COPY /go/bin/api /go/bin/
EXPOSE 3000
CMD ["/go/bin/api"]
ファイルを保存して閉じます。 ここに2つあります FROM
コマンド。 最初のものはと同じです Dockerfile.golang-alpine
、追加の AS multistage
の中に FROM
指図。 これにより、次の名前が付けられます multistage
、次に、の下部で参照します Dockerfile.multistage
ファイル。 第二に FROM
コマンド、あなたはベースを取るでしょう alpine
画像と COPY
からコンパイルされたGoアプリケーションを介して multistage
それにイメージ。 このプロセスにより、最終的な画像のサイズがさらに縮小され、制作の準備が整います。
次のコマンドでビルドを実行します。
- docker build -f Dockerfile.multistage -t prod .
多段階ビルドを使用した後、今すぐイメージサイズを確認してください。
- docker images
1つだけではなく、2つの新しい画像が見つかります。
OutputREPOSITORY TAG IMAGE ID CREATED SIZE
prod latest 82fc005abc40 38 seconds ago 11.3MB
<none> <none> d7855c8f8280 38 seconds ago 294MB
. . .
The <none>
画像は multistage
で構築された画像 FROM golang:1.10-alpine3.8 AS multistage
指図。 これは、Goアプリケーションのビルドとコンパイルに使用される仲介者にすぎませんが、 prod
このコンテキストでのイメージは、コンパイルされたGoアプリケーションのみを含む最終的なイメージです。
最初の744MBから、画像サイズを約11.3MBに縮小しました。 このような小さなイメージを追跡し、ネットワーク経由で本番サーバーに送信することは、700MBを超えるイメージよりもはるかに簡単であり、長期的にはかなりのリソースを節約できます。
結論
このチュートリアルでは、コードをコンパイルおよびビルドするために、さまざまなベースDockerイメージと中間イメージを使用して本番環境用にDockerイメージを最適化しました。 このようにして、サンプルAPIを可能な限り最小のサイズにパッケージ化しました。 これらの手法を使用して、DockerアプリケーションおよびCI/CDパイプラインのビルドとデプロイの速度を向上させることができます。
Dockerを使用したアプリケーションの構築について詳しく知りたい場合は、Dockerを使用してNode.jsアプリケーションを構築する方法のチュートリアルをご覧ください。 コンテナの最適化に関するより概念的な情報については、Kubernetes用に最適化されたコンテナの構築を参照してください。