Docker Compose の利用

読む時間の目安: 6 分

Docker Compose は、複数コンテナーによるアプリケーションを定義しやすく共有しやすくする目的で開発されたツールです。 この Compose を使う場合には、YAML ファイルを生成してそこにサービス定義を行い、ただ 1 つのコマンドだけですべてを結び合わせたり切り離したりすることができます。

Compose を利用する 最大 の利点は、アプリケーション全体の定義を 1 つのファイルに収められることです。 そのファイルはプロジェクトリポジトリのトップに置きます(ここからはバージョン管理していくことにします)。 Compose を使えば他の人がプロジェクトに参加しやすくなります。 他の人はただリポジトリをクローンするだけで Compose アプリを起動します。 現在 GitHub/GitLab 上にある実に多くのプロジェクトが、まさにそのようにして作業を進めています。

ではどうやってはじめましょうか。

Docker Compose のインストール🔗

Windows または Mac 上において Docker Desktop/Toolbox をインストールしていれば、すでに Docker Compose もインストールされています。 Play-with-Docker インスタンスにも Docker Compose がインストールされています。 Linux マシンを利用している方は Docker Compose のインストール が必要です。

インストール後は以下のコマンドを実行すれば、バージョン情報を確認できます。

$ docker-compose version

Compose ファイルの生成🔗

  1. アプリプロジェクトのルートディレクトリにdocker-compose.ymlという名前のファイルを生成します。

  2. Compose ファイルではまずスキーマバージョンの定義から書き始めます。 たいていはサポートされている最新バージョンを利用します。 Compose ファイルリファレンス を見れば、最新のスキーマバージョンや互換性マトリックスを確認できます。

     version: "3.7"
    
  3. 次にアプリケーションを構成するサービス(つまりコンテナー)一覧を定義します。

     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:12-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:12-alpine `
  sh -c "yarn install && yarn run dev"
  1. まずサービス定義の項目とコンテナーイメージを記述します。 サービス名はどのような名前でもかまいません。 この名前は自動的にネットワークエイリアスとなるため、MySQL サービスを定義する際に利用できます。

     version: "3.7"
    
     services:
       app:
         image: node:12-alpine
    
  2. image定義のすぐ近くにcommandがあるのが普通です。 ただし記述順はどちらを先にしてもかまいません。 そこでさらにこの先に進みます。

     version: "3.7"
    
     services:
       app:
         image: node:12-alpine
         command: sh -c "yarn install && yarn run dev"
    
  3. -p 3000:3000というコマンド部分を作ります。 サービスに対するportsを定義するものです。 ここでは 短い文法 で記述することにします。 もちろん 長い文法 で記述することもできます。

     version: "3.7"
    
     services:
       app:
         image: node:12-alpine
         command: sh -c "yarn install && yarn run dev"
         ports:
           - 3000:3000
    
  4. 次はワーキングディレクトリ(-w /app)とボリュームマッピング(-v "$(pwd):/app")です。 それぞれworking_dirvolumesの定義を行います。 ボリュームにも 短い文法長い文法 があります。

    Docker Compose におけるボリューム定義の利点は、カレントディレクトリからの相対パスを利用できることです。

     version: "3.7"
    
     services:
       app:
         image: node:12-alpine
         command: sh -c "yarn install && yarn run dev"
         ports:
           - 3000:3000
         working_dir: /app
         volumes:
           - ./:/app
    
  5. 最後に環境変数設定を行います。 これにはenvironmentキーを用います。

     version: "3.7"
    
     services:
       app:
         image: node:12-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
    
  2. 次はボリュームマッピングの定義です。 docker runを実行してコンテナーを起動したときは、名前つきボリュームが自動生成されていました。 しかし Compose を使って起動するときには、そのようにはなりません。 まずボリュームの定義は、最上位項目としてvolumes:というセクションを設けて、サービス定義の中のマウントポイントをここに指定します。 単純にボリューム名だけを指定すれば、デフォルトのオプションが用いられるようになります。 ただしここには 数多くのオプションが利用可能 です。

     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:
    
  3. 最後は必要な環境変数を定義するだけです。

     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:12-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. アプリや DB のコンテナーが他に起動していないことをまず確認します(docker psdocker rm -f <ids>)。

  2. 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 ファイル内ではネットワーク定義を行わなかったのです。)

  3. docker-compose logs -fコマンドを実行してログを確認してみます。 どちらのサービスのログも 1 つにまとめれてログ表示されています。 これは処理タイミングに問題があった場合に、監視を行うのが大変便利になります。 -fフラグはログ出力を「継続する」ものです。 つまりログ発生するたびに順次出力が得られます。

    コマンドをすでに実行済みであると、以下のような出力となるはずです。

     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)。

    ヒント: DB 準備を待ったアプリ起動

    アプリが起動すると MySQL が起動するまで待ちます。 そしてその後に接続を試みます。 Docker には別のコンテナーが起動、実行、接続可能といった状態になるまで待つようなビルトイン機能はありません。 Node ベースのプロジェクトでは wait-port という依存パッケージを利用することができます。 同じようなプロジェクトは、他のプログラミング言語、他のフレームワーク向けにも存在します。

  4. ここまでくれば、アプリを開いて実行確認を行うことができます。 ほら、実行コマンドたった一つで出来上がりです。

Docker ダッシュボードからのアプリ全体の確認🔗

Docker ダッシュボードを見てみると、グループ名として app があります。 これが Docker Compose における「プロジェクト名」であり、コンテナーをとりまとめるために利用されます。 このプロジェクト名はデフォルトでは、単純にdocker-compose.ymlファイルが存在しているディレクトリ名になります。

Docker ダッシュボード上の app プロジェクト

app を展開して詳細表示すると、Compose ファイル内で定義した 2 つのコンテナーが表示されます。 そこにつけられている名前にはルールがあります。 それぞれ<project-name>_<service-name>_<replica-number>というパターンを持っています。 ですからどのコンテナーがアプリであり、どれが MySQL データベースであるかは一目瞭然です。

Docker ダッシュボード上の app プロジェクトを展開

すべての削除🔗

すべてが不要になったら、単純にdocker-compose downコマンドを実行します。 あるいは Docker ダッシュボード上であれば、trash can(ゴミ箱)アイコンをクリックします。 コンテナーが停止してネットワークは削除されます。

警告

ボリュームの削除

Compose ファイル内で定めた名前つきボリュームは、docker-compose downを実行してもデフォルトでは削除 されません。 ボリュームも削除したいなら--volumesフラグをつける必要があります。 Docker ダッシュボードではアプリを削除してもボリュームは削除 されません

すべてを削除したら、今度は別のプロジェクトに切り替えてdocker-compose upを実行します。 ここからは新たなプロジェクトの開発者です。 何ともはや簡単なこと。

まとめ🔗

本節では Docker Compose を学びました。 これを使ったマルチサービスのアプリケーションなら、極めて簡単にアプリ定義し共有することが可能になります。 Compose ファイルの生成にあたっては、これまで実行してきたコマンドを Compose フォーマットに適するように修正して利用しました。

ここからはチュートリアルをまとめていきます。 もっともイメージビルドに関しては、お伝えしたいベストプラクティスがあります。 Dockerfile を利用していくためには大いなる問題だからです。 この後をご覧ください。

get started, setup, orientation, quickstart, intro, concepts, containers, docker desktop