本页面介绍了如何解决集群中的连接问题。
与在 GKE 中捕获网络数据包相关的连接问题
本部分介绍了如何排查与捕获网络数据包相关的连接问题,包括连接超时、连接遭拒错误或意外应用行为等症状。这些连接问题可能发生在节点级别或 Pod 级别。
集群网络中的连接问题通常分为以下类别:
- 无法访问 Pod:由于网络配置错误,可能无法从集群内部或外部访问 Pod。
- 服务中断:服务可能会出现中断或延迟。
- Pod 间通信问题:Pod 可能无法有效相互通信。
GKE 集群中的连接问题可能由多种原因造成,其中包括:
- 网络配置错误:网络政策、防火墙规则或路由表不正确。
- 应用错误:影响网络互动的应用代码中的错误。
- 基础架构问题:网络拥塞、硬件故障或资源限制。
以下部分介绍了如何解决有问题的节点或 Pod 的问题。
使用以下命令识别有问题的 Pod 所运行的节点:
kubectl get pods
POD_NAME -o=wide -nNAMESPACE 替换以下内容:
- 将
POD_NAME
替换为 Pod 名称。 - 将
NAMESPACE
替换为 Kubernetes 命名空间。
- 将
连接到节点:
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
根提示符下,安装tcpdump
:对于具有外部 IP 地址或 Cloud NAT 的集群:
apt update -y && apt install -y tcpdump
对于不使用 Cloud NAT 的专用集群:
如果您的专用集群未使用 Cloud NAT,则无法使用
apt
安装tcpdump
。请改为从官方代码库下载libpcap
和tcpdump
发布版本文件,并使用gcloud compute scp
或gsutil
将文件复制到虚拟机。然后,按照以下步骤手动安装代码库: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 上运行 shell,然后安装并启动tcpdump
命令。
Pod 网络连接问题
如网络概览讨论中所述,为了有效地进行问题排查,了解 Pod 如何从其网络命名空间连接到节点上的根命名空间非常重要。对于以下讨论,除非另有说明,否则假设集群使用 GKE(而非 Calico)的原生 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 握手完成,但未收到 SSL hello),请确保每个 Linux Pod 接口的 MTU 已正确设置为集群的 VPC 网络的 MTU。
ip address show cbr0
使用叠加层时(例如,Weave 或 Flannel),必须进一步减小该 MTU 以适应叠加层上的封装开销。
GKE MTU
为 Pod 接口选择的 MTU 取决于集群节点使用的容器网络接口 (CNI) 和底层 VPC MTU 设置。如需了解详情,请参阅 Pod。
Pod 接口 MTU 值为 1460
或从节点的主要接口继承。
CNI | MTU | GKE Standard |
---|---|---|
kubenet | 1460 | 默认 |
kubenet (GKE 1.26.1 版及更高版本) |
继承 | 默认 |
Calico | 1460 |
使用 如需了解详情,请参阅使用网络政策控制 Pod 与服务之间的通信。 |
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 命名空间的 UUID 包含 Pod 的 UUID,所以可以在
ps
输出中通过 grep 命令查找 Pod UUID。还可以通过 grep 查找前一行,以便在docker-containerd-shim
进程的参数中也包含容器 ID。删除 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
为 pausedocker-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
在正常情况下,您会在
docker ps
中显示的ps
中看到所有容器 ID。如果有一个看不到,则该容器为过时容器,并且您可能会看到在报告为已在使用中的 TCP 端口上侦听的docker-containerd-shim process
的子进程。为验证这一点,请在该容器的网络命名空间中执行
netstat
。获取 Pod 的任何容器进程(不是docker-containerd-shim
)的 pid。在上述示例中:
- 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 解析。