玩轉Docker映象

孫宏亮發表於2014-12-14

前言

Docker是Docker.Inc公司開源的一個基於輕量級虛擬化技術的容器引擎專案,整個專案基於Go語言開發,並遵從Apache 2.0協議。透過分層映象標準化和核心虛擬化技術,Docker使得應用開發者和運維工程師可以以統一的方式跨平臺釋出應用,並且以幾乎沒有額外開銷的情況下提供資源隔離的應用執行環境。由於眾多新穎的特性以及專案本身的開放性,Docker在不到兩年的時間裡迅速獲得諸多IT廠商的參與,其中更是包括Google、Microsoft、VMware等業界行業領導者。同時,Docker在開發者社群也是一石激起千層浪,許多如我之碼農紛紛開始關注、學習和使用Docker,許多企業,尤其是網際網路企業,也在不斷加大對Docker的投入,大有掀起一場容器革命之勢。

Docker映象命名解析

映象是Docker最核心的技術之一,也是應用釋出的標準格式。無論你是用docker pull image,或者是在Dockerfile裡面寫FROM image,從Docker官方Registry下載映象應該是Docker操作裡面最頻繁的動作之一了。那麼在我們執行docker pull image時背後到底發生了什麼呢?在回答這個問題前,我們需要先了解下docker映象是如何命名的,這也是Docker裡面比較容易令人混淆的一塊概念:Registry,Repository, Tag and Image。

下面是在本地機器執行docker images的輸出結果:

1

我們可以發現我們常說的“ubuntu”映象其實不是一個映象名稱,而是代表了一個名為ubuntu的Repository,同時在這個Repository下面有一系列打了tag的Image,Image的標記是一個GUID,為了方便也可以透過Repository:tag來引用。

那麼Registry又是什麼呢?Registry儲存映象資料,並且提供拉取和上傳映象的功能。Registry中映象是透過Repository來組織的,而每個Repository又包含了若干個Image。

  • Registry包含一個或多個Repository
  • Repository包含一個或多個Image
  • Image用GUID表示,有一個或多個Tag與之關聯

那麼在哪裡指定Registry呢?讓我們再拉取一個更完整命名的映象吧:

2

上面我試圖去拉取一個ubuntu映象,並且指定了Registry為我本機搭建的私有Registry。下面是Docker CLI中pull命令的程式碼片段 (docker/api/client/command.go中的CmdPull函式)

截圖 (4)

在執行時,上面的taglessRemote變數會被傳入localhost:5000/ubuntu。上面程式碼試圖從taglessRemote變數中解析出Registry的地址,在我們的例子中,它是localhost:5000。

那我們回過頭再來看看下面這個耳熟能詳的pull命令背後的故事吧:

3

我們跟著上面的示例程式碼,進一步進入解析函式ResolveRepositoryName的定義程式碼片段(docker/registry/registry.go)

截圖 (3)

我們發現,Docker CLI會判斷傳入的taglessRemote引數的第一部分中是否包含’.’或者':’,如果存在則認為第一部分是Registry地址,否則會使用Docker官方預設的Registry(即index.docker.io其實這裡是一個Index Server,和Registry的區別留在後面再去深究吧),即上面程式碼中高亮的部分。背後的故事還沒有結束,如果你向DockerHub上傳過映象,應該記得你上傳的映象名稱格式為user-name/repository:tag,這樣使用者Bob和使用者Alice可以有相同名稱的Repository,透過使用者名稱字首作為名稱空間隔離,比如Bob/ubuntu和Alice/ubuntu。官方映象是透過使用者名稱library來區分的,具體程式碼片段如下(docker/api/client/command.go中的CmdPull函式)

截圖 (5)

我們回過頭再去看Docker命令列中解析Tag的邏輯吧(docker/api/client/command.go中的CmdPull函式):

截圖 (6)

程式碼會試著在使用者輸入的Image名稱中找’ : ‘後面的tag,如果不存在,會使用預設的‘DEFAULTTAG’,即‘latest’。

也就是說在我們的例子裡面,命令會被解析為下面這樣(注意,下面的命令不能直接執行,因為Docker CLI不允許明確指定官方Registry地址)

4 (1)

配置Registry Mirror

Docker之所以這麼吸引人,除了它的新穎的技術外,圍繞官方Registry(Docker Hub)的生態圈也是相當吸引人眼球的地方。在Docker Hub上你可以很輕鬆下載到大量已經容器化好的應用映象,即拉即用。這些映象中,有些是Docker官方維護的,更多的是眾多開發者自發上傳分享的。而且你還可以在Docker Hub中繫結你的程式碼託管系統(目前支援Github和Bitbucket)配置自動生成映象功能,這樣Docker Hub會在你程式碼更新時自動生成對應的Docker映象,是不是很方便?

不幸的是Docker Hub並沒有在國內放伺服器或者用國內的CDN,下載個映象20分鐘最起碼,我等碼農可耗不起這麼長時間,老闆正站在身後催著我們搬運程式碼呢。為了克服跨洋網路延遲,一般有兩個解決方案:一是使用私有Registry,另外是使用Registry Mirror,我們下面一一展開聊聊.

