Kubernetes Deployment故障排除圖解指南


 個人K8s還在學習中,相關博客還沒有寫,准備學第二遍再開始學,發現這篇文章挺好,先轉載一下。


下面是一個示意圖,可幫助你調試Kubernetes Deployment(你可以在此處下載它的PDF版本 https://tonybai.com/wp-content/uploads/k8s-deployment-troubleshooting/troubleshooting-kubernetes.pdf)。

 

 

 

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

•一個Deployment - 這是一份用於創建你的應用程序的Pod副本的"食譜";

•一個Service - 一個內部負載均衡器,用於將流量路由到內部的Pod上;

•一個Ingress - 描述如何流量應該如何從集群外部流入到集群內部的你的服務上。

下面讓我們用示意圖快速總結一下要點。

 

在Kubernetes中,你的應用程序通過兩層負載均衡器暴露服務:內部的和外部的

 

 

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

 

Pod不會直接部署。Deployment會負責創建Pod並管理它們

假設你要部署一個簡單的"HelloWorld"應用,該應用的YAML文件的內容應該類似下面這樣:

// hello-world.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?

•你是否應該為每個服務創建一個新端口以免它們相互沖突?

•標簽(label)名重要嗎?它們是否在每一處都應該是一樣的?

在進行調試之前,讓我們回顧一下這三個組件是如何相互關聯的。

讓我們從Deployment和Service開始。

一. 連接Deployment和Service

令人驚訝的消息是,Service和Deployment之間根本沒有連接。

事實是:Service直接指向Pod,並完全跳過了Deployment。

因此,你應該注意的是Pod和Service之間的相互關系。

你應該記住三件事:

•Service selector應至少與Pod的一個標簽匹配;

•Service的targetPort應與Pod中容器的containerPort匹配;

•Service的port可以是任何數字。多個Service可以使用同一端口號,因為它們被分配了不同的IP地址。

下面的圖總結了如何連接端口:

 

 

 考慮上面被一個服務暴露的Pod

 

 

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

 

當創建一個Service時,你可以定義port和targetPort,但是哪個用來連接容器呢?

 

 

 targetPort和containerPort應該始終保持匹配

 

 

 如果容器暴露3000端口(containerPort),那么targetPort應該匹配這一個端口號

再來看看YAML,標簽和ports/targetPort應該匹配:

// hello-world.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:
    any-name: my-app

 

那deployment頂部的track: canary標簽呢?

它也應該匹配嗎?

該標簽屬於deployment,service的選擇器未使用它來路由流量。

換句話說,你可以安全地刪除它或為其分配其他值。

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!

你可以使用kubectl中的port-forward命令連接到service並測試連接。

$ kubectl port-forward service/<service name> 3000:80

 

•service/ 是服務的名稱- 在上面的YAML中是“my-service”

•3000是你希望在計算機上打開的端口

•80是service通過port字段暴露的端口

如果可以連接,則說明設置正確。

如果不行,則很可能是你填寫了錯誤的標簽或端口不匹配。

二. 連接Service和Ingress

接下來是配置Ingress以將你的應用暴露到集群外部。

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

Ingress按名字和暴露的端口檢索正確的服務。

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

•Ingress的servicePort應該匹配service的port

•Ingress的serviceName應該匹配服務的name

下面的圖總結了如何連接端口:

 

你已經知道servive暴露一個port

 

 

 Ingress有一個字段叫servicePort

 

service的port和Ingress的service應該始終保持匹配

 

 

 如果你為service指定的port是80,那么你也應該將ingress的servicePort改為80

實踐中,你應該查看以下幾行(下面代碼中的my-service和80):

// hello-world.yaml

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控制器,而不是連接到Service。

首先,使用以下命令檢索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(可能在其他命名空間中)並描述它以檢索端口:
$ 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。

如果訪問http://localhost:3000,則應找到提供網頁服務的應用程序。

回顧Port

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

• service selector應與Pod的標簽匹配

• service的targetPort應與Pod中容器的containerPort匹配

• service的端口可以是任何數字。多個服務可以使用同一端口,因為它們分配了不同的IP地址。

• ingress的servicePort應該匹配service的port

• serivce的名稱應與ingress中的serviceName字段匹配

知道如何構造YAML定義只是故事的一部分。

出了問題后該怎么辦?

Pod可能無法啟動,或者正在崩潰。

三. kubernetes deployment故障排除的3個步驟

在深入研究失敗的deployment之前,我們必須對Kubernetes的工作原理有一個明確定義的思維模型。

由於每個deployment中都有三個組件,因此你應該自下而上依次調試所有組件。

•你應該先確保Pods正在運行•然后,專注於讓service將流量路由到到正確的Pod•然后,檢查是否正確配置了Ingress

 

 

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

 

 

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

 

 

 最后,你應該檢查service與ingress之間的連接。

1. Pod故障排除

在大多數情況下,問題出在Pod本身。

你應該確保Pod正在運行並准備就緒。

該如何檢查呢?

$ 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處於就緒並正常運行的狀態;但是,前兩個Pod既不處於Running也不是Ready。

你如何調查出了什么問題?

有四個有用的命令可以對Pod進行故障排除:

• kubectl logs 有助於檢索Pod容器的日志

• kubectl describe pod 檢索與Pod相關的事件列表很有用

• kubectl get pod 用於提取存儲在Kubernetes中的Pod的YAML定義

• kubectl exec -ti bash 在Pod的一個容器中運行交互式命令很有用

應該使用哪一個呢?

沒有一種萬能的。

相反,我們應該結合着使用它們。

常見Pod錯誤

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

啟動錯誤包括:

• ImagePullBackoff

• ImageInspectError

• ErrImagePull

• ErrImageNeverPull

• RegistryUnavailable

• InvalidImageName

運行時錯誤包括:

• CrashLoopBackOff

• RunContainerError

• KillContainerError

• VerifyNonRootError

• RunInitContainerError

• CreatePodSandboxError

• ConfigPodSandboxError

• KillPodSandboxError

• SetupNetworkError

• TeardownNetworkError

有些錯誤比其他錯誤更常見。

以下是最常見的錯誤列表以及如何修復它們的方法。

ImagePullBackOff

當Kubernetes無法獲取到Pod中某個容器的鏡像時,將出現此錯誤。

共有三個可能的原因:

•鏡像名稱無效-例如,你拼錯了名稱,或者image不存在

•你為image指定了不存在的標簽

•你嘗試檢索的image屬於一個私有registry,而Kubernetes沒有憑據可以訪問它

前兩種情況可以通過更正image名稱和標記來解決。

針對第三種情況,你應該將私有registry的訪問憑證通過Secret添加到k8s中並在Pod中引用它。

官方文檔中有一個有關如何實現此目標的示例。

CrashLoopBackOff

如果容器無法啟動,則Kubernetes將顯示錯誤狀態為:CrashLoopBackOff。

通常,在以下情況下容器無法啟動:

•應用程序中存在錯誤,導致無法啟動•你未正確配置容器

•Liveness探針失敗太多次

你應該嘗試從該容器中檢索日志以調查其失敗的原因。

如果由於容器重新啟動太快而看不到日志,則可以使用以下命令:

$ kubectl logs <pod-name> --previous

 

這個命令打印前一個容器的錯誤消息。

RunContainerError

當容器無法啟動時,出現此錯誤。

甚至在容器內的應用程序啟動之前。

該問題通常是由於配置錯誤,例如:

•掛載不存在的卷,例如ConfigMap或Secrets

•將只讀卷安裝為可讀寫

你應該使用kubectl describe pod 命令收集和分析錯誤。

處於Pending狀態的Pod

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

為什么?

假設你的調度程序組件運行良好,可能的原因如下:

•集群沒有足夠的資源(例如CPU和內存)來運行Pod

•當前的命名空間具有ResourceQuota對象,創建Pod將使命名空間超過配額

•該Pod綁定到一個處於pending狀態的 PersistentVolumeClaim

最好的選擇是檢查kubectl describe命令輸出的“事件”部分內容:

$ kubectl describe pod <pod name>

對於因ResourceQuotas而導致的錯誤,可以使用以下方法檢查集群的日志:

$ kubectl get events --sort-by=.metadata.creationTimestamp

處於未就緒狀態的Pod

如果Pod正在運行但未就緒(not ready),則表示readiness就緒探針失敗。

當“就緒”探針失敗時,Pod未連接到服務,並且沒有流量轉發到該實例。

就緒探針失敗是應用程序的特定錯誤,因此你應檢查kubectl describe中的“ 事件”部分以識別錯誤。

2. 服務的故障排除

如果你的Pod正在運行並處於就緒狀態,但仍無法收到應用程序的響應,則應檢查服務的配置是否正確。

service旨在根據流量的標簽將流量路由到Pod。

因此,你應該檢查的第一件事是服務關聯了多少個Pod。

你可以通過檢查服務中的端點(endpoint)來做到這一點:

$ kubectl describe service <service-name> | grep Endpoints

端點是一對,並且在服務(至少)以Pod為目標時,應該至少有一個端點。

如果“端點”部分為空,則有兩種解釋:

•你沒有運行帶有正確標簽的Pod(提示:你應檢查自己是否在正確的命名空間中)

•service的selector標簽上有錯字

如果你看到端點列表,但仍然無法訪問你的應用程序,則targetPort可能是你服務中的罪魁禍首。

你如何測試服務?

無論服務類型如何,你都可以使用kubectl port-forward來連接它:

$kubectl port-forward service/<service-name> 3000:80

 

這里:

• 是服務的名稱•3000 是你希望在計算機上打開的端口

•80 是服務公開的端口

3.Ingress的故障排除

如果你已到達本節,則:

•Pod正在運行並准備就緒

•服務會將流量分配到Pod

但是你仍然看不到應用程序的響應。

這意味着最有可能是Ingress配置錯誤。

由於正在使用的Ingress控制器是集群中的第三方組件,因此有不同的調試技術,具體取決於Ingress控制器的類型。

但是在深入研究Ingress專用工具之前,你可以用一些簡單的方法進行檢查。

Ingress使用serviceName和servicePort連接到服務。

你應該檢查這些配置是否正確。

你可以通過下面命令檢查Ingress配置是否正確:

$kubectl describe ingress <ingress-name>

 

如果backend一列為空,則配置中必然有一個錯誤。

如果你可以在“backend”列中看到端點,但是仍然無法訪問該應用程序,則可能是以下問題:

•你如何將Ingress暴露於公共互聯網

•你如何將集群暴露於公共互聯網

你可以通過直接連接到Ingress Pod來將基礎結構問題與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

 

描述它以檢索端口:
# 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控制器正常工作,則應開始對其進行調試。

目前有許多不同版本的Ingress控制器。

熱門選項包括Nginx,HAProxy,Traefik等。

你應該查閱Ingress控制器的文檔以查找故障排除指南。

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

調試Ingress Nginx

Ingress-nginx項目有一個Kubectl的官方插件。

你可以用kubectl ingress-nginx來:

•檢查日志,后端,證書等。

•連接到ingress

•檢查當前配置

你應該嘗試的三個命令是:

•kubectl ingress-nginx lint,它會檢查 nginx.conf

•kubectl ingress-nginx backend,以檢查后端(類似於kubectl describe ingress )

•kubectl ingress-nginx logs,查看日志

請注意,你可能需要為Ingress控制器指定正確的名稱空間--namespace 。

四. 總結

如果你不知道從哪里開始,那么在Kubernetes中進行故障排除可能是一項艱巨的任務。

你應該始終牢記從下至上解決問題:從Pod開始,然后通過Service和Ingress向上移動堆棧。

你在本文中了解到的調試技術也可以應用於其他對象,例如:

•failing Job和CronJob

•StatefulSets和DaemonSets

本文翻譯自learnk8s上的文章A visual guide on troubleshooting Kubernetes deployments。

微博:https://weibo.com/bigwhite20xx 微信公眾號:iamtonybai 博客:tonybai.com github: https://github.com/bigwhite

 

 


免責聲明!

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



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