fbpx

[和訳] Swarmのトラブルシューティング方法論 #docker

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

本稿は Swarm Troubleshooting Methodology の 2018/8/28 時点の和訳です。Docker EE 17.06.2-EE-6 を対象としています。

ソフトウェアをスタックとみなす

トラブルシューティングの方法論は他の技術と同様、問題の種類または問題の障害モードによって決まります。問題は通常、(アプリケーションの所有者またはユーザーによって)スタックの最上部で見つかり、そこからエラーを最下部に向かって追跡していきます。スタックの最上部または最下部のどちらで問題が見つかったかに関わらず、もう一方まで追跡することで根本的な原因または問題による影響が明らかになります。スタックのどこかで始まり、アプリケーションに多大な影響を広げていく問題の例には、次のようなものがあります:

  • ノードの物理的な障害
  • ローカルストレージボリュームの容量がいっぱいになる
  • OSのカーネルパニック
  • ファイルディスクリプタの枯渇

ソフトウェアをたまねぎとみなす

ソフトウェアコンポーネントの調査は、外側から始まり、内側に進んでゆくため、たまねぎを剥くのと非常によく似ています。カプセル化したソフトウェアオブジェクトはそれぞれをラッピングしているため、これらのオブジェクトを調査するには、徐々に内側に向かって作業していくことで、アプリケーションの現在の状態の把握できるようになります。

Dockerには、カプセル化したレイヤーが数多くあります: OSのカーネル、コンテナ、作業単位としてコンテナをカプセル化したタスク、アプリケーションコンポーネントを表すサービスまたはPod、そして全部のアプリケーションを表すスタックなどです。これらはすべてKubernetesとSwarmの第一級のオブジェクトであり、独立して調査することが可能です。

スタック全体から個々のコンテナへの調査の実行方法については、このガイドで後ほど説明します。

ソフトウェアを一連の流れとみなす

分散コンピューティングにおける多くの問題は、複数の異なるコンポーネントを介して追跡していきます。これらは、通常、アプリケーション経由で接続した異なるコンテナまたはホストである可能性があります。これらの分散プロセスを一連の流れとみなしていきます。トラブルシューティングの際には、エンド・トゥ・エンドのパス全体を考慮し、どちらかの端から始まり、もう一方の端に向かって作業する必要があります。

一連の流れとみなして追跡する問題例:

  • ネットワーク間の分離
  • DNS名前解決
  • 分散型マルチコンテナアプリケーションの問題
  • コンセンサスに基づいたシステムの定足数の喪失

一連の流れとみなせる問題は数多くあるので、これらはほんの数例にすぎません。これらのタイプのモデルの1つまたは組み合わせが、問題の原因となる可能性があります。

Dockerトラブルシューティングのベストプラクティス

Client Bundleの使用

UCP Client Bundleは、Docker Engineとkubectlのコンテキストを設定します。UCPクラスタと対話式に作業する場合は、Client Bundleを使用する必要があります。

UCPクラスタのトラブルシューティングを行う場合のベストプラクティスは、常にUCP Client Bundleを使用することです。

  • Client Bundleは、インフラコンテナやその他の重要なスタックに障害を与えないように、アカウントにアクセス許可を適用します。
  • Client Bundleを使用すると、特定のノードにログインすることなく、クラスタ全体に適用するコマンドを実行できます。

次の記述は、Client BundleをダウンロードしてローカルのDockerクライアントに適用する方法です:


UCP_URL=あなたのUCPのURL
USER=あなたのUCPユーザ名
PASSWORD=あなたのUCPパスワード
 
