超長可視化指南!你必須了解的K8S部署的debug思路

image

本文將幫助你厘清在Kubernetes中調(diào)試 deployment的思路。下圖是完整的故障排查思路,如果你想獲得更清晰的圖片,請?jiān)诠娞柡笈_(RancherLabs)回復(fù)“troubleshooting”。

image

當(dāng)你希望在Kubernetes中部署一個(gè)應(yīng)用程序,你通常需要定義三個(gè)組件:

  • Deployment——這是創(chuàng)建名為Pods的應(yīng)用程序副本的方法

  • Serivce——內(nèi)部負(fù)載均衡器,將流量路由到Pods

  • Ingress——可以描述流量如何從集群外部流向Service

接下來,我們通過圖片快速回顧一下。

image

在Kubernetes中,你的應(yīng)用程序通過兩層負(fù)載均衡器暴露:內(nèi)部和外部。

image

內(nèi)部負(fù)載均衡器稱為Service,而外部負(fù)載均衡器則稱為Ingress。

image

Pod未直接部署,因此,Deployment創(chuàng)建Pod并監(jiān)視它們。

假設(shè)你想部署一個(gè)簡單的Hello World應(yīng)用程序,那么對于此類應(yīng)用程序,其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: /

這個(gè)定義很長,容易忽略組件之間的相互關(guān)系。

例如:

  • 你什么時(shí)候應(yīng)該使用80端口,什么時(shí)候使用端口8080?

  • 你是否應(yīng)該為每個(gè)服務(wù)創(chuàng)建一個(gè)新端口,以免它們沖突?

  • 標(biāo)簽(label)名稱重要嗎?是否應(yīng)該每一處都一樣?

在進(jìn)行debug之前,我們先來回顧一下這三個(gè)組件之間的關(guān)系如何。

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

連接Deployment和Service

實(shí)際上,Deployment和Service根本沒有連接。相反,該Service直接指向Pod,并完全跳過Deployment。所以,你應(yīng)該關(guān)注的是Pod和Service是如何與彼此關(guān)聯(lián)的。你應(yīng)該記住三件事:

  1. Service selector至少與Pod的一個(gè)標(biāo)簽匹配

  2. Serivce targetPort應(yīng)該與Pod內(nèi)的容器的containerPort相匹配

  3. Serviceport可以是任何數(shù)字。多個(gè)Service可以使用同一個(gè)端口,因?yàn)樗鼈円呀?jīng)被分配了不同的IP地址

以下圖片總結(jié)了如何連接端口:

image

考慮由Service暴露的pod

image

當(dāng)你創(chuàng)建一個(gè)pod,你應(yīng)該在你的Pod中為每個(gè)容器定義端口containerPort

image

當(dāng)你創(chuàng)建一個(gè)Service時(shí),你能夠定義一個(gè)port和一個(gè)targetPort。但你應(yīng)該將哪一個(gè)連接到容器呢?

image

targetPortcontainerPort應(yīng)該能夠匹配

image

如果你的容器暴露端口3000,那么targetPort應(yīng)該與該數(shù)字相匹配。

如果你查看了YAML,標(biāo)簽與portstargerPort應(yīng)該匹配:

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標(biāo)簽?zāi)??也?yīng)該匹配嗎?

那個(gè)標(biāo)簽屬于deployment,并且Service selector不使用它來路由流量。換言之,你可以安全地將其移除或者給它分配不同的值。

那么matchLabelsselector呢?它需要與Pod標(biāo)簽匹配并且Deployment使用它來跟蹤Pod。

假設(shè)你做了一個(gè)正確的更改,你應(yīng)該如何測試它呢?你可以使用以下命令檢查Pod是否擁有正確的標(biāo)簽:

kubectl get pods --show-labels

或者如果你有屬于多個(gè)應(yīng)用程序的Pod:

kubectl get pods --selector any-name=my-app --show-labels

其中any-name=my-app是標(biāo)簽any-name: my-app。依舊存在問題?你也可以連接到Pod。你可以在kubectl中使用命令port-forward連接到Serivce并測試連接。

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

其中:

  • service/<service name>是serivce的名稱——在當(dāng)前YAML中,是“my-service”。

  • 3000是你希望在你的電腦上打開的端口

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

