実習で学ぶDocker入門 | 4. Docker Composeを使用して複数コンテナをデプロイする

これまでは1つのイメージまたはコンテナを扱う方法を進めてきた。しかし実際の開発では、データベースやWebサーバーなど複数の工程を組み合わせてシステムを構成することが一般的である。Dockerを使ったシステムでは、1つの関心事を1つのコンテナに割り当てる(Each container should have only one concern)ことが公式ドキュメントでも推奨されており、必然的に複数コンテナを扱う場合がよくある。本書では、開発およびテスト環境向けにローカルホストで複数コンテナを起動する方法として、Docker Composeというツールを紹介する。

複数コンテナ管理ツールDocker Composeの概要

Docker Composeは、複数コンテナで構成されたDockerアプリケーションの設定を作成し、実行するためのツールである。

複数コンテナ(データベース、キャッシュ、Web APIなど)で構成されたシステムをdockerコマンドだけで起動する場合、個別コンテナの設定をパラメータとして指定しながら、コンテナ依存関係の順序に沿ってコマンドを実行しなければならない。

一方、Docker Composeを使用すると、複数コンテナの設定と依存関係を設定ファイルに記述し、1つのコマンドで複数コンテナをまとめて実行できる。

Docker Composeが使用される主なケース

開発環境の標準化

チームメンバーがルールどおりに開発環境を手動で整備すると、時間がかかるだけでなく手作業のミスを引き起こす可能性がある。Docker Composeを使用して設定ファイルを渡せば、開発環境の詳細を気にせず、1つのコマンドで環境を整備できる。これにより開発環境整備作業を簡素化できる。

テスト環境の自動化

CI(Continuous Integration)とCD(Continuous Deployment)を行う場合、end to endテスト(すべての構成要素を結合してテストすること)を自動化するには、理想的にはテストごとに独立したテスト環境を提供する必要がある。Docker Composeを使用すれば、必要なテスト環境をコマンド1つで起動でき、検査が完了したら簡単に破棄することもできる。

単一ホストデプロイ

運用環境が単一サーバーの場合、Docker Composeを使用してデプロイできる。詳しくはドキュメントを参照してほしい。

ただし、アプリケーションを拡張するために複数ノードへデプロイする場合は、一般的にDocker EngineのSwarm modeという機能、またはAWS ECSGoogle Container Engineのようなクラウドクラスタマネージャーのほうが適している。

単一コンテナからDocker Composeを理解する

Docker Compose設定ファイル(docker-compose.yml)の作成と動かし方を知るため、Dockerイメージビルドページで作成したイメージを使用し、単一コンテナを起動・停止する方法を紹介する。事前に実習していない場合は、先に実習してから進めてほしい。

docker-compose.ymlの作成

次は前に作成したイメージからコンテナを起動するための設定ファイルである。この設定ファイルをdocker-compose.ymlという名前で保存する。

version: '3'
services:
  myfirstapp:
    image: myfirstapp
    ports:
    - "8888:5000"

この設定ファイルの内容は次のとおりである。

  • Docker Composeファイルフォーマットバージョンは3である。
    • フォーマットの詳細は公式ドキュメントを参照してほしい。
    • 注意: Docker Composeファイルフォーマットバージョン3はDocker Engine 1.13.0以上で動作可能である。インストールされていない場合は、「Dockerイメージコンテナ管理メカニズム」ページを参考にインストールする。またDocker for Windows、Docker for Mac、Docker Toolboxをインストールする場合、Docker Composeも自動的にインストールされるため、追加インストールは不要である。Linuxへのインストール方法は公式ドキュメントを参照してほしい。
  • サービス名はmyfirstappである。
    • servicesの下にある項目は外部から認識できる機能単位であり、「サービス」と呼ぶ。
    • ただしサービスはコンテナに接続されるため、サービスの実体はコンテナである。
  • サービスに接続されるコンテナはmyfirstappというイメージから起動される。
  • コンテナの5000番ポートをホストの8888番にマッピングする。これにより、コンテナのWebサーバーへhttps://localhost:8888でアクセスできる。

Docker Composeでコンテナを起動する

docker-compose.ymlがあるディレクトリで次のコマンドを実行するとコンテナが起動する。

$ docker-compose up
.. 中間省略 ...
Creating network "myfirstapp_default" with the default driver
Creating myfirstapp_1
Attaching to myfirstapp_1

Dockerイメージビルドで示したように、https://localhost:8888/へアクセスするとWebアプリケーションが表示される。

これは次のdockerコマンドを実行するのと同じ意味である。

% docker run -p 8888:5000 myfirstapp

dockerコマンドと比較したdocker-composeの良い点は、設定をおおまかに設定ファイルへ記述できる点にある。主なユースケースの節で紹介したとおり、Docker EngineおよびDocker Composeをインストールする環境さえあれば、docker-compose upで同じ環境を構築できる。

