Docker Engine の Swarm 管理と保守

読む時間の目安: 5 分

Docker Engine において Swarm を起動している場合、マネージャーノード が主要な要素であり、Swarm を管理し、Swarm の状態を保持します。 ですからマネージャーノードの主な機能を理解しておくことが重要です。 これを知ることによって、適切なデプロイと Swarm の保守が可能になります。

ノードの動作 を参照して、Docker Swarm モードの概要や、マネージャーノード、ワーカーノードの違いについて理解しておいてください。

Swarm におけるマネージャーノードの操作

Swarm マネージャーノードは Raft 合意アルゴリズム(Raft Consensus Algorithm)を利用して、Swarm の状態を管理しています。 Swarm を管理する上では、Raft の一般的な考え方をいくつか理解しておくだけで十分です。

マネージャーノードの数には制限がありません。 マネージャーノードをいくつに設定するかは、性能とフォールトトレランスとのトレードオフです。 マネージャーノードを Swarm に追加すれば、障害には強くなります。 しかしマネージャーノードが増えれば書き込み性能は低下します。 Swarm 状態の更新処理を、より多くのノードにおいて受けつけなければならないからです。 これはつまりネットワークのラウンドトリップトラフィックを意味します。

Raft では Swarm において提案される更新処理、たとえばノード追加や削除といった処理に対して、多数のマネージャーが承認する必要があります。 多数というのは quorum とも呼ばれます。 メンバーに関わる処理に関しては、状態レプリケーションと同様の制約が適用されます。

マネージャーの quorum 管理

Swarm がマネージャーの quorum(多数票)を得られなかった場合、この Swarm は管理タスクを実行できません。 Swarm のマネージャーを複数にする場合は、必ず 3 つ以上にしてください。 quorum を維持するためには、マネージャーが多数いるという状態がなければなりません。 マネージャー数は奇数にすることが推奨されています。 仮にもう 1 つ加えて偶数にしてしまうと quorum を簡単には取り扱えなくなります。 たとえばマネージャー数が 3 つあるいは 4 つである場合、1 つのマネージャーを失っても quorum を維持することができます。これが 5 つあるいは 6 つであれば 2 つまでなら失っても維持が可能です。

仮に Swarm がマネージャーの quorum を維持できなくなったとしても、既存のワーカーノード上での Swarm タスクは処理続行されます。 ただし Swarm ノードの追加、更新、削除はできなくなります。 さらに新規や既存のタスクに対する起動、停止、移動、更新ができなくなります。

マネージャーが quorum を失った場合の対処方法については quorum を失ってからの回復 を参照してください。

マネージャーの通知アドレスへのスタティック IP アドレス設定

Swarm を初期化する際には--advertise-addrフラグの指定により、Swarm ないの別のマネージャーノードに対して自身のアドレスを通知(advertise)する必要があります。 詳しくはSwarm モードによる Docker Engine の実行 を参照してください。 マネージャーノードはそのインフラストラクチャーの中でも安定した構成要素であることが求められます。 したがって通知アドレスには 固定 IP アドレス を指定すべきであり、これによってマシンの再起動を行っても Swarm が不安定にならないようにする必要があります。

Swarm 全体が再起動してマネージャーノードが次々に新たな IP アドレスを取得したら、どのノードもマネージャーに接続できなくなります。 そして各ノードは、お互いに古い IP アドレスを使ってアクセスしようと試みるために Swarm がハングしてしまいます。

動的 IP アドレスはワーカーノードに対してなら用いてもかまいません。

耐障害性のためのマネージャーノード追加

Swarm においてノード障害に対処するには、マネージャーノード数を奇数に保つことが必要です。 マネージャーを奇数にしておくと、ネットワークパーティション分割の処理が発生した際に、そのパーティンションがちょうど 2 つに分かれたのであれば、quorum がリクエストを処理し続けられる可能性が確実に高まります。 なおネットワークパーティションが 3 つ以上に分かれた場合には quorum の機能維持は保証されません。