如果你能夠連接,那么設(shè)置就是正確的。如果你無法連接,你很有可能弄錯(cuò)了標(biāo)簽或者端口未匹配。

連接Service和Ingress

暴露應(yīng)用程序的下一步是配置Ingress。Ingress必須知道如何檢索Service,然后檢索Pod并將流量路由到它們。Ingress通過名稱和暴露的端口來檢索正確的Service。

在Ingress和Service中應(yīng)該匹配兩件事:

  1. Ingress的servicePort應(yīng)該與Service的port匹配

  2. Ingress的serviceName應(yīng)該與Service的name相匹配

以下圖片將總結(jié)如何連接端口:

image

你已經(jīng)知道該服務(wù)暴露了一個(gè)端口

image

Ingress有一個(gè)名為servicePort的字段。

image

Serviceport和IngressservicePort應(yīng)該相匹配

image

如果你決定分配端口80給該service,你應(yīng)該同時(shí)更改servicePort為80

實(shí)際操作中,你需要查看這些命令行:

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

你應(yīng)該如何測試Ingress是否正常運(yùn)行呢?你可以使用和之前相同的策略,即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

驗(yàn)證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

此時(shí),每次你在你的電腦上訪問端口3000,請求就會被轉(zhuǎn)發(fā)到在Ingress controller Pod上的端口80。

如果你訪問 http://localhost:3000 ,你應(yīng)該能找到提供網(wǎng)頁的應(yīng)用程序。

簡單回顧一下

現(xiàn)在,我們來快速回顧一下什么端口和標(biāo)簽需要匹配:

  1. Service selector應(yīng)該匹配Pod的標(biāo)簽

  2. ServicetargerPort應(yīng)該匹配在Pod內(nèi)容器的containerPort

  3. Service 端口可以是任意數(shù)字。多個(gè)Service可以使用同個(gè)端口,因?yàn)樗鼈円呀?jīng)分配了不同的IP地址

  4. Ingress的servicePort應(yīng)該匹配在Service中的port

  5. Service的名稱應(yīng)該匹配在Ingress中的serviceName的字段

了解如何構(gòu)造YAML只是開始。那么,出了問題時(shí)會有什么表現(xiàn)?Pod可能無法啟動(dòng),或者直接崩潰。

3步排查K8S Deployment故障

在我們深入研究有故障的deployment之前,必須有一個(gè)明確定義的模型,以了解Kubernetes的工作方式。

既然在每個(gè)deployment中都有那三個(gè)組件,你應(yīng)該從底層開始按順序調(diào)試它們。

  1. 你應(yīng)該確保你的Pod正在運(yùn)行

  2. 著重關(guān)注使Service將流量路由到Pod

  3. 檢查Ingress是否正確配置

image

你應(yīng)該從底層開始排查Deployment故障。首先,檢查Pod是否準(zhǔn)備就緒并且正在運(yùn)行

image

如果Pod已經(jīng)準(zhǔn)備就緒,你需要檢查Service是否可以將流量分配到Pod。

image

最后你應(yīng)該檢查Service和Ingress之間的連接。

1、 故障排查Pod

在大多數(shù)情況下,問題出現(xiàn)在Pod本身。所以你應(yīng)該確保Pod正在運(yùn)行并準(zhǔn)備就緒。應(yīng)該如何檢查呢?

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

以上部分,只有最后一個(gè)Pod是正在運(yùn)行并且準(zhǔn)備就緒的,而前兩個(gè)Pod既沒有Running也沒有Ready。那么,你應(yīng)該如何定位是什么出了問題呢?

這里有4個(gè)十分有用的命令可以幫助你排查Pod的故障:

  • kubectl logs <pod name>能夠幫助檢索Pod的容器日志

  • kubectl describe pod <pod name>能夠有效地檢索與Pod相關(guān)的事件列表

  • kubectl get pod <pod name>對于提取存儲在Kubernetes中的Pod的YAML定義十分有用

  • kubectl exec -ti <pod name> bash可以用于在Pod其中一個(gè)容器中運(yùn)行一個(gè)交互式命令

