アプリの構築、依存オブジェクトとサービスのインストール、デプロイの自動化、まだまだありますが、すべてはDockerfileから始まります。Dockerfileの文法を基本から応用まで説明し、Dockerイメージを作成するベストプラクティスを紹介します。
この記事では、配布するアプリケーション向けに最小構成のLinux(ベースイメージ)を選択するようにDockerに指示をするDockerfileを書き、選択したツールと必要な設定を盛り込んで配信する、すなわち、自分のアプリを動かすのに適した専用のLinuxディストリビューションを作成します。
Dockerを選ぶ理由
Dockerなら「ビルドして配信すればどのようなアプリをどこででも」動かせます。つまり、すべてのバイナリとランタイムライブラリー、バックエンドツール、OSの設定、さらにはアプリケーションを動かすのに必要なサービスまでをアプリケーションと一緒に詰め合わせて、すぐに配信や自動でデプロイできる準備が整います。これはDockerが備えるソフトウェアのコンテナー技術により可能なのです。
この記事では裏方の技術には詳しくは触れませんが、Dockerやソフトウェアコンテナーの定義と役割については『Understanding Docker, Containers and Safer Software Delivery(Dockerとコンテナーによる安全なソフトウェア配信を理解する)』に書かれています。
Dockerのインストール
準備として、Dockerがインストールされているローカル環境かリモートサーバーが必要です。
幸いにもDockerの最新版(執筆時点で1.12)はとてもスムーズにインストールできます。Windows、OS X、Linux用のインストール手引きが用意されています。
Dockerfileについて
Dockerでイメージをビルドするには、ビルドの指示を書いたDockerfileと呼ばれるプレーンテキストファイルとcontext(詳細は後述)が必要です。ファイルの文法はApacheの設定ファイルに似たものです。1つの命令とその引数を1行に書き、すべての命令は順番に実行されます。コメントは#とそのあとの空白の後ろに書きます。Dockerfileができあがると、docker buildというコマンドでイメージをビルドします。ビルドについては後で詳しく紹介します。
Dockerfileを作り始める前に、作業環境を整えます。ホームディレクトリにmy_imageというディレクトリを作ります。ここを作業ディレクトリとして、Dockerfileを入れます。
mkdir ~/my_build
cd ~/my_build
touch Dockerfile
イメージをビルドする用意ができました。
ベースイメージの選択
イメージを作るとき、多くの場合、出発点となるもの、つまりすでにあるほかのイメージを使います。オフィシャルなUbuntu、MySQL、WordPress、またはDocker Hubから入手できる他のイメージや、過去に自分が作成したイメージも使えます。
参考:scratchと呼ばれるDockerが指定する最小構成イメージを使うと、好みのコアツールとディレクトリ構造で自分だけのベースイメージを作成できます。この記事では取り上げませんが、DockerのWebサイトにあるガイド「ベースイメージの作成」で調べられます。
たとえば、最小構成のDebianディストリビューションから始めたければ、次の内容をDockerfileに追加します。
# set the base image
FROM debian
Dockerfileを書くときに最初に使う命令がFROMです。イメージ名の後ろに:とversion_nameを付け加えることで、特定のバージョンのベースイメージが使えます。たとえば、次のようにします。
# set the base image
FROM debian:sid
このコードでは「sid」 Debian (不安定版ディストリビューション)を使っています。同様に特定のバージョンのRubyやPythonのインタープリター、MySQLや手持ちのツールを使いたいとき、オフィシャルなベースイメージを指定するのに使います。この記事では、デフォルト(安定板)のdebianイメージを使います。
maintainerの指定とメタデータの追加
オプションでMAINTAINERを指定できます。Lucero del Albaを、自分の名前かビルド担当者やチーム名に置き換えられます。
# author
MAINTAINER Lucero del Alba
必須ではありませんが、LABEL命令を使ってメタデータを付加できます。付加した情報はdocker inspectコマンドでイメージの情報を取得すれば参照できます。
# extra metadata
LABEL version="1.0"
LABEL description="First image with Dockerfile."
詳しくはDockerオブジェクのLabelに書かれています。
ディストリビューションの作成
必要なものがすべてコンテナーに含まれるように、イメージに入れるツールとライブラリーを選びます。記事の最後には、Linuxディストリビューションのビルドにとても近いことをします。
PostgreSQLデータベースを動かしているようなコンテナーはバックグラウンドで稼働させるものですが、コンテナーを操作するためにコンソールが必要になることがよくあります。ベースイメージには最低限のGNUツールしか用意されていませんので、ツールを追加する必要があります。
■キャッシュへの対応
追加パッケージをイメージにインストールしようとすると、十中八九キャッシュの問題に突き当たります。キャッシュされたメタデータがベースイメージに含まれている一方、稼働中のリポジトリから取得したデータが更新されていることがあるためです。
Debianベースのディストリビューションなら、新たなパッケージをインストールする前に次のコマンドを追加すると回避できます。
# update sources list
RUN apt-get clean
RUN apt-get update
■基本ツールのインストール
コードエディター、ロケール、gitやtmuxのようなツールなど、あとで必要になるものをすべてここでインストールして、イメージにバンドルします。
次のように1行ごとにインストールしていきます。
# install basic apps, one per line for better caching
RUN apt-get install -qy git
RUN apt-get install -qy locales
RUN apt-get install -qy nano
RUN apt-get install -qy tmux
RUN apt-get install -qy wget
すべてのツールを1行でインストールできますが、あとでパッケージを増やしたり減らしたりするときにすべてやり直すことになります。そのため、1行ごとに1パッケージをインストールしてDockerのキャッシュ機能に頼るほうが賢明です。
インストールするツールの数には注意してください。「念のため」にインストールし始めると、ビルド時間とイメージのサイズが増すばかりです。
■アプリ向けランタイムライブラリーのインストール
イメージにはアプリも入れて配信しますが、アプリには特定のバージョンのPHP、Ruby、Pythonや特定のモジュールが必要かもしれません。ここで必要なプログラムとランタイムすべてをインストールします。
このコンテナーは自分のアプリのためだけに作成するものなので、次のように好きなだけ細かく指定できます。
# install app runtimes and modules
RUN apt-get install -qy python3
RUN apt-get install -qy python3-psycopg2
RUN apt-get install -qy python3-pystache
RUN apt-get install -qy python3-yaml
例では、Python 3のPsycopg 2(PostgreSQLデータベース接続用)、Pythonモジュール用のMustache、YAMLモジュールのパッケージとあわせてインストールしています。実際に作成するDockerfileには、必要な依存項目だけをインストールしてください。
■パッケージのコンパイルとダウンロード
ディストリビューションに必要なモジュールやプログラムのパッケージが不足していることもあるでしょう。それでも稼働中のコンテナーに手作業でインストールする必要はありません! 代わりに1行ごとのRUN命令を使うと、アプリケーションに必要などのようなライブラリーでもダウンロード、コンパイル、設定のプロセスをバッチ処理できます。
「アプリの配信」の項で説明しますが、別ファイルにスクリプトを書いてビルドに追加し、実行もできます。
■クリーンアップ
簡素で小さいイメージにするために、インストール手順の最後にクリーンアップをします。
# cleanup
RUN apt-get -qy autoremove
Debianを選んだのでapt-getを使いましたが、実際にはベースイメージのディストリビューションに適したコマンドを使用してください。
アプリの配信
アプリケーションをスムーズに配信し実行できる環境をビルドするために、ADD命令を使って、ファイル、ディレクトリ、リモートURLのコンテンツをイメージに追加します。
ただしイメージに追加する前に、ファイルを適切に配置します。ここでは分かりやすくするために、Dockerfileと一緒にすべてのファイルを後述するmy_buildディレクトリに入れます。
これで、アプリとイメージに入れ込むすべてのファイルに加え、次のファイルが~/my_buildに入っています(app.pyとlib.pyはサブディレクトリapp/の中です)。
.bashrc
.profile
app/app.py
app/lib.py
Dockerfile
.bashrcと.profileスクリプトをコンテナーの/rootディレクトリに追加して、シェルでコンテナを操作するときにいつでも実行できるようにします。また、app/の中身をコンテナの/app/ディレクトリにコピーします。
次の命令を追加します。
# add scripts to the container
ADD .bashrc /root/.bashrc
ADD .profile /root/.profile
# add the application to the container
ADD app /app
環境設定
最後に、システムとアプリケーションレベルで必要な環境変数を設定します。
多くの場合はDebianデフォルトのcharsetで問題ありませんが、各国の読者のためにUTF-8を扱えるターミナルの設定方法を紹介します。localesパッケージを先ほどインストールしたので、charsetを生成してLinux環境を設定します。
# locales to UTF-8
RUN locale-gen C.UTF-8 && /usr/sbin/update-locale LANG=C.UTF-8
ENV LC_ALL C.UTF-8
アプリケーション用の環境変数や、パスワードやパスをやり取りするための環境変数を設定する必要がある場合には、DockerfileのENV命令を使います。
# app environment
ENV PYTHONIOENCODING UTF-8
ENV PYTHONPATH /app/
コンテナーを立ち上げるときに環境変数をコマンドラインから設定することもできるので、パスワードのような機密情報を共有するときに便利です。
Dockerfileの完成
実際には目的にあわせてDockerfileを修正する必要がありますが、どのようなことをするのかは理解できたでしょう。
完成したファイルは次のようになります。
# author
MAINTAINER Lucero del Alba
# extra metadata
LABEL version="1.0"
LABEL description="First image with Dockerfile."
# set the base image
FROM debian
# update sources list
RUN apt-get clean
RUN apt-get update
# install basic apps, one per line for better caching
RUN apt-get install -qy git
RUN apt-get install -qy locales
RUN apt-get install -qy nano
RUN apt-get install -qy tmux
RUN apt-get install -qy wget
# install app runtimes and modules
RUN apt-get install -qy python3
RUN apt-get install -qy python3-psycopg2
RUN apt-get install -qy python3-pystache
RUN apt-get install -qy python3-yaml
# cleanup
RUN apt-get -qy autoremove
# add scripts to the container
ADD .bashrc /root/.bashrc
ADD .profile /root/.profile
# add the application to the container
ADD app /app
# locales to UTF-8
RUN locale-gen C.UTF-8 && /usr/sbin/update-locale LANG=C.UTF-8
ENV LC_ALL C.UTF-8
# app environment
ENV PYTHONIOENCODING UTF-8
ENV PYTHONPATH /app/
イメージのビルド
my_buildディレクトリでdocker buildコマンドを使います。このとき、-tフラグでイメージに名前タグを付加できるので、例ではmy_imageとしました。コマンド末尾の.はDockerfileとcontextが現在のディレクトリに入っているという意味です。contextはその場所にあるDockerfile以外のファイルのことです。
cd ~/my_build
docker build -t my_image .
実行するとDockerfileの命令を「step」ごとに記載した長大な出力が得られます。出力結果の一部を次に示します。
Sending build context to Docker daemon 5.12 kB
Step 1 : FROM debian
---> 7b0a06c805e8
Step 2 : MAINTAINER Lucero del Alba
---> Running in d37e46e5455d
---> 2d76561de558
Removing intermediate container d37e46e5455d
Step 3 : LABEL version "1.0"
---> Running in 904dde1b4cd7
---> a74b7a492aaa
Removing intermediate container 904dde1b4cd7
Step 4 : LABEL description "First image with Dockerfile."
---> Running in 9aaef0353256
---> 027d8c10e966
Removing intermediate container 9aaef0353256
Step 5 : RUN apt-get clean
---> Running in bc9ed85dda16
---> a7407036e74a
Removing intermediate container bc9ed85dda16
Step 6 : RUN apt-get update
---> Running in 265e757a7563
Get:1 http://security.debian.org jessie/updates InRelease [63.1 kB]
Ign http://deb.debian.org jessie InRelease
Get:2 http://deb.debian.org jessie-updates InRelease [145 kB]
Get:3 http://deb.debian.org jessie Release.gpg [2373 B]
Get:4 http://deb.debian.org jessie Release [148 kB]
Get:5 http://security.debian.org jessie/updates/main amd64 Packages [402 kB]
Get:6 http://deb.debian.org jessie-updates/main amd64 Packages [17.6 kB]
Get:7 http://deb.debian.org jessie/main amd64 Packages [9064 kB]
Fetched 9843 kB in 10s (944 kB/s)
Reading package lists...
---> 93fa0a42fcdc
Removing intermediate container 265e757a7563
Step 7 : RUN apt-get install -qy git
---> Running in c9b93cecd953
(...)
イメージのリスト化
docker imagesコマンドでイメージのリストを作ります。
docker images
コマンドを実行すると、新たに作成したmy_imageとダウンロードしたほかのベースイメージが次のように出力されます。
REPOSITORY TAG IMAGE ID CREATED SIZE
my_image latest e71dc183df2b 8 seconds ago 305.6 MB
debian latest 7b0a06c805e8 2 weeks ago 123 MB
debian sid c1857cb435d7 3 weeks ago 97.77 MB
完成しました。いつでもこのイメージを配信して実行できます。
コンテナの起動
最後に、作成したイメージのインタラクティブなターミナルをdocker runコマンドで立ち上げます。
docker run -ti my_image /bin/bash
さらに詳しく知りたい人のための資料
この記事で取り上げたのはDockerfileの可能性の一部だけで、ほかにも特にサービスを実行しコンテナ間をリンクするためにポートをEXPOSEする方法や、コンテナが稼働していることをHEALTHCHECKで確認する方法、ホストマシンに対するデータ保存や読み出しのVOLUMEを指定する方法など多くの便利な機能があります。
機会があれば紹介しますが、現時点では次のような資料があります。
Docker Webサイトの資料
- Dockerfile reference(Dockerfileレファレンス)
- Best practices for writing Dockerfiles(Dockerfile作成のベストプラクティス)
SitePointの資料
- 『Understanding Docker, Containers and Safer Software Delivery(Dockerとコンテナによる安全なソフトウェア配信を理解する)』
- Dockerサブチャンネル
- すべてのDocker関連記事
(原文:How to Build an Image with the Dockerfile)
[翻訳:内藤夏樹/編集:Livit]