本文將幫助你釐清在Kubernetes中調試 deployment的思路。下圖是完整的故障排查思路,若是你想得到更清晰的圖片,請在公衆號後臺(RancherLabs)回覆「troubleshooting」。nginx
當你但願在Kubernetes中部署一個應用程序,你一般須要定義三個組件:git
接下來,咱們經過圖片快速回顧一下。github
在Kubernetes中,你的應用程序經過兩層負載均衡器暴露:內部和外部。api
內部負載均衡器稱爲Service,而外部負載均衡器則稱爲Ingress。安全
Pod未直接部署,所以,Deployment建立Pod並監視它們。bash
假設你想部署一個簡單的Hello World應用程序,那麼對於此類應用程序,其YAML文件與如下相似:app
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: /
這個定義很長,容易忽略組件之間的相互關係。負載均衡
例如:工具
在進行debug以前,咱們先來回顧一下這三個組件之間的關係如何。oop
首先,咱們從Deployment和Service開始。
實際上,Deployment和Service根本沒有鏈接。相反,該Service直接指向Pod,並徹底跳過Deployment。因此,你應該關注的是Pod和Service是如何與彼此關聯的。你應該記住三件事:
targetPort
應該與Pod內的容器的containerPort
相匹配如下圖片總結了如何鏈接端口:
考慮由Service暴露的pod
當你建立一個pod,你應該在你的Pod中爲每一個容器定義端口containerPort
當你建立一個Service時,你可以定義一個port
和一個targetPort
。但你應該將哪個鏈接到容器呢?
targetPort
與containerPort
應該可以匹配
若是你的容器暴露端口3000,那麼targetPort
應該與該數字相匹配。
若是你查看了YAML,標籤與ports
或targerPort
應該匹配:
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不使用它來路由流量。換言之,你能夠安全地將其移除或者給它分配不一樣的值。
那麼matchLabels
selector呢?它須要與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/<service name>
是serivce的名稱——在當前YAML中,是「my-service」。若是你可以鏈接,那麼設置就是正確的。若是你沒法鏈接,你頗有可能弄錯了標籤或者端口未匹配。
暴露應用程序的下一步是配置Ingress。Ingress必須知道如何檢索Service,而後檢索Pod並將流量路由到它們。Ingress經過名稱和暴露的端口來檢索正確的Service。
在Ingress和Service中應該匹配兩件事:
servicePort
應該與Service的port
匹配serviceName
應該與Service的name
相匹配如下圖片將總結如何鏈接端口:
你已經知道該服務暴露了一個端口
Ingress有一個名爲servicePort
的字段。
Serviceport
和IngressservicePort
應該相匹配
若是你決定分配端口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 ,你應該能找到提供網頁的應用程序。
如今,咱們來快速回顧一下什麼端口和標籤須要匹配:
targerPort
應該匹配在Pod內容器的containerPort
servicePort
應該匹配在Service中的portserviceName
的字段瞭解如何構造YAML只是開始。那麼,出了問題時會有什麼表現?Pod可能沒法啓動,或者直接崩潰。
在咱們深刻研究有故障的deployment以前,必須有一個明肯定義的模型,以瞭解Kubernetes的工做方式。
既然在每一個deployment中都有那三個組件,你應該從底層開始按順序調試它們。
你應該從底層開始排查Deployment故障。首先,檢查Pod是否準備就緒而且正在運行
若是Pod已經準備就緒,你須要檢查Service是否能夠將流量分配到Pod。
最後你應該檢查Service和Ingress之間的鏈接。
在大多數狀況下,問題出如今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 <pod name>
可以幫助檢索Pod的容器日誌kubectl describe pod <pod name>
可以有效地檢索與Pod相關的事件列表kubectl get pod <pod name>
對於提取存儲在Kubernetes中的Pod的YAML定義十分有用kubectl exec -ti <pod name> bash
能夠用於在Pod其中一個容器中運行一個交互式命令你應該使用哪個呢?實際上,沒有一種命令是萬能的,你能夠根據實際狀況結合使用。
Pod可能會出現啓動和運行時的錯誤。
啓動錯誤包括:
運行時錯誤包括:
這些錯誤中,有些比其餘錯誤更爲常見。如下是最多見的錯誤以及如何修復它們:
當Kubernetes沒法檢索Pod其中之一的容器鏡像時,將出現此錯誤。
有三種常見緣由:
前兩個緣由能夠經過更正鏡像名稱和tag解決。最後一個,你須要將憑據添加到「Secret」中的私有鏡像倉庫中,並在Pod中引用它。
官方文檔可讓你更加清楚:
https://kubernetes.io/docs/ta...
若是容器沒法啓動,Kubernetes狀態將顯示CrashLoopBackOff消息。
一般狀況下,容器在如下場景中沒法啓動:
你應該嘗試並檢索該容器的日誌以肯定出現故障的緣由。
若是因爲你的容器重啓過快而沒法查看日誌,你可使用如下命令:
kubectl logs <pod-name> --previous
它將從以前的容器中打印錯誤信息。
容器不能啓動時出現錯誤,甚至在容器內的應用程序啓動以前就沒法啓動。
這個問題一般因爲錯誤配置致使的,如:
你應該使用kubectl describe pod <pod-name>
來收集和分析錯誤。
當你建立一個Pod時,Pod保持在Pending狀態。這是爲何呢?假設你的調度組件運行了解,那麼有如下幾個緣由:
那麼,最好的選擇是使用命令kubectl describe
檢查事件:
kubectl describe pod <pod name>
對於因爲ResourceQuotas形成的錯誤,可使用如下方法檢查集羣的日誌:
kubectl get events --sort-by=.metadata.creationTimestamp
若是Pod正在運行可是不Ready,這意味着Readiness探針出現故障。當Readiness探針出現故障時,Pod沒法附加到Service上,而且流量沒法轉發到實例上。
Readiness探針故障是特定於應用程序的錯誤,所以使用kubectl describe
來檢查事件部分,以驗證錯誤。
若是你的Pod正在運行而且準備就緒,可是你依舊沒法接收來自應用程序的響應,你應該檢查Service是否配置正確。
Service旨在根據pod的標籤將流量路由到Pod。因此第一件事,你須要檢查Service target多少個Pod。能夠經過檢查Service中的Endpoint來完成此步驟:
kubectl describe service <service-name> | grep Endpoints
一個endpoint是一對<ip address:port>
`,而且當Service(至少)target一個pod時。至少有一對。
若是「Endpoint」部分是空的,那麼有兩種解釋:
若是你看到了endpoint列表,但依舊沒法訪問你的應用程序,那麼你的Service中的targetPort
多是罪魁禍首。
你應該怎麼測試Service?不管Service類型是什麼,均可以使用kubectl port-forward
鏈接到它:
kubectl port-forward service/<service-name> 3000:80
其中:
`<service-name>
是Service的名稱3000
是你想要在電腦上打開的端口80
是由Service暴露的端口若是你走到了這個部分,這意味着:
但你依舊沒法接收app的響應。那麼這頗有多是Ingress配置出現錯誤。
因爲使用的Ingress controller是集羣中的第三方組件,那麼根據Ingress controller的類型會由不一樣的調試技術。可是在深刻研究Ingress特定的工具以前,你可使用一些簡單的方法檢查。
Ingress使用serviceName
和servicePort
鏈接Service。你應該檢查那些是否正確配置。你可使用如下命令檢查Ingress是否正確配置:
kubectl describe ingress <ingress-name>
若是Backend列是空的,那麼配置中確定存在錯誤。
若是你能在Backend列中看到endpoint,但依舊沒法訪問應用程序,那麼多是如下問題:
你能夠經過直接鏈接到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有kubectl的官方插件,你能夠訪問如下網址查看:
https://kubernetes.github.io/...
你可使用kubectl ingress-nginx
來進行如下操做:
你還能夠嘗試如下三個命令:
kubectl ingress-nginx lint
這是用來檢查nginx.conf
kubectl ingress-nginx backend
來檢查Backend(與kubectl describe ingress <ingress-name>
相似)kubectl ingress-nginx logs
來檢查日誌請注意,你須要使用--namespace <name>
來指定正確的命名空間。
若是你毫無頭緒,那麼在Kubernetes中進行故障排除多是一項艱鉅的任務。
你應該永遠記住以從下至上的順序解決問題:現檢查Pod,而後向上移動堆棧至Service和Ingress。
而本文中的debug技術在其餘地方也是通用的,例如:
但願你們都沒有bug!