排解叢集連線問題

Google Kubernetes Engine (GKE) 叢集網路連線問題可能會導致連線逾時、連線遭拒、無法連上 Pod、服務中斷或 Pod 間通訊失敗。

請參閱這份文件,瞭解如何擷取網路封包 (使用 toolbox 和 tcpdump) 及診斷 Pod 網路問題,排解叢集連線問題。

平台管理員和營運人員以及應用程式開發人員,可運用這項資訊診斷及修正影響 GKE 工作負載的網路問題。如要進一步瞭解我們在 Google Cloud內容中提及的常見角色和範例工作,請參閱「常見的 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。請改為從官方存放區下載 libpcap 和 tcpdump 版本檔案,然後使用 gcloud compute scp 或 gcloud storage cp 將檔案複製到 VM。然後按照下列步驟手動安裝程式庫:

      # Copy the downloaded packages to a staging area
      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/
      
      # Extract the archives
      tar -xvzf tcpdump-VERSION.tar.gz
      tar -xvzf libpcap-VERSION.tar.gz
      
      # Build and install libpcap (a dependency for tcpdump)
      cd libpcap-VERSION
      ./configure ; make ; make install
      
      # Build and install tcpdump
      cd ../tcpdump-VERSION
      ./configure ; make ; make install
      
      # Verify tcpdump installation
      tcpdump --version
      

      更改下列內容:

      • USER_NAME:檔案所在系統的使用者名稱。
      • VERSION:tcpdump 和 libpcap 套件的特定版本號碼。
  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 網路連線問題

如網路總覽討論中所述,瞭解 Pod 如何從網路命名空間連線至節點上的根命名空間,是有效排除問題的關鍵。除非另有說明,否則在以下討論中,請假設叢集使用 GKE 原生 CNI,而非 Calico。也就是說,未套用任何網路政策。

所選節點上的 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 已正確設為叢集虛擬私有雲網路的 MTU。

ip address show cbr0

使用疊加層 (例如 Weave 或 Flannel) 時,必須進一步降低這個 MTU,以容納疊加層的封裝負載。

GKE MTU

為 Pod 介面選取的 MTU 取決於叢集節點使用的容器網路介面 (CNI),以及基礎虛擬私有雲 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 找出前一行,這樣您也會在引數中取得含有容器 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 for nginx (echoserver-ctr)
  5. 請在 docker ps 輸出內容中檢查以下項目:

    # docker ps --no-trunc | egrep '276e173b0846e24b704d4|ab4c7762f5abf40951770|44e76e50e5ef4156fd5d3'
    44e76e50e5ef4156fd5d383744fa6a5f14460582d0b16855177cbed89a3cbd1f   registry.k8s.io/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 的任何容器程序 (非 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 錯誤
  • 資源壓力 / 記憶體不足

後續步驟