Lesson 4

オリジナルのイメージを作成する

Lesson 4 Chapter 1
Dockerfileの作成

これまでのLessonにより、イメージの取得からコンテナの作成・実行までを行うことができるようになりました。

しかし、コンテナの実行(docker run、docker start)により何を行うか?ということが指定できませんでした。

docker run hello-worldはHello from Docker!を出力しますが、それはそのようにイメージが作られているからなのです。

オリジナルのイメージを作成するには

自分でオリジナルのイメージを作成するには、Dockerfileという設定ファイルを用意します。

そして、Dockerfileに記述した内容を元にイメージを作成することができます。

Lesson4ではDockerfileを作成し、dockerコマンドにより(1)オリジナルのイメージを作成、(2)コンテナを実行する流れを学習します。

image_4_1_1.png Lesson4で学習する内容

Dockerfileの例

以下に、Dockerfileの簡単な一例を示します。

Dockerfile
FROM ubuntu
RUN apt update
RUN apt install -y vim
ENV DIRPATH=/home
WORKDIR $DIRPATH
COPY myfile.txt .
CMD cat myfile.txt
                    

このように、Dockerfileはいくつかの命令と引数のセットの組み合わせによって構成されており、一行につき一命令で上から順番に実行されます。

Dockerfileからイメージを作成

それでは、このDockerfileを用いてイメージを作成し、コンテナを作成&実行してみましょう。Dockerfileの中身の詳細については以降のchapterで詳しくみていくので、まずは体験してみて下さい。

・ステップ1

まずは作業ディレクトリを作成して下さい。

ここでは、仮にC:\Users\ユーザ名\Desktop\lesson4_1としましょう。その中にDockerfileという名前のファイルを作成し、上記のDockerfileの内容をコピーして貼り付けて下さい。

・ステップ2

次に、myfile.txtという名前のファイルを作成し、以下のテキストを書き込みます。

myfile.txt
This is myfile
                    

・ステップ3

C:\Users\ユーザ名\Desktop\lesson4_1内にDockefilemyfile.txtというファイルを作成したら、①PowerShellでその作業ディレクトリに移動します。

image_4_1_2.png

・ステップ4

作業ディレクトリ内に移動したら、②docker build -t my-first-image .を実行して下さい。

docker buildコマンド

docker build -t my-first-image .の最後の.を忘れないようにして下さい。これにより、イメージをビルドする際に指定するDockerfileの位置をカレントディレクトリに指定しています。

このコマンドは、Dockerfileからmy-first-imageという名前をつけたイメージを作成します。

image_4_1_3.png Dockerfileからイメージを作成

・ステップ5

そして、③docker run my-first-imageを実行して下さい。

image_4_1_4.png

myfile.txt内に記述したテキストを表示することができました。

docker runコマンド

docker runコマンドは指定したイメージ名を、最初にローカル内から探します。今回の場合はdocker buildコマンドによって作成されたmy-first-imageという名前のイメージからコンテナの作成&実行が行われました。ローカル内に該当のイメージがない場合は、Docker Hubからイメージを検索して取得します。(Docker Hubにmy-first-imageという名前のイメージは存在しないのでエラーが発生します)

以上で、Dockerfileからイメージを作成し、コンテナを作成&実行することができました。

docker container ls -aでコンテナの一覧を表示してみましょう。

image_4_1_5.png

④コンテナが作成されていることが確認できます。

コンテナの実行方法の指定

次にdocker runコマンドのオプションにより、どのようにコンテナを実行するか指定してみましょう。

・コンテナを実行終了時に破棄する

docker run --rm my-first-imageを実行して下さい。先ほどと同様にmyfile.txt内のテキストが表示されます。

image_4_1_6.png

--rmオプションをつけると、コンテナは実行終了時に削除されます。よって、上記のコマンドを何回実行してもその度にコンテナが作成され破棄されるので、作成されたコンテナが溜まっていくことがありません。

docker container ls -aで作成されていないことを確認しておきましょう。

・コンテナの中に入って操作する

いままでのコンテナの実行はテキストを表示して終了するだけで、インタラクティブにコンテナを実行することができませんでした。

次は起動中のコンテナの中に入ってテキストデータの編集をしてみましょう。

docker run -it my-first-image /bin/bashを実行します。

image_4_1_7.png

コンテナ内のhomeディレクトリに入ることができました。

