做者 | 聲東 阿里雲售後技術專家api
導讀:阿里雲售後技術團隊的同窗,天天都在處理各式各樣千奇百怪的線上問題。常見的有網絡鏈接失敗、服務器宕機、性能不達標及請求響應慢等。但若是要評選的話,什麼問題看起來微不足道事實上卻讓人絞盡腦汁,我相信確定是「刪不掉」的問題,好比文件刪不掉、進程結束不掉、驅動卸載不了等。這樣的問題就像冰山,隱藏在它們背後的複雜邏輯,每每超過咱們的預想。服務器
今天咱們討論的這個問題,跟 K8s 集羣的 Namespace 有關。Namespace 是 K8s 集羣資源的「收納」機制。咱們能夠把相關的資源「收納」到同一個 Namespace 裏,以免不相關資源之間沒必要要的影響。網絡
Namespace 自己也是一種資源。經過集羣 API Server 入口,咱們能夠新建 Namespace,而對於再也不使用的 Namespace,咱們須要清理掉。Namespace 的 Controller 會經過 API Server,監視集羣中 Namespace 的變化,而後根據變化來執行預先定義的動做。app
有時候,咱們會遇到下圖中的問題,即 Namespace 的狀態被標記成了 "Terminating",但卻沒有辦法被徹底刪除。less
由於刪除操做是經過集羣 API Server 來執行的,因此咱們要分析 API Server 的行爲。跟大多數集羣組件相似,API Server 提供了不一樣級別的日誌輸出。爲了理解 API Server 的行爲,咱們將日誌級別調整到最高級。而後,經過建立刪除 tobedeletedb 這個 Namespace 來重現問題。微服務
但惋惜的是,API Server 並無輸出太多和這個問題有關的日誌。工具
相關的日誌,能夠分爲兩部分:性能
Kube Controller Manager 實現了集羣中大多數的 Controller,它在重複獲取 tobedeletedb 的信息,基本上能夠判斷,是 Namespace 的 Controller 在獲取這個 Namespace 的信息。測試
和上一節相似,咱們經過開啓 Kube Controller Manager 最高級別日誌,來研究這個組件的行爲。在 Kube Controller Manager 的日誌裏,能夠看到 Namespace 的 Controller 在不斷地嘗試一個失敗了的操做,就是清理 tobedeletedb 這個 Namespace 裏「收納」的資源。阿里雲
這裏咱們須要理解一點,就是 Namespace 做爲資源的「收納盒」,實際上是邏輯意義上的概念。它並不像現實中的收納工具,能夠把小的物件收納其中。Namespace 的「收納」其實是一種映射關係。
這一點之因此重要,是由於它直接決定了,刪除 Namespace 內部資源的方法。若是是物理意義上的「收納」,那咱們只須要刪除「收納盒」,裏邊的資源就一併被刪除了。而對於邏輯意義上的關係,咱們則須要羅列全部資源,並刪除那些指向須要刪除的 Namespace 的資源。
怎麼樣羅列集羣中的全部資源呢?這個問題須要從集羣 API 的組織方式提及。K8s 集羣的 API 不是鐵板一塊的,它是用分組和版原本組織的。這樣作的好處顯而易見,就是不一樣分組的 API 能夠獨立迭代,互不影響。常見的分組如 apps,它有 v一、v1beta1 和 v1beta2 三個版本。完整的分組/版本列表,可使用 kubectl api-versions 命令看到。
咱們建立的每個資源,都必然屬於某一個 API 分組/版本。如下邊 Ingress 爲例,咱們指定 Ingress 資源的分組/版本爲 networking.k8s.io/v1beta1。
kind: Ingress metadata: name: test-ingress spec: rules: - http: paths: - path: /testpath backend: serviceName: test servicePort: 80
用一個簡單的示意圖來總結 API 分組和版本。
實際上,集羣有不少 API 分組/版本,每一個 API 分組/版本支持特定的資源類型。咱們經過 yaml 編排資源時,須要指定資源類型 kind,以及 API 分組/版本 apiVersion。而要列出資源,咱們須要獲取 API 分組/版本的列表。
理解了 API 分組/版本的概念以後,再回頭看 Kube Controller Manager 的日誌,就會豁然開朗。顯然 Namespace 的 Controller 在嘗試獲取 API 分組/版本列表,當遇到 metrics.k8s.io/v1beta1 的時候,查詢失敗了。而且查詢失敗的緣由是 "the server is currently unable to handle the request"。
在上一節中,咱們發現 Kube Controller Manager 在獲取 metrics.k8s.io/v1beta1 這個 API 分組/版本的時候失敗了。而這個查詢請求,顯然是發給 API Server 的。因此咱們回到 API Server 日誌,分析 metrics.k8s.io/v1beta1 相關的記錄。在相同的時間點,咱們看到 API Server 也報了一樣的錯誤 "the server is currently unable to handle the request"。
顯然這裏有一個矛盾,就是 API Server 明顯在正常工做,爲何在獲取 metrics.k8s.io/v1beta1 這個 API 分組版本的時候,會返回 Server 不可用呢?爲了回答這個問題,咱們須要理解一下 API Server 的「外掛」機制。
集羣 API Server 有擴展本身的機制,開發者能夠利用這個機制,來實現 API Server 的「外掛」。這個「外掛」的主要功能,就是實現新的 API 分組/版本。API Server 做爲代理,會把相應的 API 調用,轉發給本身的「外掛」。
以 Metrics Server 爲例,它實現了 metrics.k8s.io/v1beta1 這個 API 分組/版本。全部針對這個分組/版本的調用,都會被轉發到 Metrics Server。以下圖,Metrics Server 的實現,主要用到一個服務和一個 pod。
而上圖中最後的 apiservice,則是把「外掛」和 API Server 聯繫起來的機制。下圖能夠看到這個 apiservice 詳細定義。它包括 API 分組/版本,以及實現了 Metrics Server 的服務名。有了這些信息,API Server 就能把針對 metrics.k8s.io/v1beta1 的調用,轉發給 Metrics Server。
通過簡單的測試,咱們發現,這個問題其實是 API server 和 metrics server pod 之間的通訊問題。在阿里雲 K8s 集羣環境裏,API Server 使用的是主機網絡,即 ECS 的網絡,而 Metrics Server 使用的是 Pod 網絡。這二者之間的通訊,依賴於 VPC 路由表的轉發。
以上圖爲例,若是 API Server 運行在 Node A 上,那它的 IP 地址就是 192.168.0.193。假設 Metrics Server 的 IP 是 172.16.1.12,那麼從 API Server 到 Metrics Server 的網絡鏈接,必需要經過 VPC 路由表第二條路由規則的轉發。
檢查集羣 VPC 路由表,發現指向 Metrics Server 所在節點的路由表項缺失,因此 API server 和 Metrics Server 之間的通訊出了問題。
爲了維持集羣 VPC 路由表項的正確性,阿里雲在 Cloud Controller Manager 內部實現了 Route Controller。這個 Controller 在時刻監聽着集羣節點狀態,以及 VPC 路由表狀態。當發現路由表項缺失的時候,它會自動把缺失的路由表項填寫回去。
如今的狀況,顯然和預期不一致,Route Controller 顯然沒有正常工做。這個能夠經過查看 Cloud Controller Manager 日誌來確認。在日誌中,咱們發現,Route Controller 在使用集羣 VPC id 去查找 VPC 實例的時候,沒有辦法獲取到這個實例的信息。
可是集羣還在,ECS 還在,因此 VPC 不可能不在了。這一點咱們能夠經過 VPC id 在 VPC 控制檯確認。那下邊的問題,就是爲何 Cloud Controller Manager 沒有辦法獲取到這個 VPC 的信息呢?
Cloud Controller Manager 獲取 VPC 信息,是經過阿里雲開放 API 來實現的。這基本上等於從雲上一臺 ECS 內部,去獲取一個 VPC 實例的信息,而這須要 ECS 有足夠的權限。目前的常規作法是,給 ECS 服務器授予 RAM 角色,同時給對應的 RAM 角色綁定相應的角色受權。
若是集羣組件,以其所在節點的身份,不能獲取雲資源的信息,那基本上有兩種可能性。一是 ECS 沒有綁定正確的 RAM 角色;二是 RAM 角色綁定的 RAM 角色受權沒有定義正確的受權規則。檢查節點的 RAM 角色,以及 RAM 角色所管理的受權,咱們發現,針對 vpc 的受權策略被改掉了。
當咱們把 Effect 修改爲 Allow 以後,沒多久,全部的 Terminating 狀態的 namespace 所有都消失了。
整體來講,這個問題與 K8s 集羣的 6 個組件有關係,分別是 API Server 及其擴展 Metrics Server,Namespace Controller 和 Route Controller,以及 VPC 路由表和 RAM 角色受權。
經過分析前三個組件的行爲,咱們定位到,集羣網絡問題致使了 API Server 沒法鏈接到 Metrics Server;經過排查後三個組件,咱們發現致使問題的根本緣由是 VPC 路由表被刪除且 RAM 角色受權策略被改動。
K8s 集羣 Namespace 刪除不掉的問題,是線上比較常見的一個問題。這個問題看起來無關痛癢,但實際上不只複雜,並且意味着集羣重要功能的缺失。這篇文章全面分析了一個這樣的問題,其中的排查方法和原理,但願對你們排查相似問題有必定的幫助。
「阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的技術圈。」