CL LAB

HOME > CL LAB > [和訳] コンテナネットワークのトラブルシューティング #docker

[和訳] コンテナネットワークのトラブルシューティング #docker

 ★ 0

本稿は 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ネットワークのトラブルシューティングに進みましょう。その両者の手順は次の通りです。

ユーザ定義ネットワークのトラブルシューティング

  1. 影響を受けている"フロントエンド"コンテナのネットワークネームスペースを使用してnetshootを開始します:

docker run -it --rm --net container:<container_name> nicolaka/netshoot

  1. すべてのバックエンドIPアドレスを調べます:

nslookup tasks.backend_service_name 2>/dev/null |awk '$1=="Address"{print $3}'

  1. 各バックエンドタスクのIPアドレスに対して、待ち受けているべきポート番号に対して、netcatでTCPテストを実行します:

for backend_ip in $(nslookup tasks.<backend_service_name> > 2>/dev/null |awk '$1=="Address"{print $3}'); do
nc -zw2 $backend_ip <listening_port>>/dev/null || echo $backend_ip failed;
done

  • <backend_service_name> はバックエンドサービス名に置き換えます。
  • <listening_port> はバックエンドタスクを待ち受けしているポート番号に置き換えます。
  1. 接続に失敗していないが、Ingressネットワークを通じてのリクエスト送信に問題が発生し続けている場合は、Ingressネットワークのトラブルシューティングの次のセクションに進みましょう。接続に失敗がなく、問題がコンテナ間にのみ発生している場合は、タスクが失敗するまで別のサービス群あるいはホスト群をチェックします。

  2. 失敗したバックエンドIPアドレスがあった場合は、影響を受けたIPアドレスの逆引きを行い、そのサービス名とタスクIDを特定します:

nslookup <IPアドレス>

結果は servicename.slot.taskid.networkname という形になるでしょう。

  1. netshootコンテナから出て、2つのコンテナ間ネットワークに対して docker network inspect -v の結果を収集します。netcatテストに失敗した、Servicesセクション内のタスクのHostIPを書き留めます。
    7.マネージャノード上で、失敗したすべてのサービス名とタスク名について、次の結果を収集します:

docker service ps <service_name>
docker inspect --type task <task_id>

  1. まだ存在しているタスクについては (「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"

  1. ネットワークパス内のすべてのホストから https://github.com/adamancini/libnetwork/blob/master/support.sh の出力を収集します。これにより、必要に応じてカーネルプログラミングを公開し、検証できるようにします。

Ingressネットワークのトラブルシューティング

セキュリティ上の理由によりIngressネットワーク上のサービスディスカバリを無効化しているので、テスト用に確立するバックエンドIPアドレスに対して、nslookup tasks.serviceを使うことはできません。代わりにカーネルのipvsロードバランサプログラミングを使用します。

  1. マネージャノード上で、Ingressネットワーク(ここではサービス名に置き換えています)上のサービス用VIPを特定するため、docker service inspectを実行します:

ingress_id=$(docker network ls -qf name=ingress --no-trunc); docker service inspect <service_name> --format '{{range .Endpoint.VirtualIPs}}{{if eq "'${ingress_id}'" .NetworkID}}{{.Addr}}{{end}}{{end}}'

  1. curlを用いて、サービスのIngress公開済みポートへのネットワークリクエストに対して、正しく応答しているDockerホストを特定します:

curl -4 -H 'Host: myapp.example.com' http://host:<listening_port>

  1. netshootコンテナを使用して、そのホスト上のingress_sboxネームスペースに入ります:

docker run -it --rm -v /var/run/docker/netns:/netns --privileged=true nicolaka/netshoot nsenter --net=/netns/ingress_sbox sh

  1. 問題のサービスについて、10進数のIngressファイアウォールマークを調べます:

iptables -nvL -t mangle |awk '/<vip>/ {printf("%d", $NF)}'; echo

<vip>は、docker service inspectコマンドによって確認したサービスのIPアドレスで置き換えます。

  1. ipvsadmを使ってすべてのバックエンドタスクのIPアドレスを記載します。

ipvsadm -l -f <fwmark>

<fwmark>は、iptablesのmangleテーブルから抽出した10進数のファイアウォールマークで置き換えます。

  1. 各バックエンドタスクのIPアドレスで待ち受けしているべきポート番号に対して、netcat TCPテストを実行します:

for backend_ip in $(ipvsadm -l -f <fwmark> |awk 'NR>3{print $2}' |cut -d: -f1); do
nc -zw1 $backend_ip <listening_port>>/dev/null || echo $backend_ip failed;
done

  • <listening_port>はバックエンドタスクを待ち受けしているポート番号に置き換えます。
  • <fwmark>は、iptablesのmangleテーブルから抽出した10進数のファイアウォールマークに置き換えます。
  1. netshootコンテナから出て、docker network inspect -v ingressの結果を収集します。失敗したバックエンドIPアドレスの、すべてのタスクのHostIP、サービス名、タスクIDを書き留めます。
  2. マネージャノード上で、失敗したすべてのサービス名とタスク名について、次の結果を収集します:

docker service ps <service_name>
docker inspect --type task <task_id>

  1. まだ存在するタスク(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"

  1. ネットワークパス内のすべてのホストから https://github.com/docker/libnetwork/blob/master/support/support.sh の出力を集めます。これにより、必要に応じてカーネルプログラミングを公開し、検証できるようにします。

CL LAB Mail Magazine

CL LABの情報を逃さずチェックしよう!

メールアドレスを登録すると記事が投稿されるとメールで通知します。

メールアドレス: 登録

※登録後メールに記載しているリンクをクリックして認証してください。

Related post

Docker社公認トレーニングコース