Kubernetes 已經成為了雲原生時代基礎設施的事實標準,越來越多的應用系統在 Kubernetes 環境中執行。Kubernetes 已經依靠其強大的自動化運維能力解決了業務系統的大多數執行維護問題,然而還是要有一些狀況是需要運維人員去手動處理的。那麼和傳統運維相比,面向 Kubernetes 解決業務運維問題是否有一些基本思路,是否可以藉助其他工具簡化排查流程,就是今天探討的主題。
首先有必要明確一點,什麼樣的問題算是 Kubernetes 領域的業務系統問題。Kubernetes 目前已經是雲原生時代各類 「上雲」 業務系統所處執行環境的事實標準。
我們假定你已經擁有了一套健壯的 Kubernetes 環境,業務系統的執行狀態不會受到底層執行環境異常的影響,當業務系統出現問題時,Kubernetes 也可以正確的收集到業務系統的執行狀態資訊。
有了這假定條件之後,我們就可以將業務系統問題約束在業務從部署到正常執行起來這一時間區間內。所以本文探討的業務系統問題的範疇包括:
解決這一類的問題的意義是顯而易見的,因為將業務系統執行起來是一種最基礎的需求。具備一套健壯的 Kubernetes 執行環境或者是編寫了一套業務系統程式碼都不會為我們產生直接的價值。只有將業務系統程式碼執行到一個穩定的環境中,面向終端使用者提供服務時才會為我們產生真正的價值。
值得慶幸的是,解決這類問題多半隻需要我們踩一次坑。對於大多數全新的業務系統而言,部署到 Kubernetes 環境中去時,所可能遭遇的問題只需要被處理一次。一旦部署完成,業務系統就可以專注於迭代功能,不斷迴圈完成釋出過程即可,順利進入了一個迴圈往復的 CI/CD 流程之中。
除去基礎需求這一顯而易見的意義,我們也會探討如何降低解決這類問題的難度,解決問題難度的降低本身也具有意義。雲原生時代,我們倡導每個開發人員都能夠掌控自己的業務系統,這種掌控也對開發人員提出了新的要求,即掌控 Kubernetes 的使用。這有點將運維層面的工作附加給開發人員的意思,實際推廣過程並不順利。為了便於開發人員使用 Kubernetes 來部署與偵錯自己開發的業務系統,企業可以選擇雲原生應用平臺來降低開發人員使用 Kubernetes 的門檻,Rainbond 就是這樣一款雲原生應用管理平臺,其易用性的特點降低了開發人員的學習門檻,同時能夠為業務系統賦能。
正常情況下,負責部署業務系統的工作人員是通過宣告式的組態檔來定義業務系統的,其中的關鍵部分稱之為規約(Spec)。這些規約欄位通過格式嚴苛的 Yaml 型別組態檔來定義,正確填寫其中的鍵與值需要龐雜的 Kubernetes 知識的保障。而掌握組態檔的格式,以及設定中的內容,往往是開發人員學習原生 Kubernetes 的首個陡峭門檻。
原生的使用方式中,kubectl 命令列工具會為這些組態檔提供嚴苛的校驗機制,然而在校驗無法通過時,能夠給出的提示卻並不是很友好。
以一份非常簡單的 Yaml 組態檔為例:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-nginx
name: my-nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: my-nginx
template:
metadata:
labels:
app: my-nginx
spec:
containers:
- image: nginx
name: nginx
env:
- name: DEMO_GREETING
value: "true" # 此處必須用引號擴起來,因為這是個 string 型別
securityContext:
privileged: true # 此處必須不能使用引號,因為這是個 bool 型別
設定中有兩個 true
值,然而其中一個必須使用引號,而另一個則不是,這對一些新手而言並不是很友好。而載入這份組態檔的錯誤版本時,系統給出的報錯雖然可以定位問題,但是互動體驗更加不友好。
$ kubectl apply -f my-deployment.yaml
Error from server (BadRequest): error when creating "my-deployment.yaml": Deployment in version "v1" cannot be handled as a Deployment: v1.Deployment.Spec: v1.DeploymentSpec.Template: v1.PodTemplateSpec.Spec: v1.PodSpec.Containers: []v1.Container: v1.Container.Env: []v1.EnvVar: v1.EnvVar.Value: ReadString: expects " or n, but found t, error found in #10 byte of ...|,"value":true}],"ima|..., bigger context ...|ainers":[{"env":[{"name":"DEMO_GREETING","value":true}],"image":"nginx","name":"nginx"}]}}}}
像這樣的問題,在類似 Rainbond 這樣的雲原生應用管理平臺中,則不會出現。產品設計之時,就已經遮蔽了一些常見輸入錯誤,使用者不需要關注傳入值的型別問題,平臺會自行進行轉換。
平臺會自動為環境變數新增引號以匹配 string 型別:
以開啟/關閉來體現 bool 型別:
對於一些特殊輸入,也會進行合理校驗,提供的反饋資訊更加人性化:
藉助這些功能,即使是小白使用者也可以正確的定義業務系統的規格。
業務系統的規格定義完成後,就可以提交給 Kubernetes 系統了,下一步,Kubernetes 將會藉助自身排程機制,將業務系統分配到合適的宿主機上執行起來。在進行排程的過程中,業務系統會在一小段時間內處於 Pending
(待定的) 的狀態,然而長期處於 Pending
狀態則說明排程過程中出現了問題。
Kubernetes 以事件的形式,記錄了業務系統在進入執行狀態之前的每一個步驟。一旦出現了 Warning
甚至更嚴重級別的事件時,就說明業務系統的部署過程受阻了。瞭解如何檢視這些事件,並理解其背後代表的意義,對於排查排程問題非常有幫助。
能夠讓業務系統長期處於 Pending
狀態的常見問題包括:映象拉取失敗、資源不足等。使用原生 Kubernetes 時,難免和命令列打交道,來獲取對應 Pod 的事件資訊。
$ kubectl describe pod <podName> -n <nameSpace>
當所有的計算節點都沒有足夠的記憶體資源來排程業務系統的 Pod 時,事件資訊是這樣的:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 Insufficient memory.
而拉取映象失敗則是這樣的:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Failed 26s kubelet, cn-shanghai.10.10.10.25 Error: ErrImagePull
Normal BackOff 26s kubelet, cn-shanghai.10.10.10.25 Back-off pulling image "nginx_error"
Warning Failed 26s kubelet, cn-shanghai.10.10.10.25 Error: ImagePullBackOff
Normal Pulling 15s (x2 over 29s) kubelet, cn-shanghai.10.10.10.25 Pulling image "nginx_error"
對事件列表的解讀,是需要較深厚的 Kubernetes 領域知識的。開發者需要從事件列表中找到關鍵詞,進而採取正確的行動來解決問題。
在 Rainbond 雲原生應用管理平臺中,已經事先想到了降低問題排查成本的需求,使用者點選代表有問題的業務系統 Pod 的方塊,即可瞭解其詳細資訊。在這個頁面中,濃縮了核心問題的說明、當前 Pod 的狀態以及說明,可以幫助使用者極快的定位問題。
當業務系統完成了排程過程後,Kubernetes 系統就會將業務系統對應的 Pod 啟動起來,到這裡,已經距離業務系統對外提供服務很近了。但是不要掉以輕心,Pod 啟動時是有可能遭遇執行異常的。
一般情況下,正常執行中的 Pod 是體現 Running
狀態的,開發人員可以通過命令列的方式獲取其狀態:
$ kubectl get pod <podName> -n <nameSpace>
但是如果處於異常狀態,則可能得到以下結果:
NAME READY STATUS RESTARTS AGE
demo-test-my-nginx-6b78f5fc8-f9rkz 0/1 CrashLoopBackOff 3 86s
CrashLoopBackOff
是一種異常的狀態,除此之外還可能出現一些其他的異常狀態,比如:OOMkilled
、 Evicted
等。對於每一種錯誤型別的處理也不盡相同。這需要非常豐富的 Kubernetes 問題排查經驗。
比如對於 CrashLoopBackOff
這種異常狀態,它意味著 Pod 中的某個容器無法正常執行,程式碼執行過程中遭遇了不可容忍的問題,報錯退出了。正確的處理,是應該查詢問題 Pod 的紀錄檔,瞭解業務程式碼層面的異常。
$ kubectl logs -f <podName> -n <nameSpace>
這種排查的思路是可以固化的,與所部署的業務系統本身沒有關係,所以 Rainbond 雲原生應用管理平臺做了一些人性化的設計,如果業務系統的 Pod 處於這種異常狀態並被操作記錄捕獲,那麼使用者點選這條異常的操作記錄,即可直接跳轉到紀錄檔頁面檢視問題紀錄檔。這種設計隱式的為使用者提供了排查思路,即使使用者自己並沒有意識到應該這麼做。
還有一種特殊型別的執行過程中問題需要注意。 CrashLoopBackOff
這種問題一般出現在 Pod 啟動時,使用者很容易就可以捕捉到,而類似於 OOMkilled
這種問題一般是在業務系統執行很久之後,才會出現。這種問題不容易被使用者捕捉到,這是因為 Kubernetes 會自動重啟出現這類問題的業務系統 Pod 來自動恢復,從而導致問題的湮沒。
Rainbond 雲原生應用管理平臺會自動記錄這一類異常狀態,並留下相應紀錄檔供後續的分析,瞭解到到底是 Pod 中的哪個容器導致了記憶體洩露。
基於原生 Kubernetes 進行業務系統的各階段問題排查,需要開發人員對 Kubernetes 知識體系有較深入的瞭解,並且能夠接受命令列互動式操作體驗。這無形中提升了對開發人員的技術要求,也對其強加了一些運維領域的工作內容,使雲原生落地體驗受阻。開發人員也不應該拿到可以直接操作 Kubernetes 的命令列許可權,這不符合安全規定。
為了能夠讓開發人員合理的偵錯業務系統,選用一款雲原生管理平臺將會是個正確的選擇。雲原生應用管理平臺的設計者,深入瞭解過開發人員的訴求,通過為開發人員提供簡單易用的功能,以及人性化的設計,讓開發人員偵錯業務系統變得事半功倍。