K8S deployment故障可視化排查指南


這是一個示意圖,可幫助您調試Kubernetes中的deployemnt,


當您希望在Kubernetes中部署應用程序時,通常定義三個組件:

一個deployment - 這是創建名為Pods的應用程序副本的秘訣一個service - 內部負載平衡器路由流量到pod一個ingress - 從外部訪問集群服務的網絡流向的描述
以下是快速視覺回顧。

在Kubernetes中,您的應用程序通過兩層負載均衡器公開:內部和外部。

內部的負載均衡器稱為Service,而外部的負載均衡器稱為Ingress。

pod未直接部署。相反,deploymeny會在其上創建和watchPod。

假設您希望部署一個簡單的Hello World應用程序,則該應用程序的YAML應該類似於以下內容:

<code>apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
track: canary
spec:
selector:
matchLabels:
any-name: my-app
template:
metadata:
labels:
any-name: my-app
spec:
containers:
- name: cont1
image: learnk8s/app:1.0.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- port: 80
targetPort: 8080
selector:
name: app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- http:
paths:
- backend:
serviceName: app


servicePort: 80
path: /
/<code>

定義很長,很容易忽略組件之間的相互關係
例如:

什麼時候應使用端口80,何時應使用端口8080?您是否應該為每個服務創建一個新端口,以免它們衝突?標籤名稱重要嗎?所有的都應該一樣嗎?

在進行調試之前,讓我們回顧一下這三個組件如何相互鏈接。
讓我們從Deployment和Service開始。

連接Deployment和Service

令人驚訝的消息是,Deployment和Service根本沒有連接。
而是,該服務直接指向Pod,並完全跳過部署。
因此,您應該注意的是Pod和Service之間的相互關係。
您應該記住三件事:

服務選擇器應至少與Pod的一個標籤匹配服務targetPort應與containerPortPod中容器的匹配服務port可以是任何號碼。多個服務可以使用同一端口,因為它們分配了不同的IP地址。
下圖總結了如何連接端口:

考慮Service暴露的以下Pod。

創建Pod時,應為Pod containerPort中的每個容器定義端口。

創建服務時,可以定義port和targetPort。但是您應該連接哪一個容器?

targetPort並且containerPort應該始終匹配

如果您的容器暴露了端口3000,則targetPort應當與該端口號匹配。

如果您查看YAML,則標籤和ports/ targetPort應該匹配:

<code>apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
track: canary
spec:
selector:
matchLabels:
any-name: my-app
template:
metadata:
labels:
any-name: my-app
spec:
containers:
- name: cont1
image: learnk8s/app:1.0.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- port: 80
targetPort: 8080
selector:
any-name: my-app
/<code>

Deployment 頭部的track: canary是什麼?
那也應該匹配嗎?
該標籤屬於Deployment,Service的選擇器未使用它來路由流量。
換句話說,您可以安全地刪除它或為其分配其他值。
那matchLabels選擇器呢?
它始終必須與Pod標籤匹配,並且由Deployment用來跟蹤Pod。


假設您進行了正確的更改,如何測試它?
您可以使用以下命令檢查Pod是否具有正確的標籤:

<code>kubectl get pods --show-labels
/<code>

或者,如果您具有屬於多個應用程序的Pod:

<code>kubectl get pods --selector any-name=my-app --show-labels
/<code>

any-name=my-app標籤在哪裡any-name: my-app。
還有問題嗎?
您也可以連接到Pod!
您可以使用kubectl中的port-forward命令連接到服務並測試連接。

<code>kubectl port-forward service/<service> 3000:80
/<service>/<code>

如果:

service/ 是服務的名稱-在當前的YAML中是my-service3000是您希望在計算機上打開的端口80是服務在port現場暴露的端口
如果可以連接,則說明設置正確。
如果不行,則很可能是您放錯了標籤或端口不匹配。連接Service和ingress暴露您的應用的下一步是配置Ingress。

Ingress必須知道如何檢索服務,然後檢索Pod並將流量路由到它們。

Ingress按名稱和公開的端口檢索正確的服務。

在Ingress和Service中應該匹配兩件事:

在Ingress中該servicePort應該匹配port的服務在Ingress中該serviceName應該匹配name的服務
下圖總結了如何連接端口:

您已經知道該服務公開了一個端口。

Ingress有一個名為servicePort的字段

Service端口和ingress servicePort應始終匹配。

如果決定為服務分配端口80,則也應將servicePort更改為80。


在實踐中,您應該查看以下幾行:

<code>apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- port: 80
targetPort: 8080
selector:
any-name: my-app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- http:
paths:
- backend:
serviceName: my-service
servicePort: 80
path: /
/<code>

您如何測試Ingress的功能?
您可以使用與以前相同的策略kubectl port-forward,但是應該連接到Ingress控制器,而不是連接到服務。
首先,使用以下命令檢索Ingress控制器的Pod名稱:

