這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
提到registry v2,主要改進是支援並行pull鏡像,鏡像層id變成唯一的,解決同一個tag可能對應多個鏡像的問題等等。如果還不太瞭解,可以且聽我細細道來。
首先不得不說的是v2 新加了一個概念Digest
他是基於內容進行定址(Content-addressable)演算法算出來的一串hash值。簡單的說就是內容不同,得出了的digest值是不同的,但是內容相同的話,得出的digest值是一定相同的。我們的每個鏡像層id就是根據每個鏡像層的內容得出來的digest的。
所以你在改動鏡像層以後產生的digest就不同了,而不動的話,這個digest還是不變的,那麼這個digest id是什麼時候產生的呢?我們在本地構建鏡像時產生的鏡像層id每次都是不一樣的,這個digest是我們在push鏡像時產生的。
為了驗證內容相同,push到registry得到的digest相同,我做了個小實驗,用如下Dockerfile 注釋掉第三行和不注釋構建了兩次鏡像,再push到registry
如果是v1的話,push上去得到的層id肯定是不一樣的,但是v2裡面,注釋第三行得到了5個鏡像層,不注釋掉第三行得到了6個鏡像層,並且第一次的5個層都包含在第二次6個裡面。所以得出結論這個digest確實是根據內容產生的。
接下來說一下鏡像的id
鏡像id也是產生了一個digest值,鏡像id是根據_manifest這個檔案,也就是鏡像層id和鏡像名字等一些其他資訊產生的digest。我們在每次向v2 push鏡像時候,最後都會返回給docker client一個digest值,這個值就代表了鏡像的digest id。這個id的作用就是可以指定唯一的鏡像了。類似tag使用。
因為我們知道v1時候用tag有個弊病就是多次構建的鏡像可以使用同一個tag,導致我們用tag標識鏡像的時候可能並不是我們想要的,而用了digest就不會出現這種問題。
我們在寫Dockerfile的時候引用鏡像就可以這麼用:
FROM localhost:5000/test@sha256:ac81211548c0d228e10daaf76f6e0024e5f91879c8a7e105e777d6f964270449
像使用tag一樣,用本地docker查看鏡像digests時候可以使用:docker images --digests,
當然,目前來說你看到的都是<none>,我們需要從registry上pull下來,使用
docker pull localhost:5000/test@sha256:ac81211548c0d228e10daaf76f6e0024e5f91879c8a7e105e777d6f964270449
瞭解了digest,我們來看一看registry的儲存結構
這部分最好可以對著registry的檔案夾結構來看。簡單的畫了個草圖。
是v2的檔案夾層級關係
這個是目錄下具體的樣子,可以先看一會兒,可以看到registry 下面有兩個檔案夾blobs和repositories,
blobs下面儲存了registry的所有基本資料元素,包括鏡像層digest和鏡像digest,之後在通過某種方式將調用這裡的資訊。
blobs檔案夾下面先分了一個等級,是digest的前兩位字元組成的檔案夾為了篩選digest,避免了這個檔案夾太大,查看時都難。這樣方便定位。每個blob裡都有一個data檔案,儲存相關資訊。
repositories下面儲存的是各個鏡像名字命名的檔案夾,進入一個鏡像檔案夾後,可以看到三個子檔案夾,_layers, _manifests, _uploads,_layers這個檔案夾是跟這個鏡像有關的所有鏡像層,進入其中一個鏡像層檔案夾,下面只有一個檔案--link命名的檔案,裡面的內容就是一個digest。
這就跟blobs下面的鏡像層建立起了聯絡,在repositories這個檔案夾下,都是通過link檔案與blobs檔案夾下的檔案建立的聯絡。
_upload這個檔案夾,平時點進去是空的,這個檔案夾主要作用是上傳用的。我們上傳鏡像層的時候,先上傳到這個檔案夾下,等完成傳輸以後,在將這個檔案夾下的內容移動到blobs下面,然後將原來的檔案刪除。
_manifest這個檔案夾非常重要,是鏡像的相關資訊。他下面有兩個子檔案夾,revisions和tags,revisions這個檔案夾下是這個鏡像的所有可用的鏡像digest,裡面的link檔案指向了鏡像的digest。我們去blobs裡面找相應的id對應的檔案,查看檔案下面的data,我們發現這個data檔案裡面儲存的資訊,和我們通過registry v2 rest api請求manifest資訊相同~在看_manifest/tags/。下面就是這個鏡像的不同tag了。又分了current和index分別表示當前tag對應的digest和此tag下的所有鏡像digest。
下面來介紹一下如何搭建token驗證的registry
先看一下官方給的圖
可以看到,v2和v1相比,訪問形式完全變了,去掉了index server,加上了一個auth server。
訪問順序是這樣的
我們通過docker client讓docker deamon先訪問registry,registry如果不需要身分識別驗證,則直接返回結果,若需要驗證,返回401並在header附帶一些資訊,
daemon根據資訊訪問auth server。auth server判斷通過了驗證,並返回給daemon一串token。
daemon帶著這串token再去訪問registry則可以獲得到資訊,pull ,push,api都走的這套流程。
接下來貼下我的設定檔config.yml,配置了選項auth/token表示要token驗證。這個流程確實需要好好讀一下,跟她的加密方式有很大關係
version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: redis
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
secret: randomstringsecret
tls:
certificate: /root/sslkeys/domain.crt
key: /root/sslkeys/domain.key
auth:
token:
realm: https://registry.tenxcloud.com:5001/auth
service: test123.tenxcloud.com:5000
issuer: qwertyui
rootcertbundle: /root/sslkeys/domain.crt
當然,我是通過容器啟動的v2,把這個config volume進去的
我的啟動命令:docker run -d -p 5000:5000 --restart=always --name registry -v pwd
/sslkeys:/root/sslkeys -v pwd
/config.yml:/etc/docker/registry/config.yml -v pwd
/data:/var/lib/registry registry:2.1.1
auth/token下的4個子選項都是必須配置的,realm表示我的auth server地址,service表示的registry的地址,issuer是一串標示符,隨便寫一下,auth server加密的時候也需要配置同樣的字串。rootcertbundle配置一個秘鑰,對token進行加密。(我這裡複用了我的tls token)
當我們第一次未帶token訪問registry時會返回401並附帶如下資訊:Www-Authenticate: Bearer realm="test123.tenxcloud.com:5000 ",service="test123.tenxcloud.com:5000 "scope="repository:husseingalal/hello:push",realm和service就是前面說的,scope表示的是我要做的操作,repository代表鏡像,接著是鏡像名字,然後是pull或者是push或者二者都有
知道了各個參數的功能,就可以搭建我們的auth server了,我從github下找到了一個項目:https://github.com/cesanta/docker_auth
用go語言寫的,其中的帳號密碼儲存方式有:寫入檔案,ldap和google帳號的。這個server實現了動態載入設定檔,設定檔有變化這個server會進行安全的重啟,所以可以對檔案動態添加帳號密碼。當然也可以自己寫身分識別驗證,添加資料庫等方式的,只需要繼承Authenticator這個interface就可以。添加起來很容易。身分識別驗證後有許可權控制acl,並且我們也可以自訂許可權控制,繼承Authorizer這個interface即可。
前兩項通過後就是產生token(這一部分這個項目已經封裝好了,不用改動),
v2的token採用的JWT加密方式,JWT分了三個部分,header,payload,signature,header裡面帶的資訊是token加密方式,一般都是base64, payload裡帶的都是需要的基本資料,user,許可權,到期時間,還有前面說的issure 等等,
signature是header+payload後用秘鑰進行加密,這個秘鑰就是配置在registry裡的rootcertbundle對應的秘鑰。
三部分通過 . 串連, 因為有秘鑰加密,保證了token資訊不會被竄改,這種加密方式保證了將許可權驗證和registry分離也 是安全的, 將token發送回daemon後daemon就可以帶著token去正常訪問registry了,如果是rest api,直接將其寫成Bearer token就可以請求了。
本文作者:時速雲工程師丁麒偉 原文連結:registry v2 解析以及如何?token驗證