天天看點

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

作者:shengdong

今天我們讨論的這個問題,跟K8S叢集的命名空間有關。命名空間是K8S叢集資源的“收納”機制。我們可以把相關的資源,“收納”到同一個命名空間裡,以避免不相關資源之間不必要的影響。

命名空間本身也是一種資源。通過叢集API Server入口,我們可以建立命名空間,而對于不再使用的命名空間,我們需要清理掉。命名空間的Controller會通過API Server,監視叢集中命名空間的變化,然後根據變化來執行預先定義的動作。

有時候,我們會遇到下圖中的問題,即命名空間的狀态被标記成了“Terminating”,但卻沒有辦法被完全删除。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

從叢集入口開始

因為删除操作是通過叢集API Server來執行的,是以我們要分析API Server的行為。跟大多數叢集元件類似,API Server提供了不同級别的日志輸出。為了了解API Server的行為,我們将日志級别調整到最進階。然後,通過建立删除tobedeletedb這個命名空間來重制問題。

但可惜的是,API Server并沒有輸出太多和這個問題有關的日志。

相關的日志,可以分為兩部分。一部分是命名空間被删除的記錄,記錄顯示用戶端工具是kubectl,以及發起操作的源IP位址是192.168.0.41,這符合預期;另外一部分是Kube Controller Manager在重複的擷取這個命名空間的資訊。

Kube Controller Manager實作了叢集中大多數的Controller,它在重複擷取tobedeletedb的資訊,基本上可以判斷,是命名空間的Controller在擷取這個命名空間的資訊。

Controller在做什麼?

和上一節類似,我們通過開啟Kube Controller Manager最進階别日志,來研究這個元件的行為。在Kube Controller Manager的日志裡,可以看到命名空間的Controller在不斷地嘗試一個失敗了的操作,就是清理tobedeletedb這個命名空間裡“收納”的資源。

怎麼樣删除“收納盒”裡的資源

這裡我們需要了解一點,就是命名空間作為資源的“收納盒”,其實是邏輯意義上的概念。它并不像現實中的收納工具,可以把小的物件收納其中。命名空間的“收納”實際上是一種映射關系。

這一點之是以重要,是因為它直接決定了,删除命名空間内部資源的方法。如果是實體意義上的“收納”,那我們隻需要删除“收納盒”,裡邊的資源就一并被删除了。而對于邏輯意義上的關系,我們則需要羅列所有資源,并删除那些指向需要删除的命名空間的資源。

API、Group、Version

怎麼樣羅列叢集中的所有資源呢,這個問題需要從叢集API的組織方式說起。K8S叢集的API不是鐵闆一塊的,它是用分組和版本來組織的。這樣做的好處顯而易見,就是不同分組的API可以獨立的疊代,互不影響。常見的分組如apps,它有v1,v1beta1和v1beta2這三個版本。完整的分組/版本清單,可以使用kubectl api-versions指令看到。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

我們建立的每一個資源,都必然屬于某一個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分組和版本。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

實際上,叢集有很多API分組/版本,每個API分組/版本支援特定的資源類型。我們通過yaml編排資源時,需要指定資源類型kind,以及API分組/版本apiVersion。而要列出資源,我們需要擷取API分組/版本的清單。

Controller為什麼不能删除命名空間裡的資源

了解了API分組/版本的概念之後,再回頭看Kube Controller Manager的日志,就會豁然開朗。顯然命名空間的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”。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

顯然這裡有一個沖突,就是API Server明顯在正常工作,為什麼在擷取metrics.k8s.io/v1beta1這個API分組版本的時候,會傳回Server不可用呢?為了回答這個問題,我們需要了解一下API Server的“外挂”機制。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

叢集API Server有擴充自己的機制,開發者可以利用這個機制,來實作API Server的“外挂”。這個“外挂”的主要功能,就是實作新的API分組/版本。API Server作為代理,會把相應的API調用,轉發給自己的“外挂”。

以Metrics Server為例,它實作了metrics.k8s.io/v1beta1這個API分組/版本。所有針對這個分組/版本的調用,都會被轉發到Metrics Server。如下圖,Metrics Server的實作,主要用到一個服務和一個pod。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

而上圖中最後的apiservice,則是把“外挂”和API Server聯系起來的機制。下圖可以看到這個apiservice詳細定義。它包括API分組/版本,以及實作了Metrics Server的服務名。有了這些資訊,API Server就能把針對metrics.k8s.io/v1beta1的調用,轉發給Metrics Server。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

節點與Pod之間的通信

經過簡單的測試,我們發現,這個問題實際上是API server和metrics server pod之間的通信問題。在阿裡雲K8S叢集環境裡,API Server使用的是主機網絡,即ECS的網絡,而Metrics Server使用的是Pod網絡。這兩者之間的通信,依賴于VPC路由表的轉發。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

以上圖為例,如果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之間的通信出了問題。

Route Controller為什麼不工作?

為了維持叢集VPC路由表項的正确性,阿裡雲在Cloud Controller Manager内部實作了Route Controller。這個Controller在時刻監聽着叢集節點狀态,以及VPC路由表狀态。當發現路由表項缺失的時候,它會自動把缺失的路由表項填寫回去。

現在的情況,顯然和預期不一緻,Route Controller顯然沒有正常工作。這個可以通過檢視Cloud Controller Manager日志來确認。在日志中,我們發現,Route Controller在使用叢集VPC id去查找VPC執行個體的時候,沒有辦法擷取到這個執行個體的資訊。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

但是叢集還在,ECS還在,是以VPC不可能不在了。這一點我們可以通過VPC id在VPC控制台确認。那下邊的問題,就是為什麼Cloud Controller Manager沒有辦法擷取到這個VPC的資訊呢?

叢集節點通路雲資源

Cloud Controller Manager擷取VPC資訊,是通過阿裡雲開放API來實作的。這基本上等于,從雲上一台ECS内部,去擷取一個VPC執行個體的資訊,而這需要ECS有足夠的權限。目前的正常做法是,給ECS伺服器授予RAM角色,同時給對應的RAM角色綁定相應的角色授權。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

如果叢集元件,以其所在節點的身份,不能擷取雲資源的資訊,那基本上有兩種可能性。一是ECS沒有綁定正确的RAM角色;二是RAM角色綁定的RAM角色授權沒有定義正确的授權規則。檢查節點的RAM角色,以及RAM角色所管理的授權,我們發現,針對vpc的授權政策被改掉了。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

當我們把Effect修改成Allow之後,沒多久,所有的Terminating狀态的namespace全部都消失了。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

問題大圖

總體來說,這個問題與K8S叢集的6個元件有關系,分别是API Server及其擴充Metrics Server,Namespace Controller和Route Controller,以及VPC路由表和RAM角色授權。

K8S從懵圈到熟練 - 我們為什麼會删除不了叢集的命名空間?

通過分析前三個元件的行為,我們定位到,叢集網絡問題導緻了API Server無法連接配接到Metrics Server;通過排查後三個元件,我們發現導緻問題的根本原因是VPC路由表被删除且RAM角色授權政策被改動。

後記

K8S叢集命名空間删除不掉的問題,是線上比較常見的一個問題。這個問題看起來無關痛癢,但實際上不僅複雜,而且意味着叢集重要功能的缺失。這篇文章全面分析了一個這樣的問題,其中的排查方法和原理,希望對大家排查類似問題有一定的幫助。

繼續閱讀