第4章: Dockerfile で挙動を再認識

docker hub に存在する image を活用すれば一瞬で新しい環境がつくれてとても便利です。実際に開発を進める場合は docker hub に存在する image に開発に最適な条件をくみあわせることが多いですがその時に場当たり的にコマンドを手動でうちつづけると再現性がよくありません。そういう場合は Dockerfile を活用します。

docker 環境の題材について

今回はプログラミング言語 Perl を題材にしています。Perl を選んでいる理由は私が Perl で開発の仕事をしているのでその辺はご容赦ください。基本的にどのプログラミング言語でも考え方は共通すると思うので、他の言語で構築される場合も参考になるかと思います。ご要望があればまたの機会で別の言語での構築方法も紹介したいと思います。

取り扱っているツールについて少し説明

  • Perl 5.32: プログラミング言語アプリケーション、2020/12月現在で最新版
  • plenv: Perl をバージョンごとにインストールするためのアプリ
  • cpanm: Perl の拡張モジュールをインストールするためのアプリ
  • Carton: Perl の拡張モジュールのインストール状況を管理するためのアプリ
  • Mojolicious: Perl製の Web アプリケーション開発フレームワーク

4.1 作成する docker 環境の概要について

  • 最終的に ctr-dev-perl-5.32.0 コンテナの中に入って作業ができる状態にする
  • 配布するのは Dockerfile のみとして Dockerfile を起動すればコンテナが再現できるようにしたい
  • Perlバージョンは固定されているので plenv はインストールしないことにする
  • cpanm と Carton までを準備して必要なモジュールをインストールする環境までつくる

4.1.1 この章で作りたいコンテナの概念図

        +-----------------------+
        |       container       |
        | (ctr-dev-perl-5.32.0) |
        +-----------------------+
        ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
      +---------------------------+
      |           image           |
      |     (dev-perl-5.32.0)     |
      +---------------------------+
      ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
    +-------------------------------+
    |          Dockerfile           |
    +-------------------------------+
    ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
  +-----------------------------------+
  |               Docker              |
  +-----------------------------------+
+---------------------------------------+
|               OS (Mac)                |
+---------------------------------------+

4.1.2 image の構成図

+--------------------------+
| image                    |
|        +--------+        |
|        | Carton |        |
|        +--------+        |
|      +------------+      |
|      |   cpanm    |      |
|      +------------+      |
|    +----------------+    |
|    | Perl (5.32.0)  |    |
|    +----------------+    |
|  +--------------------+  |
|  | OS (Linux-Debian)  |  |
|  +--------------------+  |
+--------------------------+

4.2 docker の公式イメージを活用

docker hub には各種プログラミング言語やミドルウェアアプリケーションとよばれているものの公式イメージがたくさん存在します。自分でイメージを作ることもできるのですが、特別な理由がなければ公開されているイメージを活用するのが間違いは少ないようです。ここでは公式イメージを流用してオリジナルのイメージ名をつけたイメージを作成します。

オリジナルのイメージは ~/tmp ディレクトリに作成することにしましょうか

メモ

  • perl の公式 docker hub を活用する
  • Perl - <https://hub.docker.com/_/perl>
  • docker hub の情報によれば Perl 公式イメージの OS は Linux-Debian となっている
  • perl のバージョンは 5.32.0 を指定する
  • イメージ名は s2-dev-perl-5.32.0 にする

4.2.1 公式イメージの概念図

 s2-dev-perl-5.32.0
+--------------------------+
| image                    |
|    +----------------+    |
|    | Perl (5.32.0)  |    |
|    +----------------+    |
|  +--------------------+  |
|  | OS (Linux-Debian)  |  |
|  +--------------------+  |
+--------------------------+ 

4.2.2 コマンドの解説

  • docker image build -t s2-dev-perl-5.32.0 ~/tmp/docker_sample/
    • ~/tmp/docker_sample/ に存在する Dockerfile を使って s2-dev-perl-5.32.0 という名前のイメージ作成

4.2.3 Dockerfile を使ってイメージを作成する例

 (今回は tmp というディレクトリにサンプルをつくる)
