排查集群中的连接问题

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

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

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

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

  • 无法访问 Pod:由于网络配置错误,可能无法从集群内部或外部访问 Pod。
  • Service 中断:Service 可能会中断或延迟。
  • 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 命令

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 退出 toolbox。

  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 或从节点的主要接口继承。

间歇性连接失败

与 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 解析