vim myfile.txtでvimを立ち上げてテキストを編集しましょう。

image_4_1_8.png

キーボードのiを押して編集モードに移行してから、テキストを入力してみて下さい。

image_4_1_9.png

テキストを入力したら、キーボードのEscを押して通常モードに移行してから⑨:wqを入力しEnterでファイルを上書きして保存します。

image_4_1_10.png

ターミナル画面に戻ってきたら、⑩exitでコンテナを終了させましょう。

image_4_1_11.png

これでコンテナ内のファイルを編集することができました。

コンテナがホストOSから分離された実行環境であることを確かめるため、ローカルのファイルは書き換えられていないことを確認しましょう。ファイルをダブルクリックでメモ帳を開きます。

image_4_1_12.png

確かに、上書きされていないことが確認できます。それでは先ほどファイルを編集して終了したコンテナを立ち上げて、ファイルの中身を見てみましょう。

docker container ls -aでコンテナIDを確認します。

image_4_1_13.png

docker start -a -i [コンテナID]でコンテナを実行します。

image_4_1_14.png

先ほどと同じようにコンテナの中に入れました。vim myfile.txtでファイルの中身を見てみましょう。

image_4_1_15.png

確かに、ファイルが変更されています。⑫:qでvimを終了し、exitでコンテナから抜け出しましょう。

Lesson 4 Chapter 2
FROM

それでは、ここから続くchapterで、Dockerfileの各命令について学習していきましょう。

FROM

FROM命令ではベースイメージを指定します。

chapter1の例では①FROM ubuntuとあるように、ubuntu(ubuntu:latest)をベースイメージに指定しています。以降の命令によって新しい情報を積み重ねてイメージを構築していく土台となるもので、必ず必要な命令になります。基本的には公開レポジトリにあるイメージを指定します。

ここで以下のような疑問を持たれるかもしれません。

指定したベースイメージのベースイメージは何になっているのでしょうか?

Docker Hubで公開されているイメージもDockerfileで記述されていますので、そのDockerfileを遡って確認してみましょう。

ここではredisを例に見ていきます。

Docker Hubにアクセスしてredisを検索します。

image_4_2_1.png

②Docker Official Imageのものを見てみましょう。

image_4_2_2.png

③latestタグのものをクリックします。

image_4_2_3.png

Dockerfileが表示されるのでベースイメージを確認すると、④FROM debian:bullseye-slimとなっています。

image_4_2_4.png

次にdebianを検索して、⑤Docker Official Imageのものをクリックします。

image_4_2_5.png

bullseye-slimタグのものを見てみましょう。

image_4_2_6.png

ベースイメージは⑦FROM scratchとなっています。

今度はscratchイメージを見にいきます。こちらからアクセスして下さい。

image_4_2_7.png

説明を読んでみると、scratchイメージは空のイメージでベースイメージを構築する際の最小限のものになると書かれています。また、ベースとなる空のイメージであるのでdocker pulldocker runで取得することはできず、DockerfileのFROM命令で参照することしかできないとあります。

以上を整理してみると以下の図のようになります。

image_4_2_8.png redisイメージの参照元

このようにFROM命令で指定したベースイメージが参照しているイメージを確認することができました。気になった他のイメージがあればDocker Hubのページから同じように検索して調べてみて下さい。

Lesson 4 Chapter 3
RUN

続いてRUN命令について学習します。

RUN命令

RUN命令はイメージの構築時に実行するコマンドを指定します。

太字の強調をしましたが、イメージの構築時というのが大事なポイントです。

Dockerfile
FROM ubuntu
RUN apt update ①
RUN apt install -y vim ①
ENV DIRPATH=/home
WORKDIR $DIRPATH
COPY myfile.txt .
CMD cat myfile.txt
                    

chapter1の例では①2つのRUN命令によって、FROM命令で指定したubuntuイメージに対してvimをインストールしています。

これにより構築されたイメージにはvimがインストールされ、このイメージから実行したコンテナ内ではvimが使えるようになっています。

chapter1でコンテナの中に入って操作したときにvimを使ってファイルを編集しましたが、ubuntuイメージには初期でvimはインストールされていないので、RUNコマンドによりインストールしていたというわけになります。

RUN命令(やその他のコマンドを指定する命令)にはシェル形式(shell form)と実行形式(exec form)という2つの書き方の形式があります。シェル形式は今回の例で書いた形式になります。

