天天看點

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

作者 | 聲東 阿裡雲售後技術專家

導讀:相比 K8s 叢集的其他功能,私有鏡像的自動拉取,看起來可能是比較簡單的。而鏡像拉取失敗,大多數情況下都和權限有關。是以,在處理相關問題的時候,我們往往會輕松的說:這問題很簡單,肯定是權限問題。但實際的情況是,我們經常為一個問題,花了多個人的時間卻找不到原因。這主要還是我們對鏡像拉取,特别是私有鏡像自動拉取的原理了解不深。這篇文章,作者将帶領大家讨論下相關原理。

順序上來說,私有鏡像自動拉取會首先通過阿裡雲 Acr credential helper 元件,再經過 K8s 叢集的 API Server 和 kubelet 元件,最後到 docker 容器運作時。但是我的叙述,會從後往前,從最基本的 docker 鏡像拉取說起。

鏡像拉取這件小事

為了讨論友善,我們來設想一個場景。很多人會使用網盤來存放一些檔案,像照片,文檔之類。當我們存取檔案的時候,我們需要給網盤提供賬戶密碼,這樣網盤服務就能驗證我們的身份。這時,我們是檔案資源的所有者,而網盤則扮演着資源伺服器的角色。賬戶密碼作為認證方式,保證隻有我們自己可以存取自己的檔案。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

這個場景足夠簡單,但很快我們就遇到新需求:我們需要使用一個線上制作相冊的應用。按正常的使用流程,我們需要把網盤的照片下載下傳到本地,然後再把照片上傳到電子相冊。這個過程是比較很繁瑣的。我們能想到的優化方法是,讓相冊應用,直接通路網盤來擷取我們的照片,而這需要我們把使用者名和密碼授權給相冊應用使用。

這樣的授權方式,優點顯而易見,但缺點也是很明顯的:我們把網盤的使用者名密碼給了相冊服務,相冊服務就擁有了讀寫網盤的能力,從資料安全角度,這個是很可怕的。其實這是很多應用都會遇到的一個一般性場景。私有鏡像拉取其實也是這個場景。這裡的鏡像倉庫,就跟網盤一樣,是資源伺服器,而容器叢集則是三方服務,它需要通路鏡像倉庫擷取鏡像。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

了解 OAuth 2.0 協定

OAuth 協定是為了解決上述問題而設計的一種标準方案,我們的讨論針對 2.0 版本。相比把賬戶密碼直接給三方應用,此協定采用了一種間接的方式來達到同樣的目的。如下圖,這個協定包括六個步驟,分别是三方應用擷取使用者授權,三方應用擷取臨時 Token 以及三方應用存取資源。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

這六步了解起來不容易,主要是因為安全協定的設計,需要考慮協定的易證明性,是以我們換一種方式來解釋這個協定。簡單來說,這個協定其實就做了兩件事情:

  • 在使用者授權的情況下,三方應用擷取 token 所表示的臨時通路權限;
  • 然後三方應用使用這個 token 去擷取資源。

如果用網盤的例子來說明的話,那就是使用者授權網盤服務給相冊應用建立臨時 token,然後相冊應用使用這個 token 去網盤服務擷取使用者的照片。實際上 OAuth 2.0 各個變種的核心差别,在于第一件事情,就是使用者授權資源伺服器的方式。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結
  1. 最簡單的一種,适用于三方應用本身就擁有被通路資源控制權限的情況。這種情況下,三方應用隻需要用自己的賬戶密碼登入資源伺服器并申請臨時 token 即可;
  2. 當使用者對三方應用足夠信任的情況下,使用者直接把賬戶密碼給三方應用,三方應用使用賬戶密碼向資源伺服器申請臨時 token;
  3. 使用者通過資源伺服器提供的接口,登入資源伺服器并授權資源伺服器給三方應用發放 token;
  4. 完整實作 OAuth 2.0 協定,也是最安全的。三方應用首先擷取以驗證碼表示的使用者授權,然後用此驗證碼從資源伺服器換取臨時 token,最後使用 token 存取資源。

從上面的描述我們可以看到,資源伺服器實際上扮演了鑒權和資源管理兩種角色,這兩者分開實作的話,協定流程會變成下圖這樣。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

Docker 扮演的角色

大圖

鏡像倉庫 Registry 的實作,目前使用“把賬戶密碼給三方應用”的方式。即假設使用者對 Docker 足夠信任,使用者直接将賬戶密碼交給 Docker,然後 Docker 使用賬戶密碼跟鑒權伺服器申請臨時 token。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

了解 docker login

首先,我們在拉取私有鏡像之前,要使用 docker login 指令來登入鏡像倉庫。這裡的登入其實并沒有和鏡像倉庫建立什麼會話之類的關系。登入主要就做了三件事情:

  • 第一件事情是跟使用者要賬戶密碼。

如下圖,當執行登入指令,這個命會提示輸入賬戶密碼,這件事情對應的是大圖的第一步。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結
  • 第二件事情,docker 通路鏡像倉庫的 https 位址,并通過挑戰 v2 接口來确認,接口是否會傳回 Docker-Distribution-Api-Version 頭字段。

這件事情在協定圖中沒有對應的步驟。它的作用跟 ping 差不多,隻是确認下 v2 鏡像倉庫是否線上,以及版本是否比對。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結
  • 第三件事情,docker 使用使用者提供的賬戶密碼,通路 Www-Authenticate 頭字段傳回的鑒權伺服器的位址 Bearer realm。

