このページでは、クラスタの接続に関する問題の解決方法について説明します。
GKE でのネットワーク パケットのキャプチャに関連する接続の問題
このセクションでは、ネットワーク パケットのキャプチャに関連する接続の問題のトラブルシューティング方法について説明します。この問題の症状としては、接続タイムアウト、接続拒否エラー、予期しないアプリケーション動作などがあります。これらの接続の問題は、ノードレベルまたは Pod レベルで発生する可能性があります。
クラスタ ネットワークの接続に関する問題の多くは、次のカテゴリに分類されます。
- Pod に到達できない: ネットワークの構成ミスにより、クラスタの内外から Pod にアクセスできない場合があります。
- サービスの中断: サービスが中断または遅延している可能性があります。
- Pod 間の通信に関する問題: Pod が相互に通信できない場合があります。
GKE クラスタの接続の問題は、次のようなさまざまな原因で発生する可能性があります。
- ネットワークの構成ミス: ネットワーク ポリシー、ファイアウォール ルール、ルーティング テーブルが正しく構成されていない。
- アプリケーションのバグ: ネットワーク インタラクションに影響するアプリケーション コードのエラー。
- インフラストラクチャの問題: ネットワークの輻輳、ハードウェア障害、リソースの制限。
次のセクションでは、問題のあるノードまたは Pod で問題を解決する方法について説明します。
次のコマンドを使用して、問題のある Pod が実行されているノードを特定します。
kubectl get pods POD_NAME -o=wide -n NAMESPACE
次のように置き換えます。
POD_NAME
: Pod の名前。NAMESPACE
: Kubernetes Namespace。
ノードに接続します。
gcloud compute ssh NODE_NAME \ --zone=ZONE
次のように置き換えます。
NODE_NAME
: ノードの名。ZONE
: ノードが実行されているゾーンの名前。
特定の Pod をデバッグするには、Pod に関連付けられている
veth
インターフェースを特定します。ip route | grep POD_IP
POD_IP
は、Pod の IP アドレスに置き換えます。ツールボックス コマンドを実行します。
toolbox
コマンド
toolbox
は、デバッグとトラブルシューティングのために、GKE ノード内にコンテナ化された環境を提供するユーティリティです。このセクションでは、toolbox
ユーティリティをインストールして、ノードのトラブルシューティングを行う方法について説明します。
ノードに接続した状態で、
toolbox
ツールを起動します。toolbox
これにより、
toolbox
ユーティリティを利用するファイルがダウンロードされます。toolbox
の root プロンプトで、tcpdump
をインストールします。外部 IP アドレスまたは Cloud NAT を使用するクラスタの場合:
apt update -y && apt install -y tcpdump
Cloud NAT を使用しない限定公開クラスタの場合:
Cloud NAT を使用しない限定公開クラスタがある場合、
apt
を使用してtcpdump
をインストールすることはできません。代わりに、公式リポジトリからlibpcap
リリース ファイルとtcpdump
リリース ファイルをダウンロードし、gcloud compute scp
またはgsutil
を使用して VM にコピーします。次の手順でライブラリを手動でインストールします。cp /media/root/home/USER_NAME/tcpdump-VERSION.tar.gz /usr/sbin/ cp /media/root/home/USER_NAME/libpcap-VERSION.tar.gz /usr/sbin/ cd /usr/sbin/ tar -xvzf tcpdump-VERSION.tar.gz tar -xvzf libpcap-VERSION.tar.gz cd libpcap-VERSION ./configure ; make ; make install cd ../tcpdump-VERSION ./configure ; make ; make install tcpdump --version
次のように置き換えます。
USER_NAME
: ファイルが配置されているシステムのユーザー名。VERSION
:tcpdump
パッケージとlibpcap
パッケージの特定のバージョン番号。
パケット キャプチャを開始します。
tcpdump -i eth0 -s 100 "port PORT" \ -w /media/root/mnt/stateful_partition/CAPTURE_FILE_NAME
次のように置き換えます。
PORT
: ポート番号の名前。CAPTURE_FILE_NAME
: キャプチャ ファイルの名前。
パケット キャプチャを停止し、
tcpdump
を中断します。「
exit
」と入力してツールボックスを終了します。パケット キャプチャ ファイルを一覧取得してサイズを確認します。
ls -ltr /mnt/stateful_partition/CAPTURE_FILE_NAME
パケット キャプチャをノードから、コンピュータの現在の作業ディレクトリにコピーします。
gcloud compute scp NODE_NAME:/mnt/stateful_partition/CAPTURE_FILE_NAME \ --zone=ZONE
次のように置き換えます。
NODE_NAME
: ノードの名。CAPTURE_FILE_NAME
: キャプチャ ファイルの名前。ZONE
: ゾーンの名前。
代替コマンド
問題のある Pod の接続に関する問題のトラブルシューティングは、次の方法で行うこともできます。
Pod コンテナに接続されたエフェメラル デバッグ ワークロード。
kubectl exec
を使用してターゲット Pod でシェルを直接実行し、tcpdump
コマンドをインストールして起動します。
Pod ネットワーク接続の問題
ネットワークの概要で説明したように、トラブルシューティングを効果的に行うには、Pod がネットワークの Namespace からノードのルート Namespace までどのように接続されているかを把握することが重要です。以下の説明では、特に明記しない限り、クラスタは Calico CNI ではなく GKE のネイティブ CNI を使用するものとします。つまり、ネットワーク ポリシーは適用されていません。
一部のノードの Pod が使用できない
一部のノードの Pod がネットワークに接続できない場合は、Linux ブリッジが起動していることを確認してください。
ip address show cbr0
Linux ブリッジが停止している場合は、次のように起動します。
sudo ip link set cbr0 up
ノードが、cbr0 に接続された Pod の MAC アドレスを学習していることを確認します。
arp -an
一部のノードの Pod の接続状態が悪い
一部のノードの Pod の接続状態が悪い場合は、まず、ツールボックス コンテナで tcpdump
を実行して、パケットが失われていないか確認します。
sudo toolbox bash
まだインストールしていない場合は、ツールボックスに tcpdump
をインストールします。
apt install -y tcpdump
cbr0 に tcpdump
を実行します。
tcpdump -ni cbr0 host HOSTNAME and port PORT_NUMBER and [TCP|UDP|ICMP]
大きなパケットがブリッジのダウンストリームでドロップされているように見える場合(たとえば、TCP handshake は完了しているが SSL hello は受信されていない)、各 Linux Pod インターフェースの MTU がクラスタの VPC ネットワークの MTU に正しく設定されていることを確認します。
ip address show cbr0
オーバーレイを使用する場合は(Weave や Flannel など)、オーバーレイのカプセル化オーバーヘッドに対応するために、この MTU をさらに小さくする必要があります。
GKE MTU
Pod インターフェースに選択される MTU は、クラスタノードで使用される Container Network Interface(CNI)と、基盤となる VPC MTU の設定によって異なります。詳細については、Pod をご覧ください。
Pod インターフェースの MTU 値は 1460
であるか、ノードのプライマリ インターフェースから継承されます。
CNI | MTU | GKE Standard |
---|---|---|
kubenet | 1460 | デフォルト |
kubenet (GKE バージョン 1.26.1 以降) |
継承 | デフォルト |
Calico | 1460 |
詳細については、ネットワーク ポリシーを使用して Pod と Service 間の通信を制御するをご覧ください。 |
netd | 継承 | 次のいずれかを使用して有効にします。 |
GKE Dataplane V2 | 継承 |
詳細については、GKE Dataplane V2 の使用をご覧ください。 |
断続的に接続が失敗する
Pod との接続は iptables によって転送されます。フローは conntrack テーブルのエントリとしてトラッキングされ、ノードあたりのワークロードが多いと、conntrack テーブルが枯渇して失敗となることがあります。これは、ノードのシリアル コンソールに、たとえば次のように記録されます。
nf_conntrack: table full, dropping packet
断続的な失敗の原因が conntrack の枯渇であると判断できる場合は、クラスタのサイズを大きくする(つまり、ノードあたりのワークロード数とフロー数を減らす)か、nf_conntrack_max
を増やすことができます。
new_ct_max=$(awk '$1 == "MemTotal:" { printf "%d\n", $2/32; exit; }' /proc/meminfo)
sysctl -w net.netfilter.nf_conntrack_max="${new_ct_max:?}" \
&& echo "net.netfilter.nf_conntrack_max=${new_ct_max:?}" >> /etc/sysctl.conf
NodeLocal DNSCache を使用して接続トラッキング エントリを減らすこともできます。
コンテナが「bind: Address already in use」と報告される
アプリケーションがバインドしようとしているポートがすでに予約されているため、Pod 内のコンテナを起動できないことが、コンテナログで示されています。コンテナはクラッシュ ループしています。たとえば、Cloud Logging に次のように記録されているとします。
resource.type="container"
textPayload:"bind: Address already in use"
resource.labels.container_name="redis"
2018-10-16 07:06:47.000 CEST 16 Oct 05:06:47.533 # Creating Server TCP listening socket *:60250: bind: Address already in use
2018-10-16 07:07:35.000 CEST 16 Oct 05:07:35.753 # Creating Server TCP listening socket *:60250: bind: Address already in use
Docker がクラッシュすると、実行中のコンテナが処理されずに最新状態でなくなることがあります。プロセスはまだ Pod に割り当てられたネットワーク名前空間で実行されており、そのポートをリッスンしています。Docker と kubelet は古いコンテナを認識せず、新しいプロセスで新しいコンテナを起動しようとします。しかし、ポートにバインドしようとすると、すでに Pod に関連付けられているネットワーク名前空間にポートが追加されるため、バインドできません。
この問題を診断するには、
.metadata.uuid
フィールドに Pod の UUID が必要です。kubectl get pod -o custom-columns="name:.metadata.name,UUID:.metadata.uid" ubuntu-6948dd5657-4gsgg name UUID ubuntu-6948dd5657-4gsgg db9ed086-edba-11e8-bdd6-42010a800164
ノードから次のコマンドの出力を取得します。
docker ps -a ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep [Pod UUID]
この Pod から実行されているプロセスを確認します。cgroup Namespace の UUID に Pod の UUID が含まれるため、
ps
の出力で Pod の UUID を grep で検索します。引数にコンテナ ID が含まれるdocker-containerd-shim
プロセスも表示されるように、前の行も grep 検索に含めます。出力を見やすくするために、その他の cgroup 列は切り捨てます。# ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep -B 1 db9ed086-edba-11e8-bdd6-42010a800164 | sed s/'blkio:.*'/''/ 1283089 959 Sl futex_wait_queue_me 4026531993 docker-co docker-containerd-shim 276e173b0846e24b704d4 12: 1283107 1283089 Ss sys_pause 4026532393 pause /pause 12: 1283150 959 Sl futex_wait_queue_me 4026531993 docker-co docker-containerd-shim ab4c7762f5abf40951770 12: 1283169 1283150 Ss do_wait 4026532393 sh /bin/sh -c echo hello && sleep 6000000 12: 1283185 1283169 S hrtimer_nanosleep 4026532393 sleep sleep 6000000 12: 1283244 959 Sl futex_wait_queue_me 4026531993 docker-co docker-containerd-shim 44e76e50e5ef4156fd5d3 12: 1283263 1283244 Ss sigsuspend 4026532393 nginx nginx: master process nginx -g daemon off; 12: 1283282 1283263 S ep_poll 4026532393 nginx nginx: worker process
このリストからコンテナ ID を確認でき、これは
docker ps
にも表示されます。この場合、次の手順に従います。
docker-containerd-shim 276e173b0846e24b704d4
は pause を実行中docker-containerd-shim ab4c7762f5abf40951770
は sh で sleep を実行中(sleep-ctr)docker-containerd-shim 44e76e50e5ef4156fd5d3
は nginx を実行中(echoserver-ctr)
これらを
docker ps
の出力で確認します。# docker ps --no-trunc | egrep '276e173b0846e24b704d4|ab4c7762f5abf40951770|44e76e50e5ef4156fd5d3' 44e76e50e5ef4156fd5d383744fa6a5f14460582d0b16855177cbed89a3cbd1f gcr.io/google_containers/echoserver@sha256:3e7b182372b398d97b747bbe6cb7595e5ffaaae9a62506c725656966d36643cc "nginx -g 'daemon off;'" 14 hours ago Up 14 hours k8s_echoserver-cnt_ubuntu-6948dd5657-4gsgg_default_db9ed086-edba-11e8-bdd6-42010a800164_0 ab4c7762f5abf40951770d3e247fa2559a2d1f8c8834e5412bdcec7df37f8475 ubuntu@sha256:acd85db6e4b18aafa7fcde5480872909bd8e6d5fbd4e5e790ecc09acc06a8b78 "/bin/sh -c 'echo hello && sleep 6000000'" 14 hours ago Up 14 hours k8s_sleep-cnt_ubuntu-6948dd5657-4gsgg_default_db9ed086-edba-11e8-bdd6-42010a800164_0 276e173b0846e24b704d41cf4fbb950bfa5d0f59c304827349f4cf5091be3327 registry.k8s.io/pause-amd64:3.1
通常は、
ps
で表示されるすべてのコンテナ ID が、docker ps
で表示されます。表示されない ID がある場合、それは古くなったコンテナです。おそらく、使用中であると報告されている TCP ポートをリッスンしているdocker-containerd-shim process
の子プロセスが見つかります。これを確認するには、コンテナのネットワーク名前空間で
netstat
を実行します。Pod のコンテナ プロセスの pid を取得します(docker-containerd-shim
ではありません)。上記の例では、次のようになります。
- 1283107 - pause
- 1283169 - sh
- 1283185 - sleep
- 1283263 - nginx master
- 1283282 - nginx worker
# nsenter -t 1283107 --net netstat -anp Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 1283263/nginx: mast Active UNIX domain sockets (servers and established) Proto RefCnt Flags Type State I-Node PID/Program name Path unix 3 [ ] STREAM CONNECTED 3097406 1283263/nginx: mast unix 3 [ ] STREAM CONNECTED 3097405 1283263/nginx: mast gke-zonal-110-default-pool-fe00befa-n2hx ~ # nsenter -t 1283169 --net netstat -anp Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 1283263/nginx: mast Active UNIX domain sockets (servers and established) Proto RefCnt Flags Type State I-Node PID/Program name Path unix 3 [ ] STREAM CONNECTED 3097406 1283263/nginx: mast unix 3 [ ] STREAM CONNECTED 3097405 1283263/nginx: mast
また、
ip netns
を使用してnetstat
を実行できます。ただし、Docker がリンクを実行していないため、プロセスのネットワーク名前空間を手動でリンクする必要があります。# ln -s /proc/1283169/ns/net /var/run/netns/1283169 gke-zonal-110-default-pool-fe00befa-n2hx ~ # ip netns list 1283169 (id: 2) gke-zonal-110-default-pool-fe00befa-n2hx ~ # ip netns exec 1283169 netstat -anp Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 1283263/nginx: mast Active UNIX domain sockets (servers and established) Proto RefCnt Flags Type State I-Node PID/Program name Path unix 3 [ ] STREAM CONNECTED 3097406 1283263/nginx: mast unix 3 [ ] STREAM CONNECTED 3097405 1283263/nginx: mast gke-zonal-110-default-pool-fe00befa-n2hx ~ # rm /var/run/netns/1283169
緩和策:
短期的な緩和策としては、上記の説明に従って古くなったプロセスを識別し、kill [PID]
コマンドを使用してこのプロセスを終了します。
長期的な緩和策としては、Docker がクラッシュした原因を識別し、修正します。考えられる原因は次のとおりです。
- ゾンビプロセスが蓄積して、PID 名前空間が不足した
- Docker のバグ
- リソース不足 / OOM
次のステップ
- Kubernetes DNS の問題の診断に関する一般的な情報については、DNS 解決のデバッグをご覧ください。