Docker Composeがサポートするコマンド

docker-compose upは、docker-compose.ymlに書かれたコンテナの作成や起動などをまとめて処理できる。

オプションを付けずに実行するとフォアグラウンドで実行され、ログが標準出力に表示される。次のように-dオプションを付けるとバックグラウンドで実行される。

% docker compose up -d

コンテナを停止するには次のコマンドを実行する。

% docker compose stop

コンテナを再起動するには次のコマンドを実行する。

% docker compose start

特定のコンテナを指定して起動することもできる。

% docker compose start myfirstapp

コンテナの停止と削除をまとめて行うには次のコマンドを実行する。

% docker compose down

その他のコマンドの詳細は公式ドキュメントを参照してほしい。

複数コンテナを起動してみる

Docker Composeの使い方が分かったところで、より実用的な例としてDocker公式サンプルのvoting app(投票アプリ)を紹介する。

このアプリケーションは5つのサービスで構成されている。

voting app構成

voting appの構成(github.com/dockersamples/example-voting-appから引用)

注意: ここではDocker Composeの使い方やdocker-compose.ymlの書き方に焦点を当てて説明する。サンプルの各コンテナ実装の詳細はGitHubリポジトリを参照してほしい。

サービス 名前 内容補足
voting-app ユーザーに投票を促すWebページを表示するアプリケーション。 データはRedisに保存する。PythonとFlaskによるWebアプリケーション
redis 投票結果を一時的に保存するキャッシュ。 Redis
worker 投票結果を取得してPostgresデータベースに保存するワーカー。 .NET
db 投票結果を保存するデータベース。データはDocker volumeに保管する。 Postgres
result-app リアルタイム投票結果を表示するWebアプリケーション。 Node.jsによるWebアプリケーション

voting appを起動する

アプリケーションの動作を確認するため、次のようにGitHubリポジトリからソースコードを取得し、docker-compose upコマンドでコンテナを起動する。初回はイメージビルドが走るため時間がかかる点に注意しよう。

% git clone https://github.com/dockersamples/example-voting-app.git
% cd example-voting-app
% docker-compose up

コンソール出力が止まり、コンテナが起動したらブラウザでhttps://localhost:5000へ接続する。次のような投票画面が表示される。CATSとDOGSのどちらかを選択する。

投票画面

ブラウザでhttps://localhost:5001へ接続する。投票結果画面が表示される。投票画面で選択したほうに投票が入っていることを確認できる。

投票結果画面

複数コンテナを扱うdocker-compose.ymlを書く

ここではDocker Composeの機能と設定ファイル作成について説明する。設定ファイルの詳細な書き方は公式ドキュメントを参照してほしい。

voting-appの設定ファイルは次のとおりである。設定ファイルには次が定義されている。

  • サービス(services)が5つ(vote、result、worker、redis、db)
  • volumeが1つ(db-data)
  • networkが2つ(front-tier、back-tier)
version: "3"

services:
  vote:
    build: ./vote
    command: python app.py
    volumes:
     - ./vote:/app
    ports:
      - "5000:80"
    networks:
      - front-tier
      - back-tier

  result:
    build: ./result
    command: nodemon --debug server.js
    volumes:
      - ./result:/app
    ports:
      - "5001:80"
      - "5858:5858"
    networks:
      - front-tier
      - back-tier

  worker:
    build:
      context: ./worker
    networks:
      - back-tier

  redis:
    image: redis:alpine
    container_name: redis
    ports: ["6379"]
    networks:
      - back-tier

  db:
    image: postgres:9.4
    container_name: db
    volumes:
      - "db-data:/var/lib/postgresql/data"
    networks:
      - back-tier

volumes:
  db-data:

networks:
  front-tier:
  back-tier:

services - サービス定義

単一コンテナの例で説明したように、外部から認識できる機能単位を「サービス」と呼ぶ。サービスはコンテナに接続されるため、サービスの実体はコンテナである。

volumes - volume(データ永続化領域)の定義

volumeは、コンテナのライフサイクルが終了した後でもデータを保管できるデータ領域である。特徴は次のとおりである。

  • データ永続性を目的とした機能であるため、コンテナが削除されてもvolumeが明示的に破棄されない限り、volume内のデータは保持される。
  • volumeは特定コンテナ専用のvolumeだけでなく、複数コンテナから参照できるvolumeも作成できる。
  • ホスト側のディレクトリをvolumeとしてコンテナにマウントできる。
    • この機能はホストとコンテナの間でファイルを渡すときに有効である。

各サービスのvolumesには、サービス別のvolume設定を記述している。たとえば、voteサービスはホスト側のディレクトリ./voteを自動作成されるコンテナ専用volumeとして、コンテナディレクトリ/appにマウントする。これにより、アプリケーション実行に必要なファイルをコンテナへ渡す。

services:
  (省略)
  vote:
  (省略)
    volumes:
     - ./vote:/app