<code>kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS
kube-system coredns-5644d7b6d9-jn7cq 1/1 Running
kube-system etcd-minikube 1/1 Running
kube-system kube-apiserver-minikube 1/1 Running
kube-system kube-controller-manager-minikube 1/1 Running
kube-system kube-proxy-zvf2h 1/1 Running
kube-system kube-scheduler-minikube 1/1 Running
kube-system nginx-ingress-controller-6fc5bcc 1/1 Running


/<code>

確定Ingress Pod(可能在不同的命名空間中)並描述它以檢索端口

<code>kubectl describe pod nginx-ingress-controller-6fc5bcc \\
--namespace kube-system \\
| grep Ports
Ports: 80/TCP, 443/TCP, 18080/TCP
/<code>

最後,連接到Pod:

<code>kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system
/<code>

此時,每次您訪問計算機上的端口3000時,請求都會轉發到Ingress控制器Pod上的端口80。
如果訪問本地3000端口

回顧端口

快速回顧一下哪些端口和標籤應該匹配:

服務選擇器應與Pod的標籤匹配服務targetPort應與containerPortPod中容器的匹配服務端口可以是任何數字。多個服務可以使用同一端口,因為它們分配了不同的IP地址。在servicePort該入口的應該匹配port在服務服務名稱應與serviceNameIngress 中的字段匹配知道如何構造YAML定義只是故事的一部分。
出問題了怎麼辦?
Pod可能無法啟動,或者正在崩潰。

解決Kubernetes Deployment問題的3個步驟

在深入研究異常的Deployment之前,必須有一個明確定義的Kubernetes工作方式的思維模型。
由於每個部署中都有三個組件,因此您應該從底部開始依次調試所有組件。

您應該確保Pods正在運行,然後專注於讓服務將流量路由到Pod,然後檢查是否正確配置了Ingress

您應該從底部開始對Deployment進行故障排除。首先,檢查Pod是否已就緒並正在運行。

如果Pod已就緒,則應調查服務是否可以將流量分配給Pod。

最後,您應該檢查服務與入口之間的連接。

Pod故障排除

在大多數情況下,問題出在Pod本身。
您應該確保Pod正在運行並準備就緒。
您如何檢查?

<code>kubectl get pods
NAME READY STATUS RESTARTS AGE
app1 0/1 ImagePullBackOff 0 47h


app2 0/1 Error 0 47h
app3-76f9fcd46b-xbv4k 1/1 Running 1 47h
/<code>

在上述會話中,最後一個Pod為Running and Ready - 但是,前兩個Pod 既不是Running也不為Ready。
您如何調查出了什麼問題?
有四個有用的命令可以對Pod進行故障排除:

kubectl logs 有助於檢索Pod容器的日誌kubectl describe pod 檢索與Pod相關的事件列表很有用kubectl get pod 用於提取存儲在Kubernetes中的Pod的YAML定義kubectl exec -ti bash 在Pod的一個容器中運行交互式命令很有用
您應該使用哪一個?
沒有一種萬能的。
相反,您應該結合使用它們。

常見pod錯誤

Pod可能會出現啟動和運行時錯誤。
啟動錯誤包括:

ImagePullBackoffImageInspectErrorErrImagePullErrImageNeverPullregistry不可用InvalidImageName
運行時錯誤包括:CrashLoopBackOffRunContainerErrorKillContainerErrorVerifyNonRootErrorRunInitContainerErrorCreatePodSandboxErrorConfigPodSandboxErrorKillPodSandboxErrorSetupNetworkErrorTeardownNetworkError
有些錯誤比其他錯誤更常見。
以下是最常見的錯誤以及如何修復它們的列表。

ImagePullBackOff

當Kubernetes無法檢索Pod容器之一的registry時,將出現此錯誤。
共有三個罪魁禍首:

image名稱無效-例如,您拼錯了名稱,或者image不存在您為image指定了不存在的標籤您嘗試檢索的image屬於一個私有registry,而Kubernetes沒有憑據可以訪問它

前兩種情況可以通過更正image名稱和標記來解決。
最後,您應該將憑證添加到secret中的私人resistry中,並在Pod中引用它。

CrashLoopBackOff

如果容器無法啟動,則Kubernetes將CrashLoopBackOff消息顯示為狀態。
通常,在以下情況下容器無法啟動:

應用程序中存在錯誤,導致無法啟動您未正確配置Liveness探針失敗太多次
您應該嘗試從該容器中檢索日誌,以調查其失敗的原因。
如果由於容器重新啟動太快而看不到日誌,則可以使用以下命令:

<code>kubectl logs <pod-name> --previous
/<pod-name>/<code>

將打印前一個容器的錯誤信息

RunContainerError

當容器無法啟動時出現錯誤。
甚至在容器內的應用程序啟動之前。
該問題通常是由於配置錯誤,例如:

掛載不存在的卷,例如ConfigMap或Secrets將只讀卷安裝為可讀寫

您應該使用kubectl describe pod <pod-name>收集和分析錯誤。/<pod-name>

Pods處於Pending狀態

當您創建Pod時,該Pod保持Pending狀態。


為什麼?
假設您的調度程序組件運行良好,原因如下:

