Docker イメージを小さく作るテクニックって、いろいろありますよね。不要なファイルやディレクトリを削除したり、複数の RUN 命令をひとつにまとめたりなどなど。
ところが、ベースイメージに Alpine Linux を使う(FROM alpine とする)と、Docker イメージのサイズを 劇的に小さくできる ことがわかりました。
いままで、Docker イメージのサイズを小さくするために、ちまちまとやってきたことは、なんだったんだろうという感じです。まあ、それはそれで組み合わせて使いますが . . . なんとも . . . ねえ(笑)
Alpine Linux とは
- Alpine Linux は、セキュアで軽量な Linux ディストリビューション
- musl libc と BusyBox をベースに構成されている
- 組込み系に適した Linux ディストリビューション
- パッケージ管理は、APK と呼ばれる独自のシステムを使用
実際どれくらい小さくなるのか
Ruby を組み込んだ「Ubuntu ベースの Docker イメージ」と「Alpine ベースの Docker イメージ」で、そのサイズを比較してみました。
Ubuntu ベース、Alpine ベースそれぞれの Dockerfile は、次のとおりです。
FROM ubuntu:xenial-20160525
RUN apt-get update && apt-get install -y ruby && rm -rf /var/lib/apt/lists/*
FROM alpine:3.4
RUN apk --update add ruby && rm -rf /var/cache/apk/*
これを、次のコマンドで、それぞれビルドします。
docker build -f ubuntu-ruby.dockerfile -t ubuntu-ruby .
docker build -f alpine-ruby.dockerfile -t alpine-ruby .
ちゃんと動くか動作を確認しておきます。
docker run --rm ubuntu-ruby ruby -e 'puts "hello, world"'
docker run --rm alpine-ruby ruby -e 'puts "hello, world"'
それぞれ、hello, world と表示されれば、OK です(というか、表示されます)。
docker images コマンドで、サイズを確認すると . . .
$ docker images
REPOSITORY                         TAG                 IMAGE ID            CREATED             SIZE
alpine-ruby                        latest              1ac582156fbc        28 seconds ago      20.39 MB
ubuntu-ruby                        latest              b90dadde262c        50 seconds ago      156.9 MB
なんと!!! Alpine Linux ベースの Docker イメージは、20.39MB に!!!
Ubuntu ベースの Docker イメージは、156.9MB ですから、この差はデカイです!
この差はなにか
ベースイメージを変えただけで、なぜ、こんなにも差が出るのでしょうか。
まあ、答えは簡単ですよね。ベースイメージの大きさの差がそのまま結果に表れているということですね。
ベースイメージでよく使われていると思われる Ubuntu、Debian および CentOS と Alpine で、その大きさを比較してみました(2016/06/04 時点)。
REPOSITORY                         TAG                 IMAGE ID            CREATED             SIZE
alpine                             3.4                 f70c828098f5        32 hours ago        4.799 MB
centos                             7                   904d6c400333        34 hours ago        196.8 MB
ubuntu                             xenial-20160525     2fa927b5cdd3        7 days ago          122 MB
debian                             8.4                 1742affe03b5        11 days ago         125.1 MB
Alpine のサイズは、驚きの 4.799MB !!!
Ubuntu、Debian、CentOS がいずれも 100MB を超えているので、その差は本当に大きいですね。
ただ、あまりの小ささに少し不安になりますよね。本当に大丈夫なのかと。なので、いくつかの Docker イメージを作って、試してみました。本格的に使用しているわけではありませんが、まずまずちゃんと動いているようです。
そのいくつか試した中から、Nginx の例を載せておきます。参考にどうぞ。
Nginx サンプル
パッケージのインストールは、APK を使います。APK は、Alpine Linux のパッケージ管理システムです。APK は、同種の APT や Yum と同じような感じで使えます。次の Dockerfile のサンプルで、APK の使い方を確認してみましょう。
なお、利用できるパッケージの一覧は、Alpine Linux package index で確認できます。
Dockerfile
ここでは、Nginx をソースからコンパイルしてインストールする方法を紹介します。APK は、Nginx ソースの入手と、コンパイル&インストールに必要なパッケージのインストールで使用しました。
(APK で、Nginx をインストールすると、stable 版の v1.8.0 がインストールされるようです。より新しいバージョンをインストールしたい場合や、欲しいパッケージが用意されていない場合は、このソースからコンパイルしてインストールする方法が参考になるかと思います。)
FROM alpine:3.4
ENV NGINX_VERSION 1.11.1
RUN apk --update add pcre-dev openssl-dev \
  && apk add --virtual build-dependencies build-base curl \
  && curl -SLO http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz \
  && tar xzvf nginx-${NGINX_VERSION}.tar.gz \
  && cd nginx-${NGINX_VERSION} \
  && ./configure \
       --with-http_ssl_module \
       --with-http_gzip_static_module \
       --prefix=/usr/share/nginx \
       --sbin-path=/usr/local/sbin/nginx \
       --conf-path=/etc/nginx/conf/nginx.conf \
       --pid-path=/var/run/nginx.pid \
       --http-log-path=/var/log/nginx/access.log \
       --error-log-path=/var/log/nginx/error.log \
  && make \
  && make install \
  && ln -sf /dev/stdout /var/log/nginx/access.log \
  && ln -sf /dev/stderr /var/log/nginx/error.log \
  && cd / \
  && apk del build-dependencies \
  && rm -rf \
       nginx-${NGINX_VERSION} \
       nginx-${NGINX_VERSION}.tar.gz \
       /var/cache/apk/*
VOLUME ["/var/cache/nginx"]
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
※2016/05/22 変更: alpine:3.2 → alpine:3.3、NGINX_VERSION 1.9.7 → NGINX_VERSION 1.10.0
※2016/06/04 変更: alpine:3.3 → alpine:3.4、NGINX_VERSION 1.10.0 → NGINX_VERSION 1.11.1
APK コマンドの解説:
- 
apk add: 指定したパッケージをインストールします。- 
--updateオプション: パッケージリストを更新します。最初のapk add時に必要です。
- 
--virtualオプション: インストールするパッケージ群に名前をつけます。apk delで使用します。
 この例では、build-base と curl をひとつのグループとして扱い、build-dependencies と命名しました。
 
- 
- 
apk del: 指定したパッケージをアンインストールします。
 この例では、apk addの--virtualオプションで命名した build-dependencies を指定しました。これにより、build-dependencies に紐付いた build-base と curl をアンインストールします。
使い方の例
どこか適当なディレクトリで、上記 Dockerfile を alpine-nginx.dockerfile というファイル名で保存して、次のコマンンドを実行します。最後に、'hello, world' と表示されれば成功です。
docker build -f alpine-nginx.dockerfile -t nginx .
echo 'hello, world' > index.html
docker run -d -p 8080:80 -v $(pwd):/usr/share/nginx/html nginx
curl http://$(docker-machine ip default):8080
なお、Linux 環境の場合は、最後の curl コマンドは、curl http://localhost:8080 に読み替えてください。
まとめ
作成する Docker イメージのベースイメージに、Alpine Linux を指定すると、Docker イメージのサイズを劇的に小さくできることがわかりました。
これは、試していて実感したのですが、Docker イメージのサイズが小さいと、docker pull も docker push も docker build も超高速です。これは、嬉しかったですね。
ただし、どのような Docker イメージにも適用できるかと問われると、なんとなくですが難しいケースも多々ある気がします。とはいえ、試して見る価値は、十分にあると思います。