背景
Kubernetes(K8S)是目前市場佔有率最高的開源 Docker 編排和運行平臺,K8S 前身是谷歌內部的容器平臺 Borg 衍生出來的開源 Docker 運行平臺,具備企業級容器編排的能力,深受國內外 Docker 用戶喜愛。
隨着 K8S 的普及,如何在生產環境調試 K8S 的問題成爲了大家普遍關注的問題。在 JFrog SwampUp 2017 用戶大會上,谷歌的架構師 Ray Tsang 介紹瞭如何在 K8S 線上環境做調試,排查問題。
Docker 測試環境和生產環境不一致
技術背景
示例應用程序由多個微服務組成:Guestbook UI,Hello World Service, Guestbook Service, Redis 和 Mysql 服務,其中 Hello World Service 由 Java 語言開發,並且在 K8S 集羣裏運行了10個實例。
在軟件發佈流程裏,環境的差異是最難解決的問題,Docker 的目標就是使用相同的容器鏡像,解決不同環境的差異問題。在 K8S 裏,可以通過不同的 Namespace 區分不同的環境。本示例裏使用 Staging 作爲測試環境的 Namespace。
在 Staging 環境啓動服務,並查看服務的狀態: kubectl get pods --namespace=staging
kubectl get svc --namespace=staging
從 Web 端訪問服務器都正常,Staging 環境 OK!
如何找到出問題的鏡像?
Staging 環境裏部署和測試都沒問題,這時可以將鏡像部署到 K8S 的生產環境集羣,得到以下畫面:
爲什麼同樣的鏡像在 Staging 環境運行沒問題,而部署到 K8S 的生產環境,卻報錯了?來調試下,首先打開 'helloworld-ui' 服務的 YAML 文件,看看部署失敗的鏡像版本號是多少:
可以看到出錯的服務鏡像版本是 latest。根據持續交付的概念,每次部署最新版本的鏡像到測試環境,這是沒問題的。(在生產環境用 latest 需要謹慎)
但問題來了,我們並不知道這個 'latest' 是哪個版本。不過通過鏡像的 SHA256 碼可以找到對應的鏡像,Docker inspect 可以顯示某個鏡像的 SHA256 碼。K8S 也提供了 'describe pod' 方法來顯示鏡像的信息,包括 SHA256 碼。
有了這個特徵碼,我們可以在 Artifactory 裏面進行 Checksum 搜索,快速查找鏡像:
搜索的結果顯示,這個 latest 鏡像的特徵碼,對應的版本號是50:
看看 Jenkins 構建任務裏的錯誤信息?
好的,問題鏡像版本找到了,是不是這個鏡像構建時出錯了?去 Jenkins 的日誌裏面去找找。等一下,貌似我並不知道 Jenkins 任務 ID 和鏡像 ID 直接的關係。還好,從 Artifactory 裏面可以看到這個關係。點擊50號鏡像,構建信息裏體現 Jenkins 任務的地址:
點擊 CI Server 鏈接跳轉到 Jenkins,發現50號構建任務並沒有異常。
鏡像沒問題,看看服務器異常日誌?
由於在 K8S 集羣裏已經運行了多個服務的實例,如何在同一個服務的多個實例裏統一進行日誌聚合查詢?
使用 K8S 裏可以方便的下載並運行 ELK 的 Helm Charts,做日誌的聚合查詢。谷歌內部提供了服務叫做 Big Query,也是谷歌搜索引擎用到的查詢服務,查詢 TB 級別的數據,能夠做到秒級的響應時間。
通過拖拽式的查詢條件輸入,Big Query 可以幫助你找出 'helloworld-ui' 服務的所有實例裏 Exception 的個數,從上圖來看,某一個實例拋出了25個異常。
出現這種情況怎麼處理?因爲壞的實例已經影響到了用戶服務,並且這個服務在 K8S 還有多個健康的實例在運行,所以很多公司會選擇馬上 Kill 這個實例,甚至有些公司會在每天夜晚 Kill 掉所有的實例,來避免內存泄漏的問題。
Ray 認爲這並不可取,這樣做當時可能避免了問題,但過幾天這個問題會反覆出現,所以需要分析日誌,解決問題。
既不影響用戶使用,又能夠 Debug?
有錯誤日誌可以查看是工程師最欣慰的事情,但這個實例已經影響用戶使用了,我必須要停掉這個實例,這就意味着無法重現問題。有沒有一個方法讓我既能夠在運行環境裏調試,查看實時日誌,重現問題,又不影響用戶的使用?
K8S 裏的 Service 和 Label 的概念可以解決這個難題。K8S 會爲集羣裏 service (helloworkd-ui) 的所有實例默認創建一個負載均衡,並且自動維護所有實例的個數。使用 label pod serving=false 可以實現將某個實例移除負載均衡的效果。
從上圖可以看到,helloworld-ui 服務有11個實例。這就是 K8S 的神奇之處,它不僅將有問題的 helloworld-ui 實例移除了負載均衡,工程師可以登錄這個實例進行調試,同時 K8S 的 Replication Controller 會自動新起一個實例,保持實例運行的個數始終爲10個,從而保證不影響用戶的體驗。
如何調試?
可以用熟悉的 docker exec -it instanceId bash 登錄到實例裏進行調試。當然 K8S 也提供了更強大的調試方式:使用端口轉發(port-forward)的方式。
它能夠將本地端口收到的請求轉發到這個問題鏡像的實例裏進行遠程調試,快速定位問題。
那麼傳統排查問題的方式當然是增加日誌,把變量信息輸出到日誌裏,然後修復問題,這是普通的調試方式,往往這種方式定位並修改一個 bug 需要花費一個小時或更多。
谷歌云爲開發者提供了一個強大的調試工具 StackDriver Debugger,通過這個工具能夠在不重新部署應用的情況下,增加日誌輸出(logpoint)。StackDriver Debugger 也是從谷歌內部孵化出的調試工具。
增加 logpoint 之後,再次訪問這個服務,對應的變量內容會立刻顯示在日誌裏,而無需重新部署應用。這樣極大的提高了調試的效率。
繼續調試
從上圖的日誌可以看出,請求裏的 name 爲空。從源代碼去查看原因:
這個 name 是來自 Session 的屬性,也就是說第一次用戶註冊的時候,使用了空的用戶名,並且存入 Session,當再次讀取的時候發生了異常,這下問題找到了,改代碼,提交,測試,部署,收工!
還有更酷的 Snapshot 功能
遠程 debug 的功能很酷,但它的問題在於它阻斷了用戶的返回,也叫做 Stop the world,從而無法爲線上服務進行遠程 debug。谷歌雲的調試工具還支持 Snapshot 的概念,在谷歌雲平臺裏,你可以爲你的某行代碼設置 snapshot 點,設置之後,這個點一旦被觸發,調試工具會記錄當時的所有變量信息,而不會阻斷程序,進而讓生產環境的實時調試變得可能。
總結
K8S 爲用戶提供的強大的 Docker 容器管理和運行的能力,並且支持了負載均衡,Label 分組服務實例,日誌聚合檢索,生產環境調試的工具等等,爲容器的開發和調試提供了極大的便利。文中提到的谷歌的日誌聚合檢索功能 Big Query 以及谷歌的 Debug 工具 StackDriver Debugger 在谷歌雲裏都提供了服務,你只需註冊一個谷歌雲賬號即可使用。
從谷歌的產品設計可以看出,谷歌是專注的爲開發者提高生產效率,這也是谷歌自身工程師文化基因的體現。即便不是谷歌雲的用戶,也可以借鑑谷歌的思路,找到快速解決線上問題的辦法,提高效率,降低服務不可用時間。
參考資料:
谷歌雲調試工具:
https://cloud.google.com/debugger/
原視頻地址
https://youtu.be/INJMA8gHnro?list=PLY0Zjn5rFo4MFIwbYtQx4wD1KK7HleIzk