Chapter 09

イメージの仕組み

ここだけは理解しよう

  • Docker イメージは複数のイメージが積み重なったもの
  • 生成された DockerImage は Read Only
  • Docker Container を作成することで、
    変更可能なレイヤーが新しく作成され、その上でプロセスを動かす
  • Dockerfile の書き方によってファイルの読み書きがボトルネックとなり、遅いイメージができる

diffによる(RW)Containerの確認

# コンテナ実行
docker run \
  --name ck-diff \
  alpine touch /tmp/hoge.txt

# diffの確認
docker container diff ck-diff

赤枠の箇所が alpine の image からコンテナ後に発生した diff(変更点) を示しています。
tmpディレクトhoge.txt を作成したことを示しています。
image1.png

Docker Imageの層の積み重ねをみてみましょう

# hostにはnodeがないことを確認します
node -v

# nodeの環境を作成して、コンテナに入ります
docker run \
  -it -p 3000:3000 \
  -v `pwd`:/data \
  node:12-alpine ash

# nodeのコンテナでnodeがあることを確認
node -v

ここからはコンテナで node を使うための準備です。
image2.png

# nodeのコンテナ内でのコマンド
cd /data
npm install -g express-generator
express myapp
cd myapp
npm install

# expressの実行確認ができたら、コンテナからでましょう
npm start
# host側で下記のコマンドを実行しましょう
cd  myapp/
ls

vi Dockerfile
docker build -t myapp:1 .
docker run -p 3000:3000 myapp:1

上記の Dockerfile の中身です。

Dockerfile
FROM node

WORKDIR /scripts

COPY . .

RUN npm install
RUN groupadd app
RUN useradd -g app -m app
RUN mv /root/.config /home/app/
RUN chown -R app:app /scripts /home/app/.config

USER app

EXPOSE 3000

CMD ["npm", "start"]

このような感じで express の web アプリが起動しています。
iamge3.png

# myappのサイズを確認します。
docker images myapp:1

image4.png

イメージは複数のイメージを積み重ねて最終的に 1 つのイメージになります。
また、積み重ねられたイメージのことを中間イメージと呼びます。
docker historyコマンドを利用することで Image の歴史をみることができます。

docker history myapp:1

image5.png

# ベースとなっているnodeを確認します。
docker images node

alpineのnodenode の比較ができていますが、
明らかに from で取得する node のサイズが大きいです。
てか、こんなに大きいのか…
image6.png

では、ベース Image を小さいのにしましょう。

Docker Hubからサイズなどを確認しましょう。
image7.png

Dockerfile
# slimに変更
FROM node:slim

WORKDIR /scripts

COPY . .

RUN npm install
RUN groupadd app
RUN useradd -g app -m app
RUN mv /root/.config /home/app/
RUN chown -R app:app /scripts /home/app/.config

USER app

EXPOSE 3000

CMD ["npm", "start"]

上記の変更を行い、再度コンテナを作りましょう。

vi Dockerfile
docker build -t myapp:2 .
docker run -p 3000:3000 myapp:2

では、作成した myapp の Image サイズを確認します。

docker images myapp

image8.png

次に history コマンドを実行してみましょう。
12 行目までのユーザが記載している部分に変更はないです。

docker history myapp:2

image9.png

最後にレイヤーを少し減らしましょう。
レイヤーは RUN ,COPY,ADDを指します。

Dockerfile
FROM node:slim

WORKDIR /scripts

COPY . .

# RUNを&&で記載する。
RUN npm install \
  && groupadd app \
  && useradd -g app -m app \
  && mv /root/.config /home/app/ \
  && chown -R app:app /scripts /home/app/.config

USER app

EXPOSE 3000

CMD ["npm", "start"]

上記の修正を行い、Image を作成する。

vi Dockerfile
docker build -t myapp:3 .

では、作成ができたので、サイズと history を確認する。

docker images myapp
docker history myapp:3

RUNのレイヤーが減っていることを確認できます。
レイヤーを減らすことで UnisonFileSystem への影響を減らすことができます。
image10.png

UnisonFileSystemとは

こちらは、理解が難しかったのでUnison FileSystemをそのまま引用します。

最後に、Dockerのファイルシステムについです。
Docker Container から Docker Image へファイルを読み込む際、気をつけないとオーバーヘッドが大きくなります。
Containerレイヤーに操作対象のパスが存在しない場合、Imageレイヤーにファイルが無いかの捜査を行います。
この捜査は1レイヤーごとに見ていくため、レイヤーが多くなればなるほどオーバーヘッドが大きくなっていきます。
また、Dockerがデフォルトで使用しているファイルシステムではCopy On Write方式でファイルの読み書きを行います。
ファイルの更新がかかる度に捜査を実行するため、ログのように書き込みの激しいパスはDataVolumeを使用してUnison FileSystemを回避すると良いでしょう。

Dockerfile の RUN で実行した何かしらをコンテナ側から参照しようとします。
コンテナレイヤーに操作対象パスがないとレイヤーを辿っていくため、
その際にオーバヘッドがかかるといった意味合いと認識しています。

対策として、
Image にファイルやデータを入れるのでなく DataVolume を利用する方がいいらしいです。