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


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

先來熟悉下Service工作邏輯:

 

 

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

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

確認您的 Pods 是運行狀態:

$ 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
問題1:Service 存在嗎?

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

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

u@pod$ wget -O- hostnames
Resolving hostnames (hostnames)... failed: Name or service not known.
wget: unable to resolve host address 'hostnames'

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

$ kubectl get svc hostnames
No resources found.
Error from server (NotFound): services "hostnames" not found

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

$ 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 啟動的 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$ 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 和 Service 可能位於不同的 Namespace 中,請嘗試使用限定命名空間的名稱:

u@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

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

u@pod$ 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” 是我們正在操作的 Namespace。”svc” 表示這是一個 Service。”cluster.local” 是您的集群域,在您自己的集群中可能會有所不同。

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

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

u@node$ 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

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

u@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 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$ 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 部分,或者甚至回到文檔的頂部重新開始,但不是調試您自己的 Service,而是調試 DNS。

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

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

u@node$ curl 10.0.1.175:80
hostnames-0uton

u@node$ curl 10.0.1.175:80
hostnames-yp2kp

u@node$ curl 10.0.1.175:80
hostnames-bvc05

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

問題5:Service 是對的嗎?

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

$ kubectl get service hostnames -o json
{
    "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 是運行狀態。我們可以再檢查一下:

$ kubectl get pods -l app=hostnames
NAME              READY     STATUS    RESTARTS   AGE
hostnames-0uton   1/1       Running   0          1h
hostnames-bvc05   1/1       Running   0          1h
hostnames-yp2kp   1/1       Running   0          1h

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

-l app=hostnames 參數是一個標簽選擇器 - 就像我們的 Service 一樣。在 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

這證實 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 沒有崩潰或正在重新啟動。頻繁的重新啟動可能會導致斷斷續續的連接問題。

$ 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

如果重新啟動計數很高,請查閱有關如何調試 pods 獲取更多信息。

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

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

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

u@node$ 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

下一步,確認它並沒有出現明顯的失敗,比如連接主節點失敗。要做到這一點,您必須查看日志。訪問日志取決於您的 Node 操作系統。在某些操作系統是一個文件,如 /var/log/messages 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:53.999055    5063 server.go:255] Tearing down userspace rules. Errors here are acceptable.
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

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

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 | 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

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

IPVS
u@node$ 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
...

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),你會得到類似上面的結果。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM