前回(第1回)は、Dockerコンテナに対応するアプリケーションを開発・実行するために、Docker Composeというツールを使うのが便利ということで、例としてDocker Composeを使ってWordPressをコマンド1つで実行する方法を紹介しました。WordPressのような、しっかりとしたアプリケーション以外でもDocker Composeが使える場面があります。

今回は、Docker Composeを使ってウェブサーバ(Apache httpd)を実行し、コンテンツを表示する例を見ていきましょう。

なぜDocker Composeなのか?

単純にウェブサーバとして実行するアプリケーションであれば、Dockerだけで何ら困らないでしょう。例えば、Apache httpdサーバを実行するには、次のようにしてコンテナを実行できます。

docker run -d httpd

しかし、実際には、複数のコンテナ(ウェブサーバやデータベースサーバなど)を同時に起動し、相互に通信するケースも多いでしょう。

docker run -d httpd
docker run -d mariadb
…等々

そうなりますと、コンテナ間の通信のためにネットワークの管理が必要ですし、データを共有するためのボリューム管理も必要です。毎回dockerコマンドを使っても実現できますが、あまりDockerらしいスマートなやりかたとはいえません。コンテナの活用にふさわしい、スケールしやすい環境を整えるためには、Docker Composeの活用が楽でしょう。

単純なApache httpdサーバをComposeで起動するには?

今回は簡単な例として、シンプルなウェブサーバ(Apache httpd)を、ComposeファイルとDockerfileを書いて実行する例を紹介します。

Dockerでhttpdコンテナを実行する方法

Apache httpdを1つだけ起動するのであれば、Docker Composeを使わなくても非常にシンプルに済みます。次のコマンドは、Apache httpdが入っているDockerイメージ「httpd:alpine」を実行するものです。コマンドを補足しますと、イメージ名に付けるタグ「alpine」とは、Alpine Linuxベースのイメージにhttpdをセットアップしたもので、容量が110MB程度と小さなものです。オプションの「-d」はデタッチド・モードとしてバックグラウンドでコンテナを実行します。そして「-p 8080:80」は、ホスト側のポート8080をコンテナ内のポート80にマッピング(割り当て)します。

docker run -d -p 8080:80 --name docker-httpd --rm httpd:alpine

それから、コンテナが起動しているかどうかを調べましょう。「docker ps」コマンドを実行したら、次のような表示になります。

# docker ps
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                  NAMES
3c872b356aea        httpd:alpine        "httpd-foreground"   4 seconds ago       Up 2 seconds        0.0.0.0:8080->80/tcp   docker-httpd

もしも、ホスト側で既にポート8080を使用中の場合、コンテナは起動に失敗し、コンテナは実行中になりません。その場合はポート番号を8080のかわりに8090など、空いている未使用ポートを指定して起動しなおします。あとは、ブラウザから「http://<サーバのIPアドレス>:8080」を開けば、次のようなApache初期画面が表示されます。

停止するには、次のコマンドを実行するだけです。

docker kill docker-httpd

コンテナ実行時に「--rm」オプションを付けていましたので、コンテナのプロセスが停止後は、自動的にコンテナ(用のイメージレイヤ及びログなど関連情報)も自動的に削除されます。

Docker Composeでhttpdコンテナを実行する方法

次はDocker Composeを使ってhttpdを実行する方法を見ていきます。Docker Composeを使ってウェブサーバを立ち上げるには、まず、作業用のディレクトリを作成し、そのディレクトリに移動する必要があります。ここではdocker-httpdという名前のディレクトリを作成し、移動しましょう。

$ mkdir docker-httpd && cd docker-httpd