シェル形式と実行形式の書き方の違いは、以下のようになります。

シェル形式 (shell form) 実行形式 (exec form)
RUN apt install -y vim RUN ["apt", "instll", "-y", "vim"]

また、シェル形式では\(バックスラッシュ)を使うことによりRUN命令を次の行に続けることができます。今回の例では以下のようになります。

Dockerfile
FROM ubuntu
RUN apt update && \
        apt install -y vim
ENV DIRPATH=/home
WORKDIR $DIRPATH
COPY myfile.txt .
CMD cat myfile.txt
                    

Lesson 4 Chapter 4
CMD/ENTRYPOINT

続いて、CMD命令とENTRYPOINT命令について学習します。

CMD命令

CMD命令はコンテナ実行時のデフォルトのコマンドを指定します。chapter1の例を見てみましょう。

Dockerfile
FROM ubuntu
RUN apt update
RUN apt install -y vim
ENV DIRPATH=/home
WORKDIR $DIRPATH
COPY myfile.txt .
CMD cat myfile.txt ①
                    

CMD cat myfile.txtとあります。catは指定したファイルの中身を出力するLinuxコマンドになります。

これによりdocker runでこのコンテナを実行したときにcat myfile.txtが実行され、コンテナが終了するという流れになっています。

CMD命令はコンテナの実行時のコマンドを指定するといいましたが、docker run時の入力によってそれを書き換えることもできます。

docker run [イメージ名] [コマンド]で、CMD命令で指定したコマンドを書き換えてコンテナを実行します。

chapter1で、docker run -it my-first-image /bin/bashとしてコンテナを実行したときのことを思い出してみて下さい。

元々のDockerfileで指定したコマンドはcat myfile.txtでしたが、この場合、それを/bin/bashに書き換えています。これによりシェルを立ち上げることでコンテナの中に入って操作できるようにしたというわけです。

CMD命令とRUN命令の違い

CMD命令とRUN命令の違いについては特に注意して認識して下さい。

命令 実行内容
CMD コンテナ実行時のコマンドを指定(docker run時のデフォルト実行内容)
RUN イメージ構築時のコマンドを指定(docker buildでイメージを構築する時の実行内容)

それでは、コンテナの実行時に書き換えられずに実行されるコマンドを指定する命令はないのでしょうか?それがENTRYPOINT命令になります。

ENTRYPOINT命令

ENTRYPOINT命令で指定したコマンドはコンテナ実行時に書き換えられずに実行されます。

また、docker run [イメージ名]の後に続く引数はENTRYPOINTの後に追加されます。少し分かりづらいと思うので具体的に見てみましょう。

作業ディレクトリに移動して、myfile2.txtを以下の内容で追加します。

myfile2.txt
This is myfile2
                    

Dockerfileを以下のように書き換えます。

Dockerfile
FROM ubuntu
RUN apt update
RUN apt install -y vim
ENV DIRPATH=/home
WORKDIR $DIRPATH
COPY myfile.txt myfile2.txt .
ENTRYPOINT ["cat", "myfile.txt"]
                    

実行形式(exec form)

RUN命令のときに少し触れましたが、ENTRYPOINT命令の形式を実行形式(exec form)にしているところに注意して下さい。ENTRYPOINT cat myfile.txtというシェル形式の書き方だと望む動作が得られません。

これでイメージを再構築してみましょう。docker build -t my-fist-image:2 .を実行します。

image_4_4_1.png

docker buildでのタグ指定

docker buildコマンドではタグを指定してイメージを構築することができます。今回の例ではイメージ名の後ろに:2をつけてタグ名をつけています。

イメージを構築したらdocker run my-first-image:2でコンテナを実行してみましょう。

image_4_4_2.png

ENTRYPOINT命令で指定したとおり、myfile.txtの内容が出力されます。

次に、docker run my-first-image:2 myfile2.txtを実行してみて下さい。

image_4_4_3.png

これでdocker runの後に続く引数をENTRYPOINTに渡せることができました。実際にコンテナ実行時に実行されたコマンドは、cat myfile.txt myifle2.txtになります。

image_4_4_4.png docker run [イメージ名] に続く引数がENTRYPOINT命令に対する引数に渡される

このようなイメージで実行されると考えて下さい。

Lesson 4 Chapter 5
ENV

