Dockerの画像サイズを縮小したい場合 、Dockerイメージを構築する際に標準のベストプラクティスを使用する必要があります。
このブログでは、最小で最小のDockerイメージを作成するためにすばやく実装できるさまざまな最適化手法について説明しています。 。また、Dockerイメージ最適化に最適なツールのいくつかについても見ていきます。 。
コンテナーエンジンとしてのDockerを使用すると、コードの一部を簡単に取得してコンテナー内で実行できます。これにより、エンジニアはすべてのコード依存関係とファイルを1つの場所に収集し、どこでも、非常に迅速かつ簡単に実行できます。
「どこでも実行」イメージの全体的な概念は、Dockerfileと呼ばれる単純な構成ファイルから始まります。まず、コードの依存関係、コマンド、ベースイメージの詳細など、すべてのビルド手順をDockerfileに追加します。
Dockerイメージの最適化の必要性
Dockerのビルドプロセスは簡単ですが、多くの組織は肥大化したDockerイメージをビルドするという間違いを犯しています。 コンテナイメージを最適化せずに。
通常のソフトウェア開発では、各サービスに複数のバージョン/リリースがあり、各バージョンにはより多くの依存関係、コマンド、および構成が必要です。これにより、Dockerイメージのビルドに課題が生じます。同じコードをコンテナーとして出荷するには、ビルドに多くの時間とリソースが必要です。
最初のアプリケーションイメージが350MBで始まり、時間の経過とともに1.5GBを超える場合がありました。
また、不要なライブラリをインストールすることで、攻撃対象領域を増やすことで潜在的なセキュリティリスクの可能性を高めます。
したがって、DevOpsエンジニアはDockerイメージを最適化する必要があります アプリケーションのビルドまたは将来のリリース後にDockerイメージが肥大化しないようにするため。実稼働環境だけでなく、CI / CDプロセスのすべての段階で、Dockerイメージを最適化する必要があります。
また、Kubernetesなどのコンテナオーケストレーションツールでは、イメージの転送とデプロイにかかる時間を短縮するために、サイズの小さいイメージを用意するのが最適です。 。
Dockerイメージサイズを縮小する方法
一般的なアプリケーションのコンテナイメージを取得すると、ベースイメージ依存関係/ファイル/構成が含まれます 、そしてくだらない (不要なソフトウェア)。
つまり、コンテナイメージ内でこれらのリソースをどれだけ効率的に管理できるかということです。
さまざまなDockerイメージを最適化するための確立された方法を見てみましょう。 さらに、Dockerイメージの最適化をリアルタイムで理解するための実用的な例を示しました。
記事に記載されている例を使用するか、既存のDockerfileで最適化手法を試してください。
Dockerイメージの最適化を実現する方法は次のとおりです。
- ディストロレス/最小限のベース画像の使用
- 多段階ビルド
- レイヤー数の最小化
- キャッシングについて
- Dockerignoreの使用
- アプリケーションデータを他の場所に保持する
Dockerエクササイズファイル: この記事で使用されているすべてのアプリケーションコード、Dockerfile、および構成は、このGithubリポジトリでホストされています。クローンを作成して、チュートリアルに従うことができます。
方法1:最小限のベース画像を使用する
最初の焦点は、最小限のOSフットプリントで適切なベースイメージを選択することです。
そのような例の1つは、高山のベース画像です。高山の画像は最小5.59MBにすることができます 。小さいだけではありません。非常に安全です。
alpine latest c059bfaa849c 5.59MB
Nginxalpineベースイメージはわずか22MBです。
デフォルトでは、コンテナをアタッチしてデバッグするのに役立つshシェルが付属しています。
ディストロレス画像を使用して、ベース画像のサイズをさらに小さくすることができます。これは、オペレーティングシステムの簡略版です。 Distrolessベースイメージは、java、nodejs、python、Rustなどで使用できます。
Distroless画像は非常に最小限であるため、シェルも含まれていません 。では、どのようにアプリケーションをデバッグするのでしょうか。デバッグ用のbusyboxに付属しているのと同じイメージのデバッグバージョンがあります。
また、ほとんどのディストリビューションには、最小限のベースイメージが含まれています。
注: プロジェクト環境で公開されているベースイメージを直接使用することはできません。ベースイメージを使用するには、エンタープライズセキュリティチームから承認を得る必要があります。一部の組織では、セキュリティチーム自体が、テストとセキュリティスキャンの後にベースイメージを毎月公開しています。これらの画像は、共通の組織のDockerプライベートリポジトリで利用できます。
方法2:Dockerマルチステージビルドを使用する
多段階ビルドパターン は、アプリケーションコードのビルドとパッケージ化にさまざまなDockerfileを使用するビルダーパターンの概念から発展したものです。このパターンは画像サイズの縮小に役立ちますが、パイプラインの構築に関してはほとんどオーバーヘッドがかかりません。
多段階ビルドでは、ビルダーパターンと同様の利点があります。 中間画像を使用します (ビルドステージ)このアプローチでコードをコンパイルし、依存関係をインストールし、ファイルをパッケージ化します。この背後にある考え方は、不要なレイヤーを排除することです。 画像内。
その後、アプリケーションの実行に必要なアプリファイルのみが、必要なライブラリのみを含む別のイメージにコピーされます。つまり、アプリケーションの実行に必要な軽量化が行われます。
簡単なNodejsアプリケーションを作成し、そのDockerfileを最適化する実際の例を参考にして、これが実際に動作する様子を見てみましょう。
まず、コードを作成しましょう。次のフォルダ構造になります。
├── Dockerfile1
├── Dockerfile2
├── env
├── index.js
└── package.json
以下をindex.js
として保存します 。
const dotenv=require('dotenv');
dotenv.config({ path: './env' });
dotenv.config();
const express=require("express");
const app=express();
app.get('/',(req,res)=>{
res.send(`Learning to Optimize Docker Images with DevOpsCube!`);
});
app.listen(process.env.PORT,(err)=>{
if(err){
console.log(`Error: ${err.message}`);
}else{
console.log(`Listening on port ${process.env.PORT}`);
}
}
)
以下をpackage.json
として保存します 。
{
"name": "nodejs",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"dotenv": "^10.0.0",
"express": "^4.17.2"
}
}
次のポート変数をenv
という名前のファイルに保存します 。
PORT=8080
単純なDockerfile
このアプリケーションの場合はこれが必要です– Dockerfile1
として保存します 。
FROM node:16
COPY . .
RUN npm installEXPOSE 3000
CMD [ "node", "index.js" ]
構築して必要な収納スペースを見てみましょう。
docker build -t devopscube/node-app:1.0 --no-cache -f Dockerfile1 .
ビルドが完了した後。 –
を使用してサイズを確認しましょうdocker image ls
これが私たちが得るものです。
devopscube/node-app 1.0 b15397d01cca 22 seconds ago 910MB
したがって、サイズは910MBs
。
それでは、このメソッドを使用して多段階ビルドを作成しましょう。
node:16
を使用します ベースイメージとして、つまり、すべての依存関係とモジュールのインストールのイメージ その後、コンテンツを最小限の軽量の「alpine
」に移動します ‘ベースの画像。 「alpine
「画像には最小限のユーティリティしか含まれていないため、非常に軽量です。
これは、Dockerマルチステージビルドの図解です。
また、単一のDockerfile
、ベースイメージが異なる複数のステージを持つことができます。たとえば、ビルド、テスト、静的分析、パッケージ化のさまざまな段階を設定できます さまざまなベースイメージを使用します。
新しいDockerfileがどのようになるか見てみましょう。必要なファイルをベースイメージからメインイメージにコピーしているだけです。
以下をDockerfile2
として保存します 。
FROM node:16 as build
WORKDIR /app
COPY package.json index.js env ./
RUN npm install
FROM node:alpine as main
COPY --from=build /app /
EXPOSE 8080
CMD ["index.js"]
構築して必要な収納スペースを見てみましょう。
docker build -t devopscube/node-app:2.0 --no-cache -f Dockerfile2 .
ビルドが完了した後。
を使用してサイズを確認しましょうdocker image ls
これが私たちが得るものです。
devopscube/node-app 2.0 fa6ae75da252 32 seconds ago 171MB
したがって、新しい縮小画像サイズは171MBです すべての依存関係がある画像と比較して。
これは80%以上の最適化です!
ただし、ビルド段階で使用したものと同じベースイメージを使用した場合、大きな違いは見られません。
ディストロレス画像を使用して画像サイズをさらに小さくすることができます 。これが同じDockerfile
アルパインの代わりにgooglenodeJSディストロレスイメージを使用するマルチステージビルドステップを使用します。
FROM node:16 as build
WORKDIR /app
COPY package.json index.js env ./
RUN npm install
FROM gcr.io/distroless/nodejs
COPY --from=build /app /
EXPOSE 3000
CMD ["index.js"]
上記のDockerfileをビルドすると、イメージは 118MB になります 、
devopscube/distroless-node 1.0 302990bc5e76 118MB
方法3:レイヤーの数を最小限に抑える
Dockerイメージは次のように機能します–各RUN, COPY, FROM
Dockerfile命令は新しいレイヤーを追加し、各レイヤーはビルド実行時間を増やし、イメージのストレージ要件を増やします。
実用的な例を使用して、これが実際に動作する様子を見てみましょう。更新およびアップグレードされたライブラリと、vim、net-tools、dnsutilsなどの必要なパッケージがインストールされたubuntuイメージを作成しましょう。
Dockerfile
これを実現するには、次のようになります–これをDockerfile3
として保存します 。
FROM ubuntu:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y
RUN apt-get upgrade -y
RUN apt-get install vim -y
RUN apt-get install net-tools -y
RUN apt-get install dnsutils -y
このイメージのビルド時間も調べたいと思います。
Dockerデーモンには、Dockerfileにかかっている合計実行時間を表示する機能が組み込まれています。
この機能を有効にするには、次の手順を実行します–
-
daemon.json
を作成します/etc/docker/
に次の内容のファイル
{
"experimental": true
}
2.次のコマンドを実行して、機能を有効にします。
export DOCKER_BUILDKIT=1
ビルドして、ストレージとビルド時間を確認しましょう。
time docker build -t devopscube/optimize:3.0 --no-cache -f Dockerfile3 .
ターミナルに実行時間が表示されます。
time docker build -t devopscube/optimize:3.0 --no-cache -f Dockerfile3 .
[+] Building 117.1s (10/10) FINISHED
=> [internal] load build definition from Dockerfile
.
.
.
.
=> => writing image sha256:9601bcac010062c656dacacbc7c554b8ba552c7174f32fdcbd24ff9c7482a805 0.0s
=> => naming to docker.io/devopscube/optimize:3.0 0.0s
real 1m57.219s
user 0m1.062s
sys 0m0.911s
ビルドが完了した後–実行時間は117.1秒になります 。
を使用してサイズを確認しましょう
docker image ls
これが私たちが得るものです。
devopscube/optimize 3.0 9601bcac0100 About a minute ago 227MB
したがって、サイズは227MBです 。
RUNコマンドを1つのレイヤーにまとめて、Dockerfile4として保存しましょう。
FROM ubuntu:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y && apt-get upgrade -y && apt-get install --no-install-recommends vim net-tools dnsutils -y
上記のRUNコマンドでは、--no-install-recommends
を使用しました。 推奨パッケージを無効にするフラグ。 install
を使用する場合は常にお勧めします Dockerfiles
で
構築に必要なストレージと構築時間を見てみましょう。
time docker build -t devopscube/optimize:4.0 --no-cache -f Dockerfile4 .
ターミナルに実行時間が表示されます。
time docker build -t devopscube/optimize:0.4 --no-cache -f Dockerfile4 .
[+] Building 91.7s (6/6) FINISHED
=> [internal] load build definition from Dockerfile2 0.4s
.
.
.
=> => naming to docker.io/devopscube/optimize:4.0 0.0s
real 1m31.874s
user 0m0.884s
sys 0m0.679s
ビルドが完了した後–実行時間は91.7秒になります。
を使用してサイズを確認しましょう
docker image ls
これが私たちが得るものです。
devopscube/optimize 4.0 37d746b976e3 42 seconds ago 216MB
したがって、サイズは216MBです。
この最適化手法を使用すると、実行時間が117.1秒から91.7秒に短縮され、ストレージサイズが227MBから216MBに短縮されました。
方法4:キャッシングを理解する
多くの場合、コードを少し変更して、同じイメージを何度も再構築する必要があります。
Dockerは、このような場合に、ビルドの各レイヤーのキャッシュを保存することで役立ちます。将来的に役立つ可能性があることを期待しています。
この概念のため、依存関係とパッケージをインストールするために使用される行を、COPYコマンドの前にDockerfile内に追加することをお勧めします。
この背後にある理由は、dockerが必要な依存関係を使用してイメージをキャッシュできるためです。このキャッシュは、コードが変更されたときに次のビルドで使用できます。
たとえば、次の2つのDockerfileを見てみましょう。
Dockerfile5
FROM ubuntu:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y
RUN apt-get upgrade -y
RUN apt-get install vim -y
RUN apt-get install net-tools -y
RUN apt-get install dnsutils -y
COPY . .
Dockerfile6
FROM ubuntu:latest
COPY . .
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y
RUN apt-get upgrade -y
RUN apt-get install vim -y
RUN apt-get install net-tools -y
RUN apt-get install dnsutils -y
Dockerは、キャッシュ機能をより有効に使用できることができます。 COPYコマンドの配置が優れているため、Dockerfile5よりもDockerfile6を使用します。
方法5:Dockerignoreを使用する
原則として、必要なファイルのみをDockerイメージにコピーする必要があります。
.dockerignore
で構成されている場合、Dockerは作業ディレクトリに存在するファイルを無視できます。 ファイル。
Dockerイメージを最適化するときは、この機能に留意する必要があります。
方法6:アプリケーションデータを他の場所に保持する
アプリケーションデータを画像に保存すると、画像のサイズが不必要に大きくなります。
コンテナランタイムのボリューム機能を使用して、イメージをデータから分離しておくことを強くお勧めします。
Docker画像最適化ツール
以下は、最適化に役立つオープンソースツールの一部です
- ダイビング :これは、DockerおよびOCIコンテナーイメージ内のレイヤーを検出するのに役立つイメージエクスプローラーツールです。ダイブを使用すると、Dockerイメージを最適化する方法を見つけることができます。詳細については、DiveGithubリポジトリを確認してください。
- Docker Slim: Dockerイメージのセキュリティとサイズを最適化するのに役立ちます。詳細については、DockerSlimGithubリポジトリを確認してください。
このリストにツールを追加していきます。