排查集群中的连接问题


本页面介绍了如何解决集群中的连接问题。

与在 GKE 中捕获网络数据包相关的连接问题

本部分介绍了如何排查与捕获网络数据包相关的连接问题,包括连接超时、连接遭拒错误或意外应用行为等症状。这些连接问题可能发生在节点级别或 Pod 级别。

集群网络中的连接问题通常分为以下类别:

  • 无法访问 Pod:由于网络配置错误,可能无法从集群内部或外部访问 Pod。
  • 服务中断:服务可能会出现中断或延迟。
  • Pod 间通信问题:Pod 可能无法有效相互通信。

GKE 集群中的连接问题可能由多种原因造成,其中包括:

  • 网络配置错误:网络政策、防火墙规则或路由表不正确。
  • 应用错误:影响网络互动的应用代码中的错误。
  • 基础架构问题:网络拥塞、硬件故障或资源限制。

以下部分介绍了如何解决有问题的节点或 Pod 的问题。

  1. 使用以下命令识别有问题的 Pod 所运行的节点:

    kubectl get pods POD_NAME -o=wide -n NAMESPACE
    

    替换以下内容:

    • POD_NAME 替换为 Pod 名称。
    • NAMESPACE 替换为 Kubernetes 命名空间。
  2. 连接到节点:

    gcloud compute ssh NODE_NAME \
        --zone=ZONE
    

    替换以下内容:

    • NODE_NAME:您的节点名称。
    • ZONE:节点运行的可用区的名称。
  3. 如需调试特定 Pod,请识别与该 Pod 关联的 veth 接口:

    ip route | grep POD_IP
    

    POD_IP 替换为 Pod IP 地址。

  4. 运行工具箱命令

toolbox 命令

toolbox 是一个实用程序,可在 GKE 节点中提供容器化环境以进行调试和问题排查。本部分介绍了如何安装 toolbox 实用程序并使用它来排查节点问题。

  1. 在连接到节点时,启动 toolbox 工具:

    toolbox
    

    此操作会下载有助于使用 toolbox 实用程序的文件。

  2. toolbox 根提示符下,安装 tcpdump

    • 对于具有外部 IP 地址或 Cloud NAT 的集群:

      apt update -y && apt install -y tcpdump
      
    • 对于不使用 Cloud NAT 的专用集群:

      如果您的专用集群未使用 Cloud NAT,则无法使用 apt 安装 tcpdump。请改为从官方代码库下载 libpcaptcpdump 发布版本文件,并使用 gcloud compute scpgsutil 将文件复制到虚拟机。然后,按照以下步骤手动安装代码库:

      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:文件所在系统上的您的用户名。
      • VERSIONtcpdumplibpcap 软件包的特定版本号。
  3. 开始捕获数据包:

    tcpdump -i eth0 -s 100 "port PORT" \
    -w /media/root/mnt/stateful_partition/CAPTURE_FILE_NAME
    

    替换以下内容:

    • PORT:端口号的名称。
    • CAPTURE_FILE_NAME:您的捕获文件的名称。
  4. 停止数据包捕获并中断 tcpdump

  5. 输入 exit 退出工具箱。

  6. 列出数据包捕获文件并检查大小:

    ls -ltr /mnt/stateful_partition/CAPTURE_FILE_NAME
    
  7. 将数据包捕获从节点复制到计算机上的当前工作目录:

    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

使用 --enable-network-policy 启用。

如需了解详情,请参阅使用网络政策控制 Pod 与服务之间的通信

netd 继承 通过以下任一方式启用:
GKE Dataplane V2 继承

使用 --enable-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 关联的网络命名空间中。

如需诊断此问题:

  1. 您需要在 .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
    
  2. 从节点获取以下命令的输出:

    docker ps -a
    ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep [Pod UUID]
    
  3. 检查此 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
    
  4. 在此列表中,您可以看到容器 ID,它们在 docker ps 中也应该可见。

    在此示例中:

    • docker-containerd-shim 276e173b0846e24b704d4 为 pause
    • docker-containerd-shim ab4c7762f5abf40951770 为 sh 和 sleep (sleep-ctr)
    • docker-containerd-shim 44e76e50e5ef4156fd5d3 为 nginx (echoserver-ctr)
  5. 请查看 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 解析