玩兒轉Docker 鏡像
前言
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的輸出結果:
我們可以發現我們常說的“ubuntu”鏡像其實不是一個鏡像名稱,而是代表了一個名為ubuntu的Repository,同時在這個 Repository下面有一系列打了tag的Image,Image的標記是一個GUID,為了方便也可以通過Repository:tag來引用。
那么Registry又是什么呢?Registry存儲鏡像數據,并且提供拉取和上傳鏡像的功能。Registry中鏡像是通過Repository來組織的,而每個Repository又包含了若干個Image。
- Registry包含一個或多個Repository
- Repository包含一個或多個Image
- Image用GUID表示,有一個或多個Tag與之關聯
那么在哪里指定Registry呢?讓我們再拉取一個更完整命名的鏡像吧:
上面我試圖去拉取一個ubuntu鏡像,并且指定了Registry為我本機搭建的私有Registry。下面是Docker CLI中pull命令的代碼片段 (docker/api/client/command.go中的CmdPull函數)
在運行時,上面的taglessRemote變量會被傳入localhost:5000/ubuntu。上面代碼試圖從taglessRemote變量中解析出Registry的地址,在我們的例子中,它是localhost:5000。
那我們回過頭再來看看下面這個耳熟能詳的pull命令背后的故事吧:
我們跟著上面的示例代碼,進一步進入解析函數ResolveRepositoryName的定義代碼片段(docker/registry/registry.go)
我們發現,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函數)
我們回過頭再去看Docker命令行中解析Tag的邏輯吧(docker/api/client/command.go中的CmdPull函數):
代碼會試著在用戶輸入的Image名稱中找’ : ‘后面的tag,如果不存在,會使用默認的‘DEFAULTTAG
’,即‘latest’。
也就是說在我們的例子里面,命令會被解析為下面這樣(注意,下面的命令不能直接運行,因為Docker CLI不允許明確指定官方Registry地址)
#p#
配置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函數)
首先,Docker CLI會試圖獲得授權,在我們的例子中會向https://index.docker.io/v1請求認證,認證完成后,認證服務器會返回一個對應的 Token。注意,這里用戶認證與配置的Registry Mirror完全無關,這樣我們就不用擔心使用Mirror的安全問題了。接著Docker CLI會調用Docker Server(即Docker daemon程序)的創建鏡像命令,Docker Server隨之會執行具體的拉取鏡像動作,代碼片段如下(docker/graph/pull.go的pullRepository函數)
從代碼中可以發現,如果配置了Registry Mirror,Docker Server會首先從Mirror中拉取鏡像,如果Mirror拉取失敗會退而求其次從鏡像中指定的Registry拉取。大家又可以松口氣了,就算配置 的Registry Mirror失效,也不會影響用戶拉取鏡像,只不過速度就。。。
鏡像拉下來后,就可以運行容器了
#p#
附錄
下面我簡單介紹下如何在DaoCloud申請一個Mirror服務,首先登陸DaoCloud主頁
點擊”立刻注冊“,簡單填寫個人信息后,隨即登陸并自動跳轉到”控制臺“,按照提示點擊”啟動你的加速器“按鈕。
啟動成功后,你就擁有了一個你專用的Registry Mirror地址了,加速器鏈接就是你要設置”--registry-mirror
“的地址。目前每個用戶有10G的加速流量(Tips:如果流量不夠用可以邀請好友獲得獎勵流量,邀請越多獎勵越多哦)
***,要感謝國內存儲行業領先企業七牛云存儲在存儲和CDN方面提供的大力支持,正因為有了像七牛這樣技術領先又熱心促進互聯網生態發展的企業的積極參與,我們才能給開發者提供更多高質量的服務。
***,要感謝國內存儲行業領先企業七牛云存儲在存儲和CDN方面提供的大力支持,正因為有了像七牛這樣技術領先又熱心促進互聯網生態發展的企業的積極參與,我們才能給開發者提供更多高質量的服務。
結語
今天和大家一起聊了聊Docker在拉取鏡像時如何解析鏡像和執行拉取動作的,以及如何通過設置Registry Mirror克服網絡延時,加速拉取過程。涉及到的代碼只集中在Docker CLI和Docker Server,在很多方面并沒有展開,比如Registry是如何響應以及如何和Index Server聯動的,只能留給下次再和大家詳細探討了。
作者簡介
孫宏亮,DaoCloud初創團隊成員,軟件工程師,浙江大學計算機科學專業應屆畢業研究生。讀研期間活躍在PaaS和Docker開源社區,對 Cloud Foundry有深入研究和豐富實踐,擅長底層平臺代碼分析,對分布式平臺的架構有一定經驗,撰寫了大量有深度的技術博客。2014年末以合伙人身份加入 DaoCloud團隊,致力于傳播以Docker為主的容器的技術,推動互聯網應用的容器化步伐。