除錯服務
對於 Kubernetes 新安裝來說,相當常見的問題是服務無法正常運作。您已透過 Deployment (或其他工作負載控制器) 執行您的 Pod,並建立服務,但當您嘗試存取它時,卻沒有回應。這份文件希望能幫助您找出問題所在。
在 Pod 中執行命令
在此處的許多步驟中,您會想要查看叢集中執行的 Pod 所看到的內容。最簡單的方法是執行互動式 busybox Pod
kubectl run -it --rm --restart=Never busybox --image=gcr.io/google-containers/busybox sh
注意
如果您沒有看到命令提示字元,請嘗試按 Enter 鍵。如果您已經有偏好使用的執行中 Pod,您可以使用以下命令在其中執行命令
kubectl exec <POD-NAME> -c <CONTAINER-NAME> -- <COMMAND>
設定
為了進行此逐步解說,讓我們執行一些 Pod。由於您可能正在偵錯自己的服務,您可以替換您自己的詳細資訊,或者您可以跟著操作並取得第二個資料點。
kubectl create deployment hostnames --image=registry.k8s.io/serve_hostname
deployment.apps/hostnames created
kubectl
命令將列印已建立或變更的資源類型與名稱,然後可以在後續命令中使用。
讓我們將部署擴展到 3 個副本。
kubectl scale deployment hostnames --replicas=3
deployment.apps/hostnames scaled
請注意,這與您使用以下 YAML 啟動部署相同
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: hostnames
name: hostnames
spec:
selector:
matchLabels:
app: hostnames
replicas: 3
template:
metadata:
labels:
app: hostnames
spec:
containers:
- name: hostnames
image: registry.k8s.io/serve_hostname
標籤 "app" 會由 kubectl create deployment
自動設定為 Deployment 的名稱。
您可以確認您的 Pod 正在執行
kubectl get pods -l app=hostnames
NAME READY STATUS RESTARTS AGE
hostnames-632524106-bbpiw 1/1 Running 0 2m
hostnames-632524106-ly40y 1/1 Running 0 2m
hostnames-632524106-tlaok 1/1 Running 0 2m
您也可以確認您的 Pod 正在提供服務。您可以取得 Pod IP 位址清單並直接測試它們。
kubectl get pods -l app=hostnames \
-o go-template='{{range .items}}{{.status.podIP}}{{"\n"}}{{end}}'
10.244.0.5
10.244.0.6
10.244.0.7
此逐步解說使用的範例容器透過連接埠 9376 上的 HTTP 提供自己的主機名稱,但如果您正在偵錯自己的應用程式,您會想要使用您的 Pod 正在監聽的任何連接埠號碼。
從 Pod 內部
for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
wget -qO- $ep
done
這應該會產生類似以下的內容
hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok
如果您在此時沒有獲得您預期的回應,您的 Pod 可能不健康,或者可能沒有在您認為的連接埠上監聽。您可能會發現 kubectl logs
對於查看正在發生的事情很有用,或者也許您需要直接 kubectl exec
到您的 Pod 中並從那裡進行偵錯。
假設到目前為止一切都按計劃進行,您可以開始調查為什麼您的服務無法運作。
服務是否存在?
精明的讀者會注意到您實際上尚未建立服務 - 這是故意的。這是偶爾會被遺忘的步驟,也是首先要檢查的事情。
如果您嘗試存取不存在的服務會發生什麼事?如果您有另一個 Pod 透過名稱使用此服務,您會得到類似以下的內容
wget -O- hostnames
Resolving hostnames (hostnames)... failed: Name or service not known.
wget: unable to resolve host address 'hostnames'
首先要檢查的是該服務是否實際存在
kubectl get svc hostnames
No resources found.
Error from server (NotFound): services "hostnames" not found
讓我們建立服務。與之前一樣,這是為了逐步解說 - 您可以在此處使用您自己的服務詳細資訊。
kubectl expose deployment hostnames --port=80 --target-port=9376
service/hostnames exposed
並讀回它
kubectl get svc hostnames
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hostnames ClusterIP 10.0.1.175 <none> 80/TCP 5s
現在您知道服務存在。
與之前一樣,這與您使用 YAML 啟動服務相同
apiVersion: v1
kind: Service
metadata:
labels:
app: hostnames
name: hostnames
spec:
selector:
app: hostnames
ports:
- name: default
protocol: TCP
port: 80
targetPort: 9376
為了突顯完整的組態範圍,您在此處建立的服務使用與 Pod 不同的連接埠號碼。對於許多真實世界的服務,這些值可能會相同。
是否有任何網路策略 Ingress 規則影響目標 Pod?
如果您已部署任何可能影響傳入 hostnames-*
Pod 流量的網路策略 Ingress 規則,則需要審查這些規則。
請參閱 網路策略 以取得更多詳細資訊。
服務是否透過 DNS 名稱運作?
用戶端使用服務最常見的方式之一是透過 DNS 名稱。
從相同命名空間中的 Pod
nslookup hostnames
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: hostnames
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local
如果這失敗,也許您的 Pod 和服務位於不同的命名空間中,請嘗試命名空間限定名稱 (再次,從 Pod 內部)
nslookup hostnames.default
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: hostnames.default
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local
如果這有效,您需要調整您的應用程式以使用跨命名空間名稱,或在相同命名空間中執行您的應用程式與服務。如果這仍然失敗,請嘗試完整限定名稱
nslookup hostnames.default.svc.cluster.local
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: hostnames.default.svc.cluster.local
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local
請注意此處的後綴:"default.svc.cluster.local"。 "default" 是您正在操作的命名空間。 "svc" 表示這是服務。 "cluster.local" 是您的叢集網域,在您自己的叢集中可能會有所不同。
您也可以從叢集中的節點嘗試此操作
注意
10.0.0.10 是叢集的 DNS 服務 IP,您的可能會有所不同。nslookup hostnames.default.svc.cluster.local 10.0.0.10
Server: 10.0.0.10
Address: 10.0.0.10#53
Name: hostnames.default.svc.cluster.local
Address: 10.0.1.175
如果您能夠進行完整限定名稱查詢,但無法進行相對查詢,則需要檢查您的 Pod 中的 /etc/resolv.conf
檔案是否正確。從 Pod 內部
cat /etc/resolv.conf
您應該會看到類似以下的內容
nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local example.com
options ndots:5
nameserver
行必須指示您的叢集 DNS 服務。這會透過 --cluster-dns
旗標傳遞到 kubelet
。
search
行必須包含適當的後綴,讓您可以找到服務名稱。在本例中,它正在尋找本機命名空間 ("default.svc.cluster.local")、所有命名空間 ("svc.cluster.local") 和叢集 ("cluster.local") 中的服務。根據您自己的安裝,您可能在其後有其他記錄 (最多 6 個)。叢集後綴會透過 --cluster-domain
旗標傳遞到 kubelet
。在整份文件中,叢集後綴假設為 "cluster.local"。您自己的叢集可能配置不同,在這種情況下,您應該在所有先前的命令中變更它。
options
行必須將 ndots
設定得足夠高,讓您的 DNS 用戶端程式庫完全考慮搜尋路徑。 Kubernetes 預設將其設定為 5,這足以涵蓋其產生的所有 DNS 名稱。
任何服務是否透過 DNS 名稱運作?
如果以上仍然失敗,則 DNS 查詢對您的服務不起作用。您可以退一步看看還有什麼無法運作。 Kubernetes master 服務應該始終運作。從 Pod 內部
nslookup kubernetes.default
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: kubernetes.default
Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local
如果這失敗,請參閱本文的 kube-proxy 章節,甚至回到本文頂部並重新開始,但不要偵錯您自己的服務,而是偵錯 DNS 服務。
服務是否透過 IP 運作?
假設您已確認 DNS 運作正常,接下來要測試的是您的服務是否透過其 IP 位址運作。從叢集中的 Pod,存取服務的 IP (來自上面的 kubectl get
)。
for i in $(seq 1 3); do
wget -qO- 10.0.1.175:80
done
這應該會產生類似以下的內容
hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok
如果您的服務運作正常,您應該會得到正確的回應。如果沒有,則可能有很多問題發生。請繼續閱讀。
服務是否定義正確?
這聽起來可能很傻,但您真的應該再三檢查您的服務是否正確,並與您的 Pod 連接埠相符。讀回您的服務並驗證它
kubectl get service hostnames -o json
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "hostnames",
"namespace": "default",
"uid": "428c8b6c-24bc-11e5-936d-42010af0a9bc",
"resourceVersion": "347189",
"creationTimestamp": "2015-07-07T15:24:29Z",
"labels": {
"app": "hostnames"
}
},
"spec": {
"ports": [
{
"name": "default",
"protocol": "TCP",
"port": 80,
"targetPort": 9376,
"nodePort": 0
}
],
"selector": {
"app": "hostnames"
},
"clusterIP": "10.0.1.175",
"type": "ClusterIP",
"sessionAffinity": "None"
},
"status": {
"loadBalancer": {}
}
}
- 您嘗試存取的服務埠口是否已列在
spec.ports[]
中? - 針對您的 Pod,
targetPort
是否正確 (有些 Pod 使用的埠口與 Service 不同)? - 如果您打算使用數字埠口,它是一個數字 (9376) 還是字串 "9376"?
- 如果您打算使用具名埠口,您的 Pod 是否公開了具有相同名稱的埠口?
- 此埠口的
protocol
對您的 Pod 來說是否正確?
此 Service 是否有任何端點 (Endpoints)?
如果您已到此步驟,您已確認您的 Service 已正確定義,並已由 DNS 解析。現在讓我們檢查您執行的 Pod 是否確實被 Service 選取。
稍早您看到 Pod 正在執行。您可以重新檢查
kubectl get pods -l app=hostnames
NAME READY STATUS RESTARTS AGE
hostnames-632524106-bbpiw 1/1 Running 0 1h
hostnames-632524106-ly40y 1/1 Running 0 1h
hostnames-632524106-tlaok 1/1 Running 0 1h
-l app=hostnames
引數是在 Service 上設定的標籤選取器。
「AGE」欄位表示這些 Pod 大約已執行一小時,這表示它們運作良好且沒有當機。
「RESTARTS」欄位表示這些 Pod 沒有頻繁當機或重新啟動。頻繁重新啟動可能會導致間歇性連線問題。如果重新啟動計數很高,請閱讀更多關於如何 偵錯 Pod 的資訊。
Kubernetes 系統內部有一個控制迴圈,會評估每個 Service 的選取器,並將結果儲存到對應的端點 (Endpoints) 物件中。
kubectl get endpoints hostnames
NAME ENDPOINTS
hostnames 10.244.0.5:9376,10.244.0.6:9376,10.244.0.7:9376
這確認端點控制器已找到適用於您 Service 的正確 Pod。如果 ENDPOINTS
欄位是 <none>
,您應該檢查您 Service 的 spec.selector
欄位是否確實選取了您 Pod 上的 metadata.labels
值。常見的錯誤是輸入錯誤或其他錯誤,例如 Service 選取 app=hostnames
,但 Deployment 指定 run=hostnames
,就像在 1.18 之前的版本中一樣,當時 kubectl run
指令也可用於建立 Deployment。
Pod 是否正在運作?
此時,您知道您的 Service 存在且已選取您的 Pod。在本逐步解說的開始,您驗證了 Pod 本身。讓我們再次檢查 Pod 是否確實在運作 - 您可以繞過 Service 機制,直接連線到端點 (Endpoints) 上列出的 Pod。
注意
這些指令使用 Pod 埠口 (9376),而不是 Service 埠口 (80)。從 Pod 內部
for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
wget -qO- $ep
done
這應該會產生類似以下的內容
hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok
您預期端點 (Endpoints) 清單中的每個 Pod 都會傳回自己的主機名稱。如果這不是發生的情況 (或您自己的 Pod 的正確行為),您應該調查那裡發生了什麼事。
kube-proxy 是否正在運作?
如果您到此步驟,您的 Service 正在執行、具有端點 (Endpoints),且您的 Pod 實際上正在提供服務。此時,整個 Service 代理機制都令人懷疑。讓我們逐步確認。
Service 的預設實作,也是大多數叢集上使用的實作是 kube-proxy。這是一個在每個節點上執行的程式,並設定一小組機制之一,以提供 Service 抽象化。如果您的叢集未使用 kube-proxy,則以下章節將不適用,您將必須調查您正在使用的 Service 實作。
kube-proxy 是否正在執行?
確認 kube-proxy
正在您的節點上執行。直接在節點上執行,您應該會得到類似以下的結果
ps auxw | grep kube-proxy
root 4194 0.4 0.1 101864 17696 ? Sl Jul04 25:43 /usr/local/bin/kube-proxy --master=https://kubernetes-master --kubeconfig=/var/lib/kube-proxy/kubeconfig --v=2
接下來,確認它沒有發生明顯的錯誤,例如連線到 master。若要執行此操作,您必須查看記錄。存取記錄取決於您的節點作業系統。在某些作業系統上,它是一個檔案,例如 /var/log/kube-proxy.log,而其他作業系統則使用 journalctl
來存取記錄。您應該會看到類似以下的內容
I1027 22:14:53.995134 5063 server.go:200] Running in resource-only container "/kube-proxy"
I1027 22:14:53.998163 5063 server.go:247] Using iptables Proxier.
I1027 22:14:54.038140 5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns-tcp" to [10.244.1.3:53]
I1027 22:14:54.038164 5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns" to [10.244.1.3:53]
I1027 22:14:54.038209 5063 proxier.go:352] Setting endpoints for "default/kubernetes:https" to [10.240.0.2:443]
I1027 22:14:54.038238 5063 proxier.go:429] Not syncing iptables until Services and Endpoints have been received from master
I1027 22:14:54.040048 5063 proxier.go:294] Adding new service "default/kubernetes:https" at 10.0.0.1:443/TCP
I1027 22:14:54.040154 5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns" at 10.0.0.10:53/UDP
I1027 22:14:54.040223 5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns-tcp" at 10.0.0.10:53/TCP
如果您看到關於無法連線到 master 的錯誤訊息,您應該仔細檢查您的節點組態和安裝步驟。
Kube-proxy 可以以幾種模式執行。在上面列出的記錄中,Using iptables Proxier
行表示 kube-proxy 以 "iptables" 模式執行。最常見的其他模式是 "ipvs"。
Iptables 模式
在 "iptables" 模式中,您應該會在節點上看到類似以下的內容
iptables-save | grep hostnames
-A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
-A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
-A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR
對於每個 Service 的每個埠口,在 KUBE-SERVICES
中應該有 1 條規則和一個 KUBE-SVC-<hash>
鏈。對於每個 Pod 端點,在該 KUBE-SVC-<hash>
中應該有少量規則,以及一個 KUBE-SEP-<hash>
鏈,其中包含少量規則。確切的規則會根據您的確切組態而有所不同 (包括 node-ports 和 load-balancers)。
IPVS 模式
在 "ipvs" 模式中,您應該會在節點上看到類似以下的內容
ipvsadm -ln
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
...
TCP 10.0.1.175:80 rr
-> 10.244.0.5:9376 Masq 1 0 0
-> 10.244.0.6:9376 Masq 1 0 0
-> 10.244.0.7:9376 Masq 1 0 0
...
對於每個 Service 的每個埠口,加上任何 NodePorts、外部 IP 和負載平衡器 IP,kube-proxy 都會建立虛擬伺服器。對於每個 Pod 端點,它將建立對應的真實伺服器。在此範例中,服務 hostnames (10.0.1.175:80
) 具有 3 個端點 (10.244.0.5:9376
、10.244.0.6:9376
、10.244.0.7:9376
)。
kube-proxy 是否正在代理?
假設您確實看到上述其中一種情況,請再次嘗試從您的其中一個節點透過 IP 存取您的 Service
curl 10.0.1.175:80
hostnames-632524106-bbpiw
如果仍然失敗,請查看 kube-proxy
記錄中是否有類似以下的特定行
Setting endpoints for default/hostnames:default to [10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376]
如果您沒有看到這些行,請嘗試使用設定為 4 的 -v
旗標重新啟動 kube-proxy
,然後再次查看記錄。
邊緣情況:Pod 無法透過 Service IP 連線到自身
這聽起來可能不太可能,但它確實會發生,而且應該要能運作。
當網路未針對 "hairpin" 流量正確設定時,可能會發生這種情況,通常是在 kube-proxy
以 iptables
模式執行,且 Pod 以橋接網路連線時。如果 Service 的端點嘗試存取自己的 Service VIP,Kubelet
會公開一個 hairpin-mode
旗標,允許這些端點負載平衡回到自身。hairpin-mode
旗標必須設定為 hairpin-veth
或 promiscuous-bridge
。
針對此問題進行疑難排解的常見步驟如下
- 確認
hairpin-mode
設定為hairpin-veth
或promiscuous-bridge
。您應該會看到類似以下的內容。在以下範例中,hairpin-mode
設定為promiscuous-bridge
。
ps auxw | grep kubelet
root 3392 1.1 0.8 186804 65208 ? Sl 00:51 11:11 /usr/local/bin/kubelet --enable-debugging-handlers=true --config=/etc/kubernetes/manifests --allow-privileged=True --v=4 --cluster-dns=10.0.0.10 --cluster-domain=cluster.local --configure-cbr0=true --cgroup-root=/ --system-cgroups=/system --hairpin-mode=promiscuous-bridge --runtime-cgroups=/docker-daemon --kubelet-cgroups=/kubelet --babysit-daemons=true --max-pods=110 --serialize-image-pulls=false --outofdisk-transition-frequency=0
- 確認有效的
hairpin-mode
。若要執行此操作,您必須查看 kubelet 記錄。存取記錄取決於您的節點作業系統。在某些作業系統上,它是一個檔案,例如 /var/log/kubelet.log,而其他作業系統則使用journalctl
來存取記錄。請注意,有效的 hairpin 模式可能與--hairpin-mode
旗標不符,因為相容性問題。檢查 kubelet.log 中是否有任何包含關鍵字hairpin
的記錄行。應該會有記錄行指出有效的 hairpin 模式,如下所示。
I0629 00:51:43.648698 3252 kubelet.go:380] Hairpin mode set to "promiscuous-bridge"
- 如果有效的 hairpin 模式是
hairpin-veth
,請確保Kubelet
具有在節點上的/sys
中操作的權限。如果一切運作正常,您應該會看到類似以下的內容
for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done
1
1
1
1
- 如果有效的 hairpin 模式是
promiscuous-bridge
,請確保Kubelet
具有在節點上操作 linux bridge 的權限。如果cbr0
bridge 已使用且組態正確,您應該會看到
ifconfig cbr0 |grep PROMISC
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1460 Metric:1
- 如果以上方法都無效,請尋求協助。
尋求協助
如果您到此步驟,表示發生非常奇怪的事情。您的 Service 正在執行、具有端點 (Endpoints),且您的 Pod 實際上正在提供服務。您的 DNS 運作正常,且 kube-proxy
似乎沒有異常行為。然而,您的 Service 仍然無法運作。請告訴我們發生了什麼事,以便我們協助調查!
下一步
請造訪疑難排解總覽文件以取得更多資訊。