什么是Image?
廢話不多說,先上圖
最底層的Linux Kernel就是宿主機(jī)的操作系統(tǒng)內(nèi)核浮还,這一部分是共享的,稱之為boot filesystem闽巩。在這之上钧舌,是我們的Base Image,是文件和meta data的集合涎跨,也稱為root filesystem洼冻,這一層就是我們的系統(tǒng)鏡像,比如Ubuntu隅很,CentOS等撞牢,這個(gè)鏡像只包含操作系統(tǒng),不包含其它軟件叔营。我們?cè)赽ase image之上可以安裝一些軟件屋彪,比如Nginx,PHP等绒尊,這會(huì)產(chǎn)生新的一層image畜挥,如圖中image123,都是在系統(tǒng)之上裝了一些軟件婴谱,而image4是在image2的基礎(chǔ)上蟹但,又裝了一些軟件躯泰,則會(huì)產(chǎn)生新一層image。每一層image都是read only的矮湘,生成以后就不能改變斟冕。
Image獲取
那么Image是怎么獲取的口糕?在Docker中有個(gè)Dockerfile缅阳,這個(gè)Dockerfile就定義了一個(gè)Docker的Image。Dockerfile有自己的語法景描,后面我們會(huì)說到十办。先來看看一個(gè)基本的Dockerfile
FROM ubuntu:16.04 #選擇BaseImage
LABEL maintainer="Heheda <heheda@gamil.com>" #作者標(biāo)識(shí)
RUN apt-get update && apt-get install -y redis-server #在BaseImage上運(yùn)行某個(gè)命令
EXPOSE 6379 #暴露端口
ENTRYPOINT [ "/usr/bin/redis-server" ] #程序入口,此處是redis啟動(dòng)入口
通過命令docker build -t your-directory/xxx:latest .
build當(dāng)前目錄下的Dockerfile超棺,你會(huì)看到每執(zhí)行一步向族,都會(huì)生成一個(gè)id,一共五個(gè)id棠绘,對(duì)應(yīng)Dockerfile里的五條語句件相,每一步都是image的一層(layer)狭吼,層是可以共享的疙赠。以上是手動(dòng)創(chuàng)建Image的方式。
另一種獲取Image的方式是從Docker的Registry中獲取酣难。Registry類似docker的github让虐,里面有許許多多的Image供選擇紊撕。
用docker pull targetImageName
來獲取
小提示,如果需要在虛擬機(jī)中執(zhí)行docker命令是不輸入sudo赡突,那么需要把當(dāng)前用戶加到docker用戶組里对扶。具體命令如下
sudo groupadd docker
sudo gpasswd -a vagrant docker
sudo service docker restart
最后退出賬戶重新登錄,再執(zhí)行docker命令就不需要sudo了
創(chuàng)建一個(gè)簡(jiǎn)單的BaseImage
- 創(chuàng)建一個(gè)目錄惭缰,里面可以編寫一段簡(jiǎn)單的程序浪南,俗套來了,這里用C語言寫個(gè)HelloWorld
mkdir hello-world
vim hello.c
用gcc將c文件編譯好漱受,得到hello這個(gè)二進(jìn)制可執(zhí)行文件
gcc -static hello.c -o hello
編寫Dockerfile
FROM scratch #因?yàn)槲覀兪莿?chuàng)建BaseImage逞泄,不是安裝軟件,所以用scratch
ADD hello / #將hello這個(gè)可執(zhí)行文件放在根目錄
CMD ["/hello"] #執(zhí)行根目錄下的hello程序
- build image
docker build -t guojh/hello-world .
這條命令 -t是指定image的tag拜效,后面跟的參數(shù)就是tag的名字喷众,最后的點(diǎn)表示Dockerfile在當(dāng)前目錄。不加-t也是可以的
可以看到創(chuàng)建過程一共執(zhí)行了3步紧憾,也就是3層到千,對(duì)應(yīng)Dockerfile里的三條語句。
用docker image ls
查看創(chuàng)建的guojh/hello-world鏡像
使用docker history imageID
可以查看image的創(chuàng)建記錄赴穗°舅模可以看到膀息,我們FROM指定的是scratch,在執(zhí)行過程中這并不算一層了赵,因?yàn)槲覀儎?chuàng)建的是BaseImage潜支。
最后使用docker run guojh/hello-world
執(zhí)行我們的image
Container
Container是通過Image創(chuàng)建的,會(huì)拷貝一份Image柿汛。之后會(huì)在Image上創(chuàng)建一個(gè)Container層冗酿,負(fù)責(zé)讀寫操作÷缍希可以將Image和Container類比成類和實(shí)例裁替,Image描述了一個(gè)鏡像該有的樣子,Container根據(jù)Image的描述實(shí)例化一個(gè)鏡像貌笨。Image在職責(zé)上負(fù)責(zé)存儲(chǔ)和分發(fā)弱判,Container負(fù)責(zé)運(yùn)行。
通過docker run xxx
就是最快的根據(jù)Image創(chuàng)建Container的方式锥惋。
用docker container ls
或者docker ps
可以查看當(dāng)前Container昌腰,執(zhí)行命令后我們發(fā)現(xiàn)沒有任何Container,這是因?yàn)槲覀兊膆ello-world鏡像打印出hello, my docker后就退出了膀跌,我們可以通過-a參數(shù)查看正在運(yùn)行及已經(jīng)退出的Container
如何不讓Container退出呢遭商?我們需要運(yùn)行一個(gè)有不會(huì)退出的進(jìn)程的image,比如操作系統(tǒng)Image淹父,centos
如果我們直接docker run centos:7
株婴,那么Container依舊會(huì)退出。如果不希望推出暑认,可以加-t參數(shù)困介,采用交互式運(yùn)行
docker run -it centos:7
查詢現(xiàn)有Container,可以看到up的Container是centos
如果要?jiǎng)h除容器蘸际,使用docker container rm ContainerID
或者docker rm ContainerID
如果刪除多個(gè)鏡像座哩,使用docker container rm $(docker container ls -aq)
,-ap是列出所有鏡像ID粮彤。
如果想刪除退出狀態(tài)的Container根穷,使用docker rm $(docker container ls -f "status=exited" -q)
構(gòu)建自己的Docker鏡像
第一個(gè)需要了解的命令docker container commit
,將當(dāng)前運(yùn)行的Container變成一個(gè)鏡像导坟。
首先我們運(yùn)行前面的centos這個(gè)baseImage屿良,運(yùn)行docker run -it centos:7
這個(gè)centos7是不包含vim軟件的,我們就在baseImage的基礎(chǔ)上惫周,安裝一個(gè)尘惧,運(yùn)行命令sudo yum install vim
進(jìn)行安裝。
安裝完成后递递,退出centos喷橙,運(yùn)行docker container ls -a
查看當(dāng)前container啥么。
我們可以看到,紅線標(biāo)出的就是剛才我們退出的container贰逾,這個(gè)container包含了vim悬荣。我們使用
docker container commit
命令來創(chuàng)建Image。這個(gè)命令需要兩個(gè)參數(shù)疙剑,一個(gè)是container的名字氯迂,一個(gè)是Image的tag,tag默認(rèn)是latest的核芽,所以我們不用加latest了囚戚。最終命令是這樣docker container commit epic_stonebraker guojh/centos7-vim
創(chuàng)建完成后酵熙,查看image轧简,你會(huì)發(fā)現(xiàn)多了個(gè)我們剛才創(chuàng)建的帶VIM的鏡像,比baseImage大了100MB+匾二。
這種創(chuàng)建鏡像的方式不是特別推薦哮独,因?yàn)楫?dāng)發(fā)布鏡像的時(shí)候,只提供了一個(gè)image察藐,但具體有沒有多加什么其它軟件皮璧,你并不知道,所以接下來我們說第二種創(chuàng)建Image的方式分飞,用Dockerfile悴务。
我們新建一個(gè)目錄,docker-centos-vim譬猫。進(jìn)入這個(gè)目錄后讯檐,創(chuàng)建一個(gè)Dockerfile,內(nèi)容如下:
FROM centos
RUN yum install -y vim
然后使用docker build -t your-tag
完成創(chuàng)建染服。這里有個(gè)問題别洪,image既然是只讀權(quán)限,那么如何在image上安裝vim呢柳刮?docker不會(huì)讓自己陷入這個(gè)困境的挖垛,docker build命令會(huì)運(yùn)行一個(gè)臨時(shí)的container,將baseImage運(yùn)行起來秉颗,然后安裝vim痢毒,最后生成image。以后我們就可以直接分享Dockerfile給其他人蚕甥,保證我們的Image不會(huì)安裝其他惡意軟件了哪替。另外提一點(diǎn),如果希望container在后臺(tái)運(yùn)行梢灭,加上-t即可夷家。
Dockerfile語法
FROM
FROM后面跟scratch是創(chuàng)建baseImage蒸其,F(xiàn)ROM centos是使用已有的baseImage,盡量使用官方的baseImage库快。LABEL
LABEL標(biāo)簽就是增加對(duì)鏡像的描述摸袁,比如LABLE maintainer=xxx
就標(biāo)識(shí)了鏡像的持有者。另外還有version义屏,description這些字段靠汁。對(duì)于LABEL來說,RUN
RUN是執(zhí)行一些命令闽铐,比如安裝軟件等蝶怔。這里有一點(diǎn)要注意,每執(zhí)行一次RUN兄墅,便會(huì)產(chǎn)生一層Image踢星,為了避免無用分層,盡量用&&
連接命令隙咸,如果命令太長(zhǎng)沐悦,用反斜線來?yè)Q行。比如
RUN yum install vim && yum install PHP \
yum install Nginx
WORKDIR
這條命令是設(shè)定當(dāng)前工作目錄五督。類似cd命令藏否,如果WORKDIR一個(gè)不存在的目錄,會(huì)自動(dòng)創(chuàng)建這個(gè)目錄充包。這里需要注意副签,不要使用RUN cd
,這會(huì)產(chǎn)生無用層基矮,推薦使用WORKDIR淆储,并且盡量使用絕對(duì)路徑。ADD and COPY
ADD和COPY都能將本地的文件添加到docker的image中愈捅,比如我們之前創(chuàng)建hello鏡像的時(shí)候遏考。ADD和COPY第一個(gè)區(qū)別是如果拷貝的是壓縮文件,ADD會(huì)將壓縮文件解壓縮后再拷貝蓝谨,而COPY只是單純拷貝壓縮文件而已灌具。如果只是拷貝,不需要解壓縮譬巫,還是用COPY咖楣。如果是添加遠(yuǎn)程文件,比如下載一個(gè)文件芦昔,那么還是RUN來執(zhí)行wget或者crul吧诱贿。ENV
這個(gè)關(guān)鍵字是聲明常量。比如
ENV MYSQL_VERSION 5.6
RUN apt-get install -y mysql-server="${MYSQL_VERSION}" \
&& rm -rf /var/lib/apt/lists/*
盡量使用ENV增加代碼可讀性,減少魔數(shù)的使用珠十。
- CMD
CMD是容器啟動(dòng)時(shí)默認(rèn)執(zhí)行的命令料扰,如果定義了多個(gè)CMD,只會(huì)執(zhí)行最后一個(gè)CMD焙蹭,如果docker run指定了其它命令晒杈,則會(huì)忽略CMD。
對(duì)于最后一種情況解釋一下孔厉,比如我們有這樣一個(gè)Dockerfile:
FROM centos
ENV name Docker
CMD echo "hello, $name"
假設(shè)這個(gè)Dockerfile生成的image名字是testImage拯钻,那么執(zhí)行docker run tetsImage
后,默認(rèn)會(huì)執(zhí)行CMD命令撰豺,打印hello, Docker粪般。如果執(zhí)行docker run testImage /bin/bash
,那么就不會(huì)打印hello, Docker污桦,因?yàn)槲覀冎付诉\(yùn)行/bin/bash亩歹。
- ENTRYPOINT
ENTRYPOINT讓容易以應(yīng)用程序或者服務(wù)的形式啟動(dòng),例如啟動(dòng)Nginx這種守護(hù)進(jìn)程寡润。ENTRYPOINT永遠(yuǎn)不會(huì)被忽略捆憎,一定會(huì)被執(zhí)行舅柜。最好是將要執(zhí)行的命令寫成shell梭纹,用ENTRYPOINT來執(zhí)行這個(gè)shell。比如:
COPY docker-entrypoint.sh /usr/local/bin
ENTRYPOINT ["docker-entrypoint.sh"]
- EXPOSE
暴露docker內(nèi)的端口給外部致份。
EXPOSE 5000
更多命令可以參考Dockerfile官方文檔
Dockerfile
Dockerfile中的格式有兩種变抽,一種是Shell格式,一種是Exec格式
Shell:
RUN apt=get install -y vim
CMD echo "finished..."
ENTRYPOINT echo "Hello..."
Exec:
RUN [ "apt-get", "install", "-y", "vim"]
CMD[ "/bin/echo", "finished..."]
ENTRYPOINT ['/bin/echo', "Hello..."]
如果命令中定義了常量氮块,使用常量的時(shí)候绍载,Exec格式不會(huì)識(shí)別常量,CMD可以識(shí)別常量滔蝉。如果必須讓Exec識(shí)別常量击儡,那么第一個(gè)命令應(yīng)該是/bin/bash
,后面跟上shell命令即可蝠引。如
ENV name Heheda
ENTRYPOINT ["/bin/bash", "-c", "echo $name"]
- Dockerfile中執(zhí)行命令時(shí)的動(dòng)態(tài)參數(shù)
如果Dockerfile中需要執(zhí)行一段shell命令阳谍,但參數(shù)又不確定怎么辦?可以使用ENTRYPOINT搭配CMD螃概,如:
ENTRYPOINT ["/usr/bin/bash/ps"]
CMD[]
在運(yùn)行container的時(shí)候矫夯,直接跟上需要的參數(shù)即可。如:
docker run -it containerID aux | grep parity
這樣aux | grep parity
就會(huì)被當(dāng)成ps的參數(shù)
更多Dockerfile例子可以在GitHub上搜索docker-library倉(cāng)庫(kù)吊洼。
搭建自己的Docker鏡像倉(cāng)庫(kù)
搭建很簡(jiǎn)單训貌,官方提供了一個(gè)搭建倉(cāng)庫(kù)的倉(cāng)庫(kù)鏡像,按照教程來就能搭建好。
關(guān)鍵是創(chuàng)建鏡像的時(shí)候递沪,tag需要指定為私有倉(cāng)庫(kù)的ip+端口豺鼻。比如我們的倉(cāng)庫(kù)地址是12.12.12.12:5000,那么build鏡像的時(shí)候款慨,tag為12.12.12.12:5000/imagename
拘领。
在push之前,需要在/etc/docker
目錄下創(chuàng)建damon.json文件樱调,輸入下面的代碼:
{
"insecure-registries" : ["12.12.12.12:5000"]
}
把我們的倉(cāng)庫(kù)地址被docker信任约素。
再編輯/lib/systemd/system/docker.service
文件,在文件中增加EnviromentFile=-/etc/docker/daemin.json
最后重啟docker
現(xiàn)在我們可以push到私有倉(cāng)庫(kù)了笆凌,docker push 12.12.12.12:5000/imagename
私有倉(cāng)庫(kù)沒有提供web界面圣猎,可以通過API來檢查是否上傳成功。
12.12.12.12:5000/v2/_catalog
來查看
容器操作
進(jìn)入container執(zhí)行命令或查看container運(yùn)行的程序
如果希望進(jìn)入container乞而,可以使用命令docker exec -it containerID shell命令
來實(shí)現(xiàn)送悔。比如我想進(jìn)入container的shell里,那就運(yùn)行docker exec -it containerID /bin/bash
爪模,這樣就能進(jìn)入container的bash里了欠啤。給要運(yùn)行的容器起名
運(yùn)行docker run --name=demo imageID
,指定一個(gè)名字即可屋灌,這個(gè)名字是唯一的洁段,不能重復(fù)。顯示container的詳細(xì)信息
運(yùn)行docker inspect containerID
查看container運(yùn)行l(wèi)og
運(yùn)行docker logs containerID
容器資源限制
限制container的內(nèi)存使用量
使用--memory=200M
來限制內(nèi)存使用共郭。這里指定了內(nèi)存是200MB祠丝,但linux還有交換區(qū)內(nèi)存,默認(rèn)是和memory指定的大小一致除嘹,所以實(shí)際的可用內(nèi)存有400MB写半。可以使用--memory-swap
來指定交換區(qū)內(nèi)存大小尉咕。查看等多命令使用docker run --help
限制CPU使用
這里介紹一個(gè)參數(shù)--cpu-shares
叠蝇,這是限制CPU使用權(quán)重,以整數(shù)表示年缎。比如我container A限制--cpu-share=10
悔捶,另一個(gè)container B限制--cpu-share=5
,這意味著A的CPU使用率是B的兩倍晦款。如果這兩個(gè)container占用了宿主機(jī)全部的CPU資源炎功,那么A對(duì)CPU的使用率是B的兩倍。
stress壓力測(cè)試工具
在運(yùn)行的container里安裝好stress后缓溅,第一個(gè)介紹的命令是--vm蛇损,意思是啟動(dòng)進(jìn)程及給進(jìn)程分配內(nèi)存,默認(rèn)內(nèi)存是256MB。如果需要多個(gè)進(jìn)程淤齐,在命令后面空一格股囊,跟上進(jìn)程數(shù)量,如stress --vm 2
更啄,這里就會(huì)創(chuàng)建兩個(gè)進(jìn)程稚疹。如果要指定進(jìn)程的內(nèi)存大小,用--vm-bytes祭务,比如stress --vm 2 --vm-bytes 128M
内狗。如果要查看log,加上--verbose參數(shù)义锥。
參考資料
慕課網(wǎng)Docker教程