Dockerfile 構(gòu)建鏡像常用指令
Dockerfile 是一個(gè)文本文件,其內(nèi)包含了一條條的指令(Instruction),每一條指令構(gòu)建一層,因此每一條指定的內(nèi)容,就是描述該層應(yīng)當(dāng)如何構(gòu)建。
通過使用build 命令,根據(jù)Dockerfile的描述來構(gòu)建鏡像
通過源代碼的方式
通過標(biāo)準(zhǔn)輸入流的方式
通過源代碼的路徑:
Dockerfile需要放置在項(xiàng)目的跟目錄位置
在構(gòu)建的時(shí)候,Dockerfile client會(huì)把整個(gè)context打包發(fā)送到Docker Server端,然后由server端負(fù)責(zé)build鏡像,在構(gòu)建成功后,會(huì)刪除context目錄
docker build -t {鏡像名字} {項(xiàng)目的路徑可以是相對(duì)路徑}
通過標(biāo)準(zhǔn)輸入流:
通過標(biāo)準(zhǔn)輸入流的方式獲取Dockerfile的內(nèi)容
client不會(huì)打包上傳context目錄,因此對(duì)于一些ADD、COPY等涉及host本地文件復(fù)制的操作不能夠支持
docker build -t {鏡像名字} - < Dockerfile路徑
使用dockerfile構(gòu)建鏡像步驟
1.手動(dòng)制作一次鏡像
2.根據(jù)歷史命令來編寫dockerfile文件
3.使用dockerfile構(gòu)鍵鏡像
docker build --network=host -t 鏡像名:標(biāo)簽
4.測(cè)試鏡像
示例nginx
[root@docker01 ~]# mkdir /data/dockerfile/nginx
?
[root@docker01 ~]# cd /data/dockerfile/nginx
?
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
ADD nginx.repo /etc/yum.repos.d
RUN yum -y install nginx
CMD ["nginx", "-g"," daemon off;"]
?
[root@docker01 /data/dockerfile/nginx]# ll
total 100
-rw-r--r-- 1 root root 319 Jan 9 10:11 dockerfile
-rw-r--r-- 1 root root 398 Jan 9 08:47 nginx.repo
?
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos_nginx:v1 .
[root@docker01 /data/dockerfile/nginx]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos_nginx v1 ddc845aaedd9 28 minutes ago 347MB
centos 7 5e35e350aded 8 weeks ago 203MB
?
FROM 指定基礎(chǔ)鏡像
所謂的定制鏡像,必須是以一個(gè)鏡像為基礎(chǔ),在其上進(jìn)行定制。一個(gè) Dockerfile 中的 FROM 是必備的指定,并且必須是第一條指令。
FROM {bash鏡像}
RUN 執(zhí)行命令
RUN 指令是用來執(zhí)行命令行命令的,一個(gè)Dockerfile可以包含多個(gè) RUN,按照定義順序執(zhí)行。(每運(yùn)行一個(gè)run實(shí)際是開啟一個(gè)容器,只是執(zhí)行完成后刪除容器,只保留鏡像,所有最終的鏡像也是分層疊放在一起的。)
RUN 支持兩種運(yùn)行格式:
- shell 格式:
RUN <cmd>,這個(gè)會(huì)當(dāng)做/bin/sh -c “cmd” 運(yùn)行,就像直接在命令行中輸入的命令一樣。比如上面的:
RUN echo '<h1>Hello, Nginx!</h1>' > /usr/share/nginx/html/index.html
- exec 格式:
RUN ["可執(zhí)行文件", "參數(shù)1", "參數(shù)2", ...],Docker 把它當(dāng)做json的順序來解析,必須使用雙引號(hào),且可執(zhí)行文件必須是完整路徑。
示例nginx
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/*
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
RUN curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
RUN yum -y install nginx
CMD ["nginx", "-g"," daemon off;"]
#生成鏡像
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos7_nginx:v2 .
#啟動(dòng)進(jìn)項(xiàng)測(cè)試
[root@docker01 /data/dockerfile/nginx]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos7_nginx v2 be89fe1379e3 22 minutes ago 418MB
centos_nginx v1 ddc845aaedd9 2 hours ago 347MB
centos 7 5e35e350aded 8 weeks ago 203MB
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos7_nginx:v2
c8e5f2c35d75a349840eae203b59d21d3c9ab202b3b3cf77c49d2237bc028ff8
測(cè)試訪問

因?yàn)镈ockerfile 中的每一個(gè)指令都會(huì)建立一層,RUN 也不例外,每一個(gè)RUN 的行為,就和我們手工創(chuàng)建鏡象的過程一樣,新建一層,上面的這種寫法創(chuàng)建了4層,這樣會(huì)產(chǎn)生非常臃腫、非常多層的鏡像,不僅僅增加了構(gòu)建部署的時(shí)間,也容易出錯(cuò)。上面的Dockerfile正確寫法應(yīng)該如下:
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
CMD ["nginx", "-g"," daemon off;"]
這里只是更新yum源安裝nginx,實(shí)際只是為了安裝nginx,這里將四層合成一層更加的一層。
如果需要安裝nginx官方的可以參考第一個(gè)實(shí)例制作即可。只需創(chuàng)建官方源文件copy到對(duì)應(yīng)的保存目錄即可。
WORKDIR 指定工作目錄
格式:
- WORKDIR <工作目錄路徑>
使用 WORKDIR 指令可以來制定工作目錄(或者稱為當(dāng)前目錄),以后各層的當(dāng)前目錄就被改為指定的目錄,如該目錄不存在,則會(huì)自動(dòng)創(chuàng)建。相當(dāng)于cd。
COPY 復(fù)制文件
格式:
COPY <源路徑> ... <目標(biāo)路徑>
COPY ["<源路徑1>", ... "<目標(biāo)路徑>"]
和 RUN 指令一樣,也有兩種格式,一種類似于命令行,一種類似于函數(shù)調(diào)用。
說明:這里測(cè)試一個(gè)小游戲小鳥飛飛
示例
[root@docker01 /data/dockerfile/nginx]# ll
total 100
-rw-r--r-- 1 root root 292 Jan 9 11:32 dockerfile
-rw-r--r-- 1 root root 398 Jan 9 10:32 nginx.repo
drwxr-xr-x 3 root root 98 Jan 3 19:41 xiaoniao
-rw-r--r-- 1 root root 91985 Jan 8 11:37 xiaoniao.tar.gz
?
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
COPY xiaoniao .
CMD ["nginx", "-g"," daemon off;"]
?
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos7_nginx:v4 .
?
[root@docker01 /data/dockerfile/nginx]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos7_nginx v4 b79afda2fe6f 39 seconds ago 418MB
centos7_nginx v3 4a93800b7239 20 minutes ago 418MB
centos7_nginx v2 be89fe1379e3 53 minutes ago 418MB
centos_nginx v1 ddc845aaedd9 2 hours ago 347MB
centos 7 5e35e350aded 8 weeks ago 203MB
0 minutes 0.0.0.0:80->80/tcp nice_franklin
?
[root@docker01 /data/dockerfile/nginx]# docker stop $(docker ps -q)
c8e5f2c35d75
?
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos7_nginx:v4
987610dfb4a6c076a094695bbf382c28c81795e684e4486258d617d6626c6f57
?
[root@docker01 /data/dockerfile/nginx]# docker exec -it 564a5fb1fd91 /bin/bash
[root@564a5fb1fd91 html]# ls
2000.png 404.html en-US icons index.html poweredby.png
21.js 50x.html icon.png img nginx-logo.png sound1.mp3
測(cè)試訪問

[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
COPY xiaoniao ./xiaoniao
CMD ["nginx", "-g"," daemon off;"]
?
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos7_nginx:v5 .
?
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 81:80 centos7_nginx:v5
?
[root@docker01 /data/dockerfile/nginx]# docker exec -it cdea08555c04 /bin/bash
[root@cdea08555c04 html]# ls
404.html 50x.html en-US icons img index.html nginx-logo.png poweredby.png xiaoniao
測(cè)試訪問


從上面示例可以看出, <目標(biāo)路徑> 可以是容器內(nèi)的相對(duì)路徑,如果該路徑不存在,會(huì)自動(dòng)在復(fù)制文件前創(chuàng)建缺失目錄,且<目標(biāo)路徑>也可以是相對(duì)路徑(工作目錄可以用WORKDIR 指令來指定)
如果是拷貝目錄, 那么在 <目標(biāo)地址 >也必須寫上 拷貝的目錄的名稱 。否則是將該目錄下的所文件拷貝到目標(biāo)路徑
還需注意一點(diǎn),使用COPY指令,源文件的各種元數(shù)據(jù)都會(huì)保留,比如讀、寫、執(zhí)行權(quán)限、文件變更時(shí)間等。這個(gè)特性對(duì)于鏡像定制很有用。特別是構(gòu)建相關(guān)的文件都在使用Git進(jìn)行管理的時(shí)候。
ADD 更高級(jí)的復(fù)制文件
ADD 指令和COPY 的格式和性質(zhì)基本一致。但是在 COPY 基礎(chǔ)上增加了一些功能。
比如 <源路徑> 可能是一個(gè) URL,這種情況下,會(huì)自動(dòng)去下載這個(gè)鏈接的文件到 <目標(biāo)路徑>里,下載完成的文件權(quán)限自動(dòng)設(shè)置為 600,如果不是想要的權(quán)限,那么可以通過 RUN 進(jìn)行權(quán)限調(diào)整。
如果 <源路徑> 為一個(gè) tar 壓縮文件的話,或者壓縮格式為 gzip,bzip以及xz 的情況下,ADD 指令會(huì)自動(dòng)解壓縮這個(gè)壓縮文件到 <目標(biāo)路徑>去。
示例
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao .
CMD ["nginx", "-g"," daemon off;"]
?
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos7_nginx:v6 .
?
[root@docker01 /data/dockerfile/nginx]# docker rm -f $(docker ps -qa )
564a5fb1fd91
cdea08555c04
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos7_nginx:v6
訪問測(cè)試

[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao.tar.gz .
CMD ["nginx", "-g"," daemon off;"]
?
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos7_nginx:v7 .
?
[root@docker01 /data/dockerfile/nginx]# docker rm -f $(docker ps -qa )
09273df6a44b
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos7_nginx:v7
327c84ad622ff1809c586c83c4a28d3562b4638c0d7ebf31c62d88b1cbf3234e
測(cè)試訪問

VOLUME 定義匿名卷
格式:
VOLUME ["<路徑1>","<路徑2>"...]
VOLUME <路徑>
容器運(yùn)行時(shí)應(yīng)該盡量保持容器存儲(chǔ)層不會(huì)發(fā)生寫操作,對(duì)于數(shù)據(jù)庫類需要保存動(dòng)態(tài)數(shù)據(jù)的應(yīng)用,其數(shù)據(jù)文件應(yīng)該保存在數(shù)據(jù)卷中,為了防止運(yùn)行時(shí)忘記將動(dòng)態(tài)文件所保存目錄掛載為卷,在Dockerfile中,可以事先指定某些目錄掛載為匿名卷,這樣在運(yùn)行時(shí)如果用戶不指定掛載,其應(yīng)用也可以正常運(yùn)行,不會(huì)向容器存儲(chǔ)層寫入大量數(shù)據(jù)。
持久化
數(shù)據(jù)卷(文件或目錄)
-v卷名:/data/(第一次卷是空,會(huì)將容器中的數(shù)據(jù)復(fù)制到卷中,如果卷里有數(shù)據(jù),把卷數(shù)據(jù)掛載發(fā)到容器中)
-v src(宿主機(jī)目錄):dest(容器的目錄)
數(shù)據(jù)卷容器
--volumes-from (跟某一個(gè)已經(jīng)存在的容器掛載相同的卷)
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao .
VOLUME /usr/share/nginx/html
CMD ["nginx", "-g"," daemon off;"]
?
[root@docker01 ~]# docker run -d -p 80:80 centos_nginx:v8
85a83e7686bbbe832b56a1287cb25ee93c81d2bf1f382cff46bd8f0971adc2f9
[root@docker01 ~]# docker inspect 85a83e7686bbbe|grep volume
"Type": "volume",
"Source": "/var/lib/docker/volumes/d3a703e7685bba38585127494e04e2c77cc6c9923d14a8ea719862b506348448/_data",
[root@docker01 ~]# cd /var/lib/docker
docker/ docker-engine/
[root@docker01 ~]# cd /var/lib/docker/volumes/d3a703e7685bba38585127494e04e2c77cc6c9923d14a8ea719862b506348448/_data/
[root@docker01 /var/lib/docker/volumes/d3a703e7685bba38585127494e04e2c77cc6c9923d14a8ea719862b506348448/_data]# ls
2000.png 21.js icon.png img index.html sound1.mp3
測(cè)試訪問

重新開啟一個(gè)新的容器,然后修改默認(rèn)首頁
[root@docker01 ~]# docker run -d --name xiaoniao -p 81:80 centos_nginx:v8
測(cè)試訪問

數(shù)據(jù)卷容器
--volumes-from (跟某一個(gè)已經(jīng)存在的容器掛載相同的卷)
從新啟動(dòng)一個(gè)容器與之前的容器掛載相同的卷
[root@docker01 ~]# docker run -d -p 82:80 --volumes-from xiaoniao centos_nginx:v8
13197ed39494ac33460bb2d71f9276c031a747dcd2ec53fdf11cae9ba722d232
?
[root@docker01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
13197ed39494 centos_nginx:v8 "nginx -g ' daemon o…" 2 minutes ago Up 2 minutes 0.0.0.0:82->80/tcp gifted_lamarr
20aa69a9e60d centos_nginx:v8 "nginx -g ' daemon o…" 12 minutes ago Up 12 minutes 0.0.0.0:81->80/tcp xiaoniao
85a83e7686bb centos_nginx:v8 "nginx -g ' daemon o…" 17 minutes ago Up 17 minutes 0.0.0.0:80->80/tcp elated_keller
測(cè)試訪問

EXPOSE 聲明端口
格式:
- EXPOSE <端口1> [<端口2>...]
EXPOSE 指令是聲明運(yùn)行容器時(shí)提供的服務(wù)端口,這只是一個(gè)聲明,在運(yùn)行時(shí)并不會(huì)因?yàn)檫@個(gè)聲明就會(huì)開啟這個(gè)端口的服務(wù)。在Dockerfile中寫入這樣的聲明有兩個(gè)好處,一個(gè)是幫助使用鏡像者理解這個(gè)鏡像服務(wù)的守護(hù)端口,以方便配置映射;另一個(gè)用處則是在運(yùn)行時(shí)使用隨機(jī)端口映射時(shí),也就是 docker run -P 時(shí),會(huì)自動(dòng)隨機(jī)映射EXPOSE的端口。
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao .
EXPOSE 80 22
CMD ["nginx", "-g"," daemon off;"]
?
[root@docker01 /data/dockerfile/nginx]# docker build -t centos_nginx:v9 .
?
[root@docker01 /data/dockerfile/nginx]# docker run -d -P centos_nginx:v9
b0f5378bf1ff847ea896dcadcae341e0db225ee623229e4c30b73d8c296f8f67
?
[root@docker01 /data/dockerfile/nginx]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b0f5378bf1ff centos_nginx:v9 "nginx -g ' daemon o…" 11 seconds ago Up 10 seconds 0.0.0.0:32769->22/tcp, 0.0.0.0:32768->80/tcp busy_joliot
CMD 容器啟動(dòng)命令
容器不是虛擬機(jī),容器就是進(jìn)程,既然是進(jìn)程,那么在啟動(dòng)容器的時(shí)候,需要指定所運(yùn)行的程序及參數(shù)。CMD 指令就是用于指定容器默認(rèn)的主進(jìn)程啟動(dòng)命令的。(容易被改變)
CMD 指令有三種格式:
shell 格式: CMD <命令>
exec 格式:CMD ["可執(zhí)行文件","參數(shù)1","參數(shù)2", ...]
參數(shù)列表格式:CMD ["參數(shù)1","參數(shù)2",...] , 這個(gè)時(shí)候CMD作為 ENTRYPOINT的參數(shù)。
在指令格式上,一般推薦使用exec 格式,這類格式在解析時(shí)會(huì)被解析為JSON數(shù)組,因此一定要使用雙引號(hào) “” ,而不要使用單引號(hào)。
如果使用shell 格式的話,實(shí)際的命令會(huì)被包裝為 sh -c 的參數(shù)的形式進(jìn)行執(zhí)行。比如:
CMD echo $HOME</pre>
在實(shí)際執(zhí)行中,會(huì)將其變更為:
CMD ["sh", "-c", "echo $HOME"]
在運(yùn)行時(shí)可以指定新的命令來替代鏡像設(shè)置中的這個(gè)默認(rèn)命令,比如,centos鏡像默認(rèn)的 CMD 是 /bin/bash, 如果我們直接 docker run -it centos 的時(shí)候,會(huì)直接進(jìn)入 bash。我們也可以在運(yùn)行時(shí)指定別的命令,如 docker run -it centos cat /etc/passwd 。這就是用 cat /etc/passwd命令替換了默認(rèn)的 /bin/bash 命令了。
前臺(tái)執(zhí)行和后臺(tái)執(zhí)行的問題:
Docker 不是虛擬機(jī),容器中的應(yīng)用都應(yīng)該以前臺(tái)執(zhí)行,而不是像虛擬機(jī)、物理機(jī)里面那樣,用 start / systemctl 去啟動(dòng)后臺(tái)服務(wù),容器內(nèi)沒有后臺(tái)服務(wù)的概念。
比如:如果我們將CMD 寫成這樣:
CMD service nginx start
然后會(huì)發(fā)現(xiàn)容器執(zhí)行后就立刻退出了,甚至在容器內(nèi)使用systemctl 命令結(jié)果卻發(fā)現(xiàn)根本執(zhí)行不了。這就是因?yàn)闆]有搞明白前臺(tái)、后臺(tái)的概念,沒有區(qū)分容器和虛擬機(jī)的差異,依舊在以傳統(tǒng)虛擬機(jī)的角度去理解容器
對(duì)于容器而言,其啟動(dòng)程序就是容器應(yīng)用進(jìn)程,容器就是為了主進(jìn)程而存在的,主進(jìn)程退出,容器就失去了存在的意義,從而退出,其它輔助進(jìn)程不是它需要關(guān)心的東西。
而使用 service nginx start 命令,則是希望 start來已后臺(tái)守護(hù)進(jìn)程形式啟動(dòng)nginx服務(wù)。而 CMD service nginx start 會(huì)被理解為 CMD ["sh", "-c", "service nginx start"],因此主進(jìn)程實(shí)際上是sh, 那么當(dāng)service nginx start 命令結(jié)束后,sh 也就結(jié)束了, sh 作為主進(jìn)程退出了,自然就會(huì)令容器退出。
正確的做法是直接執(zhí)行 nginx 可執(zhí)行文件,并且要求以前臺(tái)形式。如:
CMD ["nginx", "-g"," daemon off;"]
ENTRYPOINT 入口點(diǎn)
ENTRYPOINT 的格式和 RUN 指令格式一樣,分為 exec 格式 和shell格式。
ENTRYPOINT 的目的和 CMD 一樣,都是在指定容器啟動(dòng)程序及參數(shù)。ENTRYPOINT 在運(yùn)行時(shí)也可以替代,不過比CMD要繁瑣,需要通過 docker run 的參數(shù) --entrypoint 來指定。
當(dāng)指定了 ENTRYPOINT 后,CMD 的含義就發(fā)生了改變,不再是直接運(yùn)行其命令,而是將 CMD 的內(nèi)容作為參數(shù)傳給 ENTRYPOINT 指令,換句話說實(shí)際執(zhí)行時(shí),將變?yōu)椋?/p>
ENTRYPOINT "<CMD>"
那么問題來了? 有了CMD ,為什么還要有ENTRYPOINT呢? 這種 ENTRYPOINT "<CMD>" 有什么好處呢?
場(chǎng)景一:讓鏡像變成命令一樣使用
假設(shè)我們需要一個(gè)得知自己當(dāng)前公網(wǎng)IP的鏡像,那么可以使用CMD來實(shí)現(xiàn):
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
CMD ["curl","-s","https://ip.cn"]
[root@docker01 /data/dockerfile/nginx]# docker build -t centos:vi .
Sending build context to Docker daemon 267.8kB
Step 1/2 : FROM centos:7
---> 5e35e350aded
Step 2/2 : CMD ["curl","-s","https://ip.cn"]
---> Running in 1cd0d54ea938
Removing intermediate container 1cd0d54ea938
---> 5dfbd8f7e484
Successfully built 5dfbd8f7e484
Successfully tagged centos:vi
?
[root@docker01 /data/dockerfile/nginx]# docker run centos:vi
?
{"ip": "101.81.163.198", "country": "上海市", "city": "電信"}
這樣看起來是可以當(dāng)做命令來使用,不過命令一般有參數(shù),CMD 中實(shí)質(zhì)的命令是 curl, 如果希望顯示HTTP頭信息,就需要加上 -i 參數(shù)。
[root@docker01 /data/dockerfile/nginx]# docker run centos:vi -i
docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"-i\": executable file not found in $PATH": unknown.
這里可以看到可執(zhí)行文件找不到的報(bào)錯(cuò), executable file not found。上面說過,跟在鏡像名后面的是 command, 運(yùn)行時(shí)會(huì)替換 CMD 的默認(rèn)值,因此這里的 -i 替換了原來的 CMD,而不是添加在原來的 curl -s https://ip.cn 后面,而 -i 根本不是命令,所以找不到。
那么如果我們希望加入 -i 參數(shù),我們就必須重新完整的輸入這個(gè)命令:
[root@docker01 /data/dockerfile/nginx]# docker run centos:vi curl -i https://ip.cn #替換了默認(rèn)的CMD
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 67 0 67 0 0 16 0 --:--:-- 0:00:03 --:--:-- 16
HTTP/1.1 200 OK
Date: Thu, 09 Jan 2020 12:26:29 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: __cfduid=d0419e04a3638ee1d3087c5cf80f494101578572789; expires=Sat, 08-Feb-20 12:26:29 GMT; path=/; domain=.ip.cn; HttpOnly; SameSite=Lax
CF-Cache-Status: DYNAMIC
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
CF-RAY: 5526665d7be1e4cc-LAX
?
{"ip": "101.81.163.198", "country": "上海市", "city": "電信"}
顯然這不是好的解決辦法,而是用 ENTRYPOINT 就可以解決這個(gè)問題:
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
ENTRYPOINT ["curl","-s","https://ip.cn"]
?
[root@docker01 /data/dockerfile/nginx]# docker build -t centos:v1 .
Sending build context to Docker daemon 267.8kB
Step 1/2 : FROM centos:7
---> 5e35e350aded
Step 2/2 : ENTRYPOINT ["curl","-s","https://ip.cn"]
---> Running in f992ba880014
Removing intermediate container f992ba880014
---> 8b36c3256b89
Successfully built 8b36c3256b89
Successfully tagged centos:v1
[root@docker01 /data/dockerfile/nginx]# docker run centos:v1
{"ip": "101.81.163.198", "country": "上海市", "city": "電信"}
[root@docker01 /data/dockerfile/nginx]# docker run centos:v1 -i
HTTP/1.1 200 OK
Date: Thu, 09 Jan 2020 12:32:11 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: __cfduid=d3c35693353a4686d5d6eebe258bb5d5c1578573131; expires=Sat, 08-Feb-20 12:32:11 GMT; path=/; domain=.ip.cn; HttpOnly; SameSite=Lax
CF-Cache-Status: DYNAMIC
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
CF-RAY: 55266eb75f10e4d0-LAX
?
{"ip": "101.81.163.198", "country": "上海市", "city": "電信"}
從上面可以看出,不加-i 參數(shù)可以運(yùn)行成功, 加上-i參數(shù)也可以運(yùn)行成功,且達(dá)到了curl -i 的效果。這是因?yàn)楫?dāng)存在 ENTRYPOINT 后,CMD 的內(nèi)容將會(huì)作為參數(shù)傳遞給 ENTRYPOINT ,而這里 -i 就是新的CMD,因此會(huì)作為參數(shù)傳遞給curl,從而達(dá)到了我們預(yù)期的效果。
場(chǎng)景二:應(yīng)用運(yùn)行前的準(zhǔn)備工作
啟動(dòng)容器就是啟動(dòng)主進(jìn)程,但有些時(shí)候,啟動(dòng)主進(jìn)程前,需要一些準(zhǔn)備工作。比如 mysql 類的數(shù)據(jù)庫,可能需要一些數(shù)據(jù)庫配置、初始化的工作,這些工作要在最終的mysql 服務(wù)器運(yùn)行之前解決。此外,可能希望避免使用root 用戶去啟動(dòng)服務(wù)器, 從而提高安全性,而在啟動(dòng)還需要以 root 身份執(zhí)行一些必要的準(zhǔn)備工作,最后切換到服務(wù)用戶啟動(dòng)服務(wù)等?;蛘叱朔?wù)外,其它命令依舊可以使用 root 身份執(zhí)行,方便調(diào)試等。
這些準(zhǔn)備工作是和容器 CMD 無關(guān)的,無論CMD 是什么,都需要事先進(jìn)行一個(gè)預(yù)處理的工作。這種情況下,可以寫一個(gè)腳本,然后放入 ENTRYPOINT 中去執(zhí)行,而這個(gè)腳本會(huì)接到的參數(shù) (也就是CMD)作為指令,在腳本最后執(zhí)行。比如官方的redis 中就是這樣做的:
FROM alpine:3.4
...
RUN addgroup -S redis && adduser -S -G redis redis
...
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 6379
CMD ["redis-server"]
可以看到其中為了 redis 服務(wù)創(chuàng)建了redis 用戶,并在最后指定了 ENTRYPOINT 為 docker-entrypoint.sh 腳本。
#!/bin/bash
...
# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
chown -R redis .
exec su-exec redis "$0" "$@"
fi
?
exec "$@"
該腳本的內(nèi)容就是根據(jù) CMD 的內(nèi)容來判斷,如果是 redis-server 的話,則切換到 redis用戶身份啟動(dòng)服務(wù),否則依舊使用 root 身份執(zhí)行。比如:
# docker run -it redis id
uid=0(root) gid=0(root) groups=0(root)
示例1
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao .
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
?
[root@docker01 /data/dockerfile/nginx]# docker build -t centos_nginx:v10 .
?
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos_nginx:v10
ff772730832e18068e01da1d1ce654314f222dc33e4628b8db8cc5b6f3703176
?
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 81:80 centos_nginx:v10 sdhdhj
6f7772a652a5759aff7ab635453b300a775b7a1d91a30a08a5c55999c2987799
?
[root@docker01 /data/dockerfile/nginx]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ff772730832e centos_nginx:v10 "nginx -g 'daemon of…" 32 seconds ago Up 31 seconds 0.0.0.0:80->80/tcp strange_pare
?
[root@docker01 /data/dockerfile/nginx]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6f7772a652a5 centos_nginx:v10 "nginx sdhdhj" 20 seconds ago Exited (1) 20 seconds ago tender_rubin
ff772730832e centos_nginx:v10 "nginx -g 'daemon of…" 37 seconds ago Up 37 seconds 0.0.0.0:80->80/tcp strange_pare
示例2
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao .
ADD init.sh /init.sh
ENTRYPOINT ["/bin/bash","/init.sh"]
?
[root@docker01 /data/dockerfile/nginx]# docker build -t centos_nginx:v11 .
?
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos_nginx:v11
eeac9eef5c6941bbd9581d4304d8f7f48ad89a39aecfd2e6479c50f9d424a144
?
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 81:80 centos_nginx:v11 1111 #參數(shù)被當(dāng)成參數(shù),該腳本沒有參數(shù),所以可以正常啟動(dòng)
fb656151c391b84411cb8bfb6e2b8e5b6b321dc9ca1246f2b00026e13c01ed89
?
[root@docker01 /data/dockerfile/nginx]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fb656151c391 centos_nginx:v11 "/bin/bash /init.sh …" 10 seconds ago Up 10 seconds 0.0.0.0:81->80/tcp lucid_wilson
eeac9eef5c69 centos_nginx:v11 "/bin/bash /init.sh" 29 seconds ago Up 28 seconds 0.0.0.0:80->80/tcp affectionate_pike
?
[root@docker01 /data/dockerfile/nginx]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fb656151c391 centos_nginx:v11 "/bin/bash /init.sh …" 30 seconds ago Up 29 seconds 0.0.0.0:81->80/tcp lucid_wilson
eeac9eef5c69 centos_nginx:v11 "/bin/bash /init.sh" 49 seconds ago Up 48 seconds 0.0.0.0:80->80/tcp affectionate_pike
ENV 設(shè)置環(huán)境變量
格式:
ENV <key><value>
ENV<key1>=<value1> <key2>=<value2>
這個(gè)指令很簡單,就是設(shè)置環(huán)境變量,無論是后面的其它指令,如RUN,還是運(yùn)行時(shí)的應(yīng)用,都可以直接使用這里定義的環(huán)境變量。
[root@docker01 /data/dockerfile/nginx]# Dockerfile #查看編輯好的Dockerfile文件內(nèi)容
FROM centos
ENV name1 xiaobai
ENV name2=zhazha name3=cainiao
[root@docker01 /data/dockerfile/nginx]# build -t mycentos:v5 . #構(gòu)建鏡像
[root@docker01 /data/dockerfile/nginx]# run --rm -it --name test mycentos:v5 #啟動(dòng)一個(gè)容器,并以交互式啟動(dòng)
[root@b55929b2c63f /]# echo $name1 #進(jìn)入容器后調(diào)用環(huán)境變量name1
xiaobai
[root@b55929b2c63f /]# echo $name2 $name3 #調(diào)用環(huán)境變量name2,name3
zhazha cainiao
定義了環(huán)境變量,可以在后續(xù)的指令中直接使用環(huán)境變量
FROM centos
ENV URL="https://nginx.org/download/"
ADD $URLnginx.1.17.7.tar.gz /usr/local/
示例
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
ADD http://nginx.org/download/nginx-1.17.7.tar.gz /opt/
RUN tar -xvzf /opt/nginx-1.17.7.tar.gz -C /usr/local/src/ \
&& useradd -M -s /sbin/nologin nginx
WORKDIR /usr/local/src/nginx-1.17.7
RUN ./configure --user=nginx --group=nginx --prefix=/usr/local/nginx --with-file-aio --with-http_ssl_module --wi
th-http_realip_module --with-http_addition_module --with-http_xslt_module --with-http_image_filter_module --with
-http_geoip_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module -
-with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index
_module --with-http_secure_link_module --with-http_degradation_module --with-http_stub_status_module && make &&
make install
VOLUME ["/usr/local/nginx/html"]
RUN rm -rf /opt/nginx-1.17.7.tar.gz
ENV PATH=/usr/local/nginx/sbin:$PATH
EXPOSE 80
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
?
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos_nginxsrc:v4 .
?
[root@docker01 /data/dockerfile/nginx]# docker run -d -P centos_nginxsrc:v4
?
root@docker01 /data/dockerfile/nginx]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8544d8311d16 centos_nginxsrc:v4 "nginx -g 'daemon of…" 4 minutes ago Up 4 minutes 0.0.0.0:32771->80/tcp gallant_neumann
訪問測(cè)試

ARG 構(gòu)建參數(shù)
格式:
- ARG <參數(shù)名>[=<默認(rèn)值>]
構(gòu)建參數(shù)和 ENV的效果一樣,都是設(shè)置環(huán)境變量。所不同的是,ARG 所設(shè)置的構(gòu)建環(huán)境和環(huán)境變量,在將來容器運(yùn)行時(shí)是不會(huì)存在這些環(huán)境變量的。但是不要因此就使用 ARG 保存密碼之類的信息,因?yàn)?docker history 還是可以看到所有值的。
Dockerfile 中的ARG指令是定義參數(shù)名稱,以及定義其默認(rèn)值。該默認(rèn)值可以在構(gòu)建命令 docker build 中用 --build-arg <參數(shù)名>=<值> 來覆蓋。