對於新安裝的 Kubernetes,常常出現的一個問題是 Service 沒有正常工做。若是您已經運行了 Deployment 並建立了一個 Service,可是當您嘗試訪問它時沒有獲得響應,但願這份文檔能幫助您找出問題所在。node
先來熟悉下Service工做邏輯:json
爲了完成本次演練的目的,咱們先運行幾個 Pod。api
$ 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
細心的讀者會注意到咱們尚未真正建立一個 Service - 其實這是咱們有意的。這是一個有時會被遺忘的步驟,也是第一件要檢查的事情。session
那麼,若是我試圖訪問一個不存在的 Service,會發生什麼呢?假設您有另外一個 Pod,想經過名稱使用這個 Service,您將獲得以下內容:app
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 細節。dom
$ kubectl expose deployment hostnames --port=80 --target-port=9376 service/hostnames exposed
再查詢一遍,肯定一下:curl
$ 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 同樣:tcp
apiVersion: v1 kind: Service metadata: name: hostnames spec: selector: app: hostnames ports: - name: default protocol: TCP port: 80 targetPort: 9376
如今您能夠確認 Service 存在。
從相同 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 名稱。
若是上面仍然失敗 - 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。
假設咱們能夠確認 DNS 工做正常,那麼接下來要測試的是您的 Service 是否工做正常。從集羣中的一個節點,訪問 Service 的 IP(從上面的 kubectl get 命令獲取)。
u@node$ curl 10.0.1.175:80 hostnames-0utonu@node$ curl 10.0.1.175:80 hostnames-yp2kp u@node$ curl 10.0.1.175:80 hostnames-bvc05
若是 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 的同樣嗎?
若是您已經走到了這一步,咱們假設您已經確認 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。
到了這步,咱們知道您的 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 獲取更多信息。
若是您到了這裏,那麼 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),而後重試。
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),你會獲得相似上面的結果。
若是走到這一步還沒解決!那只有燒香拜佛了!
注:全文來自官方文檔翻譯而成