你應(yīng)該使用哪一個(gè)呢?實(shí)際上,沒有一種命令是萬能的,你可以根據(jù)實(shí)際情況結(jié)合使用。

常見的Pod錯(cuò)誤

Pod可能會出現(xiàn)啟動(dòng)和運(yùn)行時(shí)的錯(cuò)誤。

啟動(dòng)錯(cuò)誤包括:

  • ImagePullBackoff

  • ImageInspectError

  • ErrImagePull

  • ErrImageNeverPull

  • RegistryUnavailable

  • InvalidImageName

運(yùn)行時(shí)錯(cuò)誤包括:

  • CrashLoopBackOff

  • RunContainerError

  • KillContainerError

  • VerifyNonRootError

  • RunInitContainerError

  • CreatePodSandboxError

  • ConfigPodSandboxError

  • KillPodSandboxError

  • SetupNetworkError

  • TeardownNetworkError

這些錯(cuò)誤中,有些比其他錯(cuò)誤更為常見。以下是最常見的錯(cuò)誤以及如何修復(fù)它們:

ImagePullBackOff

當(dāng)Kubernetes無法檢索Pod其中之一的容器鏡像時(shí),將出現(xiàn)此錯(cuò)誤。

有三種常見原因:

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

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

  • 你所檢索的鏡像是私有倉庫的,并且Kubernetes沒有訪問它的憑據(jù)

前兩個(gè)原因可以通過更正鏡像名稱和tag解決。最后一個(gè),你需要將憑據(jù)添加到“Secret”中的私有鏡像倉庫中,并在Pod中引用它。

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

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

CrashLoopBackOff

如果容器無法啟動(dòng),Kubernetes狀態(tài)將顯示CrashLoopBackOff消息。

通常情況下,容器在以下場景中無法啟動(dòng):

你應(yīng)該嘗試并檢索該容器的日志以確定出現(xiàn)故障的原因。

如果由于你的容器重啟過快而無法查看日志,你可以使用以下命令:

kubectl logs <pod-name> --previous

它將從之前的容器中打印錯(cuò)誤信息。

RunContainerError

容器不能啟動(dòng)時(shí)出現(xiàn)錯(cuò)誤,甚至在容器內(nèi)的應(yīng)用程序啟動(dòng)之前就無法啟動(dòng)。

這個(gè)問題通常由于錯(cuò)誤配置導(dǎo)致的,如:

  • 安裝一個(gè)不存在的volume,如ConfigMap或Secret

  • 將只讀volume安裝為可讀寫

你應(yīng)該使用kubectl describe pod <pod-name>來收集和分析錯(cuò)誤。

Pod處于Pending狀態(tài)

當(dāng)你創(chuàng)建一個(gè)Pod時(shí),Pod保持在Pending狀態(tài)。這是為什么呢?假設(shè)你的調(diào)度組件運(yùn)行了解,那么有以下幾個(gè)原因:

  • 集群沒有足夠的資源來運(yùn)行Pod,如CPU和內(nèi)存

  • 當(dāng)前命名空間有一個(gè)ResourceQuota對象并且所創(chuàng)建的Pod會使該命名空間超過資源額度

  • Pod與一個(gè)Pending狀態(tài)的PersistentVolumeClaim綁定。

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

kubectl describe pod <pod name>

對于由于ResourceQuotas造成的錯(cuò)誤,可以使用以下方法檢查集群的日志:

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

Pod不處于Ready狀態(tài)

如果Pod正在運(yùn)行但是不Ready,這意味著Readiness探針出現(xiàn)故障。當(dāng)Readiness探針出現(xiàn)故障時(shí),Pod無法附加到Service上,并且流量無法轉(zhuǎn)發(fā)到實(shí)例上。

Readiness探針故障是特定于應(yīng)用程序的錯(cuò)誤,因此使用kubectl describe來檢查事件部分,以驗(yàn)證錯(cuò)誤。

2、 排查Service故障

如果你的Pod正在運(yùn)行并且準(zhǔn)備就緒,但是你依舊無法接收來自應(yīng)用程序的響應(yīng),你應(yīng)該檢查Service是否配置正確。

Service旨在根據(jù)pod的標(biāo)簽將流量路由到Pod。所以第一件事,你需要檢查Service target多少個(gè)Pod??梢酝ㄟ^檢查Service中的Endpoint來完成此步驟:

kubectl describe service  <service-name>  |  grep Endpoints

一個(gè)endpoint是一對`<ip address:port>``,并且當(dāng)Service(至少)target一個(gè)pod時(shí)。至少有一對。

如果“Endpoint”部分是空的,那么有兩種解釋:

  1. 任何正在運(yùn)行的Pod沒有正確的label(提示:你需要檢查以下你是否在正確的命名空間內(nèi))

  2. 在Service的selector標(biāo)簽中有錯(cuò)別字

如果你看到了endpoint列表,但依舊無法訪問你的應(yīng)用程序,那么你的Service中的targetPort可能是罪魁禍?zhǔn)住?/p>

你應(yīng)該怎么測試Service?無論Service類型是什么,都可以使用kubectl port-forward連接到它:

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

其中:

  • ``<service-name>`是Service的名稱

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

  • 80是由Service暴露的端口

3、 排查Ingress故障

如果你走到了這個(gè)部分,這意味著:

  • Pod正在運(yùn)行并且準(zhǔn)備就緒

  • Service可以分發(fā)流量給Pod

但你依舊無法接收app的響應(yīng)。那么這很有可能是Ingress配置出現(xiàn)錯(cuò)誤。

由于使用的Ingress controller是集群中的第三方組件,那么根據(jù)Ingress controller的類型會由不同的調(diào)試技術(shù)。但是在深入研究Ingress特定的工具之前,你可以使用一些簡單的方法檢查。

Ingress使用serviceNameservicePort連接Service。你應(yīng)該檢查那些是否正確配置。你可以使用以下命令檢查Ingress是否正確配置:

kubectl describe ingress <ingress-name>

如果Backend列是空的,那么配置中肯定存在錯(cuò)誤。

如果你能在Backend列中看到endpoint,但依舊無法訪問應(yīng)用程序,那么可能是以下問題:

  • 你將Ingress暴露于公網(wǎng)的方式

  • 你將集群暴露于公網(wǎng)的方式

你可以通過直接連接到Ingress Pod將基礎(chǔ)設(shè)施問題與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

此時(shí),每次你在電腦上訪問端口3000,請求將會轉(zhuǎn)發(fā)到Pod上的端口80。

那么,現(xiàn)在能夠正常運(yùn)行了嗎?

如果正常工作,問題就出在基礎(chǔ)設(shè)施。你應(yīng)該檢查流量如何路由到你的集群。

如果無法正常工作,問題就在Ingress controller。你應(yīng)該調(diào)試Ingress。

如果仍然無法使Ingress controller正常工作,則應(yīng)該開始對其進(jìn)行調(diào)試。市場有許多不同版本的Ingress controller。比較流行的包括Nginx、HAProxy、Traefik等。

你應(yīng)該查閱Ingress controller的文檔以查找故障排查指南。

既然Ingress Nginx是最流行的Ingress controller,那么在下一個(gè)部分我們將介紹一些相關(guān)的技巧。

調(diào)試Ingress Nginx

Ingress-nginx有kubectl的官方插件,你可以訪問以下網(wǎng)址查看:

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

你可以使用kubectl ingress-nginx來進(jìn)行以下操作:

  • 檢查日志、Backend、證書等

  • 連接到Ingress

  • 檢查當(dāng)前的配置

你還可以嘗試以下三個(gè)命令:

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

  • kubectl ingress-nginx backend來檢查Backend(與kubectl describe ingress <ingress-name>類似)

  • kubectl ingress-nginx logs來檢查日志

請注意,你需要使用--namespace <name>來指定正確的命名空間。

總 結(jié)

如果你毫無頭緒,那么在Kubernetes中進(jìn)行故障排除可能是一項(xiàng)艱巨的任務(wù)。

你應(yīng)該永遠(yuǎn)記住以從下至上的順序解決問題:現(xiàn)檢查Pod,然后向上移動(dòng)堆棧至Service和Ingress。

而本文中的debug技術(shù)在其他地方也是通用的,例如:

  • 出現(xiàn)故障的Jobs和CronJobs

  • StatefulSets和DaemonSets

希望大家都沒有bug!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容