% mkdir -p ~/tmp/docker_sample/
(docker hub のイメージを流用するならたった1行でよい)
% echo 'FROM perl:5.32.0' > ~/tmp/docker_sample/Dockerfile
(ファイルが存在するディレクトリまで書けば Dockerfile という名前のファイルを自動的にさがしてくれる)
% docker image build -t s2-dev-perl-5.32.0 ~/tmp/docker_sample/
(公式のイメージと Dockerfile で指示された通りのイメージと二つできる)
% docker image ls -a
REPOSITORY             TAG        IMAGE ID       CREATED         SIZE
perl                   5.32.0     daf89651cb1c   21 hours ago    859MB
s2-dev-perl-5.32.0     latest     daf89651cb1c   21 hours ago    859MB
今作った Perl イメージからのコンテナを作ってコンテナのなかに入ってみます。

コンテナの中に入ってOSがどうなっているのか、本当に Perl が準備されているのかなど実際に見てみましょう。cpanm も準備されていることが確認できます。

4.2.3 オリジナルのイメージからコンテナ起動の概念図

+--------------------------+
|         container        |
| (ctr-s2-dev-perl-5.32.0) |
+--------------------------+
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
+---------------------------+
|           image           |
|    (s2-dev-perl-5.32.0)   |
+---------------------------+

4.2.4 コンテナ起動の例

 (Debian ならば bash は使える)
% docker container run -it --name ctr-s2-dev-perl-5.32.0 s2-dev-perl-5.32.0 /bin/bash
(OS の情報)
root@b8c976e44479:/# cat /etc/os-release
(ディレクトリ構造も確認)
root@b8c976e44479:/# ls -al
(Perl のバージョン確かめておく)
root@b8c976e44479:/# which perl
root@b8c976e44479:/# perl --version
This is perl 5, version 32, subversion 0 (v5.32.0) built for x86_64-linux-gnu
(cpanm はすでに入っている)
root@b8c976e44479:/# which cpanm
/usr/local/bin/cpanm
(carton は別途用意が必要)
root@b8c976e44479:/# which carton
(慣例的に /usr/src/ にプログラムのソースコードを置くやり方があるので状況確認)
root@b8c976e44479:/# ls -al /usr/src/
root@b8c976e44479:/# exit
exit
(コンテナから抜けた後にコンテナの状況をみておく)
% docker container ls -a
CONTAINER ID   IMAGE ...
b8c976e44479   s2-dev-perl-5.32.0 ...
(起動中のコンテナはない)
% docker ps
CONTAINER ID   IMAGE ...
(コンテナをバックグラウンドで起動し、中に入り、コンテナを終了)
% docker container start ctr-s2-dev-perl-5.32.0
ctr-s2-dev-perl-5.32.0
% docker container exec -it ctr-s2-dev-perl-5.32.0 /bin/bash
root@b8c976e44479:/# exit
exit
(コンテナは起動したままの状態になっている)
% docker ps
CONTAINER ID   IMAGE ...
b8c976e44479   s2-dev-perl-5.32.0 ...
(コンテナの終了を確認まで)
% docker container stop ctr-s2-dev-perl-5.32.0
ctr-s2-dev-perl-5.32.0
% docker ps
CONTAINER ID   IMAGE ...

4.3 carton がインストールされたイメージを作成

perl の公式イメージには cpanm が使える状態ということがわかりました。この公式イメージを流用して carton が使える状態のイメージを作成したいと思います。前回利用した ~/tmp/docker_sample/Dockerfile の内容を書き換えて新しい名前のイメージを作ってみます。

メモ

  • ~/tmp/docker_sample/Dockerfile を上書きして作る
  • イメージ名は s3-dev-perl-5.32.0 にする

4.3.1 公式イメージに carton 追加した概念図

 s3-dev-perl-5.32.0
+--------------------------+
| image                    |
|        +--------+        |
|        | Carton |        |
|        +--------+        |
|      +------------+      |
|      |   cpanm    |      |
|      +------------+      |
|    +----------------+    |
|    | Perl (5.32.0)  |    |
|    +----------------+    |
|  +--------------------+  |
|  | OS (Linux-Debian)  |  |
|  +--------------------+  |
+--------------------------+

4.3.2 Dockerfile を書き換えてイメージを作成する例

  (dockerfile を書き換える)
% echo 'FROM perl:5.32.0' > ~/tmp/docker_sample/Dockerfile
% echo 'RUN cpanm Carton' >> ~/tmp/docker_sample/Dockerfile

(イメージの名前をかえて実行)
% docker image build -t s3-dev-perl-5.32.0 ~/tmp/docker_sample/

(オリジナルのイメージより少しサイズがおおいきイメージができる)
% docker image ls -a
REPOSITORY ...         SIZE
s3-dev-perl-5.32.0 ... 869MB
perl ...               859MB
s2-dev-perl-5.32.0 ... 859MB
今作った carton が含まれた Perl のコンテナのなかに入って確かめてみます。

