Docker Compose を使う

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

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

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

Docker Compose のインストール

Window や Mac で、 Docker Desktop または Toolbox をインストール済みであれば、Docker Compose が入っています! Play-wih-Docker インスタンスも、同様に Docker Compose がインストール済みです。Linux マシンを使う場合は、 Docker Compose のインストール が必要です。

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

$ docker-compose version

Compose ファイルの作成

  1. アプリプロジェクトのルートで、 docker-compose.yml という名前のファイルを作成します。

  1. この compose ファイル内では、 スキーマ バージョン(schema version) の定義から書き始めます。ほとんどの場合、サポートされている最新バージョンを使うのが一番良いでしょう。現時点のスキーマ バージョンと互換表については Compose ファイル リファレンス をご覧ください。

    version: "3.7"
    
  1. 次に、アプリケーションの一部として実行したいサービス(またはコンテナ)一覧を定義します。

    version: "3.7"
    
    services:
    

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

アプリのサービス定義

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

$ docker run -dp 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"

PowerShell の場合は、こちらのコマンドを使っていました。

PS> docker run -dp 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 サービスの定義時に役立ちます。

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

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

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

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

version: "3.7"

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

version: "3.7"

services:
  app:
    image: node:18-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 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:5.7

PowerShell の場合は、以下のコマンドを使います。

PS> 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:5.7
  1. まず新しいサービスと、その名前を mysql と定義すると、ネットワーク エイリアスも自動的に得られます。次に進み、使用するイメージも同様に定義します。

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

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

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

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

version: "3.7"

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

  mysql:
    image: mysql:5.7
    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: '5.7.27'  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 には大きな問題があるためです。それでは見てみましょう!