Docker Registry服務中的兩個重要組件,分別承擔如下不一樣的職責:git
Index:主要負責集中管理用戶帳戶,訪問權限,鏡像的校驗和 以及區分公有和私有repos(也就是公共的命名空間)等,一般是以公開的web接口來實現。web
Registry:主要負責鏡像數據的存儲,提供Pull和Push鏡像的功能,以及委派受權部分給index實現.算法
當你用docker push/pull的時候,index肯定你是否有使用/修改鏡像的權限,確認以後,registry只是負責把鏡像存好或把鏡像經過某種方式發送給你。並且,index還能指出某個特定的鏡像具體存放的registry地址並向它發出請求。除此以外,當你只是在本地運行docker images這樣的命令時,並不須要依賴和Index /registry進行交互。docker
Docker Hub 是Docker公司提供的雲平臺服務,用來分發應用:包括容器鏡像分發,變動管理,用戶/團隊管理,自動化生命週期工做流。實際上,Docker Hub包括了Index模塊功能上的實現。ubuntu
Docker Hub提供的服務主要包括:後端
>1. 託管docker鏡像 >2. 用戶受權 >3. 自動構建鏡像,以及像Build triggers、Web Hooks這樣的工做流工具。 >4. 和Github &Bitbucket集成
Docker Trusted Registry是 Docker Hub 企業版的新版本,DTR 提供了運行和管理私有鏡像的存儲服務,讓你在公司內部能夠安全的分發、交付管理本身定製的各類鏡像。一樣提供了對Registry集羣的各類性能上的監控。安全
Registry自己是一個無狀態,高可擴展性的服務端應用,用來存儲和分發docker鏡像。常常被用來與subversion,git等作類比。服務器
它實現了:併發
* 以分層的方式來存儲的鏡像以及它的描述信息 * 實現與docker client一致的API * 以web應用的方式,簡單實現一個web服務器讓鏡像可用。
說白了,Registry是用來存儲和內容分發的系統,託管已經命名而且存在不一樣tag的Docker鏡像。上層封裝了http接口的服務,底層存儲委託給Driver,也就是在對象存儲上封裝了一層Repo的鏡像graph關係,抽象成接口調用。默認的存儲驅動是本地POSIX文件系統。app
以鬆耦合的方式把Web UI,用戶認證,鏡像元數據獨立出來,給想作Private Registry服務的開發者本身實現。而Registry是全部模塊的複用部分,無狀態,單純用於鏡像服務。因爲Registry無狀態,因此能夠水平擴展。
先引入v2版本的一些新的概念。
衆所周知,鏡像在底層是分層存儲的。所謂的內容可尋址,是指鏡像的層被看成能夠根據digest來劃分鏡像的blobs。那麼什麼又是digest ?什麼是blobs?
實際上,digest是內容惟一的標識符。在內容可尋址系統中,把Digest類型設計成一種靈活的標識符。類比與CRC,md5等,這裏的digest是根據密碼學中哈希算法生成的校驗值。在Registry v2中,使用SHA256算法來生成每一個鏡像層對應的digest。這種方式易於實現和校驗。
digest的格式是一個簡單的字符串,由兩部分組成。
<algorithm>:<digest>
例如 :
sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
blob能夠簡單理解成鏡像層+ JSON數據等構成的一個鏡像數據塊。它是存儲在Registry後端文件系統中,由digest惟一標識的鏡像數據。
對於docker-registry中,鏡像id是須要確保它的保密性的,由於這個ID是全局性的,不一樣的鏡像倉庫中該鏡像的id是一致的。這樣無疑方便了鏡像管理,但同時也存在必定的風險。即,能夠經過images id來獲取鏡像的詳細信息。而鏡像的layer IDs是被隨機分配的,也就是說不能經過layer id來分享images,由於在registry下鏡像的layer id可能不一樣。同時存在着校驗的複雜性。
每一層layer都有一個對應的JSON對象來對鏡像層進行描述,以下圖:
在新版本distribution中,鏡像層是能夠被直接獲取的,而且支持併發的方式,提升了傳輸效率。這也就是所謂的鏡像是內容可尋址的。鏡像的id也再也不須要保證它的保密性,也更加安全,緣由以前講過再也不贅述。具體命令以下:
docker pull ubuntu@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
鏡像的digest以JSON的格式存放在manifest中,若是該層的數據有任何的改動,它的manifest中的digest也會改變。與v1版本的區別在於將全部鏡像層的描述放在一個JSON Object中,也就是manifest中。以下圖:
一言以蔽之,新的鏡像描述文件簡化了鏡像的定義和提高了安全性,利用這些新特性提高registry的性能,減小了帶寬和後端崩潰的可能性。
v2版本的工做流較v1版本更爲簡單,省略了在認證auth token時的往返鑑權方式。 v1版本在部署的時候能夠選擇鑑權的模式,standalone \ index_endpoint \ disable_token_auth. 若是使用disable_token_auth, 你須要本身提供權限認證的方式.(Basic auth, Oauth等)
經過以上的工做流分析,咱們能夠清晰得看到兩版本的認證方式的變化。 固然,Docker Registry能以無認證檢查的方式跑在standalone模式。可是咱們能夠在Client和Registry中架設認證受權服務,基於原生的公鑰鑑權機制增長HTTP認證方式,來提供更好的權限控制。這個認證受權服務,在兩版本中均由index中的認證服務來提供。
不過,在以前咱們先明確認證受權服務的具體需求:
認證服務器 爲Docker Registry中的Repo充當管理員的角色,經過 使用服務帳戶和公鑰 來受權和管理權限的方式,來管理 用戶帳戶和密鑰、 集中權限控制表 。
v1版本中,用戶在client端發出請求,會先去index服務上作認證,找到鏡像所在的registry地址並返回給client端。最後,client端再從registry下載鏡像,固然下載以前,registry會先去index校驗client發來請求中token的合法性。不一樣的鏡像能夠保存在不一樣的registry服務上,其索引信息都放在index服務上。
v2版本中,認證過程其實包括四個階段。尋找認證服務器、請求token、返回token、驗證token。
client端先向Registry發出請求。若是是未受權的請求,會返回401響應。而這個響應中包括三個信息,realm, scope, service 具體表明 用戶可使用scope和 service的信息向realm發請求。舉個例子:
realm="https://auth.docker.com/v2/token/",service="registry.docker.com",scope="repository:samalba/my-app:push"
以後客戶端會知道,在頭部WWW-Authenticate中 加入service 和 scope的值,向 URL https://auth.docker.com/v2/token/ 發送GET請求,來獲取token。
這裏分別舉例解釋一下遇到的參數。
參數 | 描述 | 實例 |
---|---|---|
service | 是指Registry服務所在的域名 | service="registry.docker.com" |
realm | 申請受權的URL請求地址 | realm="https://auth.docker.com/v2/token/" |
scope | 請求資源的具體內容 | scope=repository:samalba/my-app:push |
account | 客戶端的用戶名 | account=gzzhangyi2015 |
向認證服務器請求一個token來獲取訪問Repo資源的權限。須要客戶端經過TLS或者某種方式受權,而且若是客戶端證書中的key關聯帳號信息,這個token就要簽署帳號信息。若是證書關聯了多個帳號,客戶端必須指定帳號查詢參數。返回的token是JSON web token格式,而且用認證服務器的私鑰簽名。
舉個例子,首先,配置認證服務器容許未被驗證的客戶端進行握手。而後,客戶端向認證服務器發送一個HTTP請求到以下的endpoint,該請求用客戶端的TLS證書加密。(這個證書能夠是自籤的)
GET /v2/token/?service=registry.docker.com&scope=repository:samalba/my-app:push&account=jlhawn HTTP/1.1 Host: auth.docker.com
接下來,服務器首先檢查客戶端證書,提取出子Key,並找到與之關聯的用戶。 而後,服務器檢查它的權限控制列表,驗證用戶是否具備 registry.docker.com服務下的 samlba/my-app repo的使用權限。
最後,服務器會造成一個JSON Web Token來簽名並返回。
這個token是用對稱算法加密,以後在Registry端用相同的算法解密後,再來驗證這個token的一些信息。在整個流程中,Registry都不須要再向認證服務器發出請求。若是有,也只是須要更新認證服務器上的受信任的公鑰列表,用來驗證token的簽名或者使用其餘的API來更新認證服務器上的Repo資源記錄。
在Registry端 驗證Token的步驟以下:
一、確認證書頒發者(issuer :iss claim)是受信任的。通常issuer是認證服務器的FQDN。iss: auth.docker.com
二、確認Registry識別出token的接收方。aud : registry.docker.com
三、檢查當前時間在有效時間範圍以內。
四、若是token只能使用一次,就要檢查JWT的id值是否出現過,來避免token的重複申請。一般會在registry中保持jti記錄,延續一個token的有效時間,來避免重複申請token。
五、檢查 repo的使用權限,access值就包含了repo名和可以對該repo執行的操做。(決定了token是否授予用戶具備對該操做所需權限)。
六、驗證token簽名的有效性。