4.3.3 コンテナの中でシステム構築状態を確認する例

  (今回つくったものは carton がインストールされている)
% docker container run -it --name ctr-s3-dev-perl-5.32.0 s3-dev-perl-5.32.0 /bin/bash
root@83f7e16e06ca:/# which carton
/usr/local/bin/carton
root@83f7e16e06ca:/# carton --version
carton v1.0.34
root@83f7e16e06ca:/# exit
exit

4.4 好みのモジュールを配布したい場合

書き捨てのスクリプトを作成するのであれば今作った docker file を配布すればことは足りるのですが、本格的なアプリを開発する場合拡張モジュールを活用することが多いでしょう。例えば web システムを開発する場合、大抵は web アプリケーションフレームワークというものを活用します。 perl の場合は Mojolicious (Mojo) というフレームワークがあります。web アプリケーションフレームワークとは拡張モジュールのひとつなので、モジュールをインストールした状態のイメージを配布するのか配布したイメージから都度インストールするものなのか、悩ましいところです。

今回は (Runtime) モジュールを都度インストールするやり方を取りたいと思います、web システムを開発する場合は開発途中で新しく拡張モジュールを追加することがよくありますので、柔軟に対応できるようにした方が使い勝手は良いかと思います。

4.4.1 参考図

下記の図のように二つの考え方がある
  (Bundled)モジュールを含めるやり方
+--------------------------+     +--------------------------+
| image                    |     | container                |
|         +------+         |     |         +------+         |
|         | Mojo |         |     |         | Mojo |         |
|         +------+         |     |         +------+         |
|        +--------+        |     |        +--------+        |
|        | Carton |        |     |        | Carton |        |
|        +--------+        |     |        +--------+        |
|      +------------+      | --> |      +------------+      |
|      |   cpanm    |      |     |      |   cpanm    |      |
|      +------------+      |     |      +------------+      |
|    +----------------+    |     |    +----------------+    |
|    | Perl (5.32.0)  |    |     |    | Perl (5.32.0)  |    |
|    +----------------+    |     |    +----------------+    |
|  +--------------------+  |     |  +--------------------+  |
|  | OS (Linux-Debian)  |  |     |  | OS (Linux-Debian)  |  |
|  +--------------------+  |     |  +--------------------+  |
+--------------------------+     +--------------------------+

(Runtime) モジュールを都度インストールするやり方
                                                                  +--------------------------+
                                                                  | container (install)      |
                                                                  |         +------+         |
+--------------------------+     +--------------------------+     |         | Mojo |         |
| image                    |     | container                |     |         +------+         |
|        +--------+        |     |        +--------+        |     |        +--------+        |
|        | Carton |        |     |        | Carton |        |     |        | Carton |        |
|        +--------+        |     |        +--------+        |     |        +--------+        |
|      +------------+      | --> |      +------------+      | --> |      +------------+      |
|      |   cpanm    |      |     |      |   cpanm    |      |     |      |   cpanm    |      |
|      +------------+      |     |      +------------+      |     |      +------------+      |
|    +----------------+    |     |    +----------------+    |     |    +----------------+    |
|    | Perl (5.32.0)  |    |     |    | Perl (5.32.0)  |    |     |    | Perl (5.32.0)  |    |
|    +----------------+    |     |    +----------------+    |     |    +----------------+    |
|  +--------------------+  |     |  +--------------------+  |     |  +--------------------+  |
|  | OS (Linux-Debian)  |  |     |  | OS (Linux-Debian)  |  |     |  | OS (Linux-Debian)  |  |
|  +--------------------+  |     |  +--------------------+  |     |  +--------------------+  |
+--------------------------+     +--------------------------+     +--------------------------+

web アプリ開発に至っては Runtime なモジュール管理の方が使い勝手はよい

4.4.2 Bundled と Runtime の使い勝手の比較

  • Bundled
    • イメージをそのまま配布できる
    • イメージからコンテナを立ち上げる一瞬で環境が全て整っている
    • モジュールのバージョンや構成が変わった場合イメージから作り直し
    • イメージの汎用性は下がる
  • Runtime
    • イメージからコンテナを立ち上げた後にインストールをしなければいけない
    • モジュールをインストールする一手間がかかる
    • モジュールのバージョンや構成が変わってもイメージは変更しなくてよい
    • イメージの汎用性は上がる
具体的な手順の例 (Runtime なやり方)