続いてENV命令について学習します。

ENV命令

ENV命令は環境変数を設定します。

ENV [キー]=[値]という形式で指定します。

Dockerfile
FROM ubuntu
RUN apt update
RUN apt install -y vim
ENV DIRPATH=/home ①
WORKDIR $DIRPATH ②
COPY myfile.txt .
CMD cat myfile.txt
                    

chapter1の例では①キーがDIRPATHで値が/homeという環境変数を設定しています。これにより、Dockerfile内で続く以降の命令や実行したコンテナ内でもこの環境変数を用いることができるようになります。

環境変数を用いる場合は②WORKDIR $DIRPATHというキー名の頭に$マークをつけた形で使用します。これでWORKDIR /homeと書くのと同じことになります。

また、設定した環境変数がコンテナ内で生きているか確かめてみましょう。

docker run -it my-first-image /bin/bashでコンテナの中に入ります。

echo $DIRPATHを実行してみて下さい。

image_4_5_1.png

/homeと表示され、環境変数がコンテナの中でも設定されていることが確認できました。

Lesson 4 Chapter 6
WORKDIR

続いてWORKDIR命令について学習します。

WORKDIR命令

WORKDIR命令は作業ディレクトリを指定します。

Dockerfile
FROM ubuntu
RUN apt update
RUN apt install -y vim
ENV DIRPATH=/home 
WORKDIR $DIRPATH ① 
COPY myfile.txt .
CMD cat myfile.txt
                    

chapter1の例を見てみましょう。①WORKDIR $DIRPATHにより作業ディレクトリを/homeに設定しています。これによってDockerfile内で以降に続く命令の処理が/home内で行われることになります。

理解を深めるためコンテナの中に入ってubuntuイメージのディレクトリ構造を確かめてみましょう。

docker run -it my-first-image /bin/bashを実行します。

image_4_6_1.png

そうするとカレントディレクトリが②/homeになっています。

image_4_6_2.png

cd ..で一つ上のディレクトリに戻ってみましょう。そして④lsコマンドでディレクトリ構造を確認します。

ubuntuイメージでは、このようなディレクトリ構造になっていることが確認できます。

WORKDIR命令による作業ディレクトリの指定がなければ、このルートディレクトリがデフォルトの作業ディレクトリになるので、続くDockerfileでの命令もコンテナ実行時もこのルートディレクトリから始まることになります。

chapter1の例では、続くCOPY命令が/homeディレクトリに対して行われることになります。

このCOPY命令については次のchapterで見ていきましょう。

Lesson 4 Chapter 7
COPY

続いてCOPY命令とADD命令について学習します。

COPY命令

COPY命令ではファイルやディレクトリをイメージのファイルシステム上に追加します。

COPY [コピー元] [コピー先]という形式で指定します。

Dockerfile
FROM ubuntu
RUN apt update
RUN apt install -y vim
ENV DIRPATH=/home 
WORKDIR $DIRPATH 
COPY myfile.txt . ①
CMD cat myfile.txt
                    

chapter1の例では①COPY myfile.txt .となっているので、ホストOS上でDockerfileを置いているディレクトリ内のmyfile.txtファイルをイメージ上のカレントディレクトリにコピーすることになります。

前chapterで学んだように、1つ前のWORKDIR命令で作業ディレクトリを/homeに設定しているので、以降に続くCOPY命令の実行におけるカレントディレクトリは/homeになります。

結果として、ホストOS上のmyfile.txtが構築されるイメージの/homeディレクトリ内にコピーされます。

ADD命令

ADD命令もCOPY命令とほぼ同じように動作しますが、ADD命令には付加的な2つの機能がついています。

1つ目はtarファイルの展開機能です。ADD somefile.tar .とするとsomefile.tarを展開しイメージにコピーします。

2つ目はリモートURLのサポートです。ADD https://example.com/somefile.tar.xz .のようにリモートURLを指定してファイルを取得することができます。

COPY命令とADD命令の使い分け

ADD命令によるリモートURLからのファイル取得はイメージサイズが増大する問題があるので、推奨されていません。基本的には、ローカルファイルのコピーにはCOPY命令を使い、tarファイルの展開にはADD命令を使うと考えて下さい。

Lesson 4 Chapter 8
イメージとコンテナについて

これまでのLessonで、コンテナとイメージの関係についても理解が進み、dockerコマンドにも少し慣れてもらったかと思います。