AUTHTOKEN=$(curl -sk -d '{"username":"$USER","password":"$PASSWORD"}' https://$UCP_URL/auth/login | jq -r .auth_token)
 
# Client Bundleのダウンロード
curl -k -H "Authorization: Bearer $AUTHTOKEN" https://$UCP_URL/api/clientbundle -o bundle.zip

サービスの内側に向かうトラブルシューティング

「ソフトウェアはたまねぎのようだ」という概念からすれば、Swarmの第一級のオブジェクトに向かって外部からトラブルシューティングしていくことは、非常に一般的なフローです。予期しない動作をするアプリケーションによって見つかった問題は、一般的にこのような手順でトラブルシューティングします。

これは外部から内部に入る一般的なフローです:


$ docker service ls
$ docker service ps <サービス>
$ docker service inspect <サービス>
$ docker inspect <タスク>
$ docker inspect <コンテナ>
$ docker logs <コンテナ>

次の例では、問題が起きているサービスとしてucp-agentを使用しています。

docker service ls

docker service lsは、Swarmで実行しているすべてのサービスを一覧表示します。これは、サービス用のレプリカが想定通りの数で実際に稼動しているかどうかの確認に非常に役立ちます。例えば:


$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
o1rttg33awmd ucp-agent global 2/2 docker/ucp-agent:2.2.4

注目点: 2/2は、レプリカが2つあるべき状態で、現在実行中のレプリカが2つであることを示しています。

docker service inspect

docker service inspectは、指定したサービスのすべての詳細な設定値を表示します。例えば:


$ docker service inspect ucp-agent
[
{
"ID": "x8vlfxey5zxqt69mlydbil6yt",
"Version": {
"Index": 176
},
"CreatedAt": "2017-11-10T23:51:39.6345829Z",
"UpdatedAt": "2017-11-13T00:33:46.611747454Z",
"Spec": {
"Name": "ucp-agent",
"Labels": {
"com.docker.ucp.InstanceID": "in9h5oi63hmwqof3dfm5n47z5",
"com.docker.ucp.version": "2.2.4"
},
"TaskTemplate": {
"ContainerSpec": {
"Image": "docker/ucp-agent:2.2.4@sha256:bbee2b38e355e613c6b740698fba887d65f77b3c69e81bd9eebd4ae6665e43c5",
"Labels": {
"com.docker.ucp.InstanceID": "in9h5oi63hmwqof3dfm5n47z5",
"com.docker.ucp.version": "2.2.4"
},
"Command": [
"/bin/ucp-agent",
"agent"
],
"Env": [
"IMAGE_VERSION=2.2.4",
...

注目点:

  • CreatedAtは、サービスを作成した時刻を示しています。これは、「サービスをデプロイしたときに問題が起きたのか、サービスが定常状態になった後に問題が起きたのか?」という疑問の答えとなります。
  • UpdatedAt は同様に、サービスに変更があった場合、どこか他の場所で起きた問題と関係があるかどうかを示します。
  • Labelsは、スケジューリングの制約として扱ったり、単純なメタデータとして扱ったり、多種多様な用途があります。これらを検査することは重要です。

これらの値は、次のコマンドで個別に取得できます:


docker service inspect ucp-agent | jq -r '.[].CreatedAt'
2017-11-10T23:51:39.6345829Z
 
docker service inspect ucp-agent | jq -r '.[].UpdatedAt'
2017-11-13T00:33:46.611747454Z

JSONを表示する際は、JSONデータ全体から特定の値を抽出する jq のようなツールとともに使うと便利です。

docker service ps

このコマンドは、指定したサービスのタスクを一覧表示します。これは、過去に障害があったか、またはタスクが現在実行中か再起動中かの判断に役立ちます。Swarmは、コンテナをもはや実行していないタスクも表示するために、サービスの履歴を一定量保存しています。例えば:


$ docker service ps ucp-agent
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
pw6u97k0th7q ucp-agent.ung2hozu917ekkrx8zy3kxt2f docker/ucp-agent:2.2.4 ip-172-31-17-19 Running Running 3 minutes ago
tzi0i0nuwnc1 \_ ucp-agent.ung2hozu917ekkrx8zy3kxt2f docker/ucp-agent:2.2.4 ip-172-31-17-19 Shutdown Complete 3 minutes ago
2k9mk8smec4u ucp-agent.2z1mvt13mcil48a2qq9hwou2g docker/ucp-agent:2.2.4 ip-172-31-31-50 Running Running 4 minutes ago
a86qcrb6ekyu \_ ucp-agent.2z1mvt13mcil48a2qq9hwou2g docker/ucp-agent:2.2.4 ip-172-31-31-50 Shutdown Complete 4 minutes ago

注目点: 障害が発生したタスクと、頻繁に作成しては終了するタスクの大量の一覧は、再始動ループを示しています。コンテナを作成し、終了し、すぐに再スケジュールすることを高速に繰り返しています。

すべてのDockerコマンドは --format "{{json .}}" オプションを使用してJSON形式で出力できます。例えば:


$ docker service ps ucp-agent --format "{{json .}}"
{"CurrentState":"Running 9 minutes ago","DesiredState":"Running","Error":"","ID":"pw6u97k0th7q","Image":"docker/ucp-agent:2.2.4","Name":"ucp-agent.ung2hozu917ekkrx8zy3kxt2f","Node":"ip-172-31-17-19","Ports":""}
{"CurrentState":"Complete 9 minutes ago","DesiredState":"Shutdown","Error":"","ID":"tzi0i0nuwnc1","Image":"docker/ucp-agent:2.2.4","Name":"ucp-agent.ung2hozu917ekkrx8zy3kxt2f","Node":"ip-172-31-17-19","Ports":""}
{"CurrentState":"Running 10 minutes ago","DesiredState":"Running","Error":"","ID":"2k9mk8smec4u","Image":"docker/ucp-agent:2.2.4","Name":"ucp-agent.2z1mvt13mcil48a2qq9hwou2g","Node":"ip-172-31-31-50","Ports":""}
{"CurrentState":"Complete 10 minutes ago","DesiredState":"Shutdown","Error":"","ID":"a86qcrb6ekyu","Image":"docker/ucp-agent:2.2.4","Name":"ucp-agent.2z1mvt13mcil48a2qq9hwou2g","Node":"ip-172-31-31-50","Ports":""}

JSON出力を使用して、次のようなタスクIDの一覧表示を生成するために、実行するべきタスクに対してフィルタを適用します。これは、特定のデータを取り出すタスクやコンテナを、大量にループに通したいときに便利です。


$ docker service ps ucp-agent --format "{{json .}}" --filter "desired-state=running" | jq -r .ID
pw6u97k0th7q
2k9mk8smec4u

docker inspect

docker service psで得たタスクIDを使用すると、指定のサービスに属するコンテナを特定するためのコンテナIDを抽出することができます。特定のタスクのコンテナIDを取得したら、そのコンテナのログを取得したり、そのコンテナの設定に関する特定の情報を表示したりすることができます。


$ docker inspect pw6u97k0th7q | jq -r '.[].Status.ContainerStatus.ContainerID'
90cbc98062c82e7fddac3e883f9dac26773bf426704dfc3307abc5b327eb304d

docker inspect <コンテナ>

docker inspect <コンテナ>の出力は非常に大きく詳しすぎるのですが、コンテナを構成するすべてのパラメータがわかります。これにより、特定の問題を詳しく調べることが可能です。理解しやすくするため、次節では出力を細かく分割していきます。

コンテナの状態


docker inspect 90cbc98062c8 | jq '.[].State'
{
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 2809,
"ExitCode": 0,
"Error": "",
"StartedAt": "2017-11-18T04:52:07.101718306Z",
"FinishedAt": "0001-01-01T00:00:00Z"
}

  • StartedAt は、コンテナをスケジュールした時刻を表示します。
  • FinishedAtは、コンテナが正常終了した時、または強制終了した時に表示します。これは、問題のタイムラインの特定に便利です。
  • ExitCodeとErrorは、コンテナが終了した理由に関する情報を提供します。
  • OOMKilled はメモリを圧迫する問題の特定に使えるかもしれません。

ノード


docker inspect 90cbc98062c8 | jq '.[].Node'
{
"ID": "I2B6:36YM:QBX5:7T4V:VGJU:UNFF:MW6V:GOYG:7WAG:MWK6:SJNL:3ZGR|172.31.17.19:12376",
"IP": "172.31.17.19",
"Addr": "172.31.17.19:12376",
"Name": "ip-172-31-17-19",
"Cpus": 1,
"Memory": 2095976448,
"Labels": {
"kernelversion": "4.4.0-1022-aws",
"operatingsystem": "Ubuntu 16.04.2 LTS",
"ostype": "linux",
"storagedriver": "aufs"
}
}

  • IPは、任意のホストインターフェイスのIPアドレスを表示します。
  • Cpus とMemoryはホストのリソースを表示します。

ネットワークの設定


$ docker inspect 90cbc98062c8 | jq '.[].NetworkSettings'
{
"Bridge": "",
"SandboxID": "c352709cf41243e3293c9a8dac563ae10d1a84dea085dd36e259324e64f2a47f",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"2376/tcp": null
},
"SandboxKey": "/var/run/docker/netns/c352709cf412",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "e75090016d7776845598dad468afb0e34a772668e5d976f216d15d187596aecb",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "b7df85b0dd79859b9e746e874439ab04bffaaf902899420b06e6e6f557b1977b",
"EndpointID": "e75090016d7776845598dad468afb0e34a772668e5d976f216d15d187596aecb",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}

  • SandboxKey は、このコンテナで使用しているネットワークの名前空間の場所と名前を表示します。
  • IPAddressは、コンテナのプライマリインターフェイスのIPアドレスを表示します。
  • Portsは、このコンテナ用に公開しているすべてのポートを一覧表示します。
  • Networksは、このコンテナに接続しているすべてのネットワークを一覧表示します。
新規CTA