在前面兩節(jié)我們學(xué)習(xí)了如何安裝以及簡(jiǎn)單的運(yùn)行管理docker容器颓屑,在本節(jié)我們將會(huì)更多的探討關(guān)于docker鏡像的知識(shí)問(wèn)題暴心。
1峻村、什么是Docker鏡像
Docker鏡像是由文件系統(tǒng)疊加而成窖壕。 最底端是一個(gè)引導(dǎo)文件系統(tǒng)毁葱, 即bootfs垫言,這很像典型的Linux/Unix的引導(dǎo)文件系統(tǒng)。 Docker用戶(hù)幾乎永遠(yuǎn)不會(huì)和引導(dǎo)文件系統(tǒng)有什么交互倾剿。 實(shí)際上筷频, 當(dāng)一個(gè)容器啟動(dòng)后, 它將會(huì)被移到內(nèi)存中前痘, 而引導(dǎo)文件系統(tǒng)則會(huì)被卸載(unmount) 凛捏, 以留出更多的內(nèi)存供initrd磁盤(pán)鏡像使用。
Docker鏡像的第二層是root文件系統(tǒng)rootfs际度, 它位于引導(dǎo)文件系統(tǒng)之上葵袭。 rootfs可以是一種或多種操作系統(tǒng)(如Debian或者Ubuntu文件系統(tǒng))。在Docker里乖菱, root文件系統(tǒng)永遠(yuǎn)只能是只讀狀態(tài)坡锡, 并且Docker利用聯(lián)合加載(union mount) 技術(shù)又會(huì)在root文件系統(tǒng)層上加載更多的只讀文件系統(tǒng)。 聯(lián)合加載指的是一次同時(shí)加載多個(gè)文件系統(tǒng)窒所, 但是在外面看起來(lái)只能看到一個(gè)文件系統(tǒng)鹉勒。 聯(lián)合加載會(huì)將各層文件系統(tǒng)疊加到一起, 這樣最終的文件系統(tǒng)會(huì)包含所有底層的文件和目錄吵取。
Docker將這樣的文件系統(tǒng)稱(chēng)為鏡像禽额。 一個(gè)鏡像可以放到另一個(gè)鏡像的頂部。 位于下面的鏡像稱(chēng)為父鏡像(parent image),可以依次類(lèi)推脯倒,直到鏡像棧的最底部实辑,最底部的鏡像稱(chēng)為基礎(chǔ)鏡像(base image)。最后藻丢,當(dāng)從一個(gè)鏡像啟動(dòng)容器時(shí)剪撬, Docker會(huì)在該鏡像的最頂層加載一個(gè)讀寫(xiě)文
件系統(tǒng)。 我們想在Docker中運(yùn)行的程序就是在這個(gè)讀寫(xiě)層中執(zhí)行的悠反。
當(dāng)Docker第一次啟動(dòng)一個(gè)容器時(shí)残黑, 初始的讀寫(xiě)層是空的。 當(dāng)文件系統(tǒng)發(fā)生變化時(shí)斋否, 這些變化都會(huì)應(yīng)用到這一層上梨水。 比如, 如果想修改一個(gè)文件茵臭,這個(gè)文件首先會(huì)從該讀寫(xiě)層下面的只讀層復(fù)制到該讀寫(xiě)層疫诽。 該文件的只讀版本依然存在, 但是已經(jīng)被讀寫(xiě)層中的該文件副本所隱藏笼恰。
通常這種機(jī)制被稱(chēng)為寫(xiě)時(shí)復(fù)制(copy on write) 踊沸, 這也是使Docker如此強(qiáng)大的技術(shù)之一。 每個(gè)只讀鏡像層都是只讀的社证, 并且以后永遠(yuǎn)不會(huì)變化逼龟。 當(dāng)創(chuàng)建一個(gè)新容器時(shí), Docker會(huì)構(gòu)建出一個(gè)鏡像棧追葡,并在棧的最頂端添加一個(gè)讀寫(xiě)層腺律。 這個(gè)讀寫(xiě)層再加上其下面的鏡像層以及一些配置數(shù)據(jù),就構(gòu)成了一個(gè)容器宜肉。
2匀钧、查看docker鏡像
使用docker images
可以查看本地已有的鏡像:
[01:52 shexuan@hulab ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/ubuntu latest ea4c82dcd15a 2 weeks ago 85.8 MB
docker.io/ubuntu 12.04 5b117edd0b76 19 months ago 104 MB
上面的結(jié)果中第一列是鏡像的來(lái)源倉(cāng)庫(kù),可以看到兩個(gè)鏡像都來(lái)自于同一個(gè)倉(cāng)庫(kù)谬返,只是版本不同之斯。其中第二列為鏡像標(biāo)簽,每個(gè)標(biāo)簽對(duì)組成特定鏡像的一些鏡像層進(jìn)行標(biāo)記(比如遣铝, 標(biāo)簽12.04就是對(duì)所有Ubuntu 12.04鏡像的層的標(biāo)記)佑刷。這種機(jī)制使得在同一個(gè)倉(cāng)庫(kù)中可以存儲(chǔ)多個(gè)鏡像。
我們可以通過(guò)在倉(cāng)庫(kù)名后面加上一個(gè)冒號(hào)和標(biāo)簽名來(lái)指定該倉(cāng)庫(kù)中的某一鏡像:
[02:07 shexuan@hulab ~]$ docker run --rm -it ubuntu:12.04 /bin/bash
root@229010a0aed7:/#
在構(gòu)建容器時(shí)指定倉(cāng)庫(kù)的標(biāo)簽也是一個(gè)很好的習(xí)慣酿炸。 這樣便可以準(zhǔn)確地指定容器來(lái)源于哪里瘫絮。 不同標(biāo)簽的鏡像會(huì)有不同, 比如Ubutnu 12.04和14.04就不一樣填硕, 指定鏡像的標(biāo)簽會(huì)讓我們確切知道自己使用的哪一個(gè)鏡像來(lái)構(gòu)建容器的麦萤。
Docker Hub中有兩種類(lèi)型的倉(cāng)庫(kù): 用戶(hù)倉(cāng)庫(kù)(user repository) 和頂層倉(cāng)庫(kù)(top-level repository) 。 用戶(hù)倉(cāng)庫(kù)的鏡像都是由Docker用戶(hù)創(chuàng)建的,而頂層倉(cāng)庫(kù)則是由Docker內(nèi)部的人來(lái)管理的壮莹。用戶(hù)倉(cāng)庫(kù)的命名由用戶(hù)名和倉(cāng)庫(kù)名兩部分組成翅帜,如 jamtur01/puppet
, 用戶(hù)名: jamtur01, 倉(cāng)庫(kù)名: puppet。
3垛孔、拉取鏡像
用docker run命令從鏡像啟動(dòng)一個(gè)容器時(shí)藕甩, 如果該鏡像不在本地,Docker會(huì)先從Docker Hub下載該鏡像周荐。 如果沒(méi)有指定具體的鏡像標(biāo)簽,那么Docker會(huì)自動(dòng)下載latest標(biāo)簽的鏡像僵娃。
此外概作,還可以通過(guò)docker pull
命令先發(fā)制人地將該鏡像拉取到本地。
[02:32 shexuan@hulab /data/docker]$ docker pull fedora:20
20: Pulling from library/fedora
4abd98c7489c: Pull complete
Digest: sha256:5d5a02b873d298da9bca4b84440c5cd698b0832560c850d92cf389cef58bc549
Status: Downloaded newer image for fedora:20
[02:33 shexuan@hulab /data/docker]$ docker pull fedora:21
21: Pulling from library/fedora
fced6e4a4c06: Pull complete
Digest: sha256:a268e5e12257c7770eb44c24041baf5e728fba2eed1a84f007b81845ded0a485
Status: Downloaded newer image for fedora:21
[02:36 shexuan@hulab /data/docker]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest ea4c82dcd15a 2 weeks ago 85.8MB
ubuntu 12.04 5b117edd0b76 19 months ago 104MB
fedora 20 ba74bddb630e 2 years ago 291MB
fedora 21 ba6369d667d1 2 years ago 241MB
除了拉取指定的鏡像外默怨,我們還可以使用docker search
在命令行中搜索指定的鏡像:
[02:39 shexuan@hulab ~]$ docker search puppet
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
puppet/puppetserver A Docker Image for running Puppet Server. Wi… 71
alekzonder/puppeteer GoogleChrome/puppeteer image and screenshots… 40 [OK]
devopsil/puppet Dockerfile for a container with puppet insta… 27 [OK]
macadmins/puppetmaster Simple puppetmaster based on CentOS 6 26 [OK]
... ...
上面的命令在Docker Hub上查找了所有帶有puppet的鏡像讯榕。 這條命令會(huì)完成鏡像查找工作, 并返回如下信息:
- 倉(cāng)庫(kù)名匙睹;
- 鏡像描述愚屁;
- 用戶(hù)評(píng)價(jià)(Stars) —反應(yīng)出一個(gè)鏡像的受歡迎程度;
- 是否官方(Official) —由上游開(kāi)發(fā)者管理的鏡像(如fedora鏡像由Fedora團(tuán)隊(duì)管理) 痕檬;
- 自動(dòng)構(gòu)建(Automated) —表示這個(gè)鏡像是由Docker Hub的自動(dòng)構(gòu)建(Automated Build) 流程創(chuàng)建的霎槐。
除了在命令行中搜索鏡像外,還可以在Docker Hub官網(wǎng)搜索鏡像梦谜。
4丘跌、構(gòu)建鏡像
構(gòu)建Docker鏡像有以下兩種方法:
- 使用docker commit命令;
- 使用docker build命令和Dockerfile文件唁桩。
但是不推薦使用docker commit命令闭树, 而應(yīng)該使用更靈活、 更強(qiáng)大的Dockerfile來(lái)構(gòu)建Docker鏡像荒澡。
4.5.1 創(chuàng)建Docker Hub賬號(hào)
可以從https://hub.docker.com/account/signup/加入創(chuàng)建賬號(hào)Docker Hub报辱。
創(chuàng)建完賬號(hào)就可以在本地命令行進(jìn)行登陸了。
[02:53 shexuan@hulab ~]$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.comto create one.
Username: shexuan
Password:
WARNING! Your password will be stored unencrypted in /home/shexuan/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
4.5.2 用Docker的commit命令創(chuàng)建鏡像
創(chuàng)建Docker鏡像的第一種方法是使用docker commit命令单山。 可以將此想象為我們是在往版本控制系統(tǒng)里提交變更碍现。 我們先創(chuàng)建一個(gè)容器, 并在容器里做出修改饥侵, 就像修改代碼一樣鸵赫, 最后再將修改提交為一個(gè)新鏡像。
下面在ubuntu基礎(chǔ)鏡像中安裝一個(gè)vim軟件然后提交構(gòu)建一個(gè)新的鏡像:
[02:54 shexuan@hulab ~]$ docker run -it ubuntu:latest /bin/bash
root@30e6af0c5b03:/# apt-get -yqq update
root@30e6af0c5b03:/# apt-get -y install vim
... ...
root@30e6af0c5b03:/# exit
# 使用commit構(gòu)建鏡像
[03:00 shexuan@hulab ~]$ docker commit -m "ubuntu with vim" -a "shexuan" 30e6af0c5b03 shexuan/ubuntu:vim
sha256:5520623782ff5157e5abc5273d8b6dadb1ea2d44dc27d9af390abcb860334fa9
#查看鏡像
[03:02 shexuan@hulab ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
shexuan/ubuntu vim 5520623782ff 30 seconds ago 169MB
ubuntu latest ea4c82dcd15a 2 weeks ago 85.8MB
ubuntu latest ea4c82dcd15a 2 weeks ago 85.8MB
ubuntu 12.04 5b117edd0b76 19 months ago 104MB
fedora 20 ba74bddb630e 2 years ago 291MB
fedora 21 ba6369d667d1 2 years ago 241MB
-m
選項(xiàng)用來(lái)指定新創(chuàng)建的鏡像的提交信息躏升。 同時(shí)還指定了-a
選項(xiàng)辩棒, 用來(lái)列出該鏡像的作者信息。 接著指定了想要提交的容器的ID。 最后的shexuan/ubuntu:vim
指定了鏡像的用戶(hù)名和倉(cāng)庫(kù)名一睁, 并為該鏡像增加了一個(gè)vim
標(biāo)簽钻弄。
可以用docker inspect命令來(lái)查看新創(chuàng)建的鏡像的詳細(xì)信息:
[03:05 shexuan@hulab ~]$ docker inspect shexuan/ubuntu:vim
[
{
"Id": "sha256:5520623782ff5157e5abc5273d8b6dadb1ea2d44dc27d9af390abcb860334fa9",
"RepoTags": [
"shexuan/ubuntu:vim"
],
... ...
4.5.3 用Dockerfile構(gòu)建鏡像
推薦使用Dockerfile方法來(lái)代替docker commit,因?yàn)橥ㄟ^(guò)前者來(lái)構(gòu)建鏡像更具備可重復(fù)性、透明性以及冪等性者吁。
一旦有了Dockerfile窘俺, 我們就可以使用docker build命令基于該Dockerfile中的指令構(gòu)建一個(gè)新的鏡像。
第一個(gè)Dockerfile
[03:11 shexuan@hulab ~]$ mkdir static_web
[03:11 shexuan@hulab ~]$ cd static_web/
[03:11 shexuan@hulab ~/static_web]$ touch Dockerfile
我們創(chuàng)建了一個(gè)名為static_web的目錄用來(lái)保存Dockerfile复凳, 這個(gè)目錄就是我們的構(gòu)建環(huán)境(build environment) 瘤泪, Docker則稱(chēng)此環(huán)境為上下文(context) 或者構(gòu)建上下文(build context) 。 Docker會(huì)在構(gòu)建鏡像時(shí)將構(gòu)建上下文和該上下文中的文件和目錄上傳到Docker守護(hù)進(jìn)程育八。 這樣Docker守護(hù)進(jìn)程就能直接訪(fǎng)問(wèn)用戶(hù)想在鏡像中存儲(chǔ)的任何代碼对途、 文件或者其他數(shù)據(jù)。
我們還創(chuàng)建了一個(gè)空Dockerfile髓棋, 下面就通過(guò)一個(gè)例子來(lái)看看如何通過(guò)Dockerfile構(gòu)建一個(gè)能作為Web服務(wù)器的Docker鏡像:
# Dockerfile 內(nèi)容
[03:17 shexuan@hulab ~/static_web]$ cat Dockerfile
# Version: 0.0.1
# 基礎(chǔ)鏡像為 ubuntu:14.04
FROM ubuntu:14.04
# 創(chuàng)建者信息
MAINTAINER shexuan "666666@qq.com"
# 運(yùn)行命令实檀,安裝 nginx
RUN apt-get update && apt-get install -y nginx
RUN echo 'Hi, I am in your container' \
>/usr/share/nginx/html/index.html
# 指定Docker該容器內(nèi)的應(yīng)用程序?qū)?huì)使用容器的指定端口
EXPOSE 80
該Dockerfile由一系列指令和參數(shù)組成。 每條指令按声, 如FROM膳犹, 都必須為大寫(xiě)字母, 且后面要跟隨一個(gè)參數(shù)签则。Dockerfile中的指令會(huì)按順序從上到下執(zhí)行须床, 所以應(yīng)該根據(jù)需要合理安排指令的順序。
每條指令都會(huì)創(chuàng)建一個(gè)新的鏡像層并對(duì)鏡像進(jìn)行提交怀愧。 Docker大體上按照如下流程執(zhí)行Dockerfile中的指令:
- Docker從基礎(chǔ)鏡像運(yùn)行一個(gè)容器侨颈。執(zhí)行一條指令, 對(duì)容器做出修改芯义;
- 執(zhí)行類(lèi)似docker commit的操作哈垢, 提交一個(gè)新的鏡像層;
- Docker再基于剛提交的鏡像運(yùn)行一個(gè)新容器扛拨;
- 執(zhí)行Dockerfile中的下一條指令耘分, 直到所有指令都執(zhí)行完畢。
默認(rèn)情況下绑警, RUN指令會(huì)在shell里使用命令包裝器/bin/sh -c
來(lái)執(zhí)行求泰。如果是在一個(gè)不支持shell的平臺(tái)上運(yùn)行或者不希望在shell中運(yùn)行(比如避免shell字符串篡改) , 也可以使用exec格式的RUN指令计盒,在這種方式中渴频, 我們使用一個(gè)數(shù)組來(lái)指定要運(yùn)行的命令和傳遞給該命令的每個(gè)參數(shù):
RUN [ "apt-get", " install", "-y", "nginx" ]
基于Dockerfile構(gòu)建新鏡像
執(zhí)行docker build
命令時(shí), Dockerfile中的所有指令都會(huì)被執(zhí)行并且提交北启, 并且在該命令成功結(jié)束后返回一個(gè)新鏡像卜朗。 下面就來(lái)看看如何構(gòu)建一個(gè)新鏡像:
[03:17 shexuan@hulab ~/static_web]$ docker build -t="shexuan/static_web:v1" .
Sending build context to Docker daemon 2.048kB # 這里可以看到構(gòu)建環(huán)境上下文已經(jīng)上傳到了docker 守護(hù)進(jìn)程中
Step 1/5 : FROM ubuntu:14.04
14.04: Pulling from library/ubuntu
027274c8e111: Pull complete
d3f9339a1359: Pull complete
872f75707cf4: Pull complete
dd5eed9f50d5: Pull complete
Digest: sha256:e6e808ab8c62f1d9181817aea804ae4ba0897b8bd3661d36dbc329b5851b5637
Status: Downloaded newer image for ubuntu:14.04
---> f216cfb59484
Step 2/5 : MAINTAINER shexuan "666666@qq.com"
---> Running in b2c3d41d5dc2
Removing intermediate container b2c3d41d5dc2
---> 4b5254d5a9c8
Step 3/5 : RUN apt-get update && apt-get install -y nginx
---> Running in d455cbb97664
... ...
Setting up nginx (1.4.6-1ubuntu3.8) ...
Processing triggers for libc-bin (2.19-0ubuntu6.14) ...
Processing triggers for sgml-base (1.26+nmu4ubuntu1) ...
Removing intermediate container d455cbb97664
---> 04d107396cb1
Step 4/5 : RUN echo 'Hi, I am in your container' >/usr/share/nginx/html/index.html
---> Running in ad6c71d10114
Removing intermediate container ad6c71d10114
---> 021ff3c49752
Step 5/5 : EXPOSE 80
---> Running in e128310bee23
Removing intermediate container e128310bee23
---> aba837fa8cf1
Successfully built aba837fa8cf1
Successfully tagged shexuan/static_web:v1
通過(guò)指定-t
選項(xiàng)可以為新鏡像設(shè)置了倉(cāng)庫(kù)和名稱(chēng)以及標(biāo)簽拔第。
可以看到Dockerfile中的每條指令會(huì)被順序執(zhí)行, 而且作為構(gòu)建過(guò)程的最終結(jié)果场钉, 返回了新鏡像的ID蚊俺, 即aba837fa8cf1。 構(gòu)建的每一步及其對(duì)應(yīng)指令都會(huì)獨(dú)立運(yùn)行逛万, 并且在輸出最終鏡像ID之前泳猬, Docker會(huì)提交每步的構(gòu)建結(jié)果。
指令失敗時(shí)候會(huì)怎樣宇植?
下面來(lái)看一個(gè)例子: 假設(shè)我們?cè)诘?步中將軟件包的名字弄錯(cuò)了得封, 比如寫(xiě)成了ngin,再來(lái)運(yùn)行一遍構(gòu)建過(guò)程并看看當(dāng)指令失敗時(shí)會(huì)怎樣:
[03:32 shexuan@hulab ~/static_web]$ docker build -t="shexuan/static_web:v2" .
Sending build context to Docker daemon 2.048kB
Step 1/5 : FROM ubuntu:14.04
---> f216cfb59484
Step 2/5 : MAINTAINER shexuan "666666@qq.com"
---> Using cache
---> 4b5254d5a9c8
Step 3/5 : RUN apt-get update && apt-get install -y ngin
---> Running in ba6d6583e2cb
... ...
Reading state information...
E: Unable to locate package ngin
The command '/bin/sh -c apt-get update && apt-get install -y ngin' returned a non-zero code: 100
從上面可以看到鏡像構(gòu)建到第三步時(shí)出現(xiàn)了錯(cuò)誤当纱。
這時(shí)我們可以直接使用前面一個(gè)鏡像的ID呛每,也即第二步是生成的中間鏡像4b5254d5a9c8
進(jìn)入交互模式進(jìn)行調(diào)試直到找到錯(cuò)誤原因更改Dockerfile后再來(lái)構(gòu)建鏡像。
[03:35 shexuan@hulab ~/static_web]$ docker run -i -t 4b5254d5a9c8 /bin/bash
root@f253488eff83:/#
在第二次構(gòu)建鏡像的過(guò)程中我們可以發(fā)現(xiàn)Step 2/5
那里并沒(méi)有像第一次運(yùn)行時(shí)出現(xiàn)running的情況而是直接使用的cache坡氯,這是由于每一步的構(gòu)建過(guò)程都會(huì)將結(jié)果提交為鏡像,它會(huì)將之前的鏡像層看作緩存蜈出。在我們的調(diào)試?yán)永铮?我們不需要在第1步到第2步之間進(jìn)行任何修改房官, 因此Docker會(huì)將之前構(gòu)建時(shí)創(chuàng)建的鏡像當(dāng)做緩存并作為新的開(kāi)始點(diǎn)苍碟。 實(shí)際上, 當(dāng)再次進(jìn)行構(gòu)建時(shí)悯恍, Docker會(huì)直接從第3步開(kāi)始。
然而伙狐, 有些時(shí)候需要確保構(gòu)建過(guò)程不會(huì)使用緩存涮毫。 比如, 如果已經(jīng)緩存了前面的第3步贷屎, 即apt-get update
罢防, 那么Docker將不會(huì)再次刷新APT包的緩存。 這時(shí)用戶(hù)可能需要取得每個(gè)包的最新版本唉侄。 要想略過(guò)緩存功能咒吐, 可以使用docker build的--no-cache
標(biāo)志。
一個(gè)避免忘記添加--no-cache
的方法是在Dockerfile文件起始的地方添加鏡像構(gòu)建時(shí)間環(huán)境變量属划,每次構(gòu)建時(shí)候更改時(shí)間便能避免使用緩存構(gòu)建鏡像了:
FROM fedora:20
MAINTAINER James Turnbull "james@example.com"
ENV REFRESHED_AT 2014-07-01
RUN yum -q makecache
最后查看一下我們構(gòu)建的鏡像:
# 使用改正后的Dockerfile構(gòu)建鏡像恬叹,并使用不同的標(biāo)簽
[03:46 shexuan@hulab ~/static_web]$ docker build -t="shexuan/static_web:v2".
Sending build context to Docker daemon 2.048kB
Step 1/5 : FROM ubuntu:14.04
---> f216cfb59484
Step 2/5 : MAINTAINER shexuan "666666@qq.com"
---> Using cache
---> 4b5254d5a9c8
Step 3/5 : RUN apt-get update && apt-get install -y nginx
---> Using cache
---> 04d107396cb1
Step 4/5 : RUN echo 'Hi, I am in your container' >/usr/share/nginx/html/index.html
---> Using cache
---> 021ff3c49752
Step 5/5 : EXPOSE 80
---> Using cache
---> aba837fa8cf1
Successfully built aba837fa8cf1
Successfully tagged shexuan/static_web:v2
# 查看構(gòu)建的鏡像
[03:46 shexuan@hulab ~/static_web]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
shexuan/static_web v1 aba837fa8cf1 20 minutes ago 222MB
shexuan/static_web v2 aba837fa8cf1 20 minutes ago 222MB
shexuan/ubuntu vim 5520623782ff 45 minutes ago 169MB
ubuntu 14.04 f216cfb59484 2 weeks ago 188MB
ubuntu latest ea4c82dcd15a 2 weeks ago 85.8MB
ubuntu latest ea4c82dcd15a 2 weeks ago 85.8MB
ubuntu 12.04 5b117edd0b76 19 months ago 104MB
fedora 20 ba74bddb630e 2 years ago 291MB
fedora 21 ba6369d667d1 2 years ago 241MB
#查看鏡像構(gòu)建過(guò)程
[03:48 shexuan@hulab ~/static_web]$ docker history shexuan/static_web:v1
IMAGE CREATED CREATED BY SIZE COMMENT
aba837fa8cf1 26 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B
021ff3c49752 26 minutes ago /bin/sh -c echo 'Hi, I am in your container'… 27B
04d107396cb1 26 minutes ago /bin/sh -c apt-get update && apt-get install… 34.1MB
4b5254d5a9c8 27 minutes ago /bin/sh -c #(nop) MAINTAINER shexuan "66666… 0B
f216cfb59484 2 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 2 weeks ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
<missing> 2 weeks ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B
<missing> 2 weeks ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 195kB
<missing> 2 weeks ago /bin/sh -c #(nop) ADD file:ecefeeae93e44cb42… 188MB
運(yùn)行一下試試:
[03:58 shexuan@hulab ~/static_web]$ docker run -d -p 127.0.0.1::80 --name static_web shexuan/static_web:v1 nginx -g "daemon off;"
2444bce449dc41403be0c3234024d94ab3eb9b9fdb9a6920745da3f637c45fab
[04:01 shexuan@hulab ~/static_web]$ docker port static_web
80/tcp -> 127.0.0.1:32768
[04:01 shexuan@hulab ~/static_web]$ curl localhost:32768
Hi, I am in your container
4.3.4 Dockerfile指令
我們已經(jīng)看過(guò)了一些Dockerfile中可用的指令, 如RUN和EXPOSE同眯。 但是绽昼, 實(shí)際上還可以在Dockerfile中放入很多其他指令, 這些指令包括CMD须蜗、 ENTRYPOINT硅确、 ADD目溉、 COPY、 VOLUME疏魏、 WORKDIR停做、 USER、 ONBUILD大莫、和ENV等蛉腌。
CMD
CMD指令用于指定一個(gè)容器啟動(dòng)時(shí)要運(yùn)行的命令。 這有點(diǎn)兒類(lèi)似于RUN指令只厘, 只是RUN指令是指定鏡像被構(gòu)建時(shí)要運(yùn)行的命令烙丛, 而CMD是指定容器被啟動(dòng)時(shí)要運(yùn)行的命令。 這和使用docker run命令啟動(dòng)容器時(shí)指定要運(yùn)行的命令非常類(lèi)似羔味。
此外河咽,需要注意的是使用docker run命令可以覆蓋CMD指令。 如果我們?cè)贒ockerfile里指定了CMD指令赋元, 而同時(shí)在docker run命令行中也指定了要運(yùn)行的命令忘蟹, 命令行中指定的命令會(huì)覆蓋Dockerfile中的CMD指令。
ENTRYPOINT
ENTRYPOINT指令與CMD指令非常類(lèi)似搁凸, 也很容易和CMD指令弄混媚值。正如我們已經(jīng)了解到的那樣, 我們可以在docker run
命令行中覆蓋CMD指令护糖。 有時(shí)候褥芒, 我們希望容器會(huì)按照我們想象的那樣去工作, 這時(shí)候CMD就不太合適了嫡良。 而ENTRYPOINT指令提供的命令則不容易在啟動(dòng)容器時(shí)被覆蓋锰扶。實(shí)際上,docker run
命令行中指定的任何參數(shù)都會(huì)被當(dāng)做參數(shù)再次傳遞給ENTRYPOINT指令中指定的命令寝受。
我們也可以組合使用ENTRYPOINT和CMD指令來(lái)完成一些巧妙的工作坷牛。 比如, 我們可能想在Dockerfile里指定如下代碼:
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]
此時(shí)當(dāng)我們啟動(dòng)一個(gè)容器時(shí)羡蛾, 任何在命令行中指定的參數(shù)都會(huì)被傳遞給Nginx守護(hù)進(jìn)程漓帅。 比如, 我們可以指定-g "daemon off";參數(shù)讓Nginx守護(hù)進(jìn)程以前臺(tái)方式運(yùn)行痴怨。 如果在啟動(dòng)容器時(shí)不指定任何參數(shù)忙干, 則在CMD指令中指定的-h參數(shù)會(huì)被傳遞給Nginx守護(hù)進(jìn)程, 即Nginx服務(wù)器會(huì)以/usr/sbin/nginx -h的方式啟動(dòng)浪藻, 該命令用來(lái)顯示Nginx的幫助信息捐迫。
這使我們可以構(gòu)建一個(gè)鏡像, 該鏡像既可以運(yùn)行一個(gè)默認(rèn)的命令爱葵, 同時(shí)它也支持通過(guò)docker run命令行為該命令指定可覆蓋的選項(xiàng)或者標(biāo)志施戴。
如果確實(shí)需要反浓, 用戶(hù)也可以在運(yùn)行時(shí)通過(guò)docker run的--entrypoint標(biāo)志覆蓋ENTRYPOINT指令。
WORKDIR
WORKDIR指令用來(lái)在從鏡像創(chuàng)建一個(gè)新容器時(shí)赞哗, 在容器內(nèi)部設(shè)置一個(gè)工作目錄雷则, ENTRYPOINT和/或CMD指定的程序會(huì)在這個(gè)目錄下執(zhí)行。
我們可以使用該指令為Dockerfile中后續(xù)的一系列指令設(shè)置工作目錄肪笋, 也可以為最終的容器設(shè)置工作目錄月劈。
WORKDIR /opt/webapp/db
RUN bundle install
WORKDIR /opt/webapp
ENTRYPOINT [ "rackup" ]
也可以通過(guò)-w
標(biāo)志在運(yùn)行時(shí)覆蓋工作目錄。
ENV
ENV指令用來(lái)在鏡像構(gòu)建過(guò)程中設(shè)置環(huán)境變量藤乙。這些環(huán)境變量也會(huì)被持久保存到從我們的鏡像創(chuàng)建的任何容器中猜揪。
ENV TARGET_DIR /opt/app
WORKDIR $TARGET_DIR
也可以使用docker run命令行的-e標(biāo)志來(lái)傳遞環(huán)境變量。 這些變量將只會(huì)在運(yùn)行時(shí)有效:
$ sudo docker run -ti -e "WEB_PORT=8080" ubuntu env
USER
USER指令用來(lái)指定該鏡像會(huì)以什么樣的用戶(hù)去運(yùn)行坛梁。
我們可以指定用戶(hù)名或UID以及組或GID而姐, 甚至是兩者的組合。
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
也可以在docker run命令中通過(guò)-u
標(biāo)志來(lái)覆蓋該指令指定的值划咐。
VOLUME
VOLUME指令用來(lái)向基于鏡像創(chuàng)建的容器添加卷拴念。 一個(gè)卷是可以存在于一個(gè)或者多個(gè)容器內(nèi)的特定的目錄, 這個(gè)目錄可以繞過(guò)聯(lián)合文件系統(tǒng)褐缠,并提供如下共享數(shù)據(jù)或者對(duì)數(shù)據(jù)進(jìn)行持久化的功能丈莺。
- 卷可以在容器間共享和重用;
- 一個(gè)容器可以不是必須和其他容器共享卷;
- 對(duì)卷的修改是立時(shí)生效的;
- 對(duì)卷的修改不會(huì)對(duì)更新鏡像產(chǎn)生影響;
- 卷會(huì)一直存在直到?jīng)]有任何容器再使用它。
卷功能讓我們可以將數(shù)據(jù)(如源代碼) 送丰、 數(shù)據(jù)庫(kù)或者其他內(nèi)容添加到鏡像中而不是將這些內(nèi)容提交到鏡像中, 并且允許我們?cè)诙鄠€(gè)容器間共享這些內(nèi)容弛秋。 我們可以利用此功能來(lái)測(cè)試容器和內(nèi)部的應(yīng)用程序代碼器躏, 管理日志, 或者處理容器內(nèi)部的數(shù)據(jù)庫(kù)蟹略。
VOLUME ["/opt/project"]
這條指令將會(huì)為基于此鏡像創(chuàng)建的任何容器創(chuàng)建一個(gè)名為/opt/project
的掛載點(diǎn)登失。
ADD
ADD指令用來(lái)將構(gòu)建環(huán)境下的文件和目錄復(fù)制到鏡像中。ADD指令需要源文件位置和目的文件位置兩個(gè)參數(shù):
ADD software.lic /opt/application/software.lic
這里的ADD指令將會(huì)將構(gòu)建目錄下的software.lic
文件復(fù)制到鏡像中的/opt/application/software.lic
挖炬。 指向源文件的位置參數(shù)可以是一個(gè)URL揽浙, 或者構(gòu)建上下文或環(huán)境中文件名或者目錄。 不能對(duì)構(gòu)建目錄或者上下文之外的文件進(jìn)行ADD操作意敛。
在A(yíng)DD文件時(shí)馅巷, Docker通過(guò)目的地址參數(shù)末尾的字符來(lái)判斷文件源是目錄還是文件。 如果目標(biāo)地址以/
結(jié)尾草姻, 那么Docker就認(rèn)為源位置指向的是一個(gè)目錄钓猬。如果目的地址不是以/
結(jié)尾, 那么Docker就認(rèn)為源位置指向的是文件撩独。
文件源也可以使用URL的格式:
ADD http://wordpress.org/latest.zip /root/wordpress.zip
最后值得一提的是敞曹, ADD在處理本地歸檔文件(tar archive) 時(shí)還有一些小魔法账月。 如果將一個(gè)歸檔文件(合法的歸檔文件包括gzip、 bzip2澳迫、 xz)指定為源文件局齿, Docker會(huì)自動(dòng)將歸檔文件解開(kāi)(unpack)。
ADD latest.tar.gz /var/www/wordpress/
這條命令會(huì)將歸檔文件latest.tar.gz解開(kāi)到/var/www/wordpress/目錄下橄登。
COPY
COPY指令非常類(lèi)似于A(yíng)DD抓歼, 它們根本的不同是COPY只關(guān)心在構(gòu)建上下文中復(fù)制本地文件, 而不會(huì)去做文件提仁景怼(extraction) 和解壓( decompression) 的工作锭部。
文件源路徑必須是一個(gè)與當(dāng)前構(gòu)建環(huán)境相對(duì)的文件或者目錄, 本地文件都放到和Dockerfile同一個(gè)目錄下面褐。
LABEL
LABEL指令用于為Docker鏡像添加元數(shù)據(jù)拌禾。 元數(shù)據(jù)以鍵值對(duì)的形式展現(xiàn)。
LABEL version="1.0"
LABEL location="New York" type="Data Center" role="Web Server"
LABEL指令以label="value"的形式出現(xiàn)展哭。 可以在每一條指令中指定一個(gè)元數(shù)據(jù)湃窍, 或者指定多個(gè)元數(shù)據(jù), 不同的元數(shù)據(jù)之間用空格分隔匪傍。 推薦將所有的元數(shù)據(jù)都放到一條LABEL指令中您市, 以防止不同的元數(shù)據(jù)指令創(chuàng)建過(guò)多鏡像層。
STOPSIGNAL
TOPSIGNAL指令用來(lái)設(shè)置停止容器時(shí)發(fā)送什么系統(tǒng)調(diào)用信號(hào)給容器役衡。這個(gè)信號(hào)必須是內(nèi)核系統(tǒng)調(diào)用表中合法的數(shù)茵休, 如9, 或者SIGNAME格式中的信號(hào)名稱(chēng)手蝎, 如SIGKILL榕莺。
ARG
ARG指令用來(lái)定義可以在docker build命令運(yùn)行時(shí)傳遞給構(gòu)建運(yùn)行時(shí)的變量, 我們只需要在構(gòu)建時(shí)使用--build-arg
標(biāo)志即可棵介。 用戶(hù)只能在構(gòu)建時(shí)指定在Dockerfile文件中定義過(guò)的參數(shù)钉鸯。
上面例子中第二條ARG指令設(shè)置了一個(gè)默認(rèn)值, 如果構(gòu)建時(shí)沒(méi)有為該參數(shù)指定值邮辽, 就會(huì)使用這個(gè)默認(rèn)值唠雕。
$ docker build --build-arg build=1234 -t jamtur01/webapp .
這里構(gòu)建jamtur01/webapp鏡像時(shí), build變量將會(huì)設(shè)置為1234吨述,而webapp_user變量則會(huì)繼承設(shè)置的默認(rèn)值user岩睁。
ONBUILD
ONBUILD指令能為鏡像添加觸發(fā)器(trigger) 。 當(dāng)一個(gè)鏡像被用做其他鏡像的基礎(chǔ)鏡像時(shí)(比如用戶(hù)的鏡像需要從某未準(zhǔn)備好的位置添加源代碼锐极, 或者用戶(hù)需要執(zhí)行特定于構(gòu)建鏡像的環(huán)境的構(gòu)建腳本) 笙僚, 該鏡像中的觸發(fā)器將會(huì)被執(zhí)行。
ONBUILD觸發(fā)器會(huì)按照在父鏡像中指定的順序執(zhí)行灵再, 并且只能被繼承一次(也就是說(shuō)只能在子鏡像中執(zhí)行肋层, 而不會(huì)在孫子鏡像中執(zhí)行)亿笤。
5、將鏡像推送到Docker Hub
鏡像構(gòu)建完畢之后栋猖, 我們也可以將它上傳到Docker Hub上面去净薛, 這樣其他人就能使用這個(gè)鏡像了。 比如蒲拉, 我們可以在組織內(nèi)共享這個(gè)鏡像肃拜, 或者完全公開(kāi)這個(gè)鏡像。
我們可以通過(guò)docker push
命令將鏡像推送到Docker Hub雌团。
[05:02 shexuan@hulab ~]$ docker push shexuan/static_web:v1
The push refers to repository [docker.io/shexuan/static_web]
04b77fe9aaad: Pushed
65a0411b4c56: Pushed
534823441154: Mounted from library/ubuntu
6e62bd7245ce: Mounted from library/ubuntu
cce4153cfb1c: Mounted from library/ubuntu
adb1f7ffb432: Mounted from library/ubuntu
v1: digest: sha256:ef85171a44b8a7993dbcf7b42179f68f2e1a8258c4b8f44d3680512009e53225 size: 1571
還可以設(shè)置github關(guān)聯(lián)自動(dòng)構(gòu)建鏡像燃领,具體參見(jiàn)官方文檔。
6锦援、刪除鏡像
如果不再需要一個(gè)鏡像了猛蔽, 也可以將它刪除。 可以使用docker rmi
命令來(lái)刪除一個(gè)或多個(gè)鏡像灵寺。
[05:02 shexuan@hulab ~]$ docker rmi shexuan/static_web:v1
Untagged: shexuan/static_web:v1
正如前面看到的曼库,也可以使用組合命令來(lái)一次性刪除所有鏡像:
$ docker rmi `docker images -a -q`
7、運(yùn)行自己的Docker Registry
有時(shí)候我們可能希望構(gòu)建和存儲(chǔ)包含不想被公開(kāi)的信息或數(shù)據(jù)的鏡像略板。 這時(shí)候我們有以下兩種選擇毁枯。
- 利用Docker Hub上的私有倉(cāng)庫(kù);
- 在防火墻后面運(yùn)行你自己的Registry叮称。
從容器運(yùn)行Registry
從Docker容器安裝一個(gè)Registry非常簡(jiǎn)單:
$ docker run -p 5000:5000 registry:2
該命令將會(huì)啟動(dòng)一個(gè)運(yùn)行Registry應(yīng)用2.0版本的容器种玛, 并將5000端口綁定到本地宿主機(jī)。
測(cè)試新Registry
看看是否能將本地已經(jīng)存在的鏡像jamtur01/static_web上傳到我們的新Registry上去瓤檐。
首先蒂誉, 我們需要通過(guò)docker images命令來(lái)找到這個(gè)鏡像的ID:
$ sudo docker images jamtur01/static_web
REPOSITORY TAG ID CREATED SIZE
jamtur01/static_web latest 22d47c8cb6e5 24 seconds ago 12.29 kB
(virtual 326 MB)
接著, 我們找到鏡像ID距帅, 即22d47c8cb6e5, 并使用新的Registry給該鏡像打上標(biāo)簽括堤。 為了指定新的Registry目的地址碌秸, 需要在鏡像名前加上主機(jī)名和端口前綴。 在這個(gè)例子里悄窃, 我們的Registry主機(jī)名為docker.example.com讥电。
sudo docker tag 22d47c8cb6e5 docker.example.com:5000/jamtur01/static_web
為鏡像打完標(biāo)簽之后, 就能通過(guò)docker push命令將它推送到新的Registry中去了轧抗。
$ sudo docker push docker.example.com:5000/jamtur01/static_web
這個(gè)鏡像就被提交到了本地的Registry中恩敌, 并且可以將其用于使用docker run
命令構(gòu)建新容器:
$ sudo docker run -t -i docker.example.com:5000/jamtur01/static_web /bin/bash
這是在防火墻后面部署自己的Docker Registry的最簡(jiǎn)單的方式。
也有很多其他公司和服務(wù)提供定制的Docker Registry服務(wù), 如Quay横媚。