scratch
およびこのビルドに基づく小さなhttpサーバーを利用して、結果を6.32kBに絞ることができました。
あなたがビデオを好むなら、ここに記事のためのYouTubeビデオがあります!
膨満した容器
コンテナは、ソフトウェアメンテナンスの課題に対処するための万能薬として宣伝されることがよくあります。また、コンテナが好きなので、実際にはコンテナの画像に出くわし、色々な問題を抱えています。一般的な問題は、コンテナのサイズです。一部の画像では、数ギガバイトに達します。
そこで、自分自身と他のみんなに挑戦し、できるだけコンパクトな画像を作成することにしました。
仕事
ルールは非常に単純です。
- コンテナは、ファイルの内容をhttp経由で選択したポートに提供する必要があります
- ボリュームのマウントは許可されていません(いわゆる「マレック病の法則」)
簡略化されたソリューション
ベースイメージのサイズを確認するには、node.jsを使用して単純なサーバーを作成します
index.js
。
const fs = require("fs"); const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'content-type': 'text/html' }) fs.createReadStream('index.html').pipe(res) }) server.listen(port, hostname, () => { console.log(`Server: http://0.0.0.0:8080/`); });
そして、ノードの公式ベースイメージを実行して、それからイメージを作成します。
FROM node:14 COPY . . CMD ["node", "index.js"]
これがかかった
943MB
!
縮小されたベースイメージ
皮膚のサイズを小さくするための最も単純で最も明白な戦術的アプローチの1つは、より細いベース皮膚を選ぶことです。ノードの公式ベース画像は、変異体に存在する
slim
(まだはDebianに基づいているが、より少ないプレインストール依存関係)と変異体
alpine
に基づいて アルパインのLinux。
使用
node:14-slim
および
node:14-alpine
塩基としては、画像のサイズを小さくすることが可能となる
167MB
と
116MB
それに応じ。
Dockerイメージは付加的であり、各レイヤーが次のレイヤーの上に構築されるため、node.jsソリューションをさらに削減するためにここで行うことはほとんどありません。
コンパイルされた言語
物事を次のレベルに引き上げるために、実行時の依存関係がはるかに少ないコンパイル言語に移行できます。いくつかのオプションがありますが、golangはWebサービスの作成によく使用されます 。
私は最も単純なファイルサーバーを作成しました
server.go
:
package main import ( "fmt" "log" "net/http" ) func main() { fileServer := http.FileServer(http.Dir("./")) http.Handle("/", fileServer) fmt.Printf("Starting server at port 8080\n") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal(err) } }
そして、公式のgolangベースイメージを使用して、コンテナーイメージに組み込みました。
FROM golang:1.14 COPY . . RUN go build -o server . CMD ["./server"]
どれがかかったのか…
818MB
。
ここに問題があります。ベースのgolangイメージには多くの依存関係がインストールされています。これらは、goプログラムをビルドするときに役立ちますが、プログラムの実行には必要ありません。
多段アセンブリ
Dockerには、マルチステージビルドと呼ばれる機能があります。この機能を使用すると 、必要なすべての依存関係を含む環境でコードを簡単にビルドし、結果の実行可能ファイルを別のイメージにコピーできます。
これはいくつかの理由で役立ちますが、最も明白なものの1つは画像のサイズです!次のようにdockerfileをリファクタリングします。
### ### FROM golang:1.14-alpine AS builder COPY . . RUN go build -o server . ### ### FROM alpine:3.12 COPY --from=builder /go/server ./server COPY index.html index.html CMD ["./server"]
結果の画像のサイズはすべて
13.2MB
です!
静的コンパイル+スクラッチ画像
13 MBはまったく悪くはありませんが、これをさらにタイトに見せるために、まだいくつかのトリックが残っています。 スクラッチ
と呼ばれるベースイメージがありますが 、これは明確に空であり、サイズはゼロです。内部 には何もないため、それに基づいて作成されたイメージには、必要なすべての依存関係が含まれている必要があります。 goサーバーに基づいてこれを可能にするには、コンパイル時にいくつかのフラグを追加して、必要なすべてのライブラリが実行可能ファイルに静的にリンクされるようにする必要があります。
scratch
### ### FROM golang:1.14 as builder COPY . . RUN go build \ -ldflags "-linkmode external -extldflags -static" \ -a server.go ### ### FROM scratch COPY --from=builder /go/server ./server COPY index.html index.html CMD ["./server"]
特に、
external
リンクモードを設定 し、フラグを
-static
外部リンカに渡し ます。
これらの2つの変更のおかげで、画像サイズを最大にすることが可能です
8.65MB
勝利の保証としてのASM!
Goのような言語で書かれた10MB未満のサイズの画像は、ほとんどすべての状況で明らかに小型化されています...しかし、さらに小さくすることもできます。ユーザー nemasuが アセンブラーで記述した本格的なhttpサーバーをGithubに投稿しました。それはassmttpdと呼ばれ ます。
コンテナ化するのに必要なのは、提供されたレシピを実行する前に、ベースのUbuntuイメージにいくつかのビルド依存関係をインストールすることだけでした
make release
。
### ### FROM ubuntu:18.04 as builder RUN apt update RUN apt install -y make yasm as31 nasm binutils COPY . . RUN make release ### ### FROM scratch COPY --from=builder /asmttpd /asmttpd COPY /web_root/index.html /web_root/index.html CMD ["/asmttpd", "/web_root", "8080"]
結果の実行可能ファイルは
asmttpd
、スクラッチイメージにコピーされ、コマンドラインから呼び出されます。結果の画像のサイズはわずか6.34kBです!