Swarm サイズ 多数票 耐障害性
1 1 0
2 2 0
3 2 1
4 3 1
5 3 2
6 4 2
7 4 3
8 5 3
9 5 4

たとえば 5 つのノード からなる Swarm において 3 つのノード を失ったら、quorum は 1 つもありません。 こうなるとノードの追加や削除はできなくなります。 利用不能になったマネージャーノードを復旧するか、障害回復のためのコマンドを使って Swarm を回復させなければなりません。 障害からの復旧 を参照してください。

Swarm をスケールダウンして単一のマネージャーノードにすることは可能ですが、最後に残るマネージャーノードを降格させることはできません。 最低 1 つのマネージャーを残しておかないと、Swarm にアクセスできなくなり、リクエストを処理することができなくなります。 ただし単一のマネージャーノードにスケールダウンすることは、安全な操作とは言えないため推奨されません。 その最後のノードが降格処理の際に、図らずも Swarm から離れてしまった場合、Swarm を操作することはできなくなります。 これを復旧するには、ノードを再起動するか、あるいは--force-new-clusterを使って再起動することが必要になります。

Swarm へのメンバー管理には、サブシステムdocker swarmおよびdocker nodeを利用します。 ワーカーノードの追加方法や、マネージャーノードへの昇格方法についての詳細は、Swarm へのノード追加 を参照してください。

分散マネージャーノード

マネージャーノードを奇数に維持することに加えて、マネージャーの配置に関してはデータセンターのトポロジーに配慮することも必要です。 最適なフォールトトレランスを実現するには、最低でも 3 つのアベイラビリティーゾーン(availability-zones)にマネージャーノードを分散して、マシン全体の障害へ対処したり、一般的なメンテナンス計画を立てたりすることが求められます。 いずれかのゾーンにおいて障害が発生したとしても、Swarm はマネージャーノードの quorum を維持することが可能であり、リクエストを処理し負荷を分散することが可能になります。

Swarm マネージャーノード 再パーティション化 (3 つのアベイラビリティーゾーン)
3 1-1-1
5 2-2-1
7 3-2-2
9 3-3-3

マネージャーノードのみの実行

マネージャーノードは、デフォルトでワーカーノードとしても動作します。 つまりスケジューラーはマネージャーノードに対してもタスクを割り振るということです。 小さくて重大性の高くない Swarm の場合、マネージャーノードへのタスクの割り当ては比較的低リスクです。 サービスのスケジュールにあたって CPUメモリ に対する リソース制約 を適切に行っておけば十分です。

しかしマネージャーノードでは Raft 合意アルゴリズム(Raft consensus algorithm)を利用して、一貫した方法によりデータ複製を行っています。 そのためリソースが枯渇することは大いに問題となります。 したがって Swarm のハートビートやリーダー選択のような Swarm 操作に支障をきたすような処理は、マネージャーに行わせないことが必要です。

マネージャーノード処理への影響を避けるためには、マネージャーノードを排出(drain)させて、ワーカーノードとしては利用できなくします。

$ docker node update --availability drain <NODE>

ノードを排出したらそのノード上の実行タスクは、スケジューラーが Swarm 内の別のワーカーノードに再割り当てします。 そしてスケジューラーは、そのノードへのタスク割り当てを行わなくなります。

負荷分散のためのワーカーノード追加

Swarm へのノード参加 を行うことで、Swarm 内の負荷を分散することができます。 複製されたサービスタスクは Swarm 全体にわたって分散されますが、それはどれくらいの時間であっても、ワーカーノードがサービス条件を満たしている限り行われます。 特定タイプのノード上でしかサービスを実行できない制限があるとします。 たとえば所定の CPU 数やメモリ容量が要求されるような場合です。 そういった条件を満たさないワーカーノード上では、タスクが実行できない点を覚えておいてください。

Swarm の健康状態の監視

マネージャーノードの健康状態(health)を監視するには、Docker のnodesAPI を使い、HTTP のエンドポイント/nodesから JSON 書式により確認することができます。 詳しくは nodes API のドキュメント を参照してください。

