[和訳] コンテナネットワークのトラブルシューティング #docker
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
本稿は Troubleshooting Container Networking の 2018/8/27 時点の和訳です。Docker EE 17.06.2-EE-5、UCP-2.2.3、DTR-2.3.4 を対象としています。
問題
通常、コンテナ間ネットワークの問題として多く挙げられるものは、断続的に接続が途切れるという問題です。また、コンテナ間のネットワーク接続が完全に失敗しているというケースもあります。
この手順書は、ネットワーク接続に関する問題の根本原因を特定するために、必要な情報を集める手順を示すものです。
前提条件
まず、影響を受けているDocker Swarmサービス、ネットワークおよびホストなどの名前を特定します。
正しく応答していないネットワークリクエストを送っているDockerホストを特定します。もしリクエストをIngressネットワークを通してフロントエンドサービスへ、そして別のネットワークを通じてバックエンドサービスへと送っている場合は、問題を前半と後半に分け、netshootを使用してフロントエンドから直接バックエンドサービスに接続して次のトラブルシューティングを開始しましょう。
解決策
コンテナ間のネットワーク問題を解決するためには、ユーザ定義ネットワークの検査から始めます。問題が見つからない場合は、Ingressネットワークのトラブルシューティングに進みましょう。その両者の手順は次の通りです。
ユーザ定義ネットワークのトラブルシューティング
- 影響を受けている"フロントエンド"コンテナのネットワークネームスペースを使用してnetshootを開始します:
docker run -it --rm --net container:
- すべてのバックエンドIPアドレスを調べます:
nslookup tasks.backend_service_name 2>/dev/null |awk '$1=="Address"{print $3}'
- 各バックエンドタスクのIPアドレスに対して、待ち受けているべきポート番号に対して、netcatでTCPテストを実行します:
for backend_ip in $(nslookup tasks.
nc -zw2 $backend_ip
done
- <backend_service_name> はバックエンドサービス名に置き換えます。
- <listening_port> はバックエンドタスクを待ち受けしているポート番号に置き換えます。
-
接続に失敗していないが、Ingressネットワークを通じてのリクエスト送信に問題が発生し続けている場合は、Ingressネットワークのトラブルシューティングの次のセクションに進みましょう。接続に失敗がなく、問題がコンテナ間にのみ発生している場合は、タスクが失敗するまで別のサービス群あるいはホスト群をチェックします。
-
失敗したバックエンドIPアドレスがあった場合は、影響を受けたIPアドレスの逆引きを行い、そのサービス名とタスクIDを特定します:
nslookup <IPアドレス>
結果は servicename.slot.taskid.networkname という形になるでしょう。
- netshootコンテナから出て、2つのコンテナ間ネットワークに対して docker network inspect -v の結果を収集します。netcatテストに失敗した、Servicesセクション内のタスクのHostIPを書き留めます。
7.マネージャノード上で、失敗したすべてのサービス名とタスク名について、次の結果を収集します:
docker service ps
docker inspect --type task
- まだ存在しているタスクについては (「inspect --type タスク」の出力結果を確認しましょう)、CreatedとUpdatedの時間を記録します。netshootホスト、すべてのマネージャノード、そしてHostIPによって特定した応答していないタスクのホストから、この時間帯をカバーしているDockerデーモンのログを収集します。
Dockerデーモンがjournaldに記録している場合は、例えば次のようにします:
journalctl -u docker --no-pager --since "YYYY-MM-DD 00:00" --until "YYYY-MM-DD 00:00"
- ネットワークパス内のすべてのホストから https://github.com/adamancini/libnetwork/blob/master/support.sh の出力を収集します。これにより、必要に応じてカーネルプログラミングを公開し、検証できるようにします。
Ingressネットワークのトラブルシューティング
セキュリティ上の理由によりIngressネットワーク上のサービスディスカバリを無効化しているので、テスト用に確立するバックエンドIPアドレスに対して、nslookup tasks.serviceを使うことはできません。代わりにカーネルのipvsロードバランサプログラミングを使用します。
- マネージャノード上で、Ingressネットワーク(ここではサービス名に置き換えています)上のサービス用VIPを特定するため、docker service inspectを実行します:
ingress_id=$(docker network ls -qf name=ingress --no-trunc); docker service inspect
- curlを用いて、サービスのIngress公開済みポートへのネットワークリクエストに対して、正しく応答しているDockerホストを特定します:
curl -4 -H 'Host: myapp.example.com' http://host:
- netshootコンテナを使用して、そのホスト上のingress_sboxネームスペースに入ります:
docker run -it --rm -v /var/run/docker/netns:/netns --privileged=true nicolaka/netshoot nsenter --net=/netns/ingress_sbox sh
- 問題のサービスについて、10進数のIngressファイアウォールマークを調べます:
iptables -nvL -t mangle |awk '/
<vip>は、docker service inspectコマンドによって確認したサービスのIPアドレスで置き換えます。
- ipvsadmを使ってすべてのバックエンドタスクのIPアドレスを記載します。
ipvsadm -l -f
<fwmark>は、iptablesのmangleテーブルから抽出した10進数のファイアウォールマークで置き換えます。
- 各バックエンドタスクのIPアドレスで待ち受けしているべきポート番号に対して、netcat TCPテストを実行します:
for backend_ip in $(ipvsadm -l -f
nc -zw1 $backend_ip
done
- <listening_port>はバックエンドタスクを待ち受けしているポート番号に置き換えます。
- <fwmark>は、iptablesのmangleテーブルから抽出した10進数のファイアウォールマークに置き換えます。
- netshootコンテナから出て、docker network inspect -v ingressの結果を収集します。失敗したバックエンドIPアドレスの、すべてのタスクのHostIP、サービス名、タスクIDを書き留めます。
- マネージャノード上で、失敗したすべてのサービス名とタスク名について、次の結果を収集します:
docker service ps
docker inspect --type task
- まだ存在するタスク(inspect --type タスクの出力を確認します)については、CreatedとUpdatedの時間を記録します。netshootホスト、すべてのマネージャノード、そしてHostIPによって特定した、応答していないタスクのホストから、この時間帯をカバーしているDockerデーモンのログを収集します。
Dockerデーモンがjournaldに記録している場合は、例えば次のようにします:
journalctl -u docker --no-pager --since "YYYY-MM-DD 00:00" --until "YYYY-MM-DD 00:00"
- ネットワークパス内のすべてのホストから https://github.com/docker/libnetwork/blob/master/support/support.sh の出力を集めます。これにより、必要に応じてカーネルプログラミングを公開し、検証できるようにします。