4.4.3 コマンドの解説

  • docker container run -it --name ctr-s4-dev-perl-5.32.0 -v "$PWD":/usr/src/app s4-dev-perl-5.32.0 /bin/bash
    • -v オプションで今いるディレクトリとコンテナの中の /usr/src/app をむすびつける

4.4.4 コンテナの中でモジュールをインストールする例

(dockerfile を書き換える)
% echo 'FROM perl:5.32.0' > ~/tmp/docker_sample/Dockerfile
% echo 'RUN cpanm Carton' >> ~/tmp/docker_sample/Dockerfile
% echo 'RUN mkdir -p /usr/src/app' >> ~/tmp/docker_sample/Dockerfile
% echo 'WORKDIR /usr/src/app' >> ~/tmp/docker_sample/Dockerfile

(carton を使ってインストールするために cpanfile を用意)
% echo "requires 'Mojolicious', '== 8.67';" > ~/tmp/docker_sample/cpanfile

(イメージの名前をかえて実行)
% docker image build -t s4-dev-perl-5.32.0 ~/tmp/docker_sample/
Sending build context to Docker daemon  3.072kB
(... 省略)

(コンテナの作り方にひと工夫、いまいるディレクトリとコンテナの中の作業場所をあわせる)
% cd ~/tmp/docker_sample/
% docker container run -it --name ctr-s4-dev-perl-5.32.0 -v "$PWD":/usr/src/app s4-dev-perl-5.32.0 /bin/bash

(コンテナの中の状況確認、ファイルが同期されている)
root@993ef5d79012:/usr/src/app# ls -al
total 12
(... 省略)

(コンテナの中でモジュールインストール)
root@993ef5d79012:/usr/src/app# carton install
Installing modules using /usr/src/app/cpanfile
(... 省略)

(モジュール確認)
root@993ef5d79012:/usr/src/app# ls -al
total 16
(... 省略)

(コンテナの外でもファイルができている)
root@993ef5d79012:/usr/src/app# exit
exit
% ls -al ~/tmp/docker_sample/
total 24
(... 省略)

(次回からはコンテナを起動中にしてその中に入る)
% docker container start ctr-s4-dev-perl-5.32.0
ctr-s4-dev-perl-5.32.0
% docker container exec -it ctr-s4-dev-perl-5.32.0 /bin/bash

(今度は mojo のコマンドを実行)
root@993ef5d79012:/usr/src/app# carton exec -- mojo generate app MyApp
  [mkdir] /usr/src/app/my_app/script
(... 省略)

(いったぬけて、コンテナの外を確認)
root@993ef5d79012:/usr/src/app# exit
exit
% ls -al ~/tmp/docker_sample/
total 24
(... 省略)

(起動中のコンテナ終了)
% docker container stop ctr-s4-dev-perl-5.32.0
ctr-s4-dev-perl-5.32.0

4.5 まとめ

今回はイメージとコンテナをたくさん作りましたので不要なものはここで一旦削除しておきましょう。最後にここで作成した Dockerfile の完成形と再現するためのコマンドを紹介して終わりにします。

(使わないコンテナを削除)
% docker container rm \
ctr-s4-dev-perl-5.32.0 \
ctr-s3-dev-perl-5.32.0 \
ctr-s2-dev-perl-5.32.0

(使わないイメージ削除)
% docker image rm \
s4-dev-perl-5.32.0 \
s3-dev-perl-5.32.0 \
s2-dev-perl-5.32.0

Dockerfile について解説

  • ~/tmp/docker_sample/Dockerfile

# docker hub 公式 perl を親イメージとして使用
FROM perl:5.32.0
# RUN を使うとシェルで実行できる
RUN cpanm Carton
# コンテナにアプリのための場所を作る
RUN mkdir -p /usr/src/app
# 作業場所を指定
WORKDIR /usr/src/app
  • docker コマンドだけで進むとオプションをたくさんつけるのでわかりにくい
  • コンテナをもし複数同時に立ち上げたい場合はコマンド操作が煩雑になる
  • 環境構築を楽にしたいための docker なのでできるだけ簡素なコマンドですませるためには docker-compose を活用する

4.6 次回

この章では Dockerfile をつかってイメージを作るというところをやりました。Dockerfile を配布すれば簡単な環境であれば開発環境を共有することはできます。しかし今のままでのやり方では環境を再現するのにたくさんのコマンドを入力しないといけなので使い勝手はあまり良くないようです。

次回は docker-compose を活用したやり方を紹介したいと思います。