オブジェクトの広場はオージス総研グループのエンジニアによる技術発表サイトです

コンテナ・マイクロサービス

さわって理解する Docker 入門

第 4 回 Docker Compose を使った複数コンテナのデプロイ
オージス総研
大西 洋平
2017年5月18日

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

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

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

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

一方、Docker Compose を使うと複数コンテナの設定や依存関係を設定ファイルに記述し、単一のコマンドで複数コンテナを一度に起動できます。

Docker Compose が使われる主なユースケース

開発環境の標準化

チームメンバが手順書頼りに開発環境を手動で整備すると、時間がかかる上に手作業による間違いを誘発する可能性があります。Docker Compose を使うと、設定ファイルを渡しておけば、開発環境の細部を気にすることなく、単一のコマンドで環境を整備できます。これにより開発環境整備の作業を効率化できます。

テスト環境の自動化

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)の書き方や動かし方を知るため、第 2 回 で作成したイメージを使い、単一コンテナの起動と停止を行う方法を紹介します。第 2 回のチュートリアルを未実施の場合は、一度、第 2 回の手順を実施した上で、以下の手順へお進みください。

docker-compose.yml の書き方

以下は第 2 回で作成したイメージからコンテナを立ち上げるための設定ファイルです。上記の設定ファイルを docker-compose.yml という名前で保存してください。

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

この設定ファイルで示しているのは以下の設定です。

  • Docker-Compose ファイルフォーマットのバージョンは 3。
    • フォーマットの詳細についてはドキュメントを参照。
    • (注意)Docker Compose ファイルフォーマットのバージョン 3 は、Docker Engine 1.13.0 以上で動作可能です。未インストールの場合は、第 1 回を参考にインストールしてください。なお、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

第 2 回 で示した通り、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 を「名前つき volume(named volume)」と呼びます。

services:
(省略)
  db:
(省略)
    volumes:
      - "db-data:/var/lib/postgresql/data"
volumes:
  db-data:
  • vote サービスの volume のように、サービスの定義内にホストとコンテナ内のディレクトリパスのみ指定した volume は、宣言したサービス専用の volume になります。
  • 一方、db-data のような名前つき volume は複数のコンテナから参照できます。

networks - ネットワークの設定

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

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

このように、サービスごとに所属するネットワークを指定するためには、まず docker-compose.yml のトップレベルにネットワークを宣言します。

networks:
  front-tier:
  back-tier:

そして、各サービス定義の networks にサービスが参加するネットワークを指定します。以下の場合、vote サービスは back-tier と front-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 イメージのビルドについては第 1 回の記事を参照ください。

なお、image と build の両方を指定すると、ビルドした結果のイメージ名は 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 はシングルノードへのデプロイに使うツールのため、スケールアウトできず、プロダクション環境には向いていません。次回は、プロダクション環境に適したクラウド上のクラスタマネジャーについて紹介します。