1. 程式人生 > 實用技巧 >全網最詳細的 K8s Service 不能訪問排查流程

全網最詳細的 K8s Service 不能訪問排查流程

對於新安裝的 Kubernetes,經常出現的一個問題是 Service 沒有正常工作。如果您已經運行了 Deployment 並建立了一個 Service,但是當您嘗試訪問它時沒有得到響應,希望這份文件能幫助您找出問題所在。

先來熟悉下Service工作邏輯:

為了完成本次演練的目的,我們先執行幾個 Pod。

$kubectlrunhostnames--image=k8s.gcr.io/serve_hostname\
--labels=app=hostnames\
--port=9376\
--replicas=3
deployment.apps/hostnamescreated

確認您的 Pods 是執行狀態:

$kubectlgetpods-lapp=hostnames
NAMEREADYSTATUSRESTARTSAGE
hostnames-632524106-bbpiw1/1Running02m
hostnames-632524106-ly40y1/1Running02m
hostnames-632524106-tlaok1/1Running02m
問題1:Service 存在嗎?

細心的讀者會注意到我們還沒有真正建立一個 Service - 其實這是我們有意的。這是一個有時會被遺忘的步驟,也是第一件要檢查的事情。

那麼,如果我試圖訪問一個不存在的 Service,會發生什麼呢?假設您有另一個 Pod,想通過名稱使用這個 Service,您將得到如下內容:

u@pod$wget-O-hostnames
Resolvinghostnames(hostnames)...failed:Nameorservicenotknown.
wget:unabletoresolvehostaddress'hostnames'

因此,首先要檢查的是 Service 是否確實存在:

$kubectlgetsvchostnames
Noresourcesfound.
Errorfromserver(NotFound):services"hostnames"notfound

我們已經有一個罪魁禍首了,讓我們來建立 Service。就像前面一樣,這裡的內容僅僅是為了步驟的執行 - 在這裡您可以使用自己的 Service 細節。

$kubectlexposedeploymenthostnames--port=80--target-port=9376
service/hostnamesexposed

再查詢一遍,確定一下:

$kubectlgetsvchostnames
NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
hostnamesClusterIP10.0.1.175<none>80/TCP5s

與前面相同,這與您使用 YAML 啟動的 Service 一樣:

apiVersion:v1
kind:Service
metadata:
name:hostnames
spec:
selector:
app:hostnames
ports:
-name:default
protocol:TCP
port:80
targetPort:9376

現在您可以確認 Service 存在。

問題2:Service 是否通過 DNS 工作?

從相同 Namespace 下的 Pod 中執行:

u@pod$nslookuphostnames
Address1:10.0.0.10kube-dns.kube-system.svc.cluster.local

Name:hostnames
Address1:10.0.1.175hostnames.default.svc.cluster.local

如果失敗,那麼您的 Pod 和 Service 可能位於不同的 Namespace 中,請嘗試使用限定名稱空間的名稱:

u@pod$nslookuphostnames.default
Address1:10.0.0.10kube-dns.kube-system.svc.cluster.local

Name:hostnames.default
Address1:10.0.1.175hostnames.default.svc.cluster.local

如果成功,那麼需要調整您的應用,使用跨名稱空間的名稱去訪問服務,或者,在相同的 Namespace 中執行應用和 Service。如果仍然失敗,請嘗試一個完全限定的名稱:

u@pod$nslookuphostnames.default.svc.cluster.local
Address1:10.0.0.10kube-dns.kube-system.svc.cluster.local

Name:hostnames.default.svc.cluster.local
Address1:10.0.1.175hostnames.default.svc.cluster.local

注意這裡的字尾:”default.svc.cluster.local”。”default” 是我們正在操作的 Namespace。”svc” 表示這是一個 Service。”cluster.local” 是您的叢集域,在您自己的叢集中可能會有所不同。

您也可以在叢集中的 Node 上嘗試此操作:

注意:10.0.0.10 是我的 DNS Service,您的可能不同)

u@node$nslookuphostnames.default.svc.cluster.local10.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

如果您能夠使用完全限定的名稱查詢,但不能使用相對名稱,則需要檢查 /etc/resolv.conf 檔案是否正確。