方案一就是搭建或者使用現有的私有Registry,透過定期和Docker Hub同步熱門的映象,私有Registry上儲存了一些映象的副本,然後大家可以透過docker pull private-registry.com/user-name/ubuntu:latest,從這個私有Registry上拉取映象。因為這個方案需要定期同步Docker Hub映象,因此它比較適合於使用的映象相對穩定,或者都是私有映象的場景。而且使用者需要顯式的對映官方映象名稱到私有映象名稱,私有Registry更多被大家應用在企業內部場景。私有Registry部署也很方便,可以直接在Docker Hub上下載Registry映象,即拉即用,具體部署可以參考官方文件

方案二是使用Registry Mirror,它的原理類似於快取,如果映象在Mirror中命中則直接返回給客戶端,否則從存放映象的Registry上拉取並自動快取在Mirror中。最酷的是,是否使用Mirror對Docker使用者來講是透明的,也就是說在配置Mirror以後,大家可以仍然輸入docker pull ubuntu來拉取Docker Hub映象,除了速度變快了,和以前沒有任何區別。

了以更便捷的方式對接Docker Hub生態圈,使用Registry Mirror自然成為我的首選。接下來我就和大家一起看看Docker使用Mirror來拉取映象的過程。下面的例子,我使用的是由DaoCloud提供的Registry Mirror服務,在申請開通Mirror服務後你會得到一個Mirror地址,然後我們要做的就是把這個地址配置在Docker Server啟動指令碼中,重啟Docker服務後Mirror配置就生效了(如何獲得Mirror服務可以參考本篇文章的附錄)

Ubuntu下配置Docker Registry Mirror的命令如下:

sudo echo “DOCKER_OPTS=\”\$DOCKER_OPTS –registry-mirror=http://your-id.m.daocloud.io -d\”” >> /etc/default/docker
sudo service docker restart

如果你是用的Boot2Docker,配置命令為:

# 進入Boot2Docker Start Shell,並執行
sudo su
echo “EXTRA_ARGS=\”–registry-mirror=http://your-id.m.daocloud.io\”” >> /var/lib/boot2docker/profile
exit
# 重啟Boot2Docker

配置好Registry Mirror後,就可以拉取Docker映象了,經我測試,使用DaoCloud的Mirror後,拉取常見映象的速度可以達到1.5M左右,具體速度在你的網路環境可能會略有不同。

我們來看看配置了Registry Mirror後,Docker拉取映象的過程吧。首先是CLI拉取映象命令程式碼片段(docker/api/client/command.go中的CmdPull函式)

截圖 (7)

首先,Docker CLI會試圖獲得授權,在我們的例子中會向https://index.docker.io/v1請求認證,認證完成後,認證伺服器會返回一個對應的Token。注意,這裡使用者認證與配置的Registry Mirror完全無關,這樣我們就不用擔心使用Mirror的安全問題了。接著Docker CLI會呼叫Docker Server(即Docker daemon程式)的建立映象命令,Docker Server隨之會執行具體的拉取映象動作,程式碼片段如下(docker/graph/pull.go的pullRepository函式)

截圖 (8)

從程式碼中可以發現,如果配置了Registry Mirror,Docker Server會首先從Mirror中拉取映象,如果Mirror拉取失敗會退而求其次從映象中指定的Registry拉取。大家又可以鬆口氣了,就算配置的Registry Mirror失效,也不會影響使用者拉取映象,只不過速度就。。。

映象拉下來後,就可以執行容器了

截圖 (9)

 

附錄

下面我簡單介紹下如何在DaoCloud申請一個Mirror服務,首先登陸DaoCloud主頁

未命名 5 (1)

點選”立刻註冊“,簡單填寫個人資訊後,隨即登陸並自動跳轉到”控制檯“,按照提示點選”啟動你的加速器“按鈕。

截圖 (10)

啟動成功後,你就擁有了一個你專用的Registry Mirror地址了,加速器連結就是你要設定”--registry-mirror“的地址。目前每個使用者有10G的加速流量(Tips:如果流量不夠用可以邀請好友獲得獎勵流量,邀請越多獎勵越多哦)

截圖 (11)

最後,要感謝國記憶體儲行業領先企業七牛雲端儲存儲存和CDN方面提供的大力支援,正因為有了像七牛這樣技術領先又熱心促進網際網路生態發展的企業的積極參與,我們才能給開發者提供更多高質量的服務。 

結語

今天和大家一起聊了聊Docker在拉取映象時如何解析映象和執行拉取動作的,以及如何透過設定Registry Mirror克服網路延時,加速拉取過程。涉及到的程式碼只集中在Docker CLI和Docker Server,在很多方面並沒有展開,比如Registry是如何響應以及如何和Index Server聯動的,只能留給下次再和大家詳細探討了。

相關文章