はじめに

前回に引き続き、App Serviceでコンテナ化されたWebアプリケーションを実行する「Web App for Containers」の使い方について紹介します。今回は、複数のコンテナから1つのWebアプリケーションを構築する「Docker Compose」をWeb App for Containersで使用する方法を説明します。

「Web App for Containers」で複数コンテナを使ったWebアプリケーションを実行する

前回は、Webアプリケーション用のDockerコンテナをApp Service上で実行することのできるWeb App for Containersについて紹介しました。前回の例では単一のDockerイメージをWeb App for Containersで実行していましたが、今回は複数のコンテナを組み合わせて1つのアプリケーションとするDockerの機能である「Docker Compose」をWeb App for Containersで使用する方法について確認しまていきます。

Docker Composeとは

Docker Composeとは、複数のコンテナで構成されるアプリケーションのDockerイメージのビルドや各コンテナの起動・停止などを簡単に行えるようにするためのツールです。
通常、Webアプリケーションなどのシステムは、Webサーバー、アプリケーションサーバー、データベースサーバーなどの複数のサーバーやミドルウェアを組み合わせて1つのシステムとして構築することが一般的です。Dockerではこうした構成をコンテナ上で実現するための仕組みとして、「Docker Compose」というツールを提供しています。
Docker Composeを使用することで、例えばWeb用のコンテナとデータベース用のコンテナを定義して、それらを1つのまとまったシステムとして扱うことができます。通常のDockerであっても同様のことは実現可能ですが、Docker Composeでは複数のコンテナのライフサイクルをまとめて管理したりコンテナ間のネットワーク設定を自動的に構成したりしてくれるため、操作の手順が簡素になり作業ミスなどが起きにくくなるメリットがあります。

Docker Composeは、Composeファイルと呼ばれるYAML形式の定義ファイルに各コンテナの情報を記載し、「docker-compose」コマンドというCLIツールを使ってコンテナの生成、起動、停止などをコントロールする仕組みとなっています。なおDocker Composeは、Docker Desktopをインストールした際に付属しているため、追加のインストールなしに使用できます。

Docker ComposeによるFlaskとRedisを使ったWebアプリケーションの実装

ここからは実際にDocker Composeを使ったWebアプリケーションの実装方法について説明していきます。任意のディレクトリ(programs等)を作成し、その中にapp.pyファイルを作成します。前回、実装例として使用したPythonの軽量WebフレームワークであるFlaskを今回も使用します。また、今回のアプリケーションではアクセス数のカウント機能を追加で実装することとします。カウント数を記録するためのデータベースとして、Redisを使用します。Redisはメモリ上でデータを管理することで高速に動作するオープンソースのデータストアで、データベースの他にキャッシュやキューなどの用途でも利用されています。

Webアプリケーションの実装(app.py)

# Flaskのインポート
from flask import Flask
# Redisのインポート・・・①
from redis import Redis

# Flaskの使用
app = Flask(__name__)
# Redisの初期化・・・②
redis = Redis(host='redis', port=6379)

# 公開するAPIの定義
@app.route('/')
def hello():
    # Redisのデータ更新・・・③
    count = redis.incr('hits')
    return 'Hello World! {}回目のアクセスです。\n'.format(count)

# アプリケーションの起動(外部公開できる状態で起動)
if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)

前回のサンプルアプリからの変更点について説明します。今回のアプリはカウント数保存のためにRedisを使うため、新たにRedis接続用のクライアントライブラリをインポートします(①)。
次にクライアントライブラリを初期化してRedisと接続できる状態とします(②)。この際、初期化の引数として接続先となるRedisのネットワーク情報を設定します。「host」にはRedisのIPアドレスやホスト名を、「port」にはRedisが使用しているポート番号を指定します。「host」の値には通常IPアドレスやホスト名を指定しますが、今回は「redis」という値を設定しています。これはDocker Composeによって起動されるRedisのサービス名です。Docker Composeを使用して起動したコンテナ間の通信には、サービス名を使用することができます。サービス名の定義方法については後ほど説明します。またポート番号については、Redisのデフォルトポートである6379番を指定します。
Webアプリケーションが公開するAPIで、Redisに保存したアクセス数を取得して画面上に表示できるようにします(③)。redis.incr('hits')は、RedisのINCRというコマンドを実行する関数です。INCRコマンドはキーに格納されている値を1つ増やすコマンドで、この場合のキーは関数の引数に指定している「hits」になります。関数を実行すると増分された値が取得できるため、次の行でその内容を画面に表示するようにしています。このAPIを呼び出す度にRedisに保存されている値は増えていくため、アクセス数のカウントとして使用することができます。

次に、app.pyと同じディレクトリにPythonアプリケーションの依存ライブラリ定義ファイルのrequirements.txtを作成します。

Webアプリに必要な依存関係の定義(requirements.txt)

flask
redis

前回と同じく「flask」の記述と、今回は「redis」も追記します。これによってPythonでRedis接続用のクライアントライブラリを利用することができるようになります。
続いて、上記のWebアプリケーションをコンテナ化するためのDockerfileを同じディレクトリに用意します。

