Compose を始めましょう

このページにおいて Docker Compose 上で動く簡単な Python ウェブ アプリケーションを作り出しましょう。このアプリケーションは Flask フレームワークを使い、Redis を使ってアクセスカウンタを管理します。このサンプルでは Python を用いていますが、たとえ Python に詳しくない方でも、ここに示す概念は十分に理解できるようになっています。

必要条件

Docker EngineDocker Compose がインストール済かどうか確認します。Python と Redis はインストール不要です。どちらも Docker イメージで提供されます。

ステップ1:セットアップ

アプリケーションの依存関係を定義します。

  1. プロジェクト用のディレクトリを作成します。

$ mkdir composetest
$ cd composetest
  1. プロジェクトのディレクトリ内に app.py という名前のファイルを作成し、以下の内容を貼り付けます。

import time

import redis
from flask import Flask

app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)

def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)

@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.\n'.format(count)

この例では、 redis とはアプリケーションのネットワーク上にある redis コンテナのホスト名です。Redis 用のポートとしてデフォルトの 6379 を使います。

  1. プロジェクトのディレクトリ内に別の requirements.txt を作成し、以下の内容を貼り付けます。

flask
redis

ステップ2:Dockerfile の作成

このステップでは、 Docker イメージを構築する Dockerfile を書きます。そのイメージには Python 自身を含む、 Python アプリケーションが必要とする全ての依存関係を含みます。

プロジェクトのディレクトリ内で、 Dockerfile という名前のファイルを作成し、以下の内容を貼り付けます。

# syntax=docker/dockerfile:1
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]

これは Docker に対して指示を伝えます。

  • Python 3.7 イメージでイメージの構築を開始

  • 作業ディレクトリ(working directory)/code に指定

  • flask コマンドが使う環境変数を指定

  • gcc と他の依存関係をインストール

  • requirements.txt をコピーし、Python 依存関係をインストール

  • コンテナがポート 5000 をリッスンするよう、イメージに対してメタデータの記述を追加

  • プロジェクト内にある現在のディレクトリ . を、イメージ内の 作業ディレクトリ(workdir) . にコピー

  • コンテナ実行時のデフォルト コマンド flask run を指定

Dockerfile の書き方についての詳細は、 Docker ユーザガイドDockerfile リファレンス をご覧ください。

ステップ3:Compose ファイル内でサービスを定義

プロジェクトのディレクトリに移動し、 docker-compose.yml という名前のファイルを作成し、以下の内容を貼り付けます。

version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
  redis:
    image: "redis:alpine"

この Compose ファイルは webredis という2つのサービスを定義します。

web サービス

web サービスは、現在のディレクトリ内にある Dockerfile から構築したイメージを使います。それから、コンテナのポートと、ホストマシン上に公開するポート 8000結び付け(bind) ます。この例にあるサービスは、 Flask ウェブサーバのデフォルト ポート 5000 を使います。

redis サービス

redis サービスは、 Docker Hub レジストリにある公開 Redis イメージを使います。

ステップ4:Compose によるアプリケーションの構築と実行

  1. プロジェクトのディレクトリで docker-compose up を実行し、アプリケーションを起動します。

    $ docker compose up
    
    Creating network "composetest_default" with the default driver
    Creating composetest_web_1 ...
    Creating composetest_redis_1 ...
    Creating composetest_web_1
    Creating composetest_redis_1 ... done
    Attaching to composetest_web_1, composetest_redis_1
    web_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
    redis_1  | 1:C 17 Aug 22:11:10.480 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
    redis_1  | 1:C 17 Aug 22:11:10.480 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=1, just started
    redis_1  | 1:C 17 Aug 22:11:10.480 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
    web_1    |  * Restarting with stat
    redis_1  | 1:M 17 Aug 22:11:10.483 * Running mode=standalone, port=6379.
    redis_1  | 1:M 17 Aug 22:11:10.483 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
    web_1    |  * Debugger is active!
    redis_1  | 1:M 17 Aug 22:11:10.483 # Server initialized
    redis_1  | 1:M 17 Aug 22:11:10.483 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
    web_1    |  * Debugger PIN: 330-787-903
    redis_1  | 1:M 17 Aug 22:11:10.483 * Ready to accept connections
    

    Compose は Redis イメージを取得し、コードのためのイメージを構築し、それから定義したサービスを起動します。この例では、イメージの構築時に、コードは静的にコピーされます。

  1. ブラウザで http://0.0.0.0:8000/ を開き、アプリケーションの稼動を確認します。

    Linux 上で Docker をネイティブに使っている場合や、 Docker Desktop for Mac 、 Docker Desktop for Windows の場合、これでウェブアプリは Docker デーモンのホスト上でポート 8000 をリッスンします。ウェブブラウザで http://localhost:8000 を開き、 Hello World メッセージを確認します。表示できなければ、 http://127.0.0.1:8000 で試します。

    ブラウザに次のような文字が表示されるでしょう。

    Hello World! I have been seen 1 times.
    
    ブラウザで hello world
  1. このページを再読み込みします。

    数字が増えます。

    Hello World! I have been seen 2 times.
    
    ブラウザで hello world
  1. 他のターミナルウインドウに切り替え、ローカルにあるイメージを一覧表示する docker image ls を入力します。

    この時点の一覧では redisweb が表示されます。

    $ docker image ls
    
    REPOSITORY        TAG           IMAGE ID      CREATED        SIZE
    composetest_web   latest        e2c21aa48cc1  4 minutes ago  93.8MB
    python            3.4-alpine    84e6077c7ab6  7 days ago     82.5MB
    redis             alpine        9d8fa9aa0e5b  3 weeks ago    27.5MB
    

    docker inspect <タグ または id> でイメージを確認できます。

  1. アプリケーションを停止するには、2つめのターミナル内のプロジェクトディレクトリ内で docker compose down を実行するか、アプリを起動した元々のターミナルで CTRL+C を実行します。