ここでディレクトリを作成・移動したのは、Docker Composeで操作するときに作業対象となる「プロジェクト」を準備するためです。Docker Composeコマンドの実行時、現在操作している(カレントの)ディレクトリにある「docker-compose.yaml」、又は「docker-compose.yml」を探し、その記述内容に従ってコンテナやネットワークなどを一括操作します。
この作業ディレクトリのことを、Docker Composeでは「プロジェクト」と呼びます。デフォルトでは、ディレクトリ名をプロジェクト名として扱います。そのため、今回の例ではディレクトリ名「docker-httpd」が、プロジェクト名にもなります。なお、このプロジェクト名に結び付けて、ネットワーク名やボリューム名も管理されます。
次に、ディレクトリ内で「docker-compose.yaml」ファイルを作成します。この拡張子が示すように、内容の記述はYAML形式の書式に従う必要があります。
お好みのエディタでファイルを開き、内容を次のようにします。

version: '3'
services:
  web:
    build: .
    ports:
      - "8080:80"

内容を上から順番に説明します。

  • 「version: ‘3’」 …Docker Composeファイル形式のバージョン指定です。バージョンによって細かな記法や挙動が異なります。現時点では「3」を指定しておくのが望ましいでしょう。ネットワークやボリュームも操作可能であり(バージョン2以降)、かつ、Swarmモードでのサービス実行にも対応しています。
  • 「services:」 … コンテナとして実行するアプリケーション”サービス”の意味です。通常は、この中で、どのようにしてコンテナを実行するのか定義します。
  • 「web」 … これは「web」という名称のサービスを定義しています。
  • 「build: .」 … 「.」(現在のディレクトリ)にあるDockerfileでDockerイメージをビルドします。
  • 「ports:」 … このサービスが公開するポートの指定です。”ホスト側のポート番号:コンテナ内のポート番号”の順番です。

それから、次の内容で Dockerfile を準備します。

FROM httpd:alpine

これは「httpd:alpine」という名称のDockerイメージをベースにしてDockerイメージをビルド(構築)する命令です。このDockerfileはDocker Composeだからといって特段構文は変わりません。通常のDockerfile同様、必要があれば命令行を好きなように追加・編集できます。
これで、同一ディレクトリに「docker-compose.yaml」と「Dockerifle」が整いました。

DockerfileとComposeファイルでビルドする

次に、httpd アプリケーションを実行する前に、イメージのビルドを行います。Docker イメージの場合は「docker image build」か「docker build」コマンドを使いました。Docker Composeの場合は「docker-compose build」コマンドでイメージをビルドします。

$ docker-compose build
Building web
Step 1/1 : FROM httpd:alpine
alpine: Pulling from library/httpd
Digest: sha256:ca20eca5ae5c1be31bdf99d700d86d9164edd71bcf519325bde67ed04aa1008f
Status: Downloaded newer image for httpd:alpine
 ---> cb171b88ec92
Successfully built cb171b88ec92
Successfully tagged apache_web:latest

このコマンドは、イメージをビルドする「docker image build」コマンドを都度実行しなくても、自動的にDockerfileが処理され、イメージが作成されています。今回は1つのDockerイメージがビルド対象ですが、複数のDockerイメージを自動ビルドすることも可能です。ビルドするたび、毎回タグを指定する必要がないため、複数のイメージを同時に扱う場合には、より強力な手助けになるでしょう。
以上で準備が調いました。

Docker Composeでプロジェクトの実行

準備が整ったあとはサービスを実行します。「docker-compose up」コマンドに、デタッチド・モードとしてバックグラウンドで実行するのを示す「-d」オプションも付与します。(なお、このオプションがなければフォアグラウンドで実行しますので、その場合は「Ctrl+C」で中断できます。)

$ docker-compose up -d
Creating network "dockerhttpd_default" with the default driver
Creating dockerhttpd_web_1 ... done

このとき、出力結果の1行目を見ますと、自動的に専用のブリッジ・ネットワーク「dockerhttpd_default」が作成されています。これは、同一プロジェクト内のみで通信可能な、専用のブリッジ・ネットワークを自動的に作成しています。先ほどのYAMLファイルでポートのマッピングを「8080:80」と指定していたのを覚えていますでしょうか。

    ports:
      - "8080:80"

これは、「ホスト側のポート8080」を、このブリッジ・ネットワーク「dockerhttpd_defaultネットワーク内のポート80にマッピングする」という意味でした。なお、今回の例では実行するコンテナは1つですが、もしサービスとして複数のコンテナを実行する場合は、ブリッジネットワーク内で自動的にアクセスの負荷分散(ラウンドロビン方式)が処理されます。
それから、サービスが実行中かどうかは「docker-compose ps」コマンドを使います。

