Part 3:サービス

必要条件

  • Part 1 の概要説明を読んでいること。
  • Part 2 のコンテナの作成方法を学んでいること。
  • レジストリに送信 して作成した friendlyhello イメージが共有可能であることを確認します。ここではその共有イメージを使います。
  • デプロイしたコンテナとしてイメージが動作することを確認します。以下のコマンドを実行してください。docker run -p 80:80 username/repo:tag ここで username、repo、tag の部分は各環境に合わせて書き換えてください。そして http://localhost/ にアクセスします。

はじめに

Part 3では、アプリケーションをスケールアップして、負荷分散(ロード・バランシング)を有効にします。こうするためには、分散アプリケーションのレベルを一段あげる必要があります。サービス化 するということです。

  • スタック
  • サービス Services (今ここにいます)
  • コンテナ( part 2 で扱いました)

サービスとは

分散アプリケーションにおいては、その中に他の要素とは性格が異なる「サービス」と呼ばれるものがあります。例えば動画共有サイトを考えてみてください。おそらくはアプリケーションデータをデータベースに保存するためのサービスがあり、ユーザがデータをアップロードしたときにバックグラウンドでビデオ変換するサービスがあり、フロントエンドのサービスもあるでしょう。

サービスとは正に「稼動するコンテナ」なのです。どのサービスも実行するイメージはただ一つですが、そこにはイメージの動作方法がコーディングされています。ポートは何番を使うか、サービスが持つべき性能を発揮するにはレプリカをいくつ用いればよいか、などです。サービスの規模を大きくすると、このソフトウエアを実行するコンテナ・インスタンスの数が変わります。またプロセス内で実行されるこのサービスへのコンピュータ・リソース割り当てが増えます。

うれしいことに Docker では簡単にサービスの定義、実行、規模変更を行うことができます。ただ docker-compose.yml ファイルを書くだけです。

初めての docker-compose.yml ファイル

docker-compose.yml ファイルは YAML ファイルであり、Docker コンテナがどのように動作するかを定義します。

docker-compose.yml

以下の内容を任意の場所に docker-commpose.yml として保存します。 Part 2 でレジストリに 送信したイメージ を確認し、 .yml ファイルの username/repo:tag の部分を皆さんのイメージのものへ書き換えます。

version: "3"
services:
  web:
    # username/repo:tag を皆さんの名前とイメージに置き換えます
    image: username/repository:tag
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "80:80"
    networks:
      - webnet
networks:
  webnet:

この docker-compose.yml ファイルが Docker に対して以下の指示を行います:

  • Step 2 でアップロードしたイメージ をレジストリから取得。
  • イメージのインスタンスを5つ実行し web という名前のサービスとして実行。それぞれのインスタンスは(全てのコアを通じて)最大で CPU の 10% の利用までに制限し、RAM は 50MB とする。
  • コンテナが停止したときは、すぐに再起動。
  • ホスト側のポート 80 を web のポート 80 に割り当て。
  • web のコンテナに対し、 webnet という名前の負荷分散ネットワークを経由してポート 80 を共有するよう命令(内部では、コンテナ自身の一時的なポートとして、 web のポート 80 を公開 )
  • デフォルトの設定として webnet ネットワークを定義(負荷分散されるオーバレイ・ネットワーク)

ヒント

Compose ファイルのバージョン、名前、コマンドの疑問について。 Compose ファイルに version: "3" とあるのにご注意ください。こちらは swarm mode 互換を意味します。これは deploy キー を使うためであり( Compose ファイル・フォーマット・バージョン 3.x 以上のみ対応)、サブオプションとして各サービスごと(例: web )の負荷分散とパフォーマンスを最適化します。ファイルを docker stack deploy コマンドで実行可能です(こちらもサポート対象は Compose ファイルがバージョン 3.x 以上のみ)。 swarm 設定のないバージョン3のファイルは docker-compose up でも実行可能ですが、これから構築する swarm のサンプルでは、stack を使ったデプロイにフォーカスします。

新しい負荷分散アプリケーションの実行

docker stack deploy コマンドを実行する前に、以下を実行します:

docker swarm init

注釈

このコマンドの意味については Part 4 で説明します。もしも docker swarm init コマンドを実行しなければ、 "this node is not a swarm manager." (このノードは swarm マネージャではありません)とエラーが出ることになります。

次のコマンドを実行します。アプリには名前を付ける必要があります。ここでは getstartedlab と指定します:

docker stack deploy -c docker-compose.yml getstartedlab

一つのサービス・スタックから、ホストにデプロイしたイメージに対するコンテナ・インスタンスが5つ稼動しました。中身を確認してみましょう。