u@pod$cat/etc/resolv.conf
nameserver10.0.0.10
searchdefault.svc.cluster.localsvc.cluster.localcluster.localexample.com
optionsndots:5

nameserver 行必須指示您的叢集的 DNS Service,它通過 --cluster-dns 標誌傳遞到 kubelet。

search 行必須包含一個適當的字尾,以便查詢 Service 名稱。在本例中,它在本地 Namespace(default.svc.cluster.local)、所有 Namespace 中的 Service(svc.cluster.local)以及叢集(cluster.local)中查詢服務。根據您自己的安裝情況,可能會有額外的記錄(最多 6 條)。集群后綴通過 --cluster-domain 標誌傳遞給 kubelet。本文件中,我們假定它是 “cluster.local”,但是您的可能不同,這種情況下,您應該在上面的所有命令中更改它。

options 行必須設定足夠高的 ndots,以便 DNS 客戶端庫考慮搜尋路徑。在預設情況下,Kubernetes 將這個值設定為 5,這個值足夠高,足以覆蓋它生成的所有 DNS 名稱。

問題3:DNS 是否可以解析預設服務?

如果上面仍然失敗 - DNS 查詢不到您需要的 Service - 我們可以後退一步,看看還有什麼不起作用。Kubernetes 主 Service 應該一直是工作的:

u@pod$nslookupkubernetes.default
Server:10.0.0.10
Address1:10.0.0.10kube-dns.kube-system.svc.cluster.local

Name:kubernetes.default
Address1:10.0.0.1kubernetes.default.svc.cluster.local

如果失敗,您可能需要轉到這個文件的 kube-proxy 部分,或者甚至回到文件的頂部重新開始,但不是除錯您自己的 Service,而是除錯 DNS。

問題4:Service 能夠通過 IP 訪問麼?

假設我們可以確認 DNS 工作正常,那麼接下來要測試的是您的 Service 是否工作正常。從叢集中的一個節點,訪問 Service 的 IP(從上面的 kubectl get 命令獲取)。

u@node$curl10.0.1.175:80
hostnames-0uton

u@node$curl10.0.1.175:80
hostnames-yp2kp

u@node$curl10.0.1.175:80
hostnames-bvc05

如果 Service 是正常的,您應該得到正確的響應。如果沒有,有很多可能出錯的地方,請繼續。

問題5:Service 是對的嗎?

這聽起來可能很愚蠢,但您應該加倍甚至三倍檢查 Service 是否正確,並且與您的 Pod 匹配。檢視 Service 並驗證它:

