1. 概要

Dockerを広範囲に使用すると、いくつかの異なるコンテナーの管理がすぐに面倒になります。

Docker Composeは、この問題を克服するのに役立つツールであり、は一度に複数のコンテナーを簡単に処理できます。

このチュートリアルでは、その主な機能と強力なメカニズムを見ていきます。

2. YAML構成の説明

つまり、Docker Composeは、単一のdocker-compose.yml構成ファイル内で宣言された多くのルールを適用することで機能します。

これらのYAMLルールは、人間が読める形式とマシンに最適化された方法の両方で、プロジェクト全体を数行で1万フィートからスナップショットする効果的な方法を提供します。

ほとんどすべてのルールが特定のDockerコマンドを置き換えるため、最終的には次のコマンドを実行するだけで済みます。

docker-compose up

Composeによって内部で適用される数十の構成を取得できます。 これにより、Bashなどでスクリプトを作成する手間が省けます。

このファイルでは、作成ファイル形式のバージョン、少なくとも1つのサービス、およびオプションでボリュームネットワークを指定する必要があります。 :

version: "3.7"
services:
  ...
volumes:
  ...
networks:
  ...

これらの要素が実際に何であるかを見てみましょう。

2.1. サービス

まず、サービスはコンテナーの構成を参照します。

たとえば、フロントエンド、バックエンド、およびデータベースで構成されるドッキングされたWebアプリケーションを考えてみましょう。これらのコンポーネントを3つのイメージに分割し、構成で3つの異なるサービスとして定義する可能性があります。

services:
  frontend:
    image: my-vue-app
    ...
  backend:
    image: my-springboot-app
    ...
  db:
    image: postgres
    ...

サービスに適用できる設定は複数ありますが、後で詳しく説明します。

2.2. ボリュームとネットワーク

一方、 Volumes は、ホストとコンテナー間、またはコンテナー間でさえ共有されるディスクスペースの物理領域です。 つまり、ボリュームは、ホスト内の共有ディレクトリであり、一部またはすべてのコンテナから表示されます。

同様に、ネットワークは、コンテナ間、およびコンテナとホスト間の通信ルールを定義します。 共通のネットワークゾーンはコンテナのサービスを相互に検出可能にし、プライベートゾーンはそれらを仮想サンドボックスに分離します。

繰り返しになりますが、次のセクションでそれらについて詳しく説明します。

3. サービスの分析

それでは、サービスの主な設定の確認を始めましょう。

3.1. 画像を引っ張る

場合によっては、サービスに必要なイメージが Docker Hub または別のDockerレジストリで(私たちまたは他の人によって)すでに公開されていることがあります。

その場合は、画像名とタグを指定して、image属性で参照します。

services: 
  my-service:
    image: ubuntu:latest
    ...

3.2. イメージの構築

代わりに、 Dockerfile を読み取って、ソースコードからイメージをビルドする必要がある場合があります。

今回は、 build キーワードを使用して、Dockerfileへのパスを値として渡します。

services: 
  my-custom-app:
    build: /path/to/dockerfile/
    ...

パスの代わりにURLを使用することもできます。

services: 
  my-custom-app:
    build: https://github.com/my-company/my-project.git
    ...

さらに、 image名をbuild属性と組み合わせて指定できます。これにより、作成されたイメージに名前が付けられ、他のサービスで使用できるようになります

services: 
  my-custom-app:
    build: https://github.com/my-company/my-project.git
    image: my-project-image
    ...

3.3. ネットワーキングの構成

Dockerコンテナーは、Docker Compose によって暗黙的に、または構成を通じて作成されたネットワーク内で相互に通信します。 サービスは、コンテナ名とポート(たとえば、 network-example-service:80 )で参照するだけで、同じネットワーク上の別のサービスと通信できます。ただし、 expose キーワード:

services:
  network-example-service:
    image: karthequian/helloworld:latest
    expose:
      - "80"

ちなみに、この場合、 expose ディレクティブはすでにイメージDockerfileにあるため、公開しなくても機能します。

ホストからコンテナに到達するには、ポートはportsキーワードを介して宣言的に公開する必要があります。これにより、ホストでポートを別の方法で公開するかどうかを選択することもできます。

