超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形

超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
本文將幫助你釐清在Kubernetes中調試 deployment的思路。下圖是完整的故障排查思路,若是你想得到更清晰的圖片,請在公衆號後臺(RancherLabs)回覆「troubleshooting」。
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
當你但願在Kubernetes中部署一個應用程序,你一般須要定義三個組件:nginx

 

  • Deployment——這是建立名爲Pods的應用程序副本的方法git

  • Serivce——內部負載均衡器,將流量路由到Podsgithub

  • Ingress——能夠描述流量如何從集羣外部流向Serviceapi

     

接下來,咱們經過圖片快速回顧一下。
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
在Kubernetes中,你的應用程序經過兩層負載均衡器暴露:內部和外部。
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
內部負載均衡器稱爲Service,而外部負載均衡器則稱爲Ingress。
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
Pod未直接部署,所以,Deployment建立Pod並監視它們。安全

 

假設你想部署一個簡單的Hello World應用程序,那麼對於此類應用程序,其YAML文件與如下相似:
 bash

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

 
這個定義很長,容易忽略組件之間的相互關係。app

 

例如:
 負載均衡

  • 你何時應該使用80端口,何時使用端口8080?ide

  • 你是否應該爲每一個服務建立一個新端口,以避免它們衝突?工具

  • 標籤(label)名稱重要嗎?是否應該每一處都同樣?

 

在進行debug以前,咱們先來回顧一下這三個組件之間的關係如何。

 

首先,咱們從Deployment和Service開始。
 

鏈接Deployment和Service

 

實際上,Deployment和Service根本沒有鏈接。相反,該Service直接指向Pod,並徹底跳過Deployment。因此,你應該關注的是Pod和Service是如何與彼此關聯的。你應該記住三件事:
 

  1. Service selector至少與Pod的一個標籤匹配

  2. Serivce targetPort應該與Pod內的容器的containerPort相匹配

  3. Serviceport能夠是任何數字。多個Service可使用同一個端口,由於它們已經被分配了不一樣的IP地址

     

如下圖片總結了如何鏈接端口:
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
考慮由Service暴露的pod
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
當你建立一個pod,你應該在你的Pod中爲每一個容器定義端口containerPort
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
當你建立一個Service時,你可以定義一個port和一個targetPort。但你應該將哪個鏈接到容器呢?
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
targetPortcontainerPort應該可以匹配
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
若是你的容器暴露端口3000,那麼targetPort應該與該數字相匹配。
 

若是你查看了YAML,標籤與portstargerPort應該匹配:
 

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 selector不使用它來路由流量。換言之,你能夠安全地將其移除或者給它分配不一樣的值。
 

那麼matchLabelsselector呢?它須要與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鏈接到Serivce並測試鏈接。
 

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

 
其中:

 

  • service/&lt;service name&gt;是serivce的名稱——在當前YAML中,是「my-service」。

  • 3000是你但願在你的電腦上打開的端口

  • 80是Service在port字段中暴露的端口

 

若是你可以鏈接,那麼設置就是正確的。若是你沒法鏈接,你頗有可能弄錯了標籤或者端口未匹配。
 

鏈接Service和Ingress

 

暴露應用程序的下一步是配置Ingress。Ingress必須知道如何檢索Service,而後檢索Pod並將流量路由到它們。Ingress經過名稱和暴露的端口來檢索正確的Service。

 

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

 

  1. Ingress的servicePort應該與Service的port匹配

  2. Ingress的serviceName應該與Service的name相匹配

 

如下圖片將總結如何鏈接端口:
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
你已經知道該服務暴露了一個端口
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
Ingress有一個名爲servicePort的字段。
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
Serviceport和IngressservicePort應該相匹配
 
超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
 
若是你決定分配端口80給該service,你應該同時更改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,但不是鏈接到service,而是鏈接到Ingress controller。
 

首先,使用如下命令爲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

 
驗證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 controller Pod上的端口80。

 

若是你訪問 http://localhost:3000 ,你應該能找到提供網頁的應用程序。
 

簡單回顧一下

 
如今,咱們來快速回顧一下什麼端口和標籤須要匹配:

 

  1. Service selector應該匹配Pod的標籤

  2. ServicetargerPort應該匹配在Pod內容器的containerPort

  3. Service 端口能夠是任意數字。多個Service可使用同個端口,由於它們已經分配了不一樣的IP地址

  4. Ingress的servicePort應該匹配在Service中的port

  5. Service的名稱應該匹配在Ingress中的serviceName的字段

     

瞭解如何構造YAML只是開始。那麼,出了問題時會有什麼表現?Pod可能沒法啓動,或者直接崩潰。
 

3步排查K8S Deployment故障

 

在咱們深刻研究有故障的deployment以前,必須有一個明肯定義的模型,以瞭解Kubernetes的工做方式。

 

既然在每一個deployment中都有那三個組件,你應該從底層開始按順序調試它們。

 

  1. 你應該確保你的Pod正在運行

  2. 着重關注使Service將流量路由到Pod

  3. 檢查Ingress是否正確配置
     
    超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
     
    你應該從底層開始排查Deployment故障。首先,檢查Pod是否準備就緒而且正在運行
     
    超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
     
    若是Pod已經準備就緒,你須要檢查Service是否能夠將流量分配到Pod。
     
    超長可視化指南!帶你理清K8S部署的故障排查思路,讓bug無處遁形
     
    最後你應該檢查Service和Ingress之間的鏈接。
     

    一、 故障排查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。那麼,你應該如何定位是什麼出了問題呢?

 

這裏有4個十分有用的命令能夠幫助你排查Pod的故障:
 

  • kubectl logs &lt;pod name&gt;可以幫助檢索Pod的容器日誌

  • kubectl describe pod &lt;pod name&gt;可以有效地檢索與Pod相關的事件列表

  • kubectl get pod &lt;pod name&gt;對於提取存儲在Kubernetes中的Pod的YAML定義十分有用

  • kubectl exec -ti &lt;pod name&gt; bash能夠用於在Pod其中一個容器中運行一個交互式命令

 

你應該使用哪個呢?實際上,沒有一種命令是萬能的,你能夠根據實際狀況結合使用。

 

常見的Pod錯誤

 

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

 

啓動錯誤包括:

 

  • ImagePullBackoff

  • ImageInspectError

  • ErrImagePull

  • ErrImageNeverPull

  • RegistryUnavailable

  • InvalidImageName

 

運行時錯誤包括:

 

  • CrashLoopBackOff

  • RunContainerError

  • KillContainerError

  • VerifyNonRootError

  • RunInitContainerError

  • CreatePodSandboxError

  • ConfigPodSandboxError

  • KillPodSandboxError

  • SetupNetworkError

  • TeardownNetworkError

 

這些錯誤中,有些比其餘錯誤更爲常見。如下是最多見的錯誤以及如何修復它們:

 

ImagePullBackOff

 

當Kubernetes沒法檢索Pod其中之一的容器鏡像時,將出現此錯誤。

 

有三種常見緣由:

 

  • 鏡像名稱無效——例如,你錯誤拼寫名稱或鏡像不存在

  • 你給這一鏡像指定了一個不存在的tag

  • 你所檢索的鏡像是私有倉庫的,而且Kubernetes沒有訪問它的憑據

 

前兩個緣由能夠經過更正鏡像名稱和tag解決。最後一個,你須要將憑據添加到「Secret」中的私有鏡像倉庫中,並在Pod中引用它。
 

官方文檔可讓你更加清楚:

https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/

 

CrashLoopBackOff

 

若是容器沒法啓動,Kubernetes狀態將顯示CrashLoopBackOff消息。

一般狀況下,容器在如下場景中沒法啓動:
 

 

你應該嘗試並檢索該容器的日誌以肯定出現故障的緣由。
 

若是因爲你的容器重啓過快而沒法查看日誌,你可使用如下命令:
 

kubectl logs <pod-name> --previous

 
它將從以前的容器中打印錯誤信息。

 

RunContainerError

 

容器不能啓動時出現錯誤,甚至在容器內的應用程序啓動以前就沒法啓動。

 

這個問題一般因爲錯誤配置致使的,如:
 

  • 安裝一個不存在的volume,如ConfigMap或Secret

  • 將只讀volume安裝爲可讀寫

     

你應該使用kubectl describe pod &lt;pod-name&gt;來收集和分析錯誤。

 

Pod處於Pending狀態

 

當你建立一個Pod時,Pod保持在Pending狀態。這是爲何呢?假設你的調度組件運行了解,那麼有如下幾個緣由:
 

  • 集羣沒有足夠的資源來運行Pod,如CPU和內存

  • 當前命名空間有一個ResourceQuota對象而且所建立的Pod會使該命名空間超過資源額度

  • Pod與一個Pending狀態的PersistentVolumeClaim綁定。

 
那麼,最好的選擇是使用命令kubectl describe檢查事件:
 

kubectl describe pod <pod name>

 
對於因爲ResourceQuotas形成的錯誤,可使用如下方法檢查集羣的日誌:
 

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

 

Pod不處於Ready狀態

 

若是Pod正在運行可是不Ready,這意味着Readiness探針出現故障。當Readiness探針出現故障時,Pod沒法附加到Service上,而且流量沒法轉發到實例上。

 
Readiness探針故障是特定於應用程序的錯誤,所以使用kubectl describe來檢查事件部分,以驗證錯誤。
 

二、 排查Service故障

 

若是你的Pod正在運行而且準備就緒,可是你依舊沒法接收來自應用程序的響應,你應該檢查Service是否配置正確。

 

Service旨在根據pod的標籤將流量路由到Pod。因此第一件事,你須要檢查Service target多少個Pod。能夠經過檢查Service中的Endpoint來完成此步驟:
 

kubectl describe service  <service-name>  |  grep Endpoints

 
一個endpoint是一對&lt;ip address:port&gt;,而且當Service(至少)target一個pod時。至少有一對。
 

若是「Endpoint」部分是空的,那麼有兩種解釋:

 

  1. 任何正在運行的Pod沒有正確的label(提示:你須要檢查如下你是否在正確的命名空間內)

  2. 在Service的selector標籤中有錯別字

 

若是你看到了endpoint列表,但依舊沒法訪問你的應用程序,那麼你的Service中的targetPort多是罪魁禍首。

 

你應該怎麼測試Service?不管Service類型是什麼,均可以使用kubectl port-forward鏈接到它:
 

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

 
其中:
 

  • &lt;service-name&gt;是Service的名稱

  • 3000是你想要在電腦上打開的端口

  • 80是由Service暴露的端口
     

    三、 排查Ingress故障

     

若是你走到了這個部分,這意味着:

 

  • Pod正在運行而且準備就緒

  • Service能夠分發流量給Pod

 

但你依舊沒法接收app的響應。那麼這頗有多是Ingress配置出現錯誤。
 

因爲使用的Ingress controller是集羣中的第三方組件,那麼根據Ingress controller的類型會由不一樣的調試技術。可是在深刻研究Ingress特定的工具以前,你可使用一些簡單的方法檢查。

 

Ingress使用serviceNameservicePort鏈接Service。你應該檢查那些是否正確配置。你可使用如下命令檢查Ingress是否正確配置:
 

kubectl describe ingress <ingress-name>

 
若是Backend列是空的,那麼配置中確定存在錯誤。

 

若是你能在Backend列中看到endpoint,但依舊沒法訪問應用程序,那麼多是如下問題:

 

  • 你將Ingress暴露於公網的方式

  • 你將集羣暴露於公網的方式

 

你能夠經過直接鏈接到Ingress Pod將基礎設施問題與Ingress隔離開來。

 

首先,爲你的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

 
描述它以檢索端口:
 

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 controller。你應該調試Ingress。

 

若是仍然沒法使Ingress controller正常工做,則應該開始對其進行調試。市場有許多不一樣版本的Ingress controller。比較流行的包括Nginx、HAProxy、Traefik等。
 

你應該查閱Ingress controller的文檔以查找故障排查指南。

 

既然Ingress Nginx是最流行的Ingress controller,那麼在下一個部分咱們將介紹一些相關的技巧。

 

調試Ingress Nginx

 

Ingress-nginx有kubectl的官方插件,你能夠訪問如下網址查看:

https://kubernetes.github.io/ingress-nginx/kubectl-plugin/

 

你可使用kubectl ingress-nginx來進行如下操做:

 

  • 檢查日誌、Backend、證書等

  • 鏈接到Ingress

  • 檢查當前的配置

     

你還能夠嘗試如下三個命令:
 

  • kubectl ingress-nginx lint這是用來檢查nginx.conf

  • kubectl ingress-nginx backend來檢查Backend(與kubectl describe ingress &lt;ingress-name&gt;相似)

  • kubectl ingress-nginx logs來檢查日誌
     

請注意,你須要使用--namespace &lt;name&gt;來指定正確的命名空間。
 

總 結

 
若是你毫無頭緒,那麼在Kubernetes中進行故障排除多是一項艱鉅的任務。

 

你應該永遠記住以從下至上的順序解決問題:現檢查Pod,而後向上移動堆棧至Service和Ingress。
 

而本文中的debug技術在其餘地方也是通用的,例如:
 

  • 出現故障的Jobs和CronJobs

  • StatefulSets和DaemonSets

 

但願你們都沒有bug!

相關文章
相關標籤/搜索