Dockerイメージのレイヤの考え方とイメージの軽量化について

Dockerイメージのレイヤの考え方

Dockerイメージを作成する際は、Dockerfileに記述された手順に沿って、1つのイメージを作成することになります。このとき作成されたイメージはレイヤ構造をしており、1つのレイヤは手順書中の1つ手順に該当します。そのため手順が1つ増える毎に新たなレイヤが追加されて いくことになります。また、このレイヤ構造はコンテナの実行時にもそのレイヤ構想は保持されています。

このレイヤ構造を実現するために、Dockerはユニオンファイルシステムを導入しています。ユニオンファイルシステムは、複数にファイルシステム上のディレクトリやファイルをレイヤとして重ね合わせ、それらを仮想的に1つのファイルシステムとして扱う技術です。

Dockerのイメージレイヤ

コンテナ上の読み書き可能なレイヤをコンテナレイヤと呼び、イメージを構成する読み込み専用レイヤをイメージレイヤと呼びます。

もともとイメージに格納されているイメージレイヤは、読み取り専用で変更を施すことはできません。コンテナ内でファイルの変更を行った際は、イメージレイヤではなくコンテナ起動時に追加されるコンテナレイヤに記録されます。

このようなレイヤ構造を採用している理由は、コンテナを軽量化するためです。ユニオンファイルシステムを採用することで、同一ノード上で動く複数のコンテナが、Docker イメージを構成するイメージレイヤを共有することを可能にし、トータルとしてのコンテナサイズを最小化しています。

コンテナへの変更

前回、SSH接続が可能で、かつ「HelloWorld.txt」というファイルを1つ追加したコンテナイメージを作成しました。今回はこのコンテナを使ってコンテナへの変更をしてみます。

前回作成したdocker_demoというコンテナにssh接続します。