そこで、Lesson4最後のchapterでは、コンテナとイメージの実体について、もう少し詳しく見ていきたいと思います。

イメージとレイヤー

これまでのLessonで触れてこなかったことがあります。それはイメージはレイヤーで構成されているということです。1つのレイヤーから成るイメージもあれば、複数のレイヤーを重ねたイメージもあります。

言葉だけの説明では分かりづらいので、具体例を見てみましょう。

Lesson3ではdocker pullコマンドでイメージを取得しましたが、その出力の詳細を見て下さい。以下はdocker pull mysqlコマンドの出力画面です。

image_4_8_1.png

①で示した範囲をご覧下さい。ランダムな英数字の後にPull Completeと書かれている行が複数あると思います。

実は、この各行がそれぞれレイヤーを示しています。1つのイメージの取得時には、複数のレイヤーをそれぞれ個別に取得していることが分かります。

それでは、イメージの構築時にレイヤーがどのように構成されるのか見てみましょう。

Docker Hubのmysqlのページにアクセスします。

image_4_8_2.png

②lastetをクリックして下さい。

image_4_8_3.png

ここでは③イメージレイヤーの詳細を確認することができます。

20行あり、各行の右側にサイズが書かれています。このサイズが0Bになっているものはイメージのメタデータを変更するもので、新しいレイヤーを作成するものではありません。

サイズがある行を数えてみると全部で11行あり、docker pull mysqlで取得してきたときのPull Completeの行数と一致しています。

それでは、イメージの構築はどのように指定されるのだったでしょうか?

そう、Dockerfileです。今度は④からDockerfileを見にいきましょう。

image_4_8_4.png

Githubリポジトリが開き、複雑そうなファイルが確認できると思いますが、命令の種類に注目して下さい。数ある命令のうちレイヤーを作成するのはRUNCOPYADD命令のみです。

数えてみると、RUNCOPY命令が合わせて10個あり、これとFROM命令でのベースイメージの指定と合わせて計11個のレイヤーが作成されていることが確認できます。

image_4_8_5.png イメージレイヤー

このように命令1つにつき、1つのレイヤーを作成するということを実感してもらえたと思います。

構築イメージの容量を抑える

Dockerfileの命令を工夫して書くことで、構築されるレイヤーの容量、つまりは最終的に出来上がるイメージの容量を小さくすることができます。イメージが小さくなると、素早い配布が可能になります。ここでは詳述しませんが、この命令とレイヤーの関係を覚えておくと後々役立つことがあるでしょう。

コンテナとレイヤー

上記ではイメージがレイヤーで構成されていることを確認しました。それではコンテナはどのように作られているのでしょうか?

これまでのLessonで、繰り返しコンテナはイメージを元に作成されると述べてきました。

そこから、コンテナはイメージから複製されるものという印象を抱いている方もいるかもしれません。その考え方は基本的には正しいのですが、コンテナとイメージには重要な違いがあります。

それはコンテナにはイメージで構成されたレイヤーの一番上に書き込み可能なレイヤーが存在していることです。

docker runの実行時に、後ろにコンテナ実行時コマンドをつけて、CMD命令で指定した命令を書き換えてコンテナを実行した時のことを思い出して下さい。

このような新しい動作の指定やデータの変更などは、全てこの書き込み可能なコンテナレイヤーに保存されます。

image_4_8_6.png コンテナとレイヤー

これにより、1つのイメージから複数のコンテナを作成することができています。

コンテナの削除時は、この一番上の書き込み可能なレイヤーのみが削除されることになります。よって、イメージレイヤーは何も変更されず、そのまま残り続けることができるのです。

まとめ

以上、Lesson4ではDockerfileの書き方の基本をメインにコンテナとイメージの関係についても理解を深めました。

以下の、Lesson4で学習したDockerfileの命令のまとめで復習を行ない、本Lessonを終了したいと思います。

命令 実行内容
FROM ベースイメージを指定
RUN イメージ構築時のコマンドを指定
CMD コンテナ実行時のコマンドを指定(docker run実行時に書き換え可能)
ENTRYPOINT コンテナ実行時の書き換えられないコマンドを指定
ENV 環境変数の設定
WORKDIR 作業ディレクトリの設定
COPY ローカルファイルをイメージにコピー
ADD COPY + (tar展開、リモートURLサポート)