$kubectlgetservicehostnames-ojson
{
"kind":"Service",
"apiVersion":"v1",
"metadata":{
"name":"hostnames",
"namespace":"default",
"selfLink":"/api/v1/namespaces/default/services/hostnames",
"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[] 中描述的是您想要嘗試訪問的埠嗎?targetPort 對您的 Pod 來說正確嗎(許多 Pod 選擇使用與 Service 不同的埠)?如果您想把它變成一個數字埠,那麼它是一個數字(9376)還是字串 “9376”?如果您想把它當作一個指定的埠,那麼您的 Pod 是否公開了一個同名埠?埠的 protocol 和 Pod 的一樣嗎?

問題6:Service 有端點嗎?

如果您已經走到了這一步,我們假設您已經確認 Service 存在,並能通過 DNS 解析。現在,讓我們檢查一下,您執行的 Pod 確實是由 Service 選擇的。

早些時候,我們已經看到 Pod 是執行狀態。我們可以再檢查一下:

$kubectlgetpods-lapp=hostnames
NAMEREADYSTATUSRESTARTSAGE
hostnames-0uton1/1Running01h
hostnames-bvc051/1Running01h
hostnames-yp2kp1/1Running01h

“AGE” 列表明這些 Pod 已經啟動一個小時了,這意味著它們執行良好,而不是崩潰。

-l app=hostnames 引數是一個標籤選擇器 - 就像我們的 Service 一樣。在 Kubernetes 系統中有一個控制迴圈,它評估每個 Service 的選擇器,並將結果儲存到 Endpoints 物件中。

$kubectlgetendpointshostnames
NAMEENDPOINTS
hostnames10.244.0.5:9376,10.244.0.6:9376,10.244.0.7:9376

這證實 endpoints 控制器已經為您的 Service 找到了正確的 Pods。如果 hostnames 行為空,則應檢查 Service 的 spec.selector 欄位,以及您實際想選擇的 Pods 的 metadata.labels 的值。常見的錯誤是輸入錯誤或其他錯誤,例如 Service 想選擇 run=hostnames,但是 Deployment 指定的是 app=hostnames。

問題7:Pod 正常工作嗎?

到了這步,我們知道您的 Service 存在並選擇了 Pods。讓我們檢查一下 Pod 是否真的在工作 - 我們可以繞過 Service 機制,直接進入 Pod。

注意:這些命令使用的是 Pod 埠(9376),而不是 Service 埠(80)。

u@pod$wget-qO-10.244.0.5:9376
hostnames-0uton

pod$wget-qO-10.244.0.6:9376
hostnames-bvc05

u@pod$wget-qO-10.244.0.7:9376
hostnames-yp2kp

我們期望的是 Endpoints 列表中的每個 Pod 返回自己的主機名。如果這沒有發生(或者您自己的 Pod 的正確行為沒有發生),您應該調查發生了什麼。您會發現 kubectl logs 這個時候非常有用,或者使用 kubectl exec 直接進入到您的 Pod,並從那裡檢查服務。

另一件要檢查的事情是,您的 Pod 沒有崩潰或正在重新啟動。頻繁的重新啟動可能會導致斷斷續續的連線問題。

$kubectlgetpods-lapp=hostnames
NAMEREADYSTATUSRESTARTSAGE
hostnames-632524106-bbpiw1/1Running02m
hostnames-632524106-ly40y1/1Running02m
hostnames-632524106-tlaok1/1Running02m

如果重新啟動計數很高,請查閱有關如何除錯 pods 獲取更多資訊。

問題8:kube-proxy 正常工作嗎?

如果您到了這裡,那麼 Service 正在執行,也有 Endpoints,而您的 Pod 實際上也正在服務。在這一點上,整個 Service 代理機制是否正常就是可疑的了。我們來確認一下,一部分一部分來。

確認 kube-proxy 正在您的 Nodes 上執行。您應該得到如下內容:

u@node$psauxw|grepkube-proxy
root41940.40.110186417696?SlJul0425:43/usr/local/bin/kube-proxy--master=https://kubernetes-master--kubeconfig=/var/lib/kube-proxy/kubeconfig--v=2

下一步,確認它並沒有出現明顯的失敗,比如連線主節點失敗。要做到這一點,您必須檢視日誌。訪問日誌取決於您的 Node 作業系統。在某些作業系統是一個檔案,如 /var/log/messages kube-proxy.log,而其他作業系統使用 journalctl 訪問日誌。您應該看到類似的東西:

I102722:14:53.9951345063server.go:200]Runninginresource-onlycontainer"/kube-proxy"
I102722:14:53.9981635063server.go:247]UsingiptablesProxier.
I102722:14:53.9990555063server.go:255]Tearingdownuserspacerules.Errorshereareacceptable.
I102722:14:54.0381405063proxier.go:352]Settingendpointsfor"kube-system/kube-dns:dns-tcp"to[10.244.1.3:53]
I102722:14:54.0381645063proxier.go:352]Settingendpointsfor"kube-system/kube-dns:dns"to[10.244.1.3:53]
I102722:14:54.0382095063proxier.go:352]Settingendpointsfor"default/kubernetes:https"to[10.240.0.2:443]
I102722:14:54.0382385063proxier.go:429]NotsyncingiptablesuntilServicesandEndpointshavebeenreceivedfrommaster
I102722:14:54.0400485063proxier.go:294]Addingnewservice"default/kubernetes:https"at10.0.0.1:443/TCP
I102722:14:54.0401545063proxier.go:294]Addingnewservice"kube-system/kube-dns:dns"at10.0.0.10:53/UDP
I102722:14:54.0402235063proxier.go:294]Addingnewservice"kube-system/kube-dns:dns-tcp"at10.0.0.10:53/TCP