❯ docker ps
CONTAINER ID        IMAGE               COMMAND               CREATED             STATUS              PORTS                  NAMES
df47382ab785        docker_demo         "/usr/sbin/sshd -D"   39 hours ago        Up 39 hours         0.0.0.0:2022->22/tcp   musing_grothendieck
❯ ssh root@localhost -p 2022
root@localhost's password:
Welcome to Ubuntu 16.04.6 LTS (GNU/Linux 4.9.184-linuxkit x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

HelloWorld.txtの中身を変更してみます。

root@df47382ab785:/# echo "New Hello World" > ./HelloWorld.txt
root@df47382ab785:/# cat HelloWorld.txt
New Hello World

これらの変更は、イメージレイヤにある「HelloWorld.txt」がコピーされて、コンテナレイヤに追加されます。変更はコンテナレイヤにある「HelloWorld.txt」に対して実行されます。このようにコピーして、そのコピーした内容を変更する仕組みをコピーオンライトといいます。

次に新規でファイルを作成してみます。

root@df47382ab785:/# touch NewHelloWorld.txt
root@df47382ab785:/# ls
HelloWorld.txt  NewHelloWorld.txt

新規で追加されたファイルは、コンテナレイヤにだけ追加されてイメージレイヤには追加されません。

イメージのレイヤ構造

Dockerイメージのレイヤ構造はdocker historyコマンドで確認できます。

❯ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker_demo         latest              cd226ad8e4c1        41 hours ago        204MB

❯ docker history docker_demo:latest
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
cd226ad8e4c1        41 hours ago        /bin/sh -c #(nop)  CMD ["/usr/sbin/sshd" "-D…   0B
4755ec608cb3        41 hours ago        /bin/sh -c #(nop)  EXPOSE 22                    0B
d7cab348ec47        41 hours ago        /bin/sh -c echo "export VISIBLE=now" >> /etc…   594B
70d1fb61ea15        41 hours ago        /bin/sh -c #(nop)  ENV NOTVISIBLE=in users p…   0B
e1cf8a1fcd1e        41 hours ago        /bin/sh -c sed 's@session\srequired\spam_log…   2.13kB
a6244475c536        41 hours ago        /bin/sh -c sed -i 's/PermitRootLogin prohibi…   2.53kB
c33dbd0f7637        41 hours ago        /bin/sh -c echo 'root:password' | chpasswd      775B
1bd59bbb5b8f        41 hours ago        /bin/sh -c mkdir /var/run/sshd                  0B
134e36c6f5cc        41 hours ago        /bin/sh -c apt-get update && apt-get install…   80.3MB
20e1ab53736a        41 hours ago        /bin/sh -c #(nop) COPY file:a8a5c9b4357f2f4e…   16B
c6a43cd4801e        3 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>           3 weeks ago         /bin/sh -c mkdir -p /run/systemd && echo 'do…   7B
<missing>           3 weeks ago         /bin/sh -c set -xe   && echo '#!/bin/sh' > /…   745B
<missing>           3 weeks ago         /bin/sh -c rm -rf /var/lib/apt/lists/*          0B
<missing>           3 weeks ago         /bin/sh -c #(nop) ADD file:f0b8eaa718bc3965b…   123MB

今回のイメージの場合、15つのレイヤで構成されています。

今回イメージを作成したDockerfileは以下の通りです。

a033be7b15a3        About an hour ago   /bin/sh -c #(nop)  CMD ["/usr/sbin/sshd" "-D…   0B
FROM ubuntu:16.04

# HelloWorld.txtのコピー
COPY ./HelloWorld.txt /

# opensshのインストール
RUN apt-get update && apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:password' | chpasswd

#ssh設定ファイルの書き換え
RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# SSH login fix. Otherwise user is kicked off after login
RUN sed 's@session\srequired\spam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile

# SSH接続用のポート設定
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]

Dockerfileに記述した命令の結果としてレイヤが作成されていることが分かります。

Dockerイメージの軽量化

Dockerイメージはできるだけ軽量化しておくと、次のようなメリットがあります。

  • Dockerイメージの作成と起動が早くなる
  • ホストのディスク容量を圧迫しない
  • イメージの配布が効率的に行える

ということで、今回のイメージを軽量化してみたいと思います。

軽量化前のイメージは以下の通りです。

❯ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker_demo         latest              cd226ad8e4c1        43 hours ago        204MB

この状態から軽量化を実施してみます。

パッケージのインストールを必要最小限にする

apt-get install時に「--no-install-recommands」オプションを使うと「推奨」パッケージをインストールせずに必要最低限の依存パッケージをインストールしてくれます。

キャッシュファイルの削除

apt-get install後はキャッシュを削除するようにします。

rm -rf /var/lib/apt/lists/*

レイヤの結合

Dockerfileの命令は&&で結合すればレイヤの数が減らせます。

以上を踏まえてDockerfileを次のように修正しました。

FROM ubuntu:16.04

# HelloWorld.txtのコピー
COPY ./HelloWorld.txt /

# opensshのインストール
RUN apt-get update && apt-get install -y --no-install-recommends openssh-server && rm -rf /var/lib/apt/lists/* && mkdir /var/run/sshd && echo 'root:password' | chpasswd && sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config && sed 's@session\srequired\spam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile

# SSH接続用のポート設定
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]

このDockerfileでイメージをdocker_demo2として作成してみます。

❯ docker build -t docker_demo2 .

変更前のイメージとのサイズを比較してみます。

❯ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker_demo2        latest              4b9be9995efd        4 seconds ago       135MB
docker_demo         latest              cd226ad8e4c1        43 hours ago        204MB

約30MBの削減ができました。

❯ docker history docker_demo2:latest
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
4b9be9995efd        2 hours ago         /bin/sh -c #(nop)  CMD ["/usr/sbin/sshd" "-D…   0B
3bc9927700e1        2 hours ago         /bin/sh -c #(nop)  EXPOSE 22                    0B
11db7ed43af5        2 hours ago         /bin/sh -c echo "export VISIBLE=now" >> /etc…   594B
fe251c0ed48e        2 hours ago         /bin/sh -c #(nop)  ENV NOTVISIBLE=in users p…   0B
f392e3f282c5        2 hours ago         /bin/sh -c apt-get update && apt-get install…   11.5MB
20e1ab53736a        46 hours ago        /bin/sh -c #(nop) COPY file:a8a5c9b4357f2f4e…   16B
c6a43cd4801e        3 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>           3 weeks ago         /bin/sh -c mkdir -p /run/systemd && echo 'do…   7B
<missing>           3 weeks ago         /bin/sh -c set -xe   && echo '#!/bin/sh' > /…   745B
<missing>           3 weeks ago         /bin/sh -c rm -rf /var/lib/apt/lists/*          0B
<missing>           3 weeks ago         /bin/sh -c #(nop) ADD file:f0b8eaa718bc3965b…   123MB

レイヤーの数も減っていることが確認できます。

関連記事