K8s 從懵圈到熟練 – 鏡像拉取這件小事

做者 | 聲東 阿里雲售後技術專家docker

導讀:相比 K8s 集羣的其餘功能,私有鏡像的自動拉取,看起來多是比較簡單的。而鏡像拉取失敗,大多數狀況下都和權限有關。因此,在處理相關問題的時候,咱們每每會輕鬆的說:這問題很簡單,確定是權限問題。但實際的狀況是,咱們常常爲一個問題,花了多我的的時間卻找不到緣由。這主要仍是咱們對鏡像拉取,特別是私有鏡像自動拉取的原理理解不深。這篇文章,做者將帶領你們討論下相關原理。json

順序上來講,私有鏡像自動拉取會首先經過阿里雲 Acr credential helper 組件,再通過 K8s 集羣的 API Server 和 kubelet 組件,最後到 docker 容器運行時。可是個人敘述,會從後往前,從最基本的 docker 鏡像拉取提及。安全

鏡像拉取這件小事

爲了討論方便,咱們來設想一個場景。不少人會使用網盤來存放一些文件,像照片,文檔之類。當咱們存取文件的時候,咱們須要給網盤提供帳戶密碼,這樣網盤服務就能驗證咱們的身份。這時,咱們是文件資源的全部者,而網盤則扮演着資源服務器的角色。帳戶密碼做爲認證方式,保證只有咱們本身能夠存取本身的文件。服務器

1

這個場景足夠簡單,但很快咱們就遇到新需求:咱們須要使用一個在線製做相冊的應用。按正常的使用流程,咱們須要把網盤的照片下載到本地,而後再把照片上傳到電子相冊。這個過程是比較很繁瑣的。咱們能想到的優化方法是,讓相冊應用,直接訪問網盤來獲取咱們的照片,而這須要咱們把用戶名和密碼受權給相冊應用使用。微信

這樣的受權方式,優勢顯而易見,但缺點也是很明顯的:咱們把網盤的用戶名密碼給了相冊服務,相冊服務就擁有了讀寫網盤的能力,從數據安全角度,這個是很可怕的。其實這是不少應用都會遇到的一個通常性場景。私有鏡像拉取其實也是這個場景。這裏的鏡像倉庫,就跟網盤同樣,是資源服務器,而容器集羣則是三方服務,它須要訪問鏡像倉庫獲取鏡像。less

2

理解 OAuth 2.0 協議

OAuth 協議是爲了解決上述問題而設計的一種標準方案,咱們的討論針對 2.0 版本。相比把帳戶密碼直接給三方應用,此協議採用了一種間接的方式來達到一樣的目的。以下圖,這個協議包括六個步驟,分別是三方應用獲取用戶受權,三方應用獲取臨時 Token 以及三方應用存取資源。微服務

3

這六步理解起來不容易,主要是由於安全協議的設計,須要考慮協議的易證實性,因此咱們換一種方式來解釋這個協議。簡單來講,這個協議其實就作了兩件事情:優化

  • 在用戶受權的狀況下,三方應用獲取 token 所表示的臨時訪問權限;
  • 而後三方應用使用這個 token 去獲取資源。

若是用網盤的例子來講明的話,那就是用戶受權網盤服務給相冊應用建立臨時 token,而後相冊應用使用這個 token 去網盤服務獲取用戶的照片。實際上 OAuth 2.0 各個變種的核心差異,在於第一件事情,就是用戶受權資源服務器的方式。阿里雲

4

  1. 最簡單的一種,適用於三方應用自己就擁有被訪問資源控制權限的狀況。這種狀況下,三方應用只須要用本身的帳戶密碼登陸資源服務器並申請臨時 token 便可;
  2. 當用戶對三方應用足夠信任的狀況下,用戶直接把帳戶密碼給三方應用,三方應用使用帳戶密碼向資源服務器申請臨時 token;
  3. 用戶經過資源服務器提供的接口,登陸資源服務器並受權資源服務器給三方應用發放 token;
  4. 完整實現 OAuth 2.0 協議,也是最安全的。三方應用首先獲取以驗證碼錶示的用戶受權,而後用此驗證碼從資源服務器換取臨時 token,最後使用 token 存取資源。