コマンドラインからはdocker node inspect <id-node>を実行して、ノードを調べることができます。 たとえばノードに、マネージャーとしての到達性能(reachability)があるかどうかを調べるには、以下を実行します。

$ docker node inspect manager1 --format "{{ .ManagerStatus.Reachability }}"
reachable

ワーカーノードとしてタスクの受け入れが可能であるかを確認するには、以下を実行します。

$ docker node inspect manager1 --format "{{ .Status.State }}"
ready

上のコマンドからmanager1は、マネージャーとしてはreachable(到達可能)であり、ワーカーとしてはready(受け入れ可能)であることがわかります。

健康状態がunreachable(到達不能)であるのは、このマネージャーノードが他のマネージャーノードから到達できないことを意味します。 この場合、到達不能なマネージャーノードは復旧させる措置が必要になります。

  • デーモンを再起動し、マネージャーが到達可能に戻るかどうかを確認します。
  • マシンを再起動します。
  • 上の 2 つの再起動を行っても動作しない場合は、新たなマネージャーノードを追加するか、ワーカーノードをマネージャーに昇格させます。 そしてマネージャーから障害ノードのエントリを削除する必要があるので、docker node demote <NODE>docker node rm <id-node>を実行します。

上とは別に、Swarm の健康状態の概要を確認するには、マネージャーノードから以下のようにdocker node lsを実行します。

$ docker node ls
ID                           HOSTNAME  MEMBERSHIP  STATUS  AVAILABILITY  MANAGER STATUS
1mhtdwhvsgr3c26xxbnzdc3yp    node05    Accepted    Ready   Active
516pacagkqp2xc3fk9t1dhjor    node02    Accepted    Ready   Active        Reachable
9ifojw8of78kkusuc4a6c23fx *  node01    Accepted    Ready   Active        Leader
ax11wdpwrrb6db3mfjydscgk7    node04    Accepted    Ready   Active
bb1nrq2cswhtbg4mrsqnlx1ck    node03    Accepted    Ready   Active        Reachable
di9wxgz8dtuh9d2hn089ecqkf    node06    Accepted    Ready   Active

マネージャーノードのトラブルシューティング

マネージャーノードのraftディレクトリを、別ノードのものからコピーして再起動するようなことはやめてください。 そのデータディレクトリはノード ID に固有のものです。 そしてノード ID が利用されるのは Swarm に参加するときだけです。 ノード ID 空間は全体にわたってユニークでなければなりません。

クラスターに対してマネージャーノードを再参加させるには、以下を行います。

  1. docker node demote <NODE>を実行して、そのノードをワーカーに降格させます。
  2. docker node rm <NODE>を実行して、そのノードを Swarm から削除します。
  3. docker swarm joinを実行して、そのノードを Swarm に再度、新たな状態により参加させます。

Swarm へのマネージャーノードの参加に関する詳細は Swarm へのノード参加 を参照してください。

ノードの強制削除

たいていの場合、docker node rmコマンドを使って Swarm からノードを削除するには、あらかじめそのノードを停止させておくべきです。 ノードが到達不能、無反応、障害発生といった状態になったら、ノードを停止させなくても、--forceフラグを使って強制的にノードを削除することができます。 たとえばnode9に障害が発生したら以下を実行します。

$ docker node rm node9

Error response from daemon: rpc error: code = 9 desc = node node9 is not down and can't be removed

$ docker node rm --force node9

Node node9 removed from swarm

マネージャーノードを強制的に削除する場合は、その前にワーカーノードへ降格させておかなければなりません。 マネージャーノードを降格あるいは削除した場合、マネージャーノードは常に奇数にしておくべきことを忘れないでください。

Swarm のバックアップ

マネージャーノードは、Swarm の状態とマネージャーログを/var/lib/docker/swarm/ディレクトリに保存しています。 そのディレクトリに Raft ログの暗号化に用いる鍵も含まれています。 この鍵がない状態では、Swarm を復元することはできません。