Flaskアプリ用のDockerfile(Dockerfile)

# ベースイメージとしてpythonを使用する
FROM python:3.4-alpine

# アプリケーションの配置と依存関係のインストール
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt

# 公開するポートの指定
EXPOSE 5000

# 起動時のコマンドの指定
CMD ["python", "app.py"]

Dockerfileの内容については前回のものと同じになります。ベースイメージとしてpythonのイメージを使用し、先程作成したrequirements.txtを使って依存ライブラリのインストールを行います。コンテナは5000番ポートで起動するよう指定し、最後にapp.pyを実行するように定義しています。

次はDocker Compose用の定義ファイル(Composeファイル)を作成します。このファイル内で複数のコンテナの生成方法や起動方法について定義することで、それらのコンテナをコマンド1つで操作することができるようになります。
これまで作成したファイル類と同じディレクトリに「docker-compose.yml」というファイルを作成し、以下の内容を記述します。

FlaskとRedisを使ったWebアプリケーション用のComposeファイル(docker-compose.yml)

version: '3'
services:
  web:
    build: .
    ports:
      - "5000:5000"
  redis:
    image: "redis:alpine"

ComposeファイルはYAML形式で記述していきます。先頭の「version」はComposeファイルの形式のバージョンを指定します。現時点では3.x系が主流のため「3」を指定します。
次は「services」の階層です。servicesの階層には実行したいコンテナの情報を記述していきます。まずはサービス名として、コンテナに名称を付与します。上記の例では「web」と「redis」がサービス名に該当します。「web」は先程実装したFlaskのWebアプリケーション用のサービスとし、「redis」はRedis用のサービスとします。なお、前述の「app.py」のRedisの初期化処理の中で「host」の値として設定するものが、ここで定義したサービス名と一致するようにします。
サービス名に続けて、コンテナのビルドや起動方法などの具体的な設定情報を記述していきます。「web」のサービスには「build」というフィールドがあります。このフィールドに指定されたDockerfileを使ってコンテナイメージのビルドを行います。Dockerfileの参照先は、このComposeファイルからの相対パスで指定します。今回の場合はComposeファイルと同じディレクトリにDockerfileがある想定のため、カレントディレクトリを参照するように「.」を指定しています。また「ports」フィールドはホストからコンテナへ通信したい場合に設定します。
「redis」サービスの方は、webのサービスと異なりDocker Hubに公開されているコンテナイメージを使用します。既存の公開イメージを使用してコンテナを起動する場合は、「build」ではなく「image」フィールドを使い、イメージ名とタグ名を指定します。

Docker Composeによるコンテナの起動

Composeファイルが作成できたら、Docker Composeでコンテナを起動することができるようになります。Docker Composeを使用する場合は、これまで使用してきた「docker」コマンドではなく「docker-compose」コマンドを使用します。
Composeファイルのあるディレクトリで以下のコマンドを実行します。

docker-composeコマンドによるコンテナの起動ログ

$ docker-compose up

Building web ・・・①「web」サービス用コンテナのビルド
[+] Building 6.3s (10/10) FINISHED
 => [internal] load build definition from Dockerfile               0.0s

(中略)

Pulling redis (redis:alpine)... ・・・②「redis」サービス用イメージの取得
alpine: Pulling from library/redis

(中略)

③・・・各コンテナの作成
Creating programs_web_1   ... done
Creating programs_redis_1 ... done
Attaching to programs_redis_1, programs_web_1
④・・・redisコンテナの起動
redis_1  | 1:C 03 Jun 2022 04:36:23.016 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
(中略)
redis_1  | 1:M 03 Jun 2022 04:36:23.018 * Ready to accept connections
⑤・・・webコンテナの起動
web_1    |  * Serving Flask app "app" (lazy loading)
(中略)
web_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

「docker-compose up」コマンドはComposeファイルを参照し、記述内容に従ってコンテナイメージのビルド、コンテナの作成・起動を行うコマンドです。コマンドの実行ログを確認してみましょう。 まずはwebサービス用のコンテナの作成を行います(①)。Dockerfileを参照してコンテナイメージのビルドを行っていることが分かります。次はredisサービス用のコンテナの作成です(②)。redisサービスは既存のコンテナイメージを使用するため、イメージをプルするログが出力されています。各サービスのコンテナイメージが用意できたら、コンテナを作成していきます(③)。続いて各コンテナの起動時のログが出力されます。まずはredis用コンテナが起動し、Redisの起動ログが出力されています(④)。次にweb用コンテナが起動しています(⑤)。web用コンテナはFlaskアプリケーションのため、Flaskの起動ログが出力されています。

上記のようなログが出力されたら、ブラウザでアプリケーションの動作確認をしてみます。「http://localhost:5000」にアクセスします。

  • Docker Composeアプリケーションの実行結果

図のようなメッセージが表示されれば成功です。初回アクセスのため、1回目のアクセスである旨のメッセージが表示されます。ここでブラウザでページを更新すると、アクセス数が1つずつ増えていくことを確認することができます。

  • ページを数回更新した後の実行結果

