はじめに
Docker を使っていますが、イメージの大きさを削減しなくてはならない場面が出てきました。
今回その1にしたのは、後日その2がある予定なので。^^)/
C言語で書いたプログラム
今まで、Linuxマシンで動かしていたものをdocker image にするには、基本的に
- ソースをコピーする
- build する
- コンテナ起動時に呼び出して使えるようにする
というのをDockerfile の中に記述していました。単純ですがこれで動いていたので、今まで良しとしてきました。なのですが、よく考えると、これをコンテナとして実行するときには、実行ファイルと動的に読み出すライブラリだけあれば十分なはずです。コンパイラ、静的ライブラリ、ヘッダファイルなどは、build するときにだけ必要です。
ビルド環境をイメージに含めない
今回、調べたら、分かり易いが説明がいくつもありました。
↑これで使われているサンプルのプログラムは、こちらにあります。
これはGO言語での例ですが、分かり易く、そのまま動かすことができました。勉強になりました。感謝感激です。
以下は、私なりの理解です。私はC言語で動かしています。
- 一度docker image の中で動く実行ファイルを作る。これはコンテナとしてはrun させることはなく、実行ファイルを作るためだけにdocker build される
- コンテナとして run するために使うイメージを作る。ここでは、必要なbase image に、先の作った実行ファイルをコピーする。実行物をコピーするだけなので、build に必要な環境は含まれないので、image の大きさが小さくてすむ。
- 以前は、dokcer cp コマンドを使って実行ファイルをbuild コンテナから取り出していたようだが、昨今では、docker build 実行時にbuild container 内でコピーすることができる。
ということのようです。二つのコンテナを作っても良いのですが、これを一つのDockerfile で行うことができます。次のサイトは、build の高速化の説明ですが、前半にmulti-stage build をC言語を例に行っていたので、参考になりました。
実行例
用意するもの
種も仕掛けもありません。今回の実験では、ファイルは3つ。
├── Dockerfile.multi-stage
├── Dockerfile.single-stage
└── hello.c
C言語のhello world。心が和む。
#include <stdio.h>
int main()
{
printf("hello.\n");
return 0;
}
普通のDockerfile。コピーしてコンパイルする。build-essential をapt-get する。
FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install -y --no-install-recommends gcc build-essential
WORKDIR /root
COPY hello.c .
RUN gcc -o helloworld hello.c
CMD ["./helloworld"]
それから、今回の目玉のmulti-stage でbuild する Dockerfile。WORKDIR を経由して、実行ファイルだけコピーしているところがポイント。
# This is the first image:
FROM ubuntu:18.04 AS compile-image
RUN apt-get update
RUN apt-get install -y --no-install-recommends gcc build-essential
WORKDIR /root
COPY hello.c .
RUN gcc -o helloworld hello.c
# This is the second image.
FROM ubuntu:18.04 AS runtime-image
COPY --from=compile-image /root/helloworld .
CMD ["./helloworld"]
ビルドして大きさを比較してみる
これをいざビルドしてみよう。
$ docker build -f single-stage.Dockerfile -t c-hello .
$ docker build -f multi-stage.Dockerfile -t c-hello2 .
image の大きさを比べてみると、、、
$ docker
images
REPOSITORY TAG IMAGE ID CREATED
SIZE
c-hello2 latest c8470180dd3f 5
seconds ago 63.1MB
c-hello latest 0db6a6cf73e5 About a
minute ago 295MB
結構差がありますね。^^; もちろん、動きます。
$ docker run --rm c-hello2
hello.
まとめ
とりあえず、Cをソースからビルドするdocker image を作る時、image size を小さくする方法を一つ理解した。
- build するときは build-essentials を apt でインストールして使う。
- Dockerfileの途中で、build が終わった後であらためてbase image を定義し、実行用のイメージ作成には必要なバイナリだけコピーしてくる。
今後
実は python ではどうなるのじゃ、ということを調べています。同じように考え方で行けそうなのですが、動作確認できたらメモします。ふー。
2021/08/23 から日付が変わって0:48