02.Dockerfile 指令

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)建變得比較緩慢。

因此在 COPYADD 指令中選擇的時(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ù),并在最后指定了 ENTRYPOINTdocker-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): ADDCOPY余佛、ENV柠新、EXPOSELABEL辉巡、USER恨憎、WORKDIRVOLUME郊楣、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 指令。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末球化,一起剝皮案震驚了整個(gè)濱河市秽晚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌筒愚,老刑警劉巖赴蝇,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異巢掺,居然都是意外死亡句伶,警方通過(guò)查閱死者的電腦和手機(jī)劲蜻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)考余,“玉大人先嬉,你說(shuō)我怎么就攤上這事〕蹋” “怎么了疫蔓?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)身冬。 經(jīng)常有香客問(wèn)我衅胀,道長(zhǎng),這世上最難降的妖魔是什么酥筝? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任滚躯,我火速辦了婚禮,結(jié)果婚禮上嘿歌,老公的妹妹穿的比我還像新娘掸掏。我一直安慰自己,他們只是感情好宙帝,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布丧凤。 她就那樣靜靜地躺著,像睡著了一般茄唐。 火紅的嫁衣襯著肌膚如雪息裸。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天沪编,我揣著相機(jī)與錄音呼盆,去河邊找鬼。 笑死蚁廓,一個(gè)胖子當(dāng)著我的面吹牛访圃,可吹牛的內(nèi)容都是我干的柏靶。 我是一名探鬼主播覆履,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼驻粟!你這毒婦竟也來(lái)了饭宾?” 一聲冷哼從身側(cè)響起批糟,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎看铆,沒(méi)想到半個(gè)月后徽鼎,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年否淤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悄但。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡石抡,死狀恐怖檐嚣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情啰扛,我是刑警寧澤嚎京,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站隐解,受9級(jí)特大地震影響挖藏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厢漩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岩臣。 院中可真熱鬧溜嗜,春花似錦、人聲如沸架谎。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谷扣。三九已至土全,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間会涎,已是汗流浹背裹匙。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留末秃,地道東北人概页。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像练慕,于是被迫代替她去往敵國(guó)和親惰匙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 一铃将、Docker創(chuàng)建鏡像的方式有兩種: 一種通過(guò)commit的方式:把做了一系列操作的容器關(guān)閉项鬼,然后利用docke...
    jie0112閱讀 3,825評(píng)論 0 3
  • 縱有千金何道富,不如復(fù)載五車(chē)書(shū) Dockerfile文件由一些列的指令組成劲阎,它是用來(lái)構(gòu)建docker鏡像的绘盟,類(lèi)似于...
    olaH閱讀 1,099評(píng)論 2 5
  • 1.Dockerfile指令詳解 1.FROM指定基礎(chǔ)鏡像 FROM指令用于指定其后構(gòu)建新鏡像所使用的基礎(chǔ)鏡像。如...
    Michaelhbjian閱讀 1,353評(píng)論 0 0
  • 學(xué)習(xí)完整課程請(qǐng)移步 互聯(lián)網(wǎng) Java 全棧工程師 我們已經(jīng)介紹了 FROM,RUN奥此,還提及了 COPY, ADD弧哎,...
    擼帝閱讀 585評(píng)論 0 6
  • Dockerfile文件中,除了有FROM指令和RUN指令外稚虎,還有幾個(gè)比較常用的指令 1撤嫩、 COTY 復(fù)制文件 格...
    彩虹之夢(mèng)閱讀 644評(píng)論 0 0