Swarm のバックアップはどのマネージャーからでも行うことができます。 バックアップは以下の手順で行います。

  1. Swarm のオートロック機能が有効である場合、バックアップから Swarm を復元するためには解除鍵(unlock key)が必要です。 解除鍵を得たら、安全な場所に保存しておいてください。 内容がよくわからない場合は、Swarm のロックと暗号鍵の保護 を参照してください。

  2. データのバックアップを取る前に、マネージャーノード上から Docker を停止します。 これによってバックアップ中にデータが変更されることがなくなります。 マネージャーが稼動中にバックアップを取る(「ホット」バックアップを取る)ことも可能ですが、これはお勧めしません。 バックアップを復元する際に、予期しない事態となる場合があります。 マネージャーが停止していても、他のノードが Swarm データを生成していると、そのデータはバックアップには含まれません。

    メモ

    Swarm マネージャーの quorum は維持することを忘れないでください。 マネージャーを停止している最中には、さらに別のノードが失われると Swarm の quorum を失う危険性が増します。 実行するマネージャー数にはトレードオフがあります。 バックアップを取る際には常にマネージャーを停止させるのであれば、Swarm のマネージャー数を 5 つとすることを考えてみてください。 そうしておくと、バックアップ中に別のマネージャーが失われても、サービスを支障なく実行することができます。

  3. /var/lib/docker/swarmディレクトリ全体のバックアップを取ります。

  4. マネージャーを再起動します。

復元方法については バックアップからの復元 を参照してください。

障害からの復旧

バックアップからの復元

Swarm のバックアップ において説明したように Swarm のバックアップを取った後は、以下の手順に従って、新たな Swarm に対してデータ復元を行います。

  1. 復元対象とする Swarm の対象ホストマシンを使って、Docker を停止させます。

  2. 新たな Swarm の/var/lib/docker/swarmディレクトリ内容を削除します。

  3. /var/lib/docker/swarmディレクトリ内を、バックアップ内容に置き換えます。

    メモ

    新しいノードはディスク上のストレージに対して、古いときと同じ暗号鍵を用います。 この時点でディスク上のストレージに対する暗号鍵を変更することはできません。

    Swarm のオートロック機能が有効である場合、解除鍵についても古い Swarm のときと同じ鍵です。 この解除鍵は Swarm を復元するために必要となります。

  4. 新たなノード上から Docker を起動させます。 必要に応じて Swarm のロック解除を行います。 以下のコマンドを使って Swarm を再初期化します。 これを行うのは、このノードが古い Swarm に属していたノードに接続しに行かないようにするためです。 もっとも古いノードは、すでに存在していないはずです。

    $ docker swarm init --force-new-cluster
    
  5. Swarm の状態が思っているとおりかどうかを確認します。 確認としてはアプリケーションに固有のテストを行うこともでき、あるいは単にdocker service lsの出力を確認するのでもかまいません。 これによってサービスが思い通りに存在しているかどうかを確認します。

  6. オートロック機能を利用している場合は 解除鍵のローテート を行います。

  7. 新たな Swarm の収容性能を実現するために、マネージャーノードやワーカーノードを追加します。

  8. 新たな Swarm に対して、以前からのバックアップ計画を復活させます。

quorum を失ってからの回復

Swarm というものには障害に対しての耐久性があります。 いくつものノードが一時的な障害(マシン再起動やその際のクラッシュなど)や一時的エラーを起こしても、Swarm は回復できます。 ただし quorum を失っている場合、Swarm は自動的に復旧できません。 既存のワーカーノード上のタスクは動作をし続けますが、管理タスクは実行不能になります。 たとえばサービスのスケーリング、更新、ノードの追加や削除などです。 復旧する最良の策は、失われたマネージャーノードをオンライン復旧させることです。 これが無理である場合、Swarm 復旧のための手段がいくつかあるので、読み進めてください。

