筆記。 翻譯。:本文是在公共領域發布的專案資料的一部分
TL;DR:這是一個圖表,可以幫助您調試 Kubernetes 中的部署:
尋找和修復叢集中錯誤的流程圖。 原文(英文)可在
將應用程式部署到 Kubernetes 時,通常需要定義三個元件:
- 部署 - 這是一種建立應用程式副本的方法,稱為 pod;
- 服務 — 在 Pod 之間分配流量的內部負載平衡器;
- 入口 — 流量如何從外部世界到達服務的描述。
這是一個快速的圖形摘要:
1)在Kubernetes中,應用程式透過兩層負載平衡器接收來自外界的流量:內部和外部。
2)內部均衡器稱為Service,外部均衡器稱為Ingress。
3) Deployment 建立 Pod 並監控它們(它們不是手動建立的)。
假設您想部署一個簡單的應用程式 你好世界。 它的 YAML 設定如下圖所示:
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: /
這個定義相當長,很容易混淆各個元件之間的關係。
例如:
- 什麼時候應該使用連接埠 80,什麼時候應該使用 8080?
- 我應該為每個服務建立一個新端口,這樣它們就不會發生衝突嗎?
- 標籤名稱重要嗎? 它們應該在任何地方都一樣嗎?
在專注於調試之前,讓我們記住這三個元件如何相互關聯。 讓我們從部署和服務開始。
部署與服務的關係
您會感到驚訝,但部署和服務沒有任何關係。 相反,Service 繞過 Deployment 直接指向 Pod。
因此,我們感興趣的是 Pod 和 Services 如何相互關聯。 要記住三件事:
- 選擇器(
selector
) 服務必須至少符合一個 Pod 標籤。 -
targetPort
必須匹配containerPort
Pod 內的容器。 -
port
服務可以是任何東西。 不同的服務可以使用相同的端口,因為它們具有不同的IP位址。
下圖以圖形形式表示了上述所有內容:
1) 假設該服務將流量定向到某個 pod:
2)建立pod時,必須指定 containerPort
對於 Pod 中的每個容器:
3)創建服務時,必須指定 port
и targetPort
. 但要使用哪一個來連接容器呢?
4)透過 targetPort
。 必須匹配 containerPort
.
5) 假設容器中開啟了3000端口,那麼該值 targetPort
應該是一樣的。
在 YAML 檔案中,標籤和 ports
/ targetPort
必須匹配:
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 # <<<
標籤怎麼樣 track: canary
在部署部分的頂部? 應該匹配嗎?
此標籤是特定於部署的,服務不使用它來路由流量。 換句話說,它可以被刪除或分配不同的值。
選擇器怎麼樣 matchLabels
?
它必須始終與 Pod 的標籤匹配,因為 Deployment 使用它來追蹤 Pod。
假設您進行了正確的編輯。 如何檢查它們?
您可以使用以下命令檢查 pod 標籤:
kubectl get pods --show-labels
或者,如果 Pod 屬於多個應用程式:
kubectl get pods --selector any-name=my-app --show-labels
在哪裡 any-name=my-app
是一個標籤 any-name: my-app
.
還有什麼困難嗎?
您可以連接到 Pod! 為此,您需要使用命令 port-forward
在 kubectl. 它允許您連接到服務並檢查連接。
kubectl port-forward service/<service name> 3000:80
在這裡:
-
service/<service name>
- 服務名稱; 在我們的例子中是my-service
; - 3000是電腦上需要開放的連接埠;
- 80 - 欄位中指定的端口
port
服務。
如果連線已建立,則設定正確。
如果連線失敗,則表示標籤有問題或連接埠不符。
Service與Ingress的關係
提供對應用程式的存取的下一步涉及設定 Ingress。 Ingress 需要知道如何找到服務,然後找到 Pod 並將流量導向它們。 Ingress 透過名稱和開放連接埠尋找所需的服務。
Ingress 和 Service 的描述中兩個參數必須符合:
-
servicePort
Ingress 中必須符合參數port
服務中; -
serviceName
Ingress 中的欄位必須匹配name
在服務中。
下圖總結了連接埠連線:
1) 如您所知,Service 會監聽特定的 port
:
2)Ingress有一個參數叫 servicePort
:
3)該參數(servicePort
) 必須始終匹配 port
在服務定義中:
4) 如果Service中指定了80端口,則需要 servicePort
也等於 80:
實際操作中,需要注意以下幾行:
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: /
如何檢查Ingress是否正在運作?
您可以使用該方法 kubectl port-forward
,但您需要連接到 Ingress 控制器而不是服務。
首先,您需要使用 Ingress 控制器找出 pod 的名稱:
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
找到 Ingress pod(它可能位於不同的命名空間中)並執行命令 describe
找出連接埠號碼:
kubectl describe pod nginx-ingress-controller-6fc5bcc
--namespace kube-system
| grep Ports
Ports: 80/TCP, 443/TCP, 18080/TCP
最後,連接到 pod:
kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system
現在,每次您向電腦上的連接埠 3000 發送請求時,該請求都會透過 Ingress 控制器轉送到 pod 的連接埠 80。 通過去
連接埠總結
讓我們再次記住哪些連接埠和標籤必須匹配:
- Service 定義中的選擇器必須與 pod 的標籤相符;
-
targetPort
定義中的服務必須符合containerPort
pod 內的容器; -
port
定義中的服務可以是任何東西。 不同的服務可以使用相同的端口,因為它們具有不同的IP位址; -
servicePort
入口必須匹配port
在服務的定義中; - 服務名稱必須與欄位相符
serviceName
在入口處。
不幸的是,僅僅知道如何正確建構 YAML 配置是不夠的。
當出現問題時會發生什麼事?
Pod 可能無法啟動或崩潰。
診斷 Kubernetes 中應用程式問題的 3 個步驟
在開始調試部署之前,您需要充分了解 Kubernetes 的工作原理。
由於K8s中下載的每個應用程式都具有三個元件,因此應該按照一定的順序對它們進行調試,從最底層開始。
- 首先,您需要確保 Pod 正常工作,然後...
- 檢查服務是否向 Pod 提供流量,然後...
- 檢查 Ingress 配置是否正確。
視覺表現:
1)你應該從最底層開始找問題。 首先檢查 pod 是否有狀態 Ready
и Running
:
2) 如果 Pod 準備就緒 (Ready
),您應該找出該服務是否在 Pod 之間分配流量:
3)最後需要分析服務與Ingress的連結:
1. Pod 的診斷
大多數情況下,問題與 Pod 有關。 確保 Pod 列為 Ready
и Running
。 您可以使用以下命令進行檢查:
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
在上面的命令輸出中,最後一個 pod 被列為 Running
и Ready
然而,另外兩人卻並非如此。
如何理解出了什麼問題?
有四個有用的命令可用於診斷 pod:
-
kubectl logs <имя pod'а>
允許您從 pod 中的容器中提取日誌; -
kubectl describe pod <имя pod'а>
允許您查看與 pod 關聯的事件清單; -
kubectl get pod <имя pod'а>
允許您取得儲存在 Kubernetes 中的 pod 的 YAML 設定; -
kubectl exec -ti <имя pod'а> bash
允許您在 pod 容器之一中啟動互動式命令 shell
你應該選擇哪一個?
事實是,不存在通用命令。 應結合使用這些方法。
典型的 Pod 問題
Pod 錯誤主要有兩種類型:啟動錯誤和執行階段錯誤。
啟動錯誤:
-
ImagePullBackoff
-
ImageInspectError
-
ErrImagePull
-
ErrImageNeverPull
-
RegistryUnavailable
-
InvalidImageName
運行時錯誤:
-
CrashLoopBackOff
-
RunContainerError
-
KillContainerError
-
VerifyNonRootError
-
RunInitContainerError
-
CreatePodSandboxError
-
ConfigPodSandboxError
-
KillPodSandboxError
-
SetupNetworkError
-
TeardownNetworkError
有些錯誤比其他錯誤更常見。 以下是一些最常見的錯誤以及解決方法。
影像拉回關閉
當 Kubernetes 無法取得其中一個 pod 容器的映像時,就會發生此錯誤。 以下是三個最常見的原因:
- 圖片名稱不正確 - 例如,您輸入錯誤,或圖片不存在;
- 為圖像指定了不存在的標籤;
- 該鏡像儲存在私人註冊表中,Kubernetes 無權存取它。
前兩個原因很容易消除 - 只需更正圖像名稱和標籤。 對於後者,您需要在 Secret 中輸入封閉註冊表的憑證,並在 pod 中新增指向它的連結。 在 Kubernetes 文件中
崩潰循環回退
Kubenetes 拋出錯誤 CrashLoopBackOff
,如果容器無法啟動。 這通常發生在以下情況:
- 應用程式中存在一個錯誤,導致其無法啟動;
- 容器
配置不正確 ; - 活性測試失敗次數太多。
您必須嘗試從容器中取得日誌以找出其失敗的原因。 如果由於容器重新啟動太快導致日誌存取困難,可以使用以下命令:
kubectl logs <pod-name> --previous
它顯示容器先前版本的錯誤訊息。
運行容器錯誤
當容器無法啟動時會出現此錯誤。 它對應於應用程式啟動之前的時刻。 通常是由於設定不正確造成的,例如:
- 嘗試掛載不存在的捲,例如 ConfigMap 或 Secrets;
- 嘗試將唯讀卷安裝為讀寫。
該團隊非常適合分析此類錯誤 kubectl describe pod <pod-name>
.
Pod 處於 Pending 狀態
一旦創建,Pod 就保持在狀態 Pending
.
為什麼會這樣?
以下是可能的原因(我假設調度程序正常運作):
- 叢集沒有足夠的資源(例如處理能力和記憶體)來運行 Pod。
- 該物件安裝在適當的命名空間中
ResourceQuota
並且建立 pod 會導致命名空間超出配額。 - Pod 綁定為 Pending
PersistentVolumeClaim
.
在這種情況下,建議使用指令 kubectl describe
並檢查該部分 Events
:
kubectl describe pod <pod name>
如果出現與以下相關的錯誤 ResourceQuotas
,建議使用指令查看叢集日誌
kubectl get events --sort-by=.metadata.creationTimestamp
Pod 尚未就緒
如果 pod 列為 Running
,但未處於狀態 Ready
,意味著檢查其準備情況 (就緒探針) 失敗了。
發生這種情況時,Pod 不會連接到服務,並且沒有流量流向它。 就緒測試失敗是由應用程式中的問題引起的。 在這種情況下,要找到錯誤,您需要分析該部分 Events
在命令輸出中 kubectl describe
.
2. 服務診斷
如果 pod 列為 Running
и Ready
,但應用程式仍然沒有響應,您應該檢查服務設定。
服務負責根據 Pod 的標籤將流量路由到 Pod。 因此,您需要做的第一件事是檢查有多少 Pod 使用該服務。 為此,您可以檢查服務中的端點:
kubectl describe service <service-name> | grep Endpoints
端點是一對形式的值 <IP-адрес:порт>
,並且輸出中必須至少存在一對這樣的(即,至少有一個 Pod 與該服務一起工作)。
如果節 Endpoins
空,有兩種選擇:
- 沒有具有正確標籤的 Pod(提示:檢查命名空間是否選擇正確);
- 選擇器中的服務標籤有錯誤。
如果您看到端點列表但仍然無法存取應用程序,則可能的罪魁禍首是 targetPort
在服務描述中。
如何檢查服務的功能?
無論服務類型如何,都可以使用命令 kubectl port-forward
連接到它:
kubectl port-forward service/<service-name> 3000:80
在這裡:
-
<service-name>
- 服務名稱; - 3000是你在電腦上打開的連接埠;
- 80 - 服務端埠。
3. 入口診斷
如果您已經讀到這裡,那麼:
- pod 列為
Running
иReady
; - 此服務成功在 Pod 之間分配流量。
但是,您仍然無法存取該應用程式。
這意味著 Ingress 控制器很可能未正確配置。 由於Ingress控制器是叢集中的第三方元件,因此根據其類型有不同的偵錯方法。
但在使用特殊工具來設定 Ingress 之前,您可以做一些非常簡單的事情。 入口用途 serviceName
и servicePort
連接到該服務。 您需要檢查它們是否配置正確。 您可以使用以下命令執行此操作:
kubectl describe ingress <ingress-name>
如果列 Backend
為空,很有可能配置錯誤。 如果後端已就位,但應用程式仍然無法訪問,則問題可能與以下內容有關:
- 從公共網際網路進入輔助功能設定;
- 來自公共互聯網的叢集可訪問性設定。
您可以透過直接連接到 Ingress Pod 來識別基礎架構的問題。 為此,首先找到 Ingress Controller pod(它可能位於不同的命名空間中):
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
使用命令 describe
設定連接埠:
kubectl describe pod nginx-ingress-controller-6fc5bcc
--namespace kube-system
| grep Ports
最後,連接到 pod:
kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system
現在,電腦上對連接埠 3000 的所有請求都將被重新導向到 pod 的連接埠 80。
現在有用嗎?
- 如果是,那麼問題出在基礎建設。 有必要弄清楚流量是如何路由到叢集的。
- 如果不是,則問題出在 Ingress 控制器上。
如果您無法使 Ingress 控制器運作,則必須對其進行偵錯。
Ingress 控制器有很多種。 最受歡迎的是 Nginx、HAProxy、Traefik 等。 (有關現有解決方案的更多信息,請參閱
調試 Ingress Nginx 控制器
Ingress-nginx專案有官方的 kubectl ingress-nginx
可以用於:
- 日誌、後端、證書等分析;
- 與 Ingress 的連接;
- 研究當前配置。
以下三個命令將幫助您完成此操作:
-
kubectl ingress-nginx lint
- 檢查nginx.conf
; -
kubectl ingress-nginx backend
— 探索後端(類似kubectl describe ingress <ingress-name>
); -
kubectl ingress-nginx logs
— 檢查日誌。
請注意,在某些情況下,您可能需要使用標誌為 Ingress 控制器指定正確的命名空間 --namespace <name>
.
總結
如果您不知道從哪裡開始,對 Kubernetes 進行故障排除可能會很困難。 您應該始終自下而上解決問題:從 pod 開始,然後繼續到服務和 Ingress。 本文所述的調試技術可以應用於其他對象,例如:
- 空閒作業和 CronJobs;
- StatefulSet 和 DaemonSet。
我表達我的感激之情
譯者PS
另請閱讀我們的博客:
- «
用於在 Kubernetes Pod 中進行調試的 kubectl-debug 插件 “; - «
Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案] “; - «
為 Kubernetes 上運行的應用程式開發人員提供的工具 “; - «
SRE 日常生活中的 6 個實用故事 “。
來源: www.habr.com