【 docker入門 】 dockerイメージ の概要と Dockerfile の書き方を解説
docker入門 第二弾です。
第一弾では、docker hub で公開されているdockerイメージを使ってコンテナを作成する方法を解説しました。
今回は、自分で作成したdockerイメージからコンテナを起動してみましょう!というのを目標に解説していきます。
dockerイメージ の概要
docker が作成するコンテナの元となるものです。
実態は、UnionFileSystemと言う特殊なファイルシステムになっていて必要な情報が層になって保存されています。
この層をレイヤーと言いdockerはdocker hubからコンテナを起動するために必要なレイヤーを取得し、Dockerイメージとして保存します。
レイヤーは全てReadOnlyとなっておりDockerイメージの中身を書き換えることはできません。
自分でDockerイメージを構築する場合は、そのDockerイメージでコンテナを起動し、コンテナレイヤーと言われるRead/Write可能なレイヤーを作ります。
そこでの変更をDockerイメージとして保存することでDockerイメージにレイヤーを追加できます。
色々書きましたが、最初のうちは、
dockerイメージとは
- コンテナを作る元
- 読み取り専用の層になっている特殊なファイルシステム
- 変更するときは、新しい層を上に積む
というようなイメージでOKかと思います。
Dockefile から dockerイメージ を 作成する
Dockerfile とは、先程の画像でいうところのコンテナレイヤー(変更可能なレイヤー)でどんな変更を行うのかを定義するファイルです。
以前の、「dockerでコンテナを作ってアクセスする手順とコマンドのまとめ」でも少し触れた、dockerデーモンがDockerfileを参照して新しいdockerイメージを作成します。
今回は、docker社の提供するwhalesayというdockerイメージををベースイメージとしてその上にfortuneという格言を表示するコマンドをインストールします。
DockerHubから取得したwhalesayイメージをそのまま使うと以下の感じ
cowsayのあとに 入力した文字をクジラのアスキーアートが喋ってくれます。
これにfortunesをインストールして自動で格言を喋ってもらう様にします。
まずは、docher hubからベースイメージとなるwhalesayを取得してそのまま実行してみます。
結果は以下の通り
$ docker run --rm docker/whalesay cowsay hello
Unable to find image 'docker/whalesay:latest' locally
latest: Pulling from docker/whalesay
Image docker.io/docker/whalesay:latest uses outdated schema1 manifest format. Please upgrade to a schema2 image for better future compatibility. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/
e190868d63f8: Already exists
909cd34c6fd7: Already exists
0b9bfabab7c1: Already exists
a3ed95caeb02: Pull complete
00bf65475aba: Pull complete
c57b6bcc83e3: Pull complete
8978f6879e2f: Pull complete
8eed3712d2cf: Pull complete
Digest: sha256:178598e51a26abbc958b8a2e48825c90bc22e641de3d31e18aaf55f3258ba93b
Status: Downloaded newer image for docker/whalesay:latest
_______
< hello >
-------
\
\
\
## .
## ## ## ==
## ## ## ## ===
/""""""""""""""""___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~
\______ o __/
\ \ __/
\____\______/
以前の記事でもご紹介した$ docker run
コマンドによってdocker hub からイメージを取得して実行しています。
後ろについている、cowsay hello
は実行されたコンテナのプロセスID(PID)1として実行されるコマンドを引数として渡しています。
今回の場合、cowsayというコマンドがPID1で実行され、そのコマンドにhelloという引数を渡しています。
出力結果の、鯨のアスキーアート(AA)部分は、cawsayの出力結果で、helloは引数の部分になります。
Dockerfileの作成
先に今回使うDockerfileをご紹介します。
FROM docker/whalesay:latest
RUN apt-get -y update && apt-get install -y fortunes
CMD /usr/games/fortune | cowsay
行頭のFROMやRUNなど大文字で記述されたところが命令としてdockerデーモンが認識する語句になっています。
それぞれ以下のような意味になっています。
FROM
Dockerイメージのベースイメージを指定します。
この例では、whalesayイメージのlatestタグがついているベースイメージを指定しています。
RUN
冒頭の図にある、コンテナレイヤーで実行するコマンド、即ちイメージの変更内容を記述します。
今回は、apt-getコマンドを使ってfortunesというパッケージをインストールし、fortuneコマンドを使えるようにしています。
ここの内容が、コンテナレイヤーに書き込まれます。
CMD
コンテナが起動された時に実行するコマンドを指定していする命令です。
今回は、cowsay
コマンドにfortune
コマンドの結果を引数として渡してあげています。
ベースイメージのDockerfileを覗いてみる
理解を深めるため、ベースイメージとなっているwhalesayイメージのDockerfileも覗いてみましょう。
docker hubの配布ページから見ることができます。
FROM ubuntu:14.04
# install cowsay, and move the "default.cow" out of the way so we can overwrite it with "docker.cow"
RUN apt-get update && apt-get install -y cowsay --no-install-recommends && rm -rf /var/lib/apt/lists/* \
&& mv /usr/share/cowsay/cows/default.cow /usr/share/cowsay/cows/orig-default.cow
# "cowsay" installs to /usr/games
ENV PATH $PATH:/usr/games
COPY docker.cow /usr/share/cowsay/cows/
RUN ln -sv /usr/share/cowsay/cows/docker.cow /usr/share/cowsay/cows/default.cow
CMD ["cowsay"]
whalesayイメージのベースイメージはubuntuということが分かります。
先程、自前で作ったDockerfileではRUN命令のところで、apt-getコマンドを実行しているのを見て、「おや?」と思って方もいるかと思いますが、ベースイメージのベースイメージがubuntuだったから使えていたということになります。
このことからも、dockerイメージが層になっているという想像がしやすいかと思います。
また、自作のDockerfileでは出てこなかったENV命令で環境変数を設定していたり、COPY命令でローカルからイメージ内にファイルをコピーしていたりします。
この辺りのコマンドは、記事の最後にまとめています。
コピーしているファイルは、鯨のAA部分になります。本来cowsay
コマンドは以下のように牛のAAがしゃべるコマンドなのですが、ファイルを読み込ませることで別のAAを表示することができます。
$ cowsay hello 2451ms 土 2/12 23:21:48 2022
_______
< hello >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Dockerfileからdockerイメージを作成(ビルド)
Dockerfileの準備ができたら$ docker build
コマンドでイメージを作成していきます。
$ docker build -t my-whale .
-t
オプションを付けることで作成するイメージにタグをつけることができます。
最後の「.」はビルドコンテキストというものになります。
難しそうな名前ですが、意味はシンプルで参照するDockerfileがどこにあるかを指定するものになります。
まあ、Dockerfileのパスの指定ですね。
実行結果
docker build -t my-whale . 1.7m 土 2/12 23:00:15 2022
[+] Building 32.8s (6/6) FINISHED
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 162B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/docker/whalesay:latest 0.0s
=> [1/2] FROM docker.io/docker/whalesay:latest 0.3s
=> [2/2] RUN apt-get -y update && apt-get install -y fortunes 31.5s
=> exporting to image 0.8s
=> => exporting layers 0.7s
=> => writing image sha256:cd3fcaba765d5a5c8f011053d4a25318b6cf74df1c1a4e34ea9add31b8708203 0.0s
=> => naming to docker.io/library/my-whale 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
長めの出力が返ってきますが分解してみていきます。
dockerデーモンへの転送
$ docker build
コマンドを実行すると コマンドの裏で一 生懸命働いているdockerデーモンにDockerfileの情報を転送します。
出力の冒頭部分ですね。
$ docker build -t my-whale .
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 162B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B
FROM命令の処理
Dockerfileの転送が終わると、ファイル内の処理が始まります。
最初はFROM命令の処理をしてベースイメージを取得します。
=> [1/2] FROM docker.io/docker/whalesay:latest
ここでFROMに指定したdocker/whalesayのイメージを取得していることがわかります。
今回は、冒頭のお試し実行でdocker/whalesay:latestのイメージを取得していますのですぐ完了します。
ここで、イメージがローカルPCに存在しない場合は例のごとくdocker hubから取得してきます。
RUN命令の実行
RUN命令の処理は、以下の箇所です。
=> [2/2] RUN apt-get -y update && apt-get install -y fortunes 31.5s
=> exporting to image 0.8s
=> => exporting layers 0.7s
=> => writing image sha256:cd3fcaba765d5a5c8f011053d4a25318b6cf74df1c1a4e34ea9add31b8708203 0.0s
=> => naming to docker.io/library/my-whale
この時、ベースイメージが一時コンテナとして起動しておりその中でRUN命令に記載されている処理が実行されます。
ベースイメージでRUN命令が実行されたものを新しいdockerイメージとして出力されているというわけです。
作成したイメージの確認
作成したイメージは、$ docker images
コマンドで確認できます。
$ docker images 35.1s 日 2/13 10:16:15 2022
REPOSITORY TAG IMAGE ID CREATED SIZE
my-whale latest cd3fcaba765d 11 hours ago 278MB
これでイメージの作成は完了です!
ここまでのdockerイメージの構成
今回のDockerfileを使ってDockerイメージを作成するとベースイメージを含めDockerイメージの構成は以下の様になっています。
作成したイメージからコンテナを作成
dockerイメージができたらあとは実行するだけです。
コマンドは過去の記事でご紹介していますので詳しいご紹介は省略します。
$ docker run --rm --name whale my-whale 808ms 日 2/13 10:29:23 2022
________________________________________
/ The only winner in the War of 1812 was \
| Tchaikovsky. |
| |
\ -- David Gerrold /
----------------------------------------
\
\
\
## .
## ## ## ==
## ## ## ## ===
/""""""""""""""""___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~
\______ o __/
\ \ __/
\____\______/
無事動きましたね〜
Dockerfileの命令
今回の例では、Dockerfile内で、FROM/RUN/CMD
命令のみを利用しました。
実際は他にも命令がありまるのでまとめておきます。
FROM | ベースイメージを指定します。 |
RUN | コマンドを実行して、コンテナレイヤーに新しいパッケージなどを追加します。 |
CMD | コンテナにデフォルトのコマンドと引数を設定できます。コマンドは省略して引数だけを指定することもできますが、その場合後述のENTORYPOINTもでコマンドを指定する必要があります。 CMDで指定された値は、引数によって上書きすることができます。 |
LAVEL | key-valueの形式でdockerイメージに情報を付与することができます。 docker inspect コマンドでイメージの情報を表示した際に見ることができます。 |
EXPOSE | ポートの設定ができます。 ここに記載されてポートが、コンテナでListenされます。 |
ENV | コンテナの環境変数を設定できます。 |
ADD | ローカル側のファイルをコンテナにコピーします。 対象がtarファイルなどの圧縮された形式の場合、展開してものをコピーします。 また、対象がローカル側にない場合、コンテナ側にはディレクトリだけ作られます。 |
COPY | ローカル側のファイルをコンテナにコピーします。ADD 命令と同じですが余計なものまでコピーしてしまう可能性があるため、COPY命令の方が推奨されているようです。 |
ENTRYPOINT | コンテナにデフォルトのコマンドと引数を設定できます。 CMDとは違い引数で上書きすることができません。 上書きするには、 --entrypoint オプションを付けてコンテナを実行する必要があります。 |
VOLUME | ローカル側のボリュームにマウントするための命令です。 コンテナが削除されても、ローカル側に保存されたファイルは残り続けます。 |
USER | RUN/CMD/ENTRYPOINT 命令を実行するユーザを指定します。 |
WORKDIR | RUN/CMD/ENTRYPOINT 命令を実行する際のカレントディレクトリを指定します。 |
ARG | Dockerefile内で扱う変数名を定義します。 例えば、 ARG hoge と記述するこで $docker build ./ --build-arg hoge=hello のように、ビルド時に変数に値を渡すことができます。 |
ONBUILD | この命令が記述されたDockerfileをビルドした時は、特に何も起こりません。 ビルドしたイメージをベースイメージとして別のイメージを作成した時に ONBUILD 命令に記述されたコマンドが実行されます。 |
STOPSIGNAL | コンテナを停止するためのシグナルを指定します。 |
HEALTHCHECK | コンテナの正常確認を実行できます。 例えば、 数秒ごとに特定のURLに curl を実行して200 が返って来れば正常。数回連続で200 以外なら異常のように設定することができる。 |
SHELL | この命令を使うと、RUN 命令で実行されるコマンドがshellで実行されるようになります。 |
参考リンク
- overlayfsとDockerの関係性(実装も少しだけ見てみる記事)
- Dockerイメージの理解を目指すチュートリアル
- Docker ドキュメント日本語化プロジェクト
- Dockerfile reference
- DockerfileにてなぜADDよりCOPYが望ましいのか
- DockerfileのONBUILD
- Dockerfile に HEALTHCHECK を設定すると「ヘルスチェック機能」が使えるようになる
- DockerfileのRUNやCMDをBashのログインシェルで実行させる