dockerイメージとDockerfileのFROMを詳しく

<a href="https://ebios10.up.seesaa.net/image/2020-10-26_215138.png" target="_blank"><img border="0" alt="2020-10-26_215138.png" src="https://ebios10.up.seesaa.net/image/2020-10-26_215138-thumbnail2.png" width="539" height="313"></a>

#dockerイメージとDockerfileのFROMを詳しく
dockerのDockerfileについて軽く読み流していたが、きちんと理解するには以下のことを改めて問う必要があるように思った。dockerイメージとは何者で、Dockerfileとは何者で、そのDockerfileに記述されるFROMとは何者か。

#Dockerfileとは
<a href="https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html" target="_blank">Dockerfile のベスト・プラクティス Docker-docs-ja 19.03 ドキュメント</a>
<blockquote>Dockerfile はイメージを構築するために必要な全ての命令を、順番通りに記述したテキストファイルです。</blockquote>
知っている人にはdocker版のMakefileといえば通りが良いかもしれない。
dockerのイメージを構築するための手順を特定の書式と命令群に則り、順番通りに記述したものだ。要するにdockerイメージという環境の構築をテキストファイルに落とし込んだものと言う事になる。

#レイヤとは
<blockquote>Dockerfile の命令に相当する読み込み専用のレイヤによって、 Docker イメージは構成されます。それぞれのレイヤは直前のレイヤから変更した差分であり、これらのレイヤは積み重なっています。</blockquote>
唐突に出てきたレイヤとは何か。
<blockquote>FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py</blockquote>
これらの命令ごとに１つのレイヤを作成します、とある。
<blockquote>イメージを実行し、コンテナを生成すると、元のレイヤ上に新しい 書き込み可能なレイヤ（writable layer） （これが「コンテナ・レイヤ」です）を追加します。実行中のコンテナに対する全ての変更、たとえば新しいファイル書き込み、既存ファイルの編集、ファイルの削除などは、この書き込み可能なコンテナ・レイヤ内に記述されます。</blockquote>
上の例でいえば「ubuntu:18.04」レイヤに「COPY . /app」「RUN make /app」で新しいレイヤを追加、さらに「CMD python /app/app.py」でpython で/app/app.pyが実行されたイメージを作ると言う事になる。「ubuntu:18.04」レイヤに他のレイヤが積み重なったという表現はわかりやすいかもしれない。

#build とは
一言で言えば、指定したDockerfile に従ってdockerイメージを構築するコマンドだ。これも知っている人にはdocker版のmakeコマンドと言えば通りがいいかもしれない。
<blockquote>mkdir myproject && cd myproject
echo "hello" > hello
echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > Dockerfile
docker build -t helloapp:v1 .</blockquote>
上記の例でいえば、<a href="http://e-words.jp/w/BusyBox.html" target="_blank">BusyBox</a>を使ってをcatで「helloファイル」を表示するイメージを構築するDockerfileを吐き出し、そのDockerfileに従ってdockerイメージを構築している。

#Dockerfile のFROMとは
Dockerfile のFROMとは何か。
<blockquote>FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py</blockquote>
先ほどのこの例においてFROM は 「ubuntu:18.04 の Docker イメージからレイヤを作成」と解説されている。
<blockquote>mkdir myproject && cd myproject
echo "hello" > hello
echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > Dockerfile
docker build -t helloapp:v1 .</blockquote>
こちらの例ではFROM は <a href="https://hub.docker.com/_/busybox" target="_blank">busybox</a>が指定されている。もちろん、<a href="https://hub.docker.com/_/busybox" target="_blank">busyboxもdockerイメージ</a>があり、単にFROM busyboxと指定すれば、latest（最新）のイメージが指定される。

