Dockerfile 指令
概述
我們已經(jīng)介紹了 FROM
捉邢,RUN
植榕,還提及了 COPY
, ADD
脓钾,其實(shí) Dockerfile 功能很強(qiáng)大,它提供了十多個(gè)指令越锈。下面我們繼續(xù)講解其他的指令仗嗦。
COPY
格式:
COPY <源路徑>... <目標(biāo)路徑>
COPY ["<源路徑1>",... "<目標(biāo)路徑>"]
和 RUN
指令一樣,也有兩種格式甘凭,一種類(lèi)似于命令行稀拐,一種類(lèi)似于函數(shù)調(diào)用。
COPY
指令將從構(gòu)建上下文目錄中 <源路徑>
的文件/目錄復(fù)制到新的一層的鏡像內(nèi)的 <目標(biāo)路徑>
位置丹弱。比如:
COPY package.json /usr/src/app/
<源路徑>
可以是多個(gè)德撬,甚至可以是通配符,其通配符規(guī)則要滿(mǎn)足 Go 的 filepath.Match
規(guī)則蹈矮,如:
COPY hom* /mydir/
COPY hom?.txt /mydir/
<目標(biāo)路徑>
可以是容器內(nèi)的絕對(duì)路徑砰逻,也可以是相對(duì)于工作目錄的相對(duì)路徑(工作目錄可以用 WORKDIR
指令來(lái)指定)。目標(biāo)路徑不需要事先創(chuàng)建泛鸟,如果目錄不存在會(huì)在復(fù)制文件前先行創(chuàng)建缺失目錄蝠咆。
此外,還需要注意一點(diǎn),使用 COPY
指令刚操,源文件的各種元數(shù)據(jù)都會(huì)保留闸翅。比如讀、寫(xiě)菊霜、執(zhí)行權(quán)限坚冀、文件變更時(shí)間等。這個(gè)特性對(duì)于鏡像定制很有用鉴逞。特別是構(gòu)建相關(guān)文件都在使用 Git 進(jìn)行管理的時(shí)候记某。
ADD
ADD
指令和 COPY
的格式和性質(zhì)基本一致。但是在 COPY
基礎(chǔ)上增加了一些功能构捡。
比如 <源路徑>
可以是一個(gè) URL
液南,這種情況下,Docker 引擎會(huì)試圖去下載這個(gè)鏈接的文件放到 <目標(biāo)路徑>
去勾徽。下載后的文件權(quán)限自動(dòng)設(shè)置為 600
滑凉,如果這并不是想要的權(quán)限,那么還需要增加額外的一層 RUN
進(jìn)行權(quán)限調(diào)整喘帚,另外畅姊,如果下載的是個(gè)壓縮包,需要解壓縮吹由,也一樣還需要額外的一層 RUN
指令進(jìn)行解壓縮若未。所以不如直接使用 RUN
指令,然后使用 wget
或者 curl
工具下載倾鲫,處理權(quán)限陨瘩、解壓縮、然后清理無(wú)用文件更合理级乍。因此,這個(gè)功能其實(shí)并不實(shí)用帚湘,而且不推薦使用玫荣。
如果 <源路徑>
為一個(gè) tar
壓縮文件的話(huà),壓縮格式為 gzip
, bzip2
以及 xz
的情況下大诸,ADD
指令將會(huì)自動(dòng)解壓縮這個(gè)壓縮文件到 <目標(biāo)路徑>
去捅厂。
在某些情況下,這個(gè)自動(dòng)解壓縮的功能非常有用资柔,比如官方鏡像 ubuntu
中:
FROM scratch
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
但在某些情況下焙贷,如果我們真的是希望復(fù)制個(gè)壓縮文件進(jìn)去,而不解壓縮贿堰,這時(shí)就不可以使用 ADD
命令了辙芍。
在 Docker 官方的 Dockerfile 最佳實(shí)踐文檔
中要求,盡可能的使用 COPY
,因?yàn)?COPY
的語(yǔ)義很明確故硅,就是復(fù)制文件而已庶灿,而 ADD
則包含了更復(fù)雜的功能,其行為也不一定很清晰吃衅。最適合使用 ADD
的場(chǎng)合往踢,就是所提及的需要自動(dòng)解壓縮的場(chǎng)合。
另外需要注意的是徘层,ADD
指令會(huì)令鏡像構(gòu)建緩存失效峻呕,從而可能會(huì)令鏡像構(gòu)建變得比較緩慢。
因此在 COPY
和 ADD
指令中選擇的時(shí)候趣效,可以遵循這樣的原則瘦癌,所有的文件復(fù)制均使用 COPY
指令,僅在需要自動(dòng)解壓縮的場(chǎng)合使用 ADD
英支。
CMD
CMD
指令的格式和 RUN
相似佩憾,也是兩種格式:
-
shell
格式:CMD <命令>
-
exec
格式:CMD ["可執(zhí)行文件", "參數(shù)1", "參數(shù)2"...]
- 參數(shù)列表格式:
CMD ["參數(shù)1", "參數(shù)2"...]
。在指定了ENTRYPOINT
指令后干花,用CMD
指定具體的參數(shù)妄帘。
之前介紹容器的時(shí)候曾經(jīng)說(shuō)過(guò),Docker 不是虛擬機(jī)池凄,容器就是進(jìn)程抡驼。既然是進(jìn)程,那么在啟動(dòng)容器的時(shí)候肿仑,需要指定所運(yùn)行的程序及參數(shù)致盟。CMD
指令就是用于指定默認(rèn)的容器主進(jìn)程的啟動(dòng)命令的。
在運(yùn)行時(shí)可以指定新的命令來(lái)替代鏡像設(shè)置中的這個(gè)默認(rèn)命令尤慰,比如馏锡,ubuntu
鏡像默認(rèn)的 CMD
是 /bin/bash
,如果我們直接 docker run -it ubuntu
的話(huà)伟端,會(huì)直接進(jìn)入 bash
杯道。我們也可以在運(yùn)行時(shí)指定運(yùn)行別的命令,如 docker run -it ubuntu cat /etc/os-release
责蝠。這就是用 cat /etc/os-release
命令替換了默認(rèn)的 /bin/bash
命令了党巾,輸出了系統(tǒng)版本信息。
在指令格式上霜医,一般推薦使用 exec
格式齿拂,這類(lèi)格式在解析時(shí)會(huì)被解析為 JSON 數(shù)組,因此一定要使用雙引號(hào) "
肴敛,而不要使用單引號(hào)署海。
如果使用 shell
格式的話(huà),實(shí)際的命令會(huì)被包裝為 sh -c
的參數(shù)的形式進(jìn)行執(zhí)行。比如:
CMD echo $HOME
在實(shí)際執(zhí)行中叹侄,會(huì)將其變更為:
CMD [ "sh", "-c", "echo $HOME" ]
這就是為什么我們可以使用環(huán)境變量的原因巩搏,因?yàn)檫@些環(huán)境變量會(huì)被 shell 進(jìn)行解析處理。
提到 CMD
就不得不提容器中應(yīng)用在前臺(tái)執(zhí)行和后臺(tái)執(zhí)行的問(wèn)題趾代。這是初學(xué)者常出現(xiàn)的一個(gè)混淆贯底。
Docker 不是虛擬機(jī),容器中的應(yīng)用都應(yīng)該以前臺(tái)執(zhí)行撒强,而不是像虛擬機(jī)禽捆、物理機(jī)里面那樣,用 upstart/systemd 去啟動(dòng)后臺(tái)服務(wù)飘哨,容器內(nèi)沒(méi)有后臺(tái)服務(wù)的概念胚想。
一些初學(xué)者將 CMD
寫(xiě)為:
CMD service nginx start
然后發(fā)現(xiàn)容器執(zhí)行后就立即退出了。甚至在容器內(nèi)去使用 systemctl
命令結(jié)果卻發(fā)現(xiàn)根本執(zhí)行不了芽隆。這就是因?yàn)闆](méi)有搞明白前臺(tái)浊服、后臺(tái)的概念,沒(méi)有區(qū)分容器和虛擬機(jī)的差異胚吁,依舊在以傳統(tǒng)虛擬機(jī)的角度去理解容器牙躺。
對(duì)于容器而言,其啟動(dòng)程序就是容器應(yīng)用進(jìn)程腕扶,容器就是為了主進(jìn)程而存在的孽拷,主進(jìn)程退出,容器就失去了存在的意義半抱,從而退出脓恕,其它輔助進(jìn)程不是它需要關(guān)心的東西。
而使用 service nginx start
命令窿侈,則是希望 upstart 來(lái)以后臺(tái)守護(hù)進(jìn)程形式啟動(dòng) nginx
服務(wù)炼幔。而剛才說(shuō)了 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)形式運(yùn)行衙傀。比如:
CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT
ENTRYPOINT
的格式和 RUN
指令格式一樣,分為 exec
格式和 shell
格式萨咕。
ENTRYPOINT
的目的和 CMD
一樣统抬,都是在指定容器啟動(dòng)程序及參數(shù)。ENTRYPOINT
在運(yùn)行時(shí)也可以替代,不過(guò)比 CMD
要略顯繁瑣聪建,需要通過(guò) docker run
的參數(shù) --entrypoint
來(lái)指定钙畔。
當(dāng)指定了 ENTRYPOINT
后,CMD
的含義就發(fā)生了改變金麸,不再是直接的運(yùn)行其命令擎析,而是將 CMD
的內(nèi)容作為參數(shù)傳給 ENTRYPOINT
指令,換句話(huà)說(shuō)實(shí)際執(zhí)行時(shí)挥下,將變?yōu)椋?/p>
<ENTRYPOINT> "<CMD>"
那么有了 CMD
后揍魂,為什么還要有 ENTRYPOINT
呢?這種 <ENTRYPOINT> "<CMD>"
有什么好處么棚瘟?讓我們來(lái)看幾個(gè)場(chǎng)景现斋。
場(chǎng)景一:讓鏡像變成像命令一樣使用
假設(shè)我們需要一個(gè)得知自己當(dāng)前公網(wǎng) IP 的鏡像,那么可以先用 CMD
來(lái)實(shí)現(xiàn):
FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "http://ip.cn" ]
假如我們使用 docker build -t myip .
來(lái)構(gòu)建鏡像的話(huà)偎蘸,如果我們需要查詢(xún)當(dāng)前公網(wǎng) IP庄蹋,只需要執(zhí)行:
docker run myip
當(dāng)前 IP:61.148.226.66 來(lái)自:北京市 聯(lián)通
嗯,這么看起來(lái)好像可以直接把鏡像當(dāng)做命令使用了迷雪,不過(guò)命令總有參數(shù)限书,如果我們希望加參數(shù)呢?比如從上面的 CMD
中可以看到實(shí)質(zhì)的命令是 curl
振乏,那么如果我們希望顯示 HTTP 頭信息蔗包,就需要加上 -i
參數(shù)。那么我們可以直接加 -i
參數(shù)給 docker run myip
么慧邮?
docker run myip -i
docker: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".
我們可以看到可執(zhí)行文件找不到的報(bào)錯(cuò)调限,executable file not found
。之前我們說(shuō)過(guò)误澳,跟在鏡像名后面的是 command
耻矮,運(yùn)行時(shí)會(huì)替換 CMD
的默認(rèn)值。因此這里的 -i
替換了原來(lái)的 CMD
忆谓,而不是添加在原來(lái)的 curl -s http://ip.cn
后面裆装。而 -i
根本不是命令,所以自然找不到倡缠。
那么如果我們希望加入 -i
這參數(shù)哨免,我們就必須重新完整的輸入這個(gè)命令:
docker run myip curl -s http://ip.cn -i
這顯然不是很好的解決方案,而使用 ENTRYPOINT
就可以解決這個(gè)問(wèn)題£悸伲現(xiàn)在我們重新用 ENTRYPOINT
來(lái)實(shí)現(xiàn)這個(gè)鏡像:
FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]
這次我們?cè)賮?lái)嘗試直接使用 docker run myip -i
:
docker run myip
當(dāng)前 IP:61.148.226.66 來(lái)自:北京市 聯(lián)通
docker run myip -i
HTTP/1.1 200 OK
Server: nginx/1.8.0
Date: Tue, 22 Nov 2016 05:12:40 GMT
Content-Type: text/html; charset=UTF-8
Vary: Accept-Encoding
X-Powered-By: PHP/5.6.24-1~dotdeb+7.1
X-Cache: MISS from cache-2
X-Cache-Lookup: MISS from cache-2:80
X-Cache: MISS from proxy-2_6
Transfer-Encoding: chunked
Via: 1.1 cache-2:80, 1.1 proxy-2_6:8006
Connection: keep-alive
當(dāng)前 IP:61.148.226.66 來(lái)自:北京市 聯(lián)通
可以看到琢唾,這次成功了。這是因?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
類(lèi)的數(shù)據(jù)庫(kù),可能需要一些數(shù)據(jù)庫(kù)配置踪危、初始化的工作蔬浙,這些工作要在最終的 mysql 服務(wù)器運(yùn)行之前解決。
此外贞远,可能希望避免使用 root
用戶(hù)去啟動(dòng)服務(wù)畴博,從而提高安全性,而在啟動(dòng)服務(wù)前還需要以 root
身份執(zhí)行一些必要的準(zhǔn)備工作蓝仲,最后切換到服務(wù)用戶(hù)身份啟動(dòng)服務(wù)俱病。或者除了服務(wù)外袱结,其它命令依舊可以使用 root
身份執(zhí)行亮隙,方便調(diào)試等。
這些準(zhǔn)備工作是和容器 CMD
無(wú)關(guān)的垢夹,無(wú)論 CMD
為什么溢吻,都需要事先進(jìn)行一個(gè)預(yù)處理的工作。這種情況下果元,可以寫(xiě)一個(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 用戶(hù),并在最后指定了 ENTRYPOINT
為 docker-entrypoint.sh
腳本倡怎。
#!/bin/sh
# 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)容來(lái)判斷迅耘,如果是 redis-server
的話(huà),則切換到 redis
用戶(hù)身份啟動(dòng)服務(wù)器监署,否則依舊使用 root
身份執(zhí)行颤专。比如:
docker run -it redis id
uid=0(root) gid=0(root) groups=0(root)
ENV
格式有兩種:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
這個(gè)指令很簡(jiǎn)單,就是設(shè)置環(huán)境變量而已钠乏,無(wú)論是后面的其它指令血公,如 RUN
,還是運(yùn)行時(shí)的應(yīng)用缓熟,都可以直接使用這里定義的環(huán)境變量累魔。
ENV VERSION=1.0 DEBUG=on \
NAME="Happy Feet"
這個(gè)例子中演示了如何換行,以及對(duì)含有空格的值用雙引號(hào)括起來(lái)的辦法够滑,這和 Shell 下的行為是一致的垦写。
定義了環(huán)境變量,那么在后續(xù)的指令中彰触,就可以使用這個(gè)環(huán)境變量梯投。比如在官方 node
鏡像 Dockerfile
中,就有類(lèi)似這樣的代碼:
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
&& rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
在這里先定義了環(huán)境變量 NODE_VERSION
况毅,其后的 RUN
這層里分蓖,多次使用 $NODE_VERSION
來(lái)進(jìn)行操作定制《恚可以看到么鹤,將來(lái)升級(jí)鏡像構(gòu)建版本的時(shí)候,只需要更新 7.2.0
即可味廊,Dockerfile
構(gòu)建維護(hù)變得更輕松了蒸甜。
下列指令可以支持環(huán)境變量展開(kāi): ADD
、COPY
余佛、ENV
柠新、EXPOSE
、LABEL
辉巡、USER
恨憎、WORKDIR
、VOLUME
郊楣、STOPSIGNAL
憔恳、ONBUILD
。
可以從這個(gè)指令列表里感覺(jué)到痢甘,環(huán)境變量可以使用的地方很多喇嘱,很強(qiáng)大。通過(guò)環(huán)境變量塞栅,我們可以讓一份 Dockerfile
制作更多的鏡像者铜,只需使用不同的環(huán)境變量即可。
VOLUME
格式為:
VOLUME ["<路徑1>", "<路徑2>"...]
VOLUME <路徑>
之前我們說(shuō)過(guò)放椰,容器運(yùn)行時(shí)應(yīng)該盡量保持容器存儲(chǔ)層不發(fā)生寫(xiě)操作作烟,對(duì)于數(shù)據(jù)庫(kù)類(lèi)需要保存動(dòng)態(tài)數(shù)據(jù)的應(yīng)用,其數(shù)據(jù)庫(kù)文件應(yīng)該保存于卷(volume)中砾医,后面的章節(jié)我們會(huì)進(jìn)一步介紹 Docker 卷的概念拿撩。為了防止運(yùn)行時(shí)用戶(hù)忘記將動(dòng)態(tài)文件所保存目錄掛載為卷,在 Dockerfile
中如蚜,我們可以事先指定某些目錄掛載為匿名卷压恒,這樣在運(yùn)行時(shí)如果用戶(hù)不指定掛載影暴,其應(yīng)用也可以正常運(yùn)行,不會(huì)向容器存儲(chǔ)層寫(xiě)入大量數(shù)據(jù)探赫。
VOLUME /data
這里的 /data
目錄就會(huì)在運(yùn)行時(shí)自動(dòng)掛載為匿名卷型宙,任何向 /data
中寫(xiě)入的信息都不會(huì)記錄進(jìn)容器存儲(chǔ)層,從而保證了容器存儲(chǔ)層的無(wú)狀態(tài)化伦吠。當(dāng)然妆兑,運(yùn)行時(shí)可以覆蓋這個(gè)掛載設(shè)置。比如:
docker run -d -v mydata:/data xxxx
在這行命令中毛仪,就使用了 mydata
這個(gè)命名卷掛載到了 /data
這個(gè)位置搁嗓,替代了 Dockerfile
中定義的匿名卷的掛載配置。
EXPOSE
格式為 EXPOSE <端口1> [<端口2>...]
箱靴。
EXPOSE
指令是聲明運(yùn)行時(shí)容器提供服務(wù)端口腺逛,這只是一個(gè)聲明,在運(yùn)行時(shí)并不會(huì)因?yàn)檫@個(gè)聲明應(yīng)用就會(huì)開(kāi)啟這個(gè)端口的服務(wù)刨晴。在 Dockerfile 中寫(xiě)入這樣的聲明有兩個(gè)好處屉来,一個(gè)是幫助鏡像使用者理解這個(gè)鏡像服務(wù)的守護(hù)端口,以方便配置映射狈癞;另一個(gè)用處則是在運(yùn)行時(shí)使用隨機(jī)端口映射時(shí)茄靠,也就是 docker run -P
時(shí),會(huì)自動(dòng)隨機(jī)映射 EXPOSE
的端口蝶桶。
此外慨绳,在早期 Docker 版本中還有一個(gè)特殊的用處。以前所有容器都運(yùn)行于默認(rèn)橋接網(wǎng)絡(luò)中真竖,因此所有容器互相之間都可以直接訪(fǎng)問(wèn)脐雪,這樣存在一定的安全性問(wèn)題。于是有了一個(gè) Docker 引擎參數(shù) --icc=false
恢共,當(dāng)指定該參數(shù)后战秋,容器間將默認(rèn)無(wú)法互訪(fǎng),除非互相間使用了 --links
參數(shù)的容器才可以互通讨韭,并且只有鏡像中 EXPOSE
所聲明的端口才可以被訪(fǎng)問(wèn)脂信。這個(gè) --icc=false
的用法,在引入了 docker network
后已經(jīng)基本不用了透硝,通過(guò)自定義網(wǎng)絡(luò)可以很輕松的實(shí)現(xiàn)容器間的互聯(lián)與隔離狰闪。
要將 EXPOSE
和在運(yùn)行時(shí)使用 -p <宿主端口>:<容器端口>
區(qū)分開(kāi)來(lái)。-p
濒生,是映射宿主端口和容器端口埋泵,換句話(huà)說(shuō),就是將容器的對(duì)應(yīng)端口服務(wù)公開(kāi)給外界訪(fǎng)問(wèn),而 EXPOSE
僅僅是聲明容器打算使用什么端口而已丽声,并不會(huì)自動(dòng)在宿主進(jìn)行端口映射礁蔗。
WORKDIR
格式為 WORKDIR <工作目錄路徑>
。
使用 WORKDIR
指令可以來(lái)指定工作目錄(或者稱(chēng)為當(dāng)前目錄)雁社,以后各層的當(dāng)前目錄就被改為指定的目錄瘦麸,如該目錄不存在,WORKDIR
會(huì)幫你建立目錄歧胁。
之前提到一些初學(xué)者常犯的錯(cuò)誤是把 Dockerfile
等同于 Shell 腳本來(lái)書(shū)寫(xiě),這種錯(cuò)誤的理解還可能會(huì)導(dǎo)致出現(xiàn)下面這樣的錯(cuò)誤:
RUN cd /app
RUN echo "hello" > world.txt
如果將這個(gè) Dockerfile
進(jìn)行構(gòu)建鏡像運(yùn)行后厉碟,會(huì)發(fā)現(xiàn)找不到 /app/world.txt
文件喊巍,或者其內(nèi)容不是 hello
。原因其實(shí)很簡(jiǎn)單箍鼓,在 Shell 中崭参,連續(xù)兩行是同一個(gè)進(jìn)程執(zhí)行環(huán)境,因此前一個(gè)命令修改的內(nèi)存狀態(tài)款咖,會(huì)直接影響后一個(gè)命令何暮;而在 Dockerfile
中,這兩行 RUN
命令的執(zhí)行環(huán)境根本不同铐殃,是兩個(gè)完全不同的容器海洼。這就是對(duì) Dockerfile
構(gòu)建分層存儲(chǔ)的概念不了解所導(dǎo)致的錯(cuò)誤。
之前說(shuō)過(guò)每一個(gè) RUN
都是啟動(dòng)一個(gè)容器富腊、執(zhí)行命令坏逢、然后提交存儲(chǔ)層文件變更。第一層 RUN cd /app
的執(zhí)行僅僅是當(dāng)前進(jìn)程的工作目錄變更赘被,一個(gè)內(nèi)存上的變化而已是整,其結(jié)果不會(huì)造成任何文件變更。而到第二層的時(shí)候民假,啟動(dòng)的是一個(gè)全新的容器浮入,跟第一層的容器更完全沒(méi)關(guān)系,自然不可能繼承前一層構(gòu)建過(guò)程中的內(nèi)存變化羊异。
因此如果需要改變以后各層的工作目錄的位置事秀,那么應(yīng)該使用 WORKDIR
指令。