ページにアクセスする度にRedisに対してアクセス数の更新が行われ、更新されたアクセス数はRedisのデータベースに保存されます。そのため、ブラウザを一度閉じて再度開き直した場合でもアクセス数は現在の値から再開することが確認できます。これによってコンテナアプリケーションでデータの永続化が行える、動的なWebアプリケーションを作成することができることが分かりました。

Docker Composeアプリケーションの停止

「docker-compose up」コマンドで複数のコンテナを起動すると、コマンドプロンプト上に実行中のログが出力されます。Docker Composeアプリケーションを停止したい場合は、このプロンプト上で「Ctrl + C」を入力することで関連する全てのコンテナを停止することができます。
また、Docker Composeアプリケーションをバックグラウンドで起動したい場合があります。その場合は「docker-compose up -d」というオプションを付与することでバックグラウンド実行させることができるようになります。この場合は「docker-compose stop」コマンドを使ってコンテナを停止します。

Web App for ContainersへのDocker Composeアプリのデプロイ

ローカル環境でのDocker Composeアプリケーションの動作確認ができたので、次はWeb App for Containersへのデプロイを試します。
Web App for ContainersでDocker Composeを使用する際の制約として、Dockerイメージのビルドはサポートしていないため、Flaskアプリケーションについては事前にDockerイメージをビルドしてACRへプッシュしておきます。先程作成したDockerfileを使ってビルドを行うので、Dockerfileのあるディレクトリで以下のコマンドを実行します。

FlaskアプリケーションのDockerイメージの作成、ACRへのプッシュ

# Dockerイメージのビルド
$ docker build -t zerokara-webapp-compose:latest .

# イメージのタグ付け
$ docker tag zerokara-webapp-compose:latest zerokara.azurecr.io/zerokara-webapp-compose:latest

# ACRへのイメージのプッシュ
$ docker push zerokara.azurecr.io/zerokara-webapp-compose:latest

DockerイメージをビルドしてAzure Container Registry(ACR)へプッシュしています。
次に、Web App for Containers用のComposeファイルを用意します。

Web App for Containers用のComposeファイル(docker-compose_azure.yml)

version: '3'
services:
  web:
    image: "zerokara.azurecr.io/zerokara-webapp-compose:latest" # buildからimageに書き換え・・・①
    ports:
      - "5000:5000"
  redis:
    image: "redis:alpine"

前述の通りWeb App for ContainersではDocker Composeでのビルドが使用できないため、Composeファイル内のbuildフィールドをimageフィールドに置き換えています(①)。webサービスのimegeフィールドでは、先程ビルドしてACRへプッシュしたFlaskアプリケーション用のDockerイメージを参照するように記述します。 ここまで準備ができたら、Azureポータルから新しいWeb App for Containersを作成していきます。AzureポータルでApp Serviceのメニューで「作成」を選択します。

  • Docker Composeアプリ用のApp Serviceの作成画面

「Webアプリの作成」の画面が表示されたら、内容を入力していきます。任意のサブスクリプションとリソースグループを選択し、任意の名前を入力します。「公開」の項目は「Dockerコンテナー」を選択し、「オペレーティングシステム」は「Linux」を選択、地域は「Japan East」とします。App Serviceプランについては、「SKUとサイズ」が無料枠である「Free F1」のプランを選択あるいは新規作成します。

  • Docker Composeアプリ用のApp Serviceの作成画面(Docker関連)

「次: Docker」ボタンを選択し、Docker関連の内容を入力していきます。Docker Composeを使用する場合は、「オプション」で「Docker Compose(プレビュー)」を選択します。「イメージ ソース」は「Azure Container Registry」を選択します。「レジストリ」はDockerイメージをプッシュしたACRのレジストリ名を選択します。「構成ファイル」の項目ではComposeファイルのアップロードを要求されるので、先程作成したWeb App for Containers用のComposeファイル(docker-compose_azure.yml)を選びます。構成ファイルがアップロードされると、「構成」の項目にComposeファイルのプレビューが表示されます。
Composeファイルのアップロードが完了したら、画面下部の「確認および作成」ボタンを選択してWeb App for Containersの作成を開始します。しばらく待つと作成が完了するので、App Serviceのメニューから作成したWeb App for Containersを選択し、概要内にある「URL」からアプリケーションにアクセスしてみます。

  • Web App for Containersでの\実行結果

画面にメッセージが表示され、ローカル環境でDocker Composeを実行した時と同様に、ページの更新の度にアクセス数が増加するようになっていることが分かるかと思います。

まとめ

今回は複数のコンテナをまとめて実行することのできる「Docker Compose」の使い方を中心に、Web App for ContainersでDocker Composeを使用する方法について紹介しました。本格的なアプリケーションをDockerで作成する場合はDocker Composeを使用するケースが多くなると思いますので、Web App for Containersを使うことで容易にAzure上でDocker Composeを実行することができることが分かったかと思います。

WINGSプロジェクト 秋葉龍一著/山田祥寛監修
<WINGSプロジェクトについて>テクニカル執筆プロジェクト(代表山田祥寛)。海外記事の翻訳から、主にWeb開発分野の書籍・雑誌/Web記事の執筆、講演等を幅広く手がける。一緒に執筆をできる有志を募集中