Swarm にN個のマネージャーがある場合に、マネージャーノードの quorum は常に利用可能でなければなりません。 たとえば 5 つのマネージャーからなる Swarm の場合、最低でも 3 つのノードは動作状態であって、互いに通信できなければなりません。 言い換えると、Swarm は(N-1)/2個のノードまでなら、それが完全に処理不能になっても耐えることができますが、これを越えると Swarm 管理を行うような要求は処理できなくなります。 ここで言う処理不能というのは、データ損傷やハードウェア障害などを表わします。

マネージャーが quorum を失った場合、Swarm を管理することはできません。 quorum を失った状態で、Swarm 上で管理操作を行おうとすると、以下のようなエラーが発生します。

Error response from daemon: rpc error: code = 4 desc = context deadline exceeded

quorum を失った状態から復旧するための最良の策は、障害が発生したノードをオンライン復旧させることです。 それが実現できない場合、この状態を回復する最後の手段は、マネージャーノード上において--force-new-clusterを実行することです。 これを行うと、コマンド実行したマネージャーノードを除いた、他のマネージャーノードがすべて削除されます。 マネージャーがたった 1 つになるのですから quorum は維持されます。 この後には、マネージャーノードが必要な数に達するまで、ノードをマネージャーに昇格させていきます。

回復させたいノード上から、以下を実行します。

$ docker swarm init --force-new-cluster --advertise-addr node01:2377

docker swarm initコマンドにおいて--force-new-clusterフラグをつけて実行すると、コマンド実行した Docker Engine は、単一ノードからなる Swarm のマネージャーノードとなり、サービスの管理と実行が可能となります。 このマネージャーには、それまでのサービスやタスクに関する情報がそのまま保持され、ワーカーノードは Swarm に参加したままです。 そしてサービスは実行を継続します。 タスク分散を実現するためには、マネージャーノードの追加あるいは再度の追加が必要になります。 そして高可用性と quorum を維持していくのに十分なマネージャーとなっていることを確認します。

Swarm の強制的な再配分

一般には、Swarm におけるタスクを強制的に再配分させるような必要はありません。 Swarm に新たなノードを参加させたり、あるいは利用不能だったノードを Swarm に再接続させたりした場合に、Swarm ではアイドル状態にあるノードに対して、自動的な処理負荷の再配分は行いません。 これは設計方針によるものです。 仮に Swarm が配分を考えて、別のノードに定期的にタスクを振り替えるとすると、そのタスクを使用するクライアントは処理中断します。 目指すことは Swarm 内における配分を適切に行って、実行するサービスを中断させないことです。 新たなタスクが実行されたとき、あるいは実行中のタスクを持ったノードが利用不能になったとき、タスクは負荷の少ないノードに割り振られます。 最終的にはバランスよく配分されることです。 エンドユーザーに対して中断をできる限り少なくすることです。

docker service updateコマンドに--forceあるいは-fフラグをつけて実行すると、利用可能なワーカーノードに対して強制的にタスクを再分散します。 これを行うとサービスタスクは再起動します。 このときにはクライアントアプリケーションが中断するかもしれません。 設定によってサービスが ローリングアップデート を利用するようにできます。

Docker の古いバージョンを利用していて、ワーカーノードに均等に負荷を配分したい場合、そしてタスクの中断が気にならない場合は、サービスのスケールを一時的に増やして、強制的に再配分を行うことができます。 設定されているサービスのスケール数はdocker service inspect --pretty <servicename>を実行して確認します。 docker service scaleを確認すれば、タスクの実行数が最も低いノードに、新たに配分が行われることがわかります。 Swarm 内にはもっと配分を負うことができるノードが複数見つかるかもしれません。 すべてのノードにわたってバランスよく配分を実現するためには、適度にサービススケールを増やすことを何度か行うことが必要かもしれません。

望んだとおりに負荷が配分されたら、サービスのスケールを元の値に戻します。 docker service psを実行して、全ノード内でのサービスの配分状況を確認してください。

docker service scaledocker service ps を参照してください。

docker, container, swarm, manager, raft