一方、dbサービスはdocker-compose.yml最上位のvolumesに名前を付けて定義したdb-dataという名前のvolumeを、コンテナのディレクトリ/var/lib/postgresql/dataにマウントする。db-dataのようにdocker-compose.yml最上位に名前を付けて宣言されたvolumeを「named volume」と呼ぶ。

services:
  (省略)
  db:
  (省略)
    volumes:
      - "db-data:/var/lib/postgresql/data"
volumes:
  db-data:
  • voteサービスのvolumeのように、サービス定義にホストおよびコンテナディレクトリパスだけを指定して宣言したvolumeは、そのサービス専用のvolumeになる。
  • 一方、db-dataのようなnamed volumeは複数コンテナから参照できる。

networks - ネットワーク設定

networkはサービスが属するネットワークである。

voting-appでは、ホスト側からデータベースへ直接アクセスして意図しないデータ変更が発生することを防ぐため、データベースをホストから直接アクセスできないback-tierネットワークに配置し、Webサーバーはホストからアクセスするfront-tierネットワークに配置している。

このように各サービスが所属するネットワークを指定するには、まずdocker-compose.ymlの最上位にネットワークを宣言する。

networks:
  front-tier:
  back-tier:

そして各サービス定義のnetworksに、サービスが参加するネットワークを指定する。次の場合、voteサービスはback-tierfront-tierに属し、redisサービスはback-tierにだけ属する。

  vote:
  (省略)
    networks:
      - front-tier
      - back-tier
  (省略)

  redis:
  (省略)
    networks:
      - back-tier

同じネットワークに属するサービス同士は、ホスト名としてサービス名を指定して相手サービスへ接続できる。たとえば、voteサービスとredisサービスはどちらもback-tierに属するため、voteサービスからホスト名redisでredisサービスにアクセスできる。実際にvoteサービスの実装(app.py)を見ると、ホスト名にredisを指定していることが分かる(ポートは省略され、デフォルトポート6379が使用される)。

def get_redis():
    if not hasattr(g, 'redis'):
        g.redis = Redis(host="redis", db=0, socket_timeout=5)
    return g.redis

またDocker Composeは、最初にdocker-compose upするときにデフォルトネットワークを1つ作成し、各サービスをデフォルトネットワークに所属させる。たとえばvoting-appの例なら、voting_defaultが作成されている。

$ docker-compose up
  (省略)
Creating network "voting_default" with the default driver

その他のネットワーク設定の詳細は、以下の公式ドキュメントを参照してほしい。

ports - ホストとコンテナ間のポートマッピング

portsはホストとコンテナ間のポートマッピングを指定する。コンテナの80番ポートをホストの5000番ポートへマッピングすると、ブラウザでhttps://localhost:5000/へ接続したときにvoteサービスのWebアプリケーションへアクセスできる。

    ports:
      - "5000:80"

image - 使用するイメージの指定

imageで使用するイメージを指定する。次の場合、redisサービスは既存のイメージredis:alpineを使用していることが分かる。

  redis:
    image: redis:alpine

build - イメージビルド

既存イメージでコンテナの要件が満たされない場合、buildでイメージビルド設定を指定する。buildを指定すると、ローカルキャッシュにイメージがない場合、コンテナ起動前にイメージビルドが実行され、イメージが作成される。

次の場合、voteサービスは./voteディレクトリにビルドに必要な構成ファイルがあることを指定している。

services:
  vote:
    build: ./vote

実際にexample-voting-app/vote/を開くと、Webアプリケーションに必要な複数のファイルがあり、その中にDockerイメージを構築するためのDockerfileがある。Dockerイメージビルドについては「イメージコンテナ管理メカニズム」を参照してほしい。

なお、imagebuildを両方指定すると、ビルド結果のイメージ名はimageで指定したイメージ名になる。

command - コンテナ起動コマンド

commandはコンテナ起動コマンドを指定する。voteサービスはFlaskというPython用Webフレームワークで実装されている。ここではWebアプリケーションのエントリポイントであるapp.pyを実行している。

    command: python app.py

Docker Composeの説明はここまでである。ここではDocker EngineとDocker Compose機能の一部だけを扱った。ぜひサンプルコードと公式ドキュメントを見て内容を把握し、自分のサービスに活用してほしい。

結論

本記事ではDocker公式サンプルであるvoting-appを例に、Docker Compose設定ファイルの作成とコマンドの実行方法について紹介した。

ミドルウェアを提供するとき、利用者にDockerイメージとDocker Compose設定ファイルを提供すれば、環境セットを簡単に構築できる点は、ソフトウェアの提供元にとっても利用者にとって非常に有用である。

ただし前述のとおり、Docker Composeは単一ノードへのデプロイに使用するツールであるためスケールアウトできず、プロダクション環境には適していない。次はプロダクション環境に適したクラウドクラスタマネージャーを紹介する。