Dockerイメージを作成する際は、Dockerfileに記述された手順に沿って、1つのイメージを作成することになります。このとき作成されたイメージはレイヤ構造をしており、1つのレイヤは手順書中の1つ手順に該当します。そのため手順が1つ増える毎に新たなレイヤが追加されて いくことになります。また、このレイヤ構造はコンテナの実行時にもそのレイヤ構想は保持されています。
このレイヤ構造を実現するために、Dockerはユニオンファイルシステムを導入しています。ユニオンファイルシステムは、複数にファイルシステム上のディレクトリやファイルをレイヤとして重ね合わせ、それらを仮想的に1つのファイルシステムとして扱う技術です。
コンテナ上の読み書き可能なレイヤをコンテナレイヤと呼び、イメージを構成する読み込み専用レイヤをイメージレイヤと呼びます。
もともとイメージに格納されているイメージレイヤは、読み取り専用で変更を施すことはできません。コンテナ内でファイルの変更を行った際は、イメージレイヤではなくコンテナ起動時に追加されるコンテナレイヤに記録されます。
このようなレイヤ構造を採用している理由は、コンテナを軽量化するためです。ユニオンファイルシステムを採用することで、同一ノード上で動く複数のコンテナが、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 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
レイヤーの数も減っていることが確認できます。