services:
  network-example-service:
    image: karthequian/helloworld:latest
    ports:
      - "80:80"
    ...
  my-custom-app:
    image: myapp:latest
    ports:
      - "8080:3000"
    ...
  my-custom-app-replica:
    image: myapp:latest
    ports:
      - "8081:3000"
    ...

これで、ポート80がホストから見えるようになり、他の2つのコンテナーのポート3000は、ホストのポート8080および8081で使用できるようになります。 この強力なメカニズムにより、衝突することなく同じポートを公開するさまざまなコンテナを実行できます

最後に、コンテナを分離するために追加の仮想ネットワークを定義できます。

services:
  network-example-service:
    image: karthequian/helloworld:latest
    networks: 
      - my-shared-network
    ...
  another-service-in-the-same-network:
    image: alpine:latest
    networks: 
      - my-shared-network
    ...
  another-service-in-its-own-network:
    image: alpine:latest
    networks: 
      - my-private-network
    ...
networks:
  my-shared-network: {}
  my-private-network: {}

この最後の例では、 another-service-in-the-same-network がpingを実行し、network-example-serviceのポート80に到達できることがわかります。 another-service-in-its-own-networkはそうではありません。

3.4. ボリュームの設定

ボリュームには、匿名、名前付き、およびホストの3つのタイプがあります。

Dockerは匿名ボリュームと名前付きボリュームの両方を管理し、ホスト内の自己生成ディレクトリにそれらを自動的にマウントします。 匿名ボリュームは古いバージョンのDocker(1.9より前)では便利でしたが、最近では名前付きボリュームが推奨される方法です。 ホストボリュームを使用すると、ホスト内の既存のフォルダーを指定することもできます。

ホストボリュームをサービスレベルで構成し、名前付きボリュームを構成の外部レベルで構成して、後者を、それらが属するコンテナーだけでなく、他のコンテナーからも見えるようにすることができます。

services:
  volumes-example-service:
    image: alpine:latest
    volumes: 
      - my-named-global-volume:/my-volumes/named-global-volume
      - /tmp:/my-volumes/host-volume
      - /home:/my-volumes/readonly-host-volume:ro
    ...
  another-volumes-example-service:
    image: alpine:latest
    volumes:
      - my-named-global-volume:/another-path/the-same-named-global-volume
    ...
volumes:
  my-named-global-volume: 

ここで、両方のコンテナーは、マップされたパスが異なっていても、my-named-global-volume共有フォルダーへの読み取り/書き込みアクセス権を持ちます。 代わりに、2つのホストボリュームはvolume-example-serviceでのみ使用できます。

ホストのファイルシステムの/tmp フォルダーは、コンテナーの / my-volumes /host-volumeフォルダーにマップされます。 ファイルシステムのこの部分は書き込み可能です。つまり、コンテナはホストマシンでファイルを読み取るだけでなく、書き込み(および削除)することもできます。

/ home フォルダーのように:ro をルールに追加することで、読み取り専用モードでボリュームをマウントできます(Dockerコンテナーがユーザーを誤って消去することは望ましくありません) )。

3.5. 依存関係の宣言

多くの場合、サービス間に依存関係チェーンを作成して、一部のサービスが他のサービスの前にロードされる(および後にアンロードされる)ようにする必要があります。 この結果は、dependents_onキーワードを使用して実現できます。

services:
  kafka:
    image: wurstmeister/kafka:2.11-0.11.0.3
    depends_on:
      - zookeeper
    ...
  zookeeper:
    image: wurstmeister/zookeeper
    ...

ただし、Composeは zookeeper サービスの読み込みが完了するのを待たずに、kafkaサービスを開始することに注意してください。単にサービスの開始を待つだけです。 別のサービスを開始する前にサービスを完全にロードする必要がある場合は、Compose起動とシャットダウンの順序をより詳細に制御する必要があります。

4. 環境変数の管理

Composeでは環境変数の操作が簡単です。 静的環境変数を定義でき、 ${}表記で動的変数を定義することもできます。

services:
  database: 
    image: "postgres:${POSTGRES_VERSION}"
    environment:
      DB: mydb
      USER: "${USER}"