ステップ5:Compose ファイルに バインド マウント(bind mount) を追加

プロジェクトのディレクトリ内にある docker-compose.yml を編集し、 web サービスに バインド マウント を追加します。

version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
    volumes:
      - .:/code
    environment:
      FLASK_ENV: development
  redis:
    image: "redis:alpine"

新しい volumes キーは、ホスト上のプロジェクトがあるディレクトリ(現在のディレクトリ)を、コンテナ内の /code にマウントします。これにより、イメージを再構築しなくても、実行しながらコードを変更できるようになります。 environment キーは環境変数 FLASK_ENV を設定します。これは flask run に対し、開発モードでの実行と、コードに変更があれば再読込するように伝えます。このモードは開発環境下でのみ使うべきです。

ステップ6:Compose でアプリの再構築と実行

プロジェクトのディレクトリで、更新した Compose ファイルでアプリを構築して実行するため、 docker compose up を入力します。

$ docker compose up

Creating network "composetest_default" with the default driver
Creating composetest_web_1 ...
Creating composetest_redis_1 ...
Creating composetest_web_1
Creating composetest_redis_1 ... done
Attaching to composetest_web_1, composetest_redis_1
web_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

Hello World メッセージをウェブブラウザで再度確認すると、再読込で数値が増えるのが分かります。

重要

共有フォルダ、ボリューム、バインド マウント

  • プロジェクトが Users ディレクトリ( cd ~ )の外にある場合、Dockerfile とボリュームが使おうとしているドライブや場所を共有する必要があります。実行時にアプリケーションのファイルが見つからないというエラーが出た場合、ボリューム マウントが拒否されているか、サービスが起動できないため、ファイルまたはドライブの共有を試します。ボリューム マウントには、 C:\Users (Windows)や /Users (Mac)の外でプロジェクト用の共有ドライブが必要となります。そのため、Docker Desktop for Windows 上のあらゆるプロジェクトは Linux コンテナー が必要です。詳しい情報は、 Docker for Mac の ファイル共有 と、一般的な設定については コンテナ内でのデータ管理 をご覧ください。

  • 古い Windows OS 上で Oracle VirtualBox を使っている場合、 VB trouble ticket に示されている共有フォルダに関する問題が起こるかもしれません。より新しい Windows システムであれば、 Docker for Windows の要件を満たすため、VirtualBox は必要としません。

ステップ7:アプリケーションの更新

アプリケーションのコードはボリュームを使いコンテナ内にマウントしましたので、コードに対する変更は、イメージを再構築しなくても、直ちに確認できるようになります。

app.py の挨拶を書き換え、保存します。たとえば、 Hello World! メッセージを Hello from Docker! に変更します。

return 'Hello from Docker! I have been seen {} times.\n'.format(count)

ブラウザでアプリを再読み込みします。挨拶が更新され、さらにカウンタも増え続けます。

ブラウザで hello world

ステップ8:その他のコマンドを試す

バックグラウンドでサービスを実行したい場合は、 docker compose up-d フラグ(これは「 デタッチド(detached) 」モード)を付けて、 docker compose ps で現在の実行状況を確認します。

$ docker compose up -d

Starting composetest_redis_1...
Starting composetest_web_1...

$ docker compose ps

       Name                      Command               State           Ports
-------------------------------------------------------------------------------------
composetest_redis_1   docker-entrypoint.sh redis ...   Up      6379/tcp
composetest_web_1     flask run                        Up      0.0.0.0:8000->5000/tcp

docker compose run コマンドは、サービスに対して一度だけコマンドを実行できます。たとえば、 web サービスに対し、環境変数が何かを見るには、次のようにします:

$ docker compose run web env

docker compose --help で、他の利用可能なコマンドを確認できます。

docker compose up -d で Compose を起動した場合は、サービスを 停止(stop) するために、次のコマンドを実行します。

$ docker compose stop

コンテナ全体を削除し、全てを終了するには、 down コマンドを使います。 --volumes を追加すると、 Redis コンテナによって使われたデータ ボリュームも削除します。

$ docker compose down --volumes

これで、 Compose がどのように機能するかの基本が分かりました。

次は何を読みますか

参考

Get started with Docker Compose

https://docs.docker.com/compose/gettingstarted/