アプリケーション内に稼動しているサービスの ID を取得します。

docker service ls

web サービスに関する情報が出力されます。アプリ名も行先頭に表示されます。上で示した例と同じ名前をつけていれば getstartedlab_web が表示されたはずです。サービス ID をはじめ、レプリカ数、イメージ名、公開ポートもともに一覧表示されます。

Docker swarm は、コンテナを作成するタスクを実行します。タスクには状態(state)とそれぞれに ID がつきます。タスクの一覧を見てみます。

docker service ps <service>

注釈

Docker の swarm サポート機能は、SwarmKit と呼ばれるプロジェクトを利用して構築されています。SwarmKit のタスクはコンテナになる必要はありませんが、Docker swarm のタスクはコンテナを作成するように定義されています。

タスクを調べてみます。コンテナ ID を指定して出力を行います。

docker inspect --format='{{.Status.ContainerStatus.ContainerID}}' <task>

逆も同様で、コンテナ ID を調べることでタスク ID を得ることもできます。

初めに docker container ls を実行してコンテナ ID を得てから、以下を実行します:

docker inspect --format="{{index .Config.Labels \"com.docker.swarm.task.id\"}}" <container>

ここでは5つのコンテナ全てを一覧表示します。

docker container ls -q

curl http://localhost コマンドを順に数回実行するか、あるいはブラウザでこの URL を表示して何回か再読み込みをしてみてください。どちらの方法でもコンテナ ID が変化して、各リクエストごとに5つのレプリカのうちの1つがラウンドロビン方式により選ばれて応答します。これにより負荷分散が機能していることが分かります。コンテナ ID は先ほどのコマンド(docker container ls -q)の出力に合致しているはずです。

注釈

この段階では、コンテナが HTTP リクエストに応答するまで 30 秒ほどかかります。これは Docker や swarm の性能によるものではなく、Redis の依存関係による影響です。このことはチュートリアル内で後に説明します。

アプリのスケール

docker-compose.ymlreplicas の値を変更すれば、アプリのスケールを変更できます。変更を保存したら、 docker stack deploy コマンドを再度実行します。

docker stack deploy -c docker-compose.yml getstartedlab

Docker は現状のまま更新を行いますので、スタックの停止やコンテナを停止する必要はありません。

もう一度 docker container ls -q を実行してみると、デプロイしたインスタンスが再設定されたことが確認できます。レプリカをスケールアップしていれば、より多くのタスクが起動するので、つまりより多くのコンテナが起動します。

アプリと swarm の解体(停止)

docker stack rm でアプリケーションを停止します。

docker stack rm getstartedlab

このコマンドはアプリケーションを削除しますが、これまでの swarm 単一ノードは立ち上がったまま実行し続けます( docker node ls により確認できます)。swarm を停止するには docker swarm leave --force を実行します。

Docker においてはアプリケーションの起動もスケールアップも非常に簡単です。ここまでにコンテナを実稼動させる方法を学びました。大きく前進しました。次に学ぶのは、複数の Docker マシンによるクラスタ上にて、本当の意味で swarm としてのアプリを実行する方法です。

注釈

今回使ったような Compose ファイルは Docker においてアプリケーションを定義するために用います。そして Docker Cloud を用いてクラウド・プロバイダへのアップロードを行います。つまり他のハードウェアや、 Docker Enterprise エディション において選定したクラウド・プロバイダへのアップロードを行うものです。

まとめと早見表(おまけ)

このページで扱った端末操作の録画 がこちらです。

要するに docker run と入力するのが非常に簡単なことではあるものの、実稼動させるコンテナの真の実現方法は、それをサービスとして稼動させることです。サービスは Compose ファイルにおいてコンテナの動作を定義します。このファイルによってアプリのスケールアップ、制限、再デプロイを実現します。サービスへの変更は、稼動中であろうとも適切に反映されます。その際のコマンドはサービスを起動させたときの docker stack deploy と同じようにして実現できます。

ここまでのコマンドをまとめます。

docker stack ls                                        # スタックやアプリの一覧
docker stack deploy -c <composefile> <appname> # 指定する Compose ファイルの実行
docker service ls                          # アプリに関連する実行中サービス一覧
docker service ps <service>                        # アプリに関連するタスク一覧
docker inspect <task or container>                 # タスクまたはコンテナの調査
docker container ls -q                                     # コンテナ ID の一覧
docker stack rm <appname>                              # アプリケーションの解体

参考

Get Started, Part 3: Services | Docker Documentation
https://docs.docker.com/get-started/part3/