$ docker-compose ps
      Name              Command        State          Ports
-------------------------------------------------------------------
dockerhttpd_web_1   httpd-foreground   Up      0.0.0.0:8080->80/tcp

サービスの状態を示す「State」列が「Up」であれば実行中です。ブラウザから「http://<サーバのIPアドレス>」を表示すると、先ほどと同様にApacheの初期画面「It works!」が表示されましたでしょうか。

起動したサービスを停止する

一旦起動したサービスは停止したり、再起動したりできます。停止するには「docker-compose stop」です。

$ docker-compose stop
Stopping dockerhttpd_web_1 ... done

状態を調べるコマンドを実行すると、状態(State)が終了(Exit)となっているのが分かります。

$ docker-compose ps
      Name              Command         State     Ports
-------------------------------------------------------
dockerhttpd_web_1   httpd-foreground   Exit 137

停止しているサービスを再び起動するには「docker-compose up -d」を実行します。

$ docker-compose up -d
Starting dockerhttpd_web_1 ... done

サービスのデバッグ

サービスが実行中の場合、コンテナ内で操作することができます。サービスとして実行中のコンテナに対し、プロセスを追加するには「docker-compose exec <サービス名> <コマンド>」を実行します。
今回は「web」というサービスを実行中です。このコンテナ内に「/bin/bash」プロセスを追加するには、次のように実行します。

# docker-compose exec web /bin/bash
bash-5.0#

コンテナ内での操作は通常の「bash」ですので、「exit」で終了できます。
また、コンテナの中に入る必要がなければ「docker-compose logs」コマンドで、サービスの標準出力を確認することもできます。

サービスの終了・削除

使わなくなったサービスは終了・削除します。このとき、1つ1つ「docker kill」コマンドなどで止める必要がありません。「docker-compose down」コマンドを使うと、Dockerイメージの停止・削除と、このプロジェクトが使用するブリッジ・ネットワークも自動削除します。

$ docker-compose down -v
Stopping dockerhttpd_web_1 ... done
Removing dockerhttpd_web_1 ... done
Removing network dockerhttpd_default

Dockerfile の修正と、サービス実行時にビルドも行う方法

実際の利用シーンでは、頻繁にDockerfileの書き換えも発生するでしょう。今回はApacheのドキュメント・ルート(サイトにアクセス時、トップページとして表示されるディレクトリ)に書き換えたindex.htmlを置きます。まずは、docker-httpdディレクトリ内で「Hello World」を表示するindex.htmlファイルを作成します。

$ echo '<h1>Hello World</h1>' > index.html
$ cat index.html
<h1>Hello World</h1>

Dockerfileも編集し、作成したindex.htmlをCOPY命令でコンテナ内のドキュメント・ルートにコピーします。Dockerfileは次のように書き換えます。

FROM httpd:alpine
COPY ./index.html /usr/local/apache2/htdocs/index.html

それから、Docker Composeを使い、実行時にビルドも同時に行うオプション「--build」を付けて実行します。

$ docker-compose up -d --build
Creating network "dockerhttpd_default" with the default driver
Building web
Step 1/2 : FROM httpd:alpine
 ---> cb171b88ec92
Step 2/2 : COPY ./index.html /usr/local/apache2/htdocs/index.html
 ---> 7a499107c39a
Successfully built 7a499107c39a
Successfully tagged dockerhttpd_web:latest
Creating dockerhttpd_web_1 ... done

それから改めてブラウザで「http://<サーバのIPアドレス>:8080」にアクセスしましょう。次のように表示されていれば正常です。

あとはCOPY行を書き換えれば、任意のコンテンツを表示できます。

今回の振り返り

今回はApache httpdを例に、DockerコンテナではなくDocker Composeのサービスとしてアプリケーションを扱う方法を試しました。次回以降は、より細かな設定方法や操作など、Docker Composeが行えることを深掘りしていきます。