Dockerfile 中的 COPY 與 ADD 命令
原文鏈接:https://www.cnblogs.com/sparkdev/p/9573248.html
Dockerfile 中提供了兩個(gè)非常相似的命令 COPY 和 ADD虏肾,本文嘗試解釋這兩個(gè)命令的基本功能鄙信,以及其異同點(diǎn)鸡挠,然后總結(jié)其各自適合的應(yīng)用場(chǎng)景叙身。
Build 上下文的概念
在使用 docker build 命令通過(guò) Dockerfile 創(chuàng)建鏡像時(shí)纳决,會(huì)產(chǎn)生一個(gè) build 上下文(context)猬腰。所謂的 build 上下文就是 docker build 命令的 PATH 或 URL 指定的路徑中的文件的集合际起。在鏡像 build 過(guò)程中可以引用上下文中的任何文件滓鸠,比如我們要介紹的 COPY 和 ADD 命令淑仆,就可以引用上下文中的文件。
默認(rèn)情況下 docker build -t testx . 命令中的 . 表示 build 上下文為當(dāng)前目錄哥力。當(dāng)然我們可以指定一個(gè)目錄作為上下文蔗怠,比如下面的命令:
$ docker build -t testx /home/nick/hc
我們指定 /home/nick/hc 目錄為 build 上下文,默認(rèn)情況下 docker 會(huì)使用在上下文的根目錄下找到的 Dockerfile 文件吩跋。
COPY 和 ADD 命令不能拷貝上下文之外的本地文件
對(duì)于 COPY 和 ADD 命令來(lái)說(shuō)寞射,如果要把本地的文件拷貝到鏡像中,那么本地的文件必須是在上下文目錄中的文件锌钮。其實(shí)這一點(diǎn)很好解釋桥温,因?yàn)樵趫?zhí)行 build 命令時(shí),docker 客戶端會(huì)把上下文中的所有文件發(fā)送給 docker daemon梁丘∏纸考慮 docker 客戶端和 docker daemon 不在同一臺(tái)機(jī)器上的情況,build 命令只能從上下文中獲取文件氛谜。如果我們?cè)?Dockerfile 的 COPY 和 ADD 命令中引用了上下文中沒(méi)有的文件掏觉,就會(huì)收到類似下面的錯(cuò)誤:
與 WORKDIR 協(xié)同工作
WORKDIR 命令為后續(xù)的 RUN、CMD值漫、COPY澳腹、ADD 等命令配置工作目錄。在設(shè)置了 WORKDIR 命令后杨何,接下來(lái)的 COPY 和 ADD 命令中的相對(duì)路徑就是相對(duì)于 WORKDIR 指定的路徑酱塔。比如我們?cè)?Dockerfile 中添加下面的命令:
WORKDIR /app
COPY checkredis.py .
然后構(gòu)建名稱為 testx 的容器鏡像,并運(yùn)行一個(gè)容器查看文件路徑:
checkredis.py 文件就是被復(fù)制到了 WORKDIR /app 目錄下危虱。
COPY 命令的簡(jiǎn)單性
如果僅僅是把本地的文件拷貝到容器鏡像中羊娃,COPY 命令是最合適不過(guò)的。其命令的格式為:
COPY <src> <dest>
除了指定完整的文件名外埃跷,COPY 命令還支持 Go 風(fēng)格的通配符蕊玷,比如:
COPY check* /testdir/ # 拷貝所有 check 開頭的文件
COPY check?.log /testdir/ # ? 是單個(gè)字符的占位符芦瘾,比如匹配文件 check1.log
對(duì)于目錄而言,COPY 和 ADD 命令具有相同的特點(diǎn):只復(fù)制目錄中的內(nèi)容而不包含目錄自身集畅。比如我們?cè)?Dockerfile 中添加下面的命令:
WORKDIR /app
COPY nickdir .
其中 nickdir 目錄的結(jié)構(gòu)如下:
重新構(gòu)建鏡像 testx近弟,運(yùn)行一個(gè)容器并查看 /app 目錄下的內(nèi)容:
這里只有 file1 和 file2,少了一層目錄 nickdir挺智。如果想讓 file1 和 file2 還保存在 nickdir 目錄中祷愉,需要在目標(biāo)路徑中指定這個(gè)目錄的名稱,比如:
WORKDIR /app
COPY nickdir **.****/nickdir**
COPY 命令區(qū)別于 ADD 命令的一個(gè)用法是在 multistage 場(chǎng)景下赦颇。關(guān)于 multistage 的介紹和用法請(qǐng)參考筆者的《Dockerfile 中的 multi-stage》一文二鳄。在 multistage 的用法中,可以使用 COPY 命令把前一階段構(gòu)建的產(chǎn)物拷貝到另一個(gè)鏡像中媒怯,比如:
FROM golang:1.7.3
WORKDIR /go/src/github.com/sparkdevo/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/sparkdevo/href-counter/app .
CMD ["./app"]
這段代碼引用自《Dockerfile 中的 multi-stage》一文订讼,其中的 COPY 命令通過(guò)指定 --from=0 參數(shù),把前一階段構(gòu)建的產(chǎn)物拷貝到了當(dāng)前的鏡像中扇苞。
ADD 命令還可以干其它事情
ADD 命令的格式和 COPY 命令相同欺殿,也是:
ADD <src> <dest>
除了不能用在 multistage 的場(chǎng)景下,ADD 命令可以完成 COPY 命令的所有功能鳖敷,并且還可以完成兩類超酷的功能:
- 解壓壓縮文件并把它們添加到鏡像中
- 從 url 拷貝文件到鏡像中
當(dāng)然脖苏,這些功能也讓 ADD 命令用起來(lái)復(fù)雜一些,不如 COPY 命令那么直觀定踱。
解壓壓縮文件并把它們添加到鏡像中
如果我們有一個(gè)壓縮文件包棍潘,并且需要把這個(gè)壓縮包中的文件添加到鏡像中。需不需要先解開壓縮包然后執(zhí)行 COPY 命令呢崖媚?當(dāng)然不需要亦歉!我們可以通過(guò) ADD 命令一次搞定:
WORKDIR /app
ADD nickdir.tar.gz .
這應(yīng)該是 ADD 命令的最佳使用場(chǎng)景了!
從 url 拷貝文件到鏡像中
這是一個(gè)更加酷炫的用法畅哑!但是在 docker 官方文檔的最佳實(shí)踐中卻強(qiáng)烈建議不要這么用k瓤!docker 官方建議我們當(dāng)需要從遠(yuǎn)程復(fù)制文件時(shí)敢课,最好使用 curl 或 wget 命令來(lái)代替 ADD 命令阶祭。原因是,當(dāng)使用 ADD 命令時(shí)直秆,會(huì)創(chuàng)建更多的鏡像層,當(dāng)然鏡像的 size 也會(huì)更大(下面的兩段代碼來(lái)自 docker 官方文檔):
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all
如果使用下面的命令鞭盟,不僅鏡像的層數(shù)減少圾结,而且鏡像中也不包含 big.tar.xz 文件:
RUN mkdir -p /usr/src/things \ && curl -SL http://example.com/big.tar.xz \
| tar -xJC /usr/src/things \ && make -C /usr/src/things all
好吧,看起來(lái)只有在解壓壓縮文件并把它們添加到鏡像中時(shí)才需要 ADD 命令齿诉!
加速鏡像構(gòu)建的技巧
在使用 COPY 和 ADD 命令時(shí)筝野,我們可以通過(guò)一些技巧來(lái)加速鏡像的 build 過(guò)程晌姚。比如把那些最不容易發(fā)生變化的文件的拷貝操作放在較低的鏡像層中,這樣在重新 build 鏡像時(shí)就會(huì)使用前面 build 產(chǎn)生的緩存歇竟。比如筆者構(gòu)建鏡像時(shí)需要用到下面幾個(gè)文件:
其中 myhc.py 文件不經(jīng)常變化挥唠,而 checkmongo.py、checkmysql.py 和 checkredis.py 這三個(gè)文件則經(jīng)常變化焕议,那么我們可這樣來(lái)設(shè)計(jì) Dockerfile 文件:
WORKDIR /app
COPY myhc.py .
COPY check* ./</pre>
讓 COPY myhc.py . 單獨(dú)占據(jù)一個(gè)鏡像層宝磨,當(dāng) build 過(guò)一次后,每次因 checkmongo.py盅安、checkmysql.py 和 checkredis.py 這三個(gè)文件變化而導(dǎo)致的重新 build 都不會(huì)重新 build COPY myhc.py . 鏡像層:
如上圖所示唤锉,第二步和第三步都沒(méi)有重新 build 鏡像層,而是使用了之前的緩存别瞭,從第四步才開始重新 build 了鏡像層窿祥。當(dāng)文件 size 比較大且文件的數(shù)量又比較多,尤其是需要執(zhí)行安裝等操作時(shí)蝙寨,這樣的設(shè)計(jì)對(duì)于 build 速度的提升還是很明顯的晒衩。所以我們應(yīng)該盡量選擇能夠使用緩存的 Dockerfile 寫法。
總結(jié)
當(dāng)?shù)谝淮慰吹?COPY 和 ADD 命令時(shí)不免讓人感到疑惑墙歪。但分析之后大家會(huì)發(fā)現(xiàn) COPY 命令是為最基本的用法設(shè)計(jì)的浸遗,概念清晰,操作簡(jiǎn)單箱亿。而 ADD 命令基本上是 COPY 命令的超集(除了 multistage 場(chǎng)景)跛锌,可以實(shí)現(xiàn)一些方便、酷炫的拷貝操作届惋。ADD 命令在增加了功能的同時(shí)也增加了使用它的復(fù)雜度髓帽,比如從 url 拷貝壓縮文件時(shí)弊大于利。希望本文能夠解去大家對(duì) Dockerfile 中 COPY 和 ADD 命令的疑惑脑豹。
參考:
Docker COPY: Dockerfile best practices
Best practices for writing Dockerfiles
Dockerfile COPY
Dockerfile ADD
作者:sparkdev