如果這個通路成功,則鑒權伺服器會傳回 jwt 格式的 token 給 docker,然後 docker 會把賬戶密碼編碼并儲存在使用者目錄的 .docker/docker.json 檔案裡。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

下圖是我登入倉庫之後的 docker.json 檔案。這個檔案作為 docker 登入倉庫的唯一證據,在後續鏡像倉庫操作中,會被不斷的讀取并使用。其中關鍵資訊 auth 就是賬戶密碼的 base64 編碼。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

拉取鏡像是怎麼回事

鏡像一般會包括兩部分内容,一個是 manifests 檔案,這個檔案定義了鏡像的中繼資料,另一個是鏡像層,是實際的鏡像分層檔案。鏡像拉取基本上是圍繞這兩部分内容展開。因為我們這篇文章的重點是權限問題,是以我們這裡隻以 manifests 檔案拉取為例。

拉取 manifests 檔案,基本上也會做三件事情:

  • 首先,docker 直接通路鏡像manifests的位址,以便擷取 Www-Authenticate 頭字段。這個字段包括鑒權伺服器的位址 Bearer realm,鏡像服務位址 service,以及定義了鏡像和操作的 scope。
K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結
  • 接着,docker 通路上邊拿到的 Bearer realm 位址來鑒權,以及在鑒權之後擷取一個臨時的 token。這對應協定大圖使用賬戶密碼擷取臨時 token 這一步,使用的賬戶密碼直接讀取自 docker.json 檔案。
K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結
  • 最後,使用上邊的 token,以 Authorization 頭字段的方式,來下載下傳 manifests 檔案。這對應的是協定大圖下載下傳鏡像這一步。當然因為鏡像還有分層檔案,是以實際 docker 還會用這個臨時 token 多次下載下傳檔案才能完整鏡像下載下傳。
K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

K8s 實作的私有鏡像自動拉取

基本功能

K8s 叢集一般會管理多個節點,每個節點都有自己的 docker 環境。如果讓使用者分别到叢集節點上登入鏡像倉庫,這顯然是很不友善的。為了解決這個問題,K8s 實作了自動拉取鏡像的功能。這個功能的核心,是把 docker.json 内容編碼,并以 Secret 的方式作為 Pod 定義的一部分傳給 Kubelet。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

具體來說,步驟如下:

  1. 建立 secret。這個 secret 的 .dockerconfigjson 資料項包括了一份 base64 編碼的 docker.json 檔案;
  2. 建立 pod,且 pod 編排中 imagePullSecrets 指向第一步建立的 secret;
  3. Kubelet 作為叢集控制器,監控着叢集的變化。當它發現新的 pod 被建立,就會通過 API Server 擷取 pod 的定義,這包括 imagePullSecrets 引用的 secret;
  4. Kubelet 調用 docker 建立容器且把 .dockerconfigjson 傳給 docker;
  5. 最後 docker 使用解碼出來的賬戶密碼拉取鏡像,這和上一節的方法一緻。

進階方式

上邊的功能,一定程度上解決了叢集節點登入鏡像倉庫不友善的問題。但是我們在建立 Pod 的時候,仍然需要給 Pod 指定 imagePullSecrets。K8s 通過變更準入控制(Mutating Admission Control)進一步優化了上邊的基本功能。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

進一步優化的内容如下:

  1. 在第一步建立 secret 之後,添加 default service account 對 imagePullSecrets 的引用;
  2. Pod 預設使用 default service account,而 service account 變更準入控制器會在 default service account 引用 imagePullSecrets 的情況下,添加 imagePullSecrets 配置到 pod 的編排裡。

阿裡雲實作的 Acr credential helper

阿裡雲容器服務團隊,在 K8s 的基礎上實作了控制器 Acr credential helper。這個控制器可以讓同時使用阿裡雲 K8s 叢集和容器鏡像服務産品的使用者,在不用配置自己賬戶密碼的情況下,自動使用私有倉庫中的容器鏡像。

K8s 從懵圈到熟練 – 鏡像拉取這件小事鏡像拉取這件小事了解 OAuth 2.0 協定Docker 扮演的角色K8s 實作的私有鏡像自動拉取阿裡雲實作的 Acr credential helper總結

具體來說,控制器會監聽 acr-configuration 這個 configmap 的變化,其主要關心 acr-registry 和 watch-namespace 這兩個配置。前一個配置指定為臨時賬戶授權的鏡像倉庫位址,後一個配置管理可以自動拉取鏡像的命名空間。當控制器發現有命名空間需要被配置卻沒有被配置的時候,它會通過阿裡雲容器鏡像服務的 API,來擷取臨時賬戶和密碼。

有了臨時賬戶密碼,Acr credential helper 為命名空間建立對應的 Secret 以及更改 default SA 來引用這個 Secret。這樣,控制器和 K8s 叢集本身的功能,一起自動化了阿裡雲 K8s 叢集拉取阿裡雲容器鏡像服務上的鏡像的全部流程。

總結

了解私有鏡像自動拉取的實作,有一個難點和一個重點。

  • 難點是 OAuth 2.0 安全協定的原理,上文主要分析了為什麼 OAuth 會這麼設計;
  • 重點是叢集控制器原理,因為整個自動化的過程,實際上是包括 Admission control 和 Acr credential helper 在内的多個控制器協作的結果。
“ 阿裡巴巴雲原生微信公衆号(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術公衆号。”