Dockerイメージを構築する(Dockerfileを使う方法と使わない方法を比較しながら)

目的

イメージを作るのにDockerfileを使う方法が一般的だが、今回はDockerfileを使う方法と使わない方法を試して比較することで、Dockerfileの有用性を理解する。

今回構築するイメージは、歴史上の偉人や有名人の格言を牛がつぶやくアスキーアートを出力するプログラム。

______________________
< You are always busy. >
 ----------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

格言を出力するfortuneと、出力を牛がつぶやくアスキーアートに変換するcowsayを組み合わせて作る。

Dockerfileを使わずにイメージ構築

まずはDockerfileを使わずにイメージを作る。流れとしてはこんな感じ。

  • ベースイメージからコンテナを起動する
  • コンテナに入って変更を加える
  • コンテナの内容を新たなイメージとして保存する
$ docker run -it --name cowsay -h cowsay debian bash
$ apt-get update && apt-get install -y cowsay fortune
# /usr/games/fortune | /usr/games/cowsay
 _________________________________________
/ You will be recognized and honored as a \
\ community leader.                       /
 -----------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

上記のコマンドでコンテナができたので、そのコンテナの内容をdocker commitで新しいイメージに焼く。 コンテナ名(cowsay)、リポジトリ名(repo)、イメージ名(cowsay-fortune)を指定する。

docker commit [オプション] コンテナ [リポジトリ[:タグ]]

$ docker commit cowsay repo/cowsay-fortune
sha256:bf300800043047c8566b9221344f3414bd45dcd6c4949ea14f752ae2fc999e57
$ docker images
REPOSITORY                   TAG                   IMAGE ID            CREATED             SIZE
repo/cowsay-fortune   latest                bf3008000430        4 seconds ago       180MB

これで作ったイメージを使って、手軽に何回でもcowsayを実行できるようになった!

$ docker run repo/cowsay-fortune /usr/games/cowsay "Moo"
 _____
< Moo >
 -----
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

ただ、この方法だと変更を加えようとするともう一度同じ手順を繰り返さないといけないし、繰り返し行う場合にその手順を覚えておいておかなければならない。
Dockerfileはイメージを自動構築することによって、この問題を解決する。

Dockerfileを使ってイメージ構築

Dockerfileにはイメージを生成するための一連の操作を記述し、それをdocker buildコマンドで構築する。

# 使用するベースイメージ
FROM debian:latest

# イメージ内で実行するシェルコマンド
RUN apt-get update && apt-get install -y cowsay fortune

# docker runに渡す引数(実行ファイル)を指定
ENTRYPOINT ["/usr/games/cowsay"]

上記の内容をDockerfileに記述しdocker build コマンドを実行すれば、イメージを構築できる。

docker build [オプション] パス | URL | -

イメージ名とタグ(repo/cowsay-fortune2:latest)、使用するDockerfileのパス(./Dockerfile)、ビルドコンテキスト(.)を指定。
*ビルドコンテキスト: Dockerfile内の命令(ADDやCOPYなど)から参照できるファイルやディレクトリの集合。ここでは、カレントディレクトリ以下にある全てのファイルとディレクトリで構成され、ビルドプロセスの一部としてDockerデーモンに送られる。
ビルドコンテキストから不要なファイルを除くには .dockerignore ファイルを使う。

$ docker build -t repo/cowsay-fortune2 -f ./Dockerfile .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM debian:latest
 ---> 5971ee6076a0
Step 2/3 : RUN apt-get update && apt-get install -y cowsay fortune
 ---> Running in c5448328bf90
Get:1 http://deb.debian.org/debian buster InRelease [121 kB]
[中略]
Successfully built 479c6b5f70dc
Successfully tagged repo/cowsay-fortune2:latest
$ docker images 
REPOSITORY                    TAG                   IMAGE ID            CREATED             SIZE
repo/cowsay-fortune2   latest                479c6b5f70dc        6 seconds ago       180MB

このイメージを使って、cowsayを実行する。

$ docker run repo/cowsay-fortune2 "Moo"
 _____
< Moo >
 -----
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

これでもし変更が入ってもDockerfileの内容を変更して、buildするだけで良くなった!
イメージの構築手順はDockerfileに記述されているので、覚えておく必要もない。
イメージを作るのにDockerfileが欠かせないのが分かりますね。

最後にcowsayにfortuneコマンドの結果を渡せるように改良しよう。
ENTRYPOINTに独自に作成したスクリプトを渡せば修正できる。これはDockerfileを作成するときの一般的なパターンらしい。
まず、cowsayに渡す引数がなければfortuneの結果を、引数があればそのままcowsayに渡すシェルスクリプトを作成し、実行権限を付与しておく。

#! /bin/bash
if [ $# -eq 0 ];then
    /usr/games/fortune | /usr/games/cowsay
  else
    /usr/games/cowsay "$@"
fi

これをENTRYPOINTで実行するようにDockerfileを変更する。

FROM debian:latest
RUN apt-get update && apt-get install -y cowsay fortune

# ファイルをホストからイメージのファイルシステムにコピー
COPY entrypoint.sh /

ENTRYPOINT ["/entrypoint.sh"]

これでイメージを再構築し、コンテナを起動させる。

$ docker build -t repo/cowsay-fortune3:latest -f ./Dockerfile .
Sending build context to Docker daemon  3.584kB
Step 1/4 : FROM debian:latest
 ---> 5971ee6076a0
[中略]
$ docker run repo/cowsay-fortune3 
 ________________________________________
/ Your best consolation is the hope that \
| the things you failed to get weren't   |
\ really worth having.                   /
 ----------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
$ docker run repo/cowsay-fortune3  "Moo"
 _____
< Moo >
 -----
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||