從上面的描述咱們能夠看到,資源服務器實際上扮演了鑑權和資源管理兩種角色,這二者分開實現的話,協議流程會變成下圖這樣。編碼

5

Docker 扮演的角色

大圖

鏡像倉庫 Registry 的實現,目前使用「把帳戶密碼給三方應用」的方式。即假設用戶對 Docker 足夠信任,用戶直接將帳戶密碼交給 Docker,而後 Docker 使用帳戶密碼跟鑑權服務器申請臨時 token。

6

理解 docker login

首先,咱們在拉取私有鏡像以前,要使用 docker login 命令來登陸鏡像倉庫。這裏的登陸其實並無和鏡像倉庫創建什麼會話之類的關係。登陸主要就作了三件事情:

  • 第一件事情是跟用戶要帳戶密碼。

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

7

  • 第二件事情,docker 訪問鏡像倉庫的 https 地址,並經過挑戰 v2 接口來確認,接口是否會返回 Docker-Distribution-Api-Version 頭字段。

這件事情在協議圖中沒有對應的步驟。它的做用跟 ping 差很少,只是確認下 v2 鏡像倉庫是否在線,以及版本是否匹配。

8

  • 第三件事情,docker 使用用戶提供的帳戶密碼,訪問 Www-Authenticate 頭字段返回的鑑權服務器的地址 Bearer realm。

若是這個訪問成功,則鑑權服務器會返回 jwt 格式的 token 給 docker,而後 docker 會把帳戶密碼編碼並保存在用戶目錄的 .docker/docker.json 文件裏。

9

下圖是我登陸倉庫以後的 docker.json 文件。這個文件做爲 docker 登陸倉庫的惟一證據,在後續鏡像倉庫操做中,會被不斷的讀取並使用。其中關鍵信息 auth 就是帳戶密碼的 base64 編碼。

10

拉取鏡像是怎麼回事

鏡像通常會包括兩部份內容,一個是 manifests 文件,這個文件定義了鏡像的元數據,另外一個是鏡像層,是實際的鏡像分層文件。鏡像拉取基本上是圍繞這兩部份內容展開。由於咱們這篇文章的重點是權限問題,因此咱們這裏只以 manifests 文件拉取爲例。

拉取 manifests 文件,基本上也會作三件事情:

  • 首先,docker 直接訪問鏡像manifests的地址,以便獲取 Www-Authenticate 頭字段。這個字段包括鑑權服務器的地址 Bearer realm,鏡像服務地址 service,以及定義了鏡像和操做的 scope。

11

  • 接着,docker 訪問上邊拿到的 Bearer realm 地址來鑑權,以及在鑑權以後獲取一個臨時的 token。這對應協議大圖使用帳戶密碼獲取臨時 token 這一步,使用的帳戶密碼直接讀取自 docker.json 文件。

12

  • 最後,使用上邊的 token,以 Authorization 頭字段的方式,來下載 manifests 文件。這對應的是協議大圖下載鏡像這一步。固然由於鏡像還有分層文件,因此實際 docker 還會用這個臨時 token 屢次下載文件才能完整鏡像下載。

13

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

基本功能

K8s 集羣通常會管理多個節點,每一個節點都有本身的 docker 環境。若是讓用戶分別到集羣節點上登陸鏡像倉庫,這顯然是很不方便的。爲了解決這個問題,K8s 實現了自動拉取鏡像的功能。這個功能的核心,是把 docker.json 內容編碼,並以 Secret 的方式做爲 Pod 定義的一部分傳給 Kubelet。

14

具體來講,步驟以下:

  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)進一步優化了上邊的基本功能。

15

進一步優化的內容以下:

  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 集羣和容器鏡像服務產品的用戶,在不用配置本身帳戶密碼的狀況下,自動使用私有倉庫中的容器鏡像。

16

具體來講,控制器會監聽 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等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的技術公衆號。」

相關文章
相關標籤/搜索