#FROM ubuntu:18.04の場合
FROM ubuntu:18.04の場合はどうか。FROM busyboxと「:」の指定が無い場合はlatest（最新）となるが、「:」を付けるとどうなるか。
<a href="https://docs.docker.jp/engine/reference/builder.html#from" target="_blank">FROMについて解説</a>をみよう。
<blockquote>FROM &lt;image&gt; [AS &lt;name&gt;]
または
FROM &lt;image&gt;[:&lt;tag&gt;] [AS &lt;name&gt;]
または
FROM &lt;image&gt;[@&lt;digest&gt;] [AS &lt;name&gt;]
オプションとして、新たなビルドステージに対しては名前をつけることができます。 これは FROM 命令の AS name により行います。 この名前は後続の FROM や COPY --from=<name|index> 命令において利用することができ、このビルドステージにおいてビルドされたイメージを参照します。
tag と digest の設定はオプションです。 これを省略した場合、デフォルトである latest タグが指定されたものとして扱われます。 tag の値に合致するものがなければ、エラーが返されます。</blockquote>
と言う事で、FROM ubuntu:18.04と指定した場合は、ubuntuの18.04タグのイメージが指定されることになる。
具体的に<a href="https://hub.docker.com/_/ubuntu" target="_blank">ubuntuのDocker Images</a>の<a href="https://github.com/tianon/docker-brew-ubuntu-core/blob/fc9c4ef6e3d4891577936f0b103331e79e1e8281/bionic/Dockerfile" target="_blank">18.04</a>を見ると、どの様にubuntu:18.04のdockerイメージが作られているのか、Dockerfileをみることができる。
<blockquote>FROM scratch
ADD ubuntu-bionic-core-cloudimg-amd64-root.tar.gz /
中略
RUN mkdir -p /run/systemd && echo 'docker' > /run/systemd/container
CMD ["/bin/bash"]</blockquote>
ベースとなるdockerイメージがないので、FROM scratchで始まっていることに注目してほしい。

#公式の推奨FROM
<a href="https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html" target="_blank">Dockerfile のベスト・プラクティス Docker-docs-ja 19.03 ドキュメント</a>の<a href="https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html#from" target="_blank">FROMの説明</a>において、推奨が記載されている。
<blockquote>可能なら常に、イメージの土台には最新の公式イメージを利用します。私たちの推奨は Alpine イメージ です。これは非常にコントロールされながら、容量が小さい（現時点で 5MB 以下） Linux ディストリビューションです。</blockquote>

#Alpine イメージとは
<a href="https://hub.docker.com/_/alpine" target="_blank">完全なパッケージインデックスとわずか5MBのサイズを備えたAlpineLinuxに基づく最小限のDockerイメージ！</a>と謳われているのが、Alpine イメージだ。さらに「他のBusyBoxベースのイメージよりもはるかに完全なパッケージリポジトリにアクセスできます。これにより、Alpine Linuxは、ユーティリティや本番アプリケーションの優れたイメージベースになります。」とのこと。実際にAlpine イメージを採用している例として、<a href="http://www.ebios.club/article/478039208.html" target="_blank">先日取り上げた</a>ウェブサーバーソフトである<a href="https://hub.docker.com/_/caddy" target="_blank">caddy</a>を挙げてみる。<a href="https://github.com/caddyserver/caddy-docker/blob/66905660327fd16a9d95c95da1e59a3302493dc8/2.2/alpine/Dockerfile" target="_blank">latest（最新）</a>のcaddyのDockerfileをみると「FROM alpine:3.12」から始まっている。<a href="https://hub.docker.com/_/alpine" target="_blank">alpine</a>は現在「3.12.1, 3.12, 3」をlatestとしている。

#まとめで詳しく
dockerイメージとは何者で、Dockerfileとは何者で、そのDockerfileに記述されるFROMとは何者か。
dockerイメージとは指定したDockerfile に従ってbuild コマンドによって構築されたdockerコンテナ用イメージ。
Dockerfileとはdockerイメージを構築するために必要な全ての命令を、順番通りに記述したテキストファイル。
FROMとはDockerfileに記載する土台となるもの。通常はdockerイメージを指定するが、無ければFROM scratchと書く。公式推奨はFROM Alpine。