如果您看到有關無法連線主節點的錯誤訊息,則應再次檢查節點配置和安裝步驟。

kube-proxy 無法正確執行的可能原因之一是找不到所需的 conntrack 二進位制檔案。在一些 Linux 系統上,這也是可能發生的,這取決於您如何安裝叢集,例如,您正在從頭開始安裝 Kubernetes。如果是這樣的話,您需要手動安裝 conntrack 包(例如,在 Ubuntu 上使用 sudo apt install conntrack),然後重試。

問題9:kube-proxy 是否在寫 iptables 規則?

kube-proxy 的主要職責之一是寫實現 Services 的 iptables 規則。讓我們檢查一下這些規則是否已經被寫好了。

kube-proxy 可以在 “userspace” 模式、 “iptables” 模式或者 “ipvs” 模式下執行。如果您正在使用 “iptables” 模式或者 “ipvs” 模式。您應該看到以下情況之一。

Iptables
u@node$iptables-save|grephostnames
-AKUBE-SEP-57KPRZ3JQVENLNBR-s10.244.3.6/32-mcomment--comment"default/hostnames:"-jMARK--set-xmark0x00004000/0x00004000
-AKUBE-SEP-57KPRZ3JQVENLNBR-ptcp-mcomment--comment"default/hostnames:"-mtcp-jDNAT--to-destination10.244.3.6:9376
-AKUBE-SEP-WNBA2IHDGP2BOBGZ-s10.244.1.7/32-mcomment--comment"default/hostnames:"-jMARK--set-xmark0x00004000/0x00004000
-AKUBE-SEP-WNBA2IHDGP2BOBGZ-ptcp-mcomment--comment"default/hostnames:"-mtcp-jDNAT--to-destination10.244.1.7:9376
-AKUBE-SEP-X3P2623AGDH6CDF3-s10.244.2.3/32-mcomment--comment"default/hostnames:"-jMARK--set-xmark0x00004000/0x00004000
-AKUBE-SEP-X3P2623AGDH6CDF3-ptcp-mcomment--comment"default/hostnames:"-mtcp-jDNAT--to-destination10.244.2.3:9376
-AKUBE-SERVICES-d10.0.1.175/32-ptcp-mcomment--comment"default/hostnames:clusterIP"-mtcp--dport80-jKUBE-SVC-NWV5X2332I4OT4T3
-AKUBE-SVC-NWV5X2332I4OT4T3-mcomment--comment"default/hostnames:"-mstatistic--moderandom--probability0.33332999982-jKUBE-SEP-WNBA2IHDGP2BOBGZ
-AKUBE-SVC-NWV5X2332I4OT4T3-mcomment--comment"default/hostnames:"-mstatistic--moderandom--probability0.50000000000-jKUBE-SEP-X3P2623AGDH6CDF3
-AKUBE-SVC-NWV5X2332I4OT4T3-mcomment--comment"default/hostnames:"-jKUBE-SEP-57KPRZ3JQVENLNBR

KUBE-SERVICES 中應該有 1 條規則,KUBE-SVC-(hash) 中每個端點有 1 或 2 條規則(取決於 SessionAffinity),每個端點中應有 1 條 KUBE-SEP-(hash) 鏈。準確的規則將根據您的確切配置(包括節點、埠組合以及負載均衡器設定)而有所不同。

IPVS
u@node$ipvsadm-ln
ProtLocalAddress:PortSchedulerFlags
->RemoteAddress:PortForwardWeightActiveConnInActConn
...
TCP10.0.1.175:80rr
->10.244.0.5:9376Masq100
->10.244.0.6:9376Masq100
->10.244.0.7:9376Masq100
...

IPVS 代理將為每個伺服器地址(例如叢集 IP、外部 IP、節點埠 IP、負載均衡 IP等)建立虛擬伺服器,併為服務的端點建立一些相應的真實伺服器(如果有)。在這個例子中,伺服器主機(10.0.1.175:80)有 3 個端點(10.244.0.5:9376, 10.244.0.6:9376, 10.244.0.7:9376),你會得到類似上面的結果。

如果走到這一步還沒解決!那只有燒香拜佛了!