これらの値を作成するためのさまざまな方法があります。

たとえば、.propertiesファイルのように構造化された同じディレクトリの.envファイル、 key =valueに設定します。

POSTGRES_VERSION=alpine
USER=foo

それ以外の場合は、コマンドを呼び出す前にOSで設定できます。

export POSTGRES_VERSION=alpine
export USER=foo
docker-compose up

最後に、シェルで単純なワンライナーを使用すると便利な場合があります。

POSTGRES_VERSION=alpine USER=foo docker-compose up

アプローチを組み合わせることができますが、Composeは次の優先順位を使用し、重要度の低いものを高いものに上書きすることに注意してください。

  1. ファイルを作成する
  2. シェル環境変数
  3. 環境ファイル
  4. Dockerfile
  5. 変数が定義されていません

5. スケーリングとレプリカ

以前のComposeバージョンでは、 docker-composescaleコマンドを使用してコンテナーのインスタンスをスケーリングできました。 新しいバージョンでは廃止され、 scaleオプションに置き換えられました。

一方、Dockerエンジンのクラスターである Docker Swarm を悪用し、deployセクションのreplicas属性を使用してコンテナーを宣言的に自動スケーリングできます。

services:
  worker:
    image: dockersamples/examplevotingapp_worker
    networks:
      - frontend
      - backend
    deploy:
      mode: replicated
      replicas: 6
      resources:
        limits:
          cpus: '0.50'
          memory: 50M
        reservations:
          cpus: '0.25'
          memory: 20M
      ...

deploy、では、リソースのしきい値など、他の多くのオプションを指定することもできます。 ただし、は、Swarm にデプロイする場合にのみデプロイセクション全体を考慮し、それ以外の場合は無視します。

6. 実際の例:SpringCloudのデータフロー

小さな実験は単一のギアを理解するのに役立ちますが、実際のコードが実際に動作しているのを見ると、全体像が明らかになります。

Spring Cloud Data Flowは複雑なプロジェクトですが、理解できるほど単純です。 そのYAMLファイルをダウンロードして実行してみましょう:

DATAFLOW_VERSION=2.1.0.RELEASE SKIPPER_VERSION=2.0.2.RELEASE docker-compose up 

Composeは、すべてのコンポーネントをダウンロード、構成、および起動してから、コンテナーのログを現在のターミナルの単一のフローに交差させます。

また、優れたユーザーエクスペリエンスを実現するために、それぞれに固有の色を適用します。

まったく新しいDockerComposeインストールを実行すると、次のエラーが発生する可能性があります。

lookup registry-1.docker.io: no such host

この一般的な落とし穴にはさまざまな解決策がありますが、DNSとして8.8.8.8を使用するのがおそらく最も簡単です。

7. ライフサイクル管理

最後に、DockerComposeの構文を詳しく見てみましょう。

docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]

多くのオプションとコマンドが利用可能ですが、少なくともシステム全体を正しくアクティブ化および非アクティブ化するためのものを知る必要があります。

7.1. 起動

up を使用して、構成で定義されたコンテナー、ネットワーク、およびボリュームを作成して開始できることを確認しました。

docker-compose up

ただし、初回以降は、startを使用してサービスを開始できます。

docker-compose start

ファイルの名前がデフォルトの名前( docker-compose.yml )と異なる場合は、 -fおよび –を利用できます。 file フラグを使用して、代替ファイル名を指定します。

docker-compose -f custom-compose-file.yml start

-d オプションを指定して起動すると、Composeをデーモンとしてバックグラウンドで実行することもできます。

docker-compose up -d

7.2. シャットダウン

アクティブなサービスを安全に停止するために、 stop を使用できます。これにより、コンテナー、ボリューム、およびネットワークが、それらに加えられたすべての変更とともに保持されます。

docker-compose stop

プロジェクトのステータスをリセットするには、代わりに down を実行するだけで、外部ボリュームを除くすべてが破棄されます。

docker-compose down

8. 結論

このチュートリアルでは、DockerComposeとその仕組みについて学習しました。

いつものように、ソース docker-compose.yml ファイルはGitHubにあり、次の画像ですぐに利用できる便利な一連のテストがあります。