群集沒有足夠的資源(例如CPU和內存)來運行Pod當前的命名空間具有ResourceQuota對象,創建Pod將使命名空間超過配額該Pod綁定到一個待處理的 PersistentVolumeClaim
檢查event部分最好的辦法是運行kubectl describe命令:kubectl describe pod

對於因ResourceQuotas而導致的錯誤,可以使用以下方法檢查群集的日誌:kubectl get events --sort-by=.metadata.creationTimestampPods處於 not Ready狀態如果Pod正在運行但not Ready,則表明readiness探針失敗。
當readiness探針失敗時,Pod未連接到服務,並且沒有流量轉發到該實例。
準備就緒探針失敗是特定於應用程序的錯誤,因此您應通過kubectl describe檢查其中的event部分以識別錯誤。Service故障排除如果您的Pod正在運行並處於就緒狀態,但仍無法收到應用程序的響應,則應檢查服務的配置是否正確。
服務旨在根據流量的標籤將流量路由到Pod。
因此,您應該檢查的第一件事是服務定位了多少個Pod。
您可以通過檢查Service中的endpoint來做到這一點:kubectl describe service <service-name> | grep Endpoints端點是一對,並且在服務以Pod為目標時,應該至少有一個。
如果”Endpoints”部分為空,則有兩種解釋:/<service-name>您沒有運行帶有正確標籤的Pod(提示:您應檢查自己是否在正確的命名空間中)


您selector在服務標籤上有錯字如果您看到端點列表,但仍然無法訪問您的應用程序,則targetPort可能是您服務中的罪魁禍首。
您如何測試服務?
無論服務類型如何,您都可以使用kubectl port-forward它來連接:kubectl port-forward service/<service-name> 3000:80即:/<service-name> 是服務的名稱3000 是您希望在計算機上打開的端口80 是服務公開的端口

對Ingress進行故障排除

如果您已到達本節,則:

pod正在運行並準備就緒服務會將流量分配到Pod
但是您仍然看不到應用程序的響應。
這意味著最有可能Ingress配置錯誤。
由於正在使用的Ingress控制器是集群中的第三方組件,因此有不同的調試技術,具體取決於Ingress控制器的類型。
但是在深入研究Ingress專用工具之前,您可以檢查一些簡單的方法。
入口使用serviceName和servicePort連接到服務。
您應該檢查這些配置是否正確。
您可以檢查是否已使用以下命令正確配置了Ingress:kubectl describe ingress <ingress-name>如果Backend列為空,則配置中一定有一個錯誤。
如果您可以在Backend列中看到端點,但仍然無法訪問該應用程序,則可能是以下問題:/<ingress-name>您如何將Ingress暴露於公共互聯網您如何將群集暴露於公共互聯網

您可以通過直接連接到Ingress Pod來將基礎結構問題與Ingress隔離開。
首先,為您的Ingress控制器(可以位於其他名稱空間中)檢索Pod:

<code>kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS
kube-system coredns-5644d7b6d9-jn7cq 1/1 Running
kube-system etcd-minikube 1/1 Running
kube-system kube-apiserver-minikube 1/1 Running
kube-system kube-controller-manager-minikube 1/1 Running
kube-system kube-proxy-zvf2h 1/1 Running
kube-system kube-scheduler-minikube 1/1 Running
kube-system nginx-ingress-controller-6fc5bcc 1/1 Running
/<code>

describe以檢索端口:

<code>kubectl describe pod nginx-ingress-controller-6fc5bcc --namespace kube-system \\
| grep Ports
/<code>

最後,連接到Pod:

<code>kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system
/<code>

此時,每次您訪問計算機上的端口3000時,請求都會轉發到Pod上的端口80。
現在可以用嗎?

如果可行,則問題出在基礎架構中。您應該調查流量如何路由到您的群集。如果不起作用,則問題出在Ingress控制器中。您應該調試Ingress。
如果仍然無法使Ingress控制器正常工作,則應開始對其進行調試。
有許多不同版本的Ingress控制器。
熱門選項包括Nginx,HAProxy,Traefik等。
您應該查閱Ingress控制器的文檔以查找故障排除指南。

由於ingress nginx是最受歡迎的Ingress控制器,因此在下一部分中我們將介紹一些技巧。

調試Ingress Nginx

Ingress-nginx項目有一個kubectl plugin。
您可以kubectl ingress-nginx用來:

檢查日誌,後端,證書等。連接到入口檢查當前配置
您應該嘗試的三個命令是:kubectl ingress-nginx lint,它會檢查 nginx.confkubectl ingress-nginx backend,以檢查後端(類似於kubectl describe ingress )kubectl ingress-nginx logs,查看日誌請注意,您可能需要使用來為Ingress控制器指定正確的名稱空間—namespace 。

摘要

如果您不知道從哪裡開始,在Kubernetes中進行故障排除可能是一項艱鉅的任務。
您應該始終牢記從下至上解決問題:從Pod開始,然後通過Service and Ingress向上移動堆棧。
您在本文中瞭解到的相同調試技術可以應用於其他對象,例如:
失敗的Jobs和CronJobs,StatefulSets 和 DaemonSets