Docker Compose を使う

Docker Compose とは、複数コンテナのアプリケーションを定義・共有するために役立つように、開発されたツールです。Compose があれば、サービスを定義する YAML ファイルを作成し、コマンドを1つ実行するだけで、瞬時にすべて立ち上げたり、すべてを削除したりできます。

Compose を使う「大きな」利点は、アプリケーション スタックをファイルに定義し、プロジェクト用リポジトリの一番上に置けるため(これでバージョン管理できます)、プロジェクトに貢献しようとしている誰もが簡単に利用できます。誰もが必要なのは、リポジトリを複製(クローン)し、それから 構成された(compose) アプリを起動するだけです。実際、 GitHub や GitLab 上のプロジェクトで既に見かけているもしれません。

それでは、どのようにして使い始めるのでしょうか?

Docker Compose のインストール

Docker Desktop for Window や Mac や Linux をインストール済みであれば、Docker Compose が入っています! Play-wih-Docker インスタンスも、同様に Docker Compose がインストール済みです。

Docker Engine を単体でインストールした場合は、別のパッケージとして Docker Compose のインストールが必要です。 Compose プラグインのインストール を御覧ください。

インストール後は、以下のように実行できるようになり、バージョン情報が表示されます。

$ docker compose version

Compose ファイルの作成

  1. /getting-started/app フォルダのルートで docker-compose.yml という名前のファイルを作成します。

  1. compose ファイル内では、アプリケーションの一部として実行したいサービス(あるいはコンテナ)の一覧の定義から始めます。

    services:
    

それでは、 compose ファイルへ、サービスを一気に移行しましょう。

アプリのサービス定義

覚えていますか、以下はアプリ用コンテナの定義に使ったコマンドです。

$ docker run -dp 127.0.0.1:3000:3000 \
  -w /app -v "$(pwd):/app" \
  --network todo-app \
  -e MYSQL_HOST=mysql \
  -e MYSQL_USER=root \
  -e MYSQL_PASSWORD=secret \
  -e MYSQL_DB=todos \
  node:18-alpine \
  sh -c "yarn install && yarn run dev"
  1. まずはじめに、サービスを記述し、コンテナ用のイメージを定義しましょう。サービスには様々な名前を選べます。この名前が、自動的にネットワーク エイリアス(別名)となり、MySQL サービスの定義時に役立ちます。

    services:
      app:
        image: node:18-alpine
    
  1. 通常、 image 定義の近くに command がありますが、順番は無関係です。それでは、ファイルを先へと進めましょう。

    services:
      app:
        image: node:18-alpine
        command: sh -c "yarn install && yarn run dev"
    
  1. コマンドの -p 127.0.0.1:3000:3000 を移行するには、サービス用の ports を定義しましょう。ここでは 短い構文 を使いますが、より細かな 長い構文 も同様に使えます。

    services:
      app:
        image: node:18-alpine
        command: sh -c "yarn install && yarn run dev"
        ports:
          - 127.0.0.1:3000:3000
    
  1. 次に、作業ディレクトリ( -w /app )とボリュームの 割り当て(mapping)-v "$(pwd):/app" )の両方を移行するため、 working_dirvolumes 定義を使います。ボリュームも 短い構文長い構文 があります。

    Docker Compose でボリューム定義を使う利点の1つは、現在のディレクトリからの相対パスが使える点です。

    services:
      app:
        image: node:18-alpine
        command: sh -c "yarn install && yarn run dev"
        ports:
          - 127.0.0.1:3000:3000
        working_dir: /app
        volumes:
          - ./:/app
    
  1. 最後は、 environment キーを使って環境変数の定義を移行する必要があります。

    services:
      app:
        image: node:18-alpine
        command: sh -c "yarn install && yarn run dev"
        ports:
          - 127.0.0.1:3000:3000
        working_dir: /app
        volumes:
          - ./:/app
        environment:
          MYSQL_HOST: mysql
          MYSQL_USER: root
          MYSQL_PASSWORD: secret
          MYSQL_DB: todos
    

MySQL サービスの定義

それでは、 MySQL サービスを定義しましょう。先ほどコンテナを使うために実行したコマンドが、こちらです。

$ docker run -d \
  --network todo-app --network-alias mysql \
  -v todo-mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e MYSQL_DATABASE=todos \
  mysql:8.0
  1. まず新しいサービスと、その名前を mysql と定義すると、ネットワーク エイリアスも自動的に得られます。次に進み、使用するイメージも同様に定義します。

    services:
      app:
        # The app service definition
      mysql:
        image: mysql:8.0
    
  1. 次に、ボリューム 割り当て(mapping) を定義します。 docker run でコンテナを実行した時は、 名前付きボリューム(named volume) が自動的に作成されました。ですが、 Compose の実行時には、そのようになりません。トップレベルの volume: セクションでボリュームを定義する必要があり、さらに、サービス設定でもマウントポイントの指定が必要です。単にボリューム名だけを指定すると、デフォルトのオプションが使われます。他にも更に 多くの利用可能なオプション(compose-file-v3-volume-configuration-reference) があります。

    services:
      app:
        # The app service definition
      mysql:
        image: mysql:8.0
        volumes:
          - todo-mysql-data:/var/lib/mysql
    
    volumes:
      todo-mysql-data:
    
  1. さいごに、環境変数のみを指定する必要があります。

    services:
      app:
        # The app service definition
      mysql:
        image: mysql:8.0
        volumes:
          - todo-mysql-data:/var/lib/mysql
        environment:
          MYSQL_ROOT_PASSWORD: secret
          MYSQL_DATABASE: todos
    
    volumes:
      todo-mysql-data:
    

これで、 docker-compose.yml の全体は、このようになっているでしょう。

services:
  app:
    image: node:18-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 127.0.0.1:3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos

  mysql:
    image: mysql:8.0
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:

アプリケーション スタックの実行

ようやく docker-compose.yml ファイルの準備ができましたので、これで起動できます!

  1. まず、app や db のコピーが確実に停止しているのを確認します( docker ps を実行してから、 docker rm -f <ids> を実行 )。

  1. アプリケーション スタックを起動するため、 docker compose up コマンドを使います。バックグランドですべてを実行するため、 -d フラグを追加します。

    $ docker compose up -d
    

    実行したら、次のような出力が見えます。

    Creating network "app_default" with the default driver
    Creating volume "app_todo-mysql-data" with default driver
    Creating app_app_1   ... done
    Creating app_mysql_1 ... done
    

    ボリュームと同時にネットワークも作成されたのが分かるでしょう! デフォルトでは、 Docker Compose はアプリケーション スタックに指定したネットワークを自動的に作成します(そのため、 compose ファイル内にネットワークを定義しませんでした)。

  1. ログを調べるため、 docker compose logs -f コマンドを使います。1つの流れの中に、各サービスのログが相互に見えるでしょう。これは、タイミングに関連する問題を監視したい場合に、とても役立ちます。 -f フラグはログを「 フォロー(follow) 」(追跡)しますので、生成されたログはリアルタイムに表示され続けます。

    コマンドが実行済みであれば、このような出力になっているでしょう。

    mysql_1  | 2019-10-03T03:07:16.083639Z 0 [Note] mysqld: ready for connections.
    mysql_1  | Version: '8.0.31'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
    app_1    | Connected to mysql db at host mysql
    app_1    | Listening on port 3000
    

    行の始めにはサービス名が(大抵は色が付いて)表示されているため、メッセージの識別に役立ちます。特定のサービスに対するログを表示したい場合は、ログ表示コマンドの最後にサービス名を追加できます(例: docker compose logs -f app )。

  1. これでアプリが開けるようになり、実行中だと分かります。そして、どうでしょう! たった1つのコマンドでできました!

Docker ダッシュボードでアプリのスタックを表示

Docker ダッシュボードを見ますと、 app という名前のグループが見えます。これは Docker Compose による「プロジェクト名」で、同じグループのコンテナに対して使われます。デフォルトでは、プロジェクト名はシンプルに docker-compose.yaml が置かれているディレクトリ名です。

Docker ダッシュボードでのアプリのプロジェクト

app の▶マークをクリックして展開しますと、compose ファイルで定義した2つのコンテナが見えます。コンテナ名も説明的になり、 <サービス名>-<レプリカ数> のパターンに従います。そのため、どのコンテナがアプリで、どのコンテナが mysql データベースなのかを、素早く見つけるのが簡単になります。

Docker ダッシュボードでのアプリのプロジェクトを展開

全てを削除

すべてを解体(終了および削除)する準備が調えば、シンプルに docker compose down を実行するか、Docker ダッシュボード上でアプリ全体のゴミ箱をクリックします。コンテナは停止され、ネットワークも削除されます。

警告

ボリュームの削除

デフォルトでは、compose ファイルの名前付きボリュームは docker compose down の実行では削除「されません」。ボリュームも削除したい場合は、 --volumes フラグも追加する必要があります。

Docker ダッシュボードでは、アプリのスタックを削除してもボリュームは削除「されません」。

解体が終われば、他のプロジェクトに切り替えができ、 docker compose up を実行したら、そのプロジェクトに貢献する準備が調います! これ以上、本当にシンプルなことはありません!

次のステップ

このセクションでは、 Docker Compose について学びました。 Docker Compose があれば、複数のサービスがあるアプリケーションの定義と共有が、劇的に簡単にするのに役立ちます。また、実行していたコマンドを適切な compose 形式へ変換し、 Compose ファイルを作成しました。

これで、チュートリアルの仕上げに入りましょう。その前に、イメージの構築に関するいくつかのベストプラクティスについて扱います。これまで使っていた Dockerfile には大きな問題があるためです。