排解叢集連線問題


本頁說明如何解決 Google Kubernetes Engine (GKE) 叢集的連線問題。

在 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 scpgcloud storage cp 將檔案複製到 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:檔案所在系統的使用者名稱。
      • 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 上執行殼層,然後安裝並啟動 tcpdump 指令。

Pod 網路連線問題

網路總覽討論中所述,請務必瞭解 Pod 如何從網路命名空間連線至節點上的根命名空間,以便有效排除問題。除非另有說明,否則以下討論皆假設叢集使用 GKE 的原生 CNI,而非 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」。

連線失敗次數時多時少

iptables 會轉送 Pod 的連線。系統會將流程追蹤為 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 找出前一行,這樣您也會在引數中取得含有容器 ID 的 docker-containerd-shim 程序。剪下其餘 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,這些 ID 也應該會顯示在 docker ps 中。

    在這種情況下:

    • docker-containerd-shim 276e173b0846e24b704d4 暫停
    • docker-containerd-shim ab4c7762f5abf40951770 for sh with 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。如果找不到,表示容器過時,您可能會看到 docker-containerd-shim process 在 TCP 連接埠上接聽的子程序,該連接埠回報為已在使用中。

    如要驗證這點,請在容器的網路命名空間中執行 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 中的錯誤
  • 資源壓力 / 記憶體不足

後續步驟