1. 摘要
本文講解使用 Dockerfile 定制鏡像的方法缩滨,也介紹docker commit的使用方法兼搏。
2. 內(nèi)容
2.1 利用 commit 理解鏡像構(gòu)成
注意: docker commit 命令除了學(xué)習(xí)之外芹助,還有一些特殊的應(yīng)用場合励翼,比如被入侵后保存現(xiàn)場等缚去。但是,不要使用 docker commit 定制鏡像樟结,定制鏡像應(yīng)該使用 Dockerfile 來完成养交。如果你想要定制鏡像請查看下一小節(jié)。
鏡像是容器的基礎(chǔ)瓢宦,每次執(zhí)行 docker run 的時候都會指定哪個鏡像作為容器運行的基礎(chǔ)碎连。在之前的例子中,我們所使用的都是來自于 Docker Hub 的鏡像驮履。直接使用這些鏡像是可以滿足一定的需求鱼辙,而當(dāng)這些鏡像無法直接滿足需求時,我們就需要定制這些鏡像玫镐。接下來的幾節(jié)就將講解如何定制鏡像倒戏。
回顧一下之前我們學(xué)到的知識,鏡像是多層存儲恐似,每一層是在前一層的基礎(chǔ)上進(jìn)行的修改杜跷;而容器同樣也是多層存儲,是在以鏡像為基礎(chǔ)層矫夷,在其基礎(chǔ)上加一層作為容器運行時的存儲層葛闷。
現(xiàn)在讓我們以定制一個 Web 服務(wù)器為例子,來講解鏡像是如何構(gòu)建的双藕。
$ docker run --name webserver -d -p 80:80 nginx
這條命令會用 nginx 鏡像啟動一個容器淑趾,命名為 webserver,并且映射了 80 端口忧陪,這樣我們可以用瀏覽器去訪問這個 nginx 服務(wù)器扣泊。
如果是在本機運行的 Docker,那么可以直接訪問:http://localhost 嘶摊,如果是在虛擬機旷赖、云服務(wù)器上安裝的 Docker,則需要將 localhost 換為虛擬機地址或者實際云服務(wù)器地址更卒。
直接用瀏覽器訪問的話等孵,我們會看到默認(rèn)的 Nginx 歡迎頁面。
現(xiàn)在蹂空,假設(shè)我們非常不喜歡這個歡迎頁面俯萌,我們希望改成歡迎 Docker 的文字,我們可以使用 docker exec 命令進(jìn)入容器上枕,修改其內(nèi)容咐熙。
$ docker exec -it webserver bash
root@3729b97e8226:/# echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
root@3729b97e8226:/# exit
exit
我們以交互式終端方式進(jìn)入 webserver 容器,并執(zhí)行了 bash 命令辨萍,也就是獲得一個可操作的 Shell棋恼。
然后返弹,我們用 <h1>Hello, Docker!</h1> 覆蓋了 /usr/share/nginx/html/index.html 的內(nèi)容。
現(xiàn)在我們再刷新瀏覽器的話爪飘,會發(fā)現(xiàn)內(nèi)容被改變了义起。
我們修改了容器的文件,也就是改動了容器的存儲層师崎。我們可以通過 docker diff 命令看到具體的改動默终。
$ docker diff webserver
C /root
A /root/.bash_history
C /run
C /usr
C /usr/share
C /usr/share/nginx
C /usr/share/nginx/html
C /usr/share/nginx/html/index.html
C /var
C /var/cache
C /var/cache/nginx
A /var/cache/nginx/client_temp
A /var/cache/nginx/fastcgi_temp
A /var/cache/nginx/proxy_temp
A /var/cache/nginx/scgi_temp
A /var/cache/nginx/uwsgi_temp
現(xiàn)在我們定制好了變化,我們希望能將其保存下來形成鏡像犁罩。
要知道齐蔽,當(dāng)我們運行一個容器的時候(如果不使用卷的話),我們做的任何文件修改都會被記錄于容器存儲層里床估。而 Docker 提供了一個 docker commit 命令含滴,可以將容器的存儲層保存下來成為鏡像。換句話說丐巫,就是在原有鏡像的基礎(chǔ)上谈况,再疊加上容器的存儲層,并構(gòu)成新的鏡像鞋吉。以后我們運行這個新鏡像的時候鸦做,就會擁有原有容器最后的文件變化励烦。
docker commit 的語法格式為:
docker commit [選項] <容器ID或容器名> [<倉庫名>[:<標(biāo)簽>]]
我們可以用下面的命令將容器保存為鏡像:
$ docker commit \
--author "Tao Wang <twang2218@gmail.com>" \
--message "修改了默認(rèn)網(wǎng)頁" \
webserver \
nginx:v2
sha256:07e33465974800ce65751acc279adc6ed2dc5ed4e0838f8b86f0c87aa1795214
其中 --author 是指定修改的作者谓着,而 --message 則是記錄本次修改的內(nèi)容。這點和 git 版本控制相似坛掠,不過這里這些信息可以省略留空赊锚。
其中 --author 是指定修改的作者,而 --message 則是記錄本次修改的內(nèi)容屉栓。這點和 git 版本控制相似舷蒲,不過這里這些信息可以省略留空导坟。
我們可以在 docker image ls 中看到這個新定制的鏡像:
$ docker image ls nginx
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v2 07e334659748 9 seconds ago 181.5 MB
nginx 1.11 05a60462f8ba 12 days ago 181.5 MB
nginx latest e43d811ce2f4 4 weeks ago 181.5 MB
我們還可以用 docker history 具體查看鏡像內(nèi)的歷史記錄帖池,如果比較 nginx:latest 的歷史記錄污朽,我們會發(fā)現(xiàn)新增了我們剛剛提交的這一層秉剑。
$ docker history nginx:v2
IMAGE CREATED CREATED BY SIZE COMMENT
07e334659748 54 seconds ago nginx -g daemon off; 95 B 修改了默認(rèn)網(wǎng)頁
e43d811ce2f4 4 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon 0 B
<missing> 4 weeks ago /bin/sh -c #(nop) EXPOSE 443/tcp 80/tcp 0 B
<missing> 4 weeks ago /bin/sh -c ln -sf /dev/stdout /var/log/nginx/ 22 B
<missing> 4 weeks ago /bin/sh -c apt-key adv --keyserver hkp://pgp. 58.46 MB
<missing> 4 weeks ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.11.5-1 0 B
<missing> 4 weeks ago /bin/sh -c #(nop) MAINTAINER NGINX Docker Ma 0 B
<missing> 4 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
<missing> 4 weeks ago /bin/sh -c #(nop) ADD file:23aa4f893e3288698c 123 MB
新的鏡像定制好后贰逾,我們可以來運行這個鏡像罚渐。
docker run --name web2 -d -p 81:80 nginx:v2
這里我們命名為新的服務(wù)為 web2明刷,并且映射到 81 端口鸯绿。訪問 http://localhost:81 看到結(jié)果启绰,其內(nèi)容應(yīng)該和之前修改后的 webserver 一樣昂儒。
至此,我們第一次完成了定制鏡像委可,使用的是 docker commit 命令渊跋,手動操作給舊的鏡像添加了新的一層,形成新的鏡像,對鏡像多層存儲應(yīng)該有了更直觀的感覺拾酝。
慎用 docker commit
使用 docker commit 命令雖然可以比較直觀的幫助理解鏡像分層存儲的概念燕少,但是實際環(huán)境中并不會這樣使用。
首先微宝,如果仔細(xì)觀察之前的 docker diff webserver 的結(jié)果棺亭,你會發(fā)現(xiàn)除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由于命令的執(zhí)行蟋软,還有很多文件被改動或添加了镶摘。這還僅僅是最簡單的操作,如果是安裝軟件包岳守、編譯構(gòu)建凄敢,那會有大量的無關(guān)內(nèi)容被添加進(jìn)來,將會導(dǎo)致鏡像極為臃腫湿痢。
此外涝缝,使用 docker commit 意味著所有對鏡像的操作都是黑箱操作,生成的鏡像也被稱為 黑箱鏡像譬重,換句話說拒逮,就是除了制作鏡像的人知道執(zhí)行過什么命令、怎么生成的鏡像臀规,別人根本無從得知滩援。而且,即使是這個制作鏡像的人塔嬉,過一段時間后也無法記清具體的操作玩徊。這種黑箱鏡像的維護(hù)工作是非常痛苦的。
而且谨究,回顧之前提及的鏡像所使用的分層存儲的概念恩袱,除當(dāng)前層外,之前的每一層都是不會發(fā)生改變的胶哲,換句話說畔塔,任何修改的結(jié)果僅僅是在當(dāng)前層進(jìn)行標(biāo)記、添加鸯屿、修改澈吨,而不會改動上一層。如果使用 docker commit 制作鏡像碾盟,以及后期修改的話棚辽,每一次修改都會讓鏡像更加臃腫一次,所刪除的上一層的東西并不會丟失冰肴,會一直如影隨形的跟著這個鏡像屈藐,即使根本無法訪問到榔组。這會讓鏡像更加臃腫。
2.2 使用 Dockerfile 定制鏡像
從剛才的 docker commit 的學(xué)習(xí)中联逻,我們可以了解到搓扯,鏡像的定制實際上就是定制每一層所添加的配置、文件包归。如果我們可以把每一層修改锨推、安裝、構(gòu)建公壤、操作的命令都寫入一個腳本换可,用這個腳本來構(gòu)建、定制鏡像厦幅,那么之前提及的無法重復(fù)的問題沾鳄、鏡像構(gòu)建透明性的問題、體積的問題就都會解決确憨。這個腳本就是 Dockerfile译荞。
Dockerfile 是一個文本文件,其內(nèi)包含了一條條的 指令(Instruction)休弃,每一條指令構(gòu)建一層吞歼,因此每一條指令的內(nèi)容,就是描述該層應(yīng)當(dāng)如何構(gòu)建塔猾。
還以之前定制 nginx 鏡像為例篙骡,這次我們使用 Dockerfile 來定制。
在一個空白目錄中桥帆,建立一個文本文件医增,并命名為 Dockerfile:
$ mkdir mynginx
$ cd mynginx
$ touch Dockerfile
其內(nèi)容為:
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
這個 Dockerfile 很簡單慎皱,一共就兩行老虫。涉及到了兩條指令,F(xiàn)ROM 和 RUN茫多。
FROM 指定基礎(chǔ)鏡像
所謂定制鏡像祈匙,那一定是以一個鏡像為基礎(chǔ),在其上進(jìn)行定制天揖。就像我們之前運行了一個 nginx
鏡像的容器夺欲,再進(jìn)行修改一樣,基礎(chǔ)鏡像是必須指定的今膊。而 FROM
就是指定 基礎(chǔ)鏡像些阅,因此一個 Dockerfile
中 FROM
是必備的指令,并且必須是第一條指令斑唬。
在 Docker Hub 上有非常多的高質(zhì)量的官方鏡像市埋,有可以直接拿來使用的服務(wù)類的鏡像黎泣,如 nginx
、redis
缤谎、mongo
抒倚、mysql
、httpd
坷澡、php
托呕、tomcat
等;也有一些方便開發(fā)频敛、構(gòu)建项郊、運行各種語言應(yīng)用的鏡像,如 node
斟赚、openjdk
呆抑、python
、ruby
汁展、golang
等鹊碍。可以在其中尋找一個最符合我們最終目標(biāo)的鏡像為基礎(chǔ)鏡像進(jìn)行定制食绿。
如果沒有找到對應(yīng)服務(wù)的鏡像侈咕,官方鏡像中還提供了一些更為基礎(chǔ)的操作系統(tǒng)鏡像,如 ubuntu
器紧、debian
耀销、centos
、fedora
铲汪、alpine
等熊尉,這些操作系統(tǒng)的軟件庫為我們提供了更廣闊的擴展空間。
除了選擇現(xiàn)有鏡像為基礎(chǔ)鏡像外掌腰,Docker 還存在一個特殊的鏡像狰住,名為 scratch
。這個鏡像是虛擬的概念齿梁,并不實際存在催植,它表示一個空白的鏡像。
FROM scratch
...
如果你以 scratch
為基礎(chǔ)鏡像的話勺择,意味著你不以任何鏡像為基礎(chǔ)创南,接下來所寫的指令將作為鏡像第一層開始存在。
不以任何系統(tǒng)為基礎(chǔ)省核,直接將可執(zhí)行文件復(fù)制進(jìn)鏡像的做法并不罕見稿辙,對于 Linux 下靜態(tài)編譯的程序來說,并不需要有操作系統(tǒng)提供運行時支持气忠,所需的一切庫都已經(jīng)在可執(zhí)行文件里了邻储,因此直接 FROM scratch
會讓鏡像體積更加小巧未桥。使用 Go 語言 開發(fā)的應(yīng)用很多會使用這種方式來制作鏡像,這也是為什么有人認(rèn)為 Go 是特別適合容器微服務(wù)架構(gòu)的語言的原因之一芥备。
RUN 執(zhí)行命令
RUN 指令是用來執(zhí)行命令行命令的冬耿。由于命令行的強大能力,RUN 指令在定制鏡像時是最常用的指令之一萌壳。其格式有兩種:
shell 格式:RUN <命令>亦镶,就像直接在命令行中輸入的命令一樣。剛才寫的 Dockerfile 中的 RUN 指令就是這種格式袱瓮。
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
exec 格式:RUN ["可執(zhí)行文件", "參數(shù)1", "參數(shù)2"]缤骨,這更像是函數(shù)調(diào)用中的格式。
既然 RUN 就像 Shell 腳本一樣可以執(zhí)行命令尺借,那么我們是否就可以像 Shell 腳本一樣把每個命令對應(yīng)一個 RUN 呢绊起?比如這樣:
FROM debian:stretch
RUN apt-get update
RUN apt-get install -y gcc libc6-dev make wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install
之前說過,Dockerfile 中每一個指令都會建立一層燎斩,RUN 也不例外虱歪。每一個 RUN 的行為,就和剛才我們手工建立鏡像的過程一樣:新建立一層栅表,在其上執(zhí)行這些命令笋鄙,執(zhí)行結(jié)束后,commit 這一層的修改怪瓶,構(gòu)成新的鏡像萧落。
而上面的這種寫法,創(chuàng)建了 7 層鏡像洗贰。這是完全沒有意義的找岖,而且很多運行時不需要的東西,都被裝進(jìn)了鏡像里敛滋,比如編譯環(huán)境许布、更新的軟件包等等。結(jié)果就是產(chǎn)生非常臃腫矛缨、非常多層的鏡像爹脾,不僅僅增加了構(gòu)建部署的時間帖旨,也很容易出錯箕昭。 這是很多初學(xué) Docker 的人常犯的一個錯誤。
Union FS 是有最大層數(shù)限制的解阅,比如 AUFS落竹,曾經(jīng)是最大不得超過 42 層,現(xiàn)在是不得超過 127 層货抄。
上面的 Dockerfile 正確的寫法應(yīng)該是這樣:
FROM debian:stretch
RUN set -x; buildDeps='gcc libc6-dev make wget' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps
首先述召,之前所有的命令只有一個目的朱转,就是編譯、安裝 redis 可執(zhí)行文件积暖。因此沒有必要建立很多層藤为,這只是一層的事情。因此夺刑,這里沒有使用很多個 RUN 一一對應(yīng)不同的命令缅疟,而是僅僅使用一個 RUN 指令,并使用 && 將各個所需命令串聯(lián)起來遍愿。將之前的 7 層存淫,簡化為了 1 層。在撰寫 Dockerfile 的時候沼填,要經(jīng)常提醒自己桅咆,這并不是在寫 Shell 腳本,而是在定義每一層該如何構(gòu)建坞笙。
并且岩饼,這里為了格式化還進(jìn)行了換行。Dockerfile 支持 Shell 類的行尾添加 \ 的命令換行方式薛夜,以及行首 # 進(jìn)行注釋的格式忌愚。良好的格式,比如換行却邓、縮進(jìn)硕糊、注釋等,會讓維護(hù)腊徙、排障更為容易简十,這是一個比較好的習(xí)慣。
此外撬腾,還可以看到這一組命令的最后添加了清理工作的命令螟蝙,刪除了為了編譯構(gòu)建所需要的軟件,清理了所有下載民傻、展開的文件胰默,并且還清理了 apt 緩存文件。這是很重要的一步漓踢,我們之前說過牵署,鏡像是多層存儲,每一層的東西并不會在下一層被刪除喧半,會一直跟隨著鏡像奴迅。因此鏡像構(gòu)建時,一定要確保每一層只添加真正需要添加的東西挺据,任何無關(guān)的東西都應(yīng)該清理掉取具。
很多人初學(xué) Docker 制作出了很臃腫的鏡像的原因之一脖隶,就是忘記了每一層構(gòu)建的最后一定要清理掉無關(guān)文件。
構(gòu)建鏡像
好了暇检,讓我們再回到之前定制的 nginx 鏡像的 Dockerfile 來〔澹現(xiàn)在我們明白了這個 Dockerfile 的內(nèi)容,那么讓我們來構(gòu)建這個鏡像吧块仆。
在 Dockerfile 文件所在目錄執(zhí)行:
$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM nginx
---> e43d811ce2f4
Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
---> Running in 9cdc27646c7b
---> 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c
從命令的輸出結(jié)果中心墅,我們可以清晰的看到鏡像的構(gòu)建過程。在 Step 2 中榨乎,如同我們之前所說的那樣怎燥,RUN 指令啟動了一個容器 9cdc27646c7b,執(zhí)行了所要求的命令蜜暑,并最后提交了這一層 44aa4490ce2c铐姚,隨后刪除了所用到的這個容器 9cdc27646c7b。
這里我們使用了 docker build 命令進(jìn)行鏡像構(gòu)建肛捍。其格式為:
docker build [選項] <上下文路徑/URL/->
在這里我們指定了最終鏡像的名稱 -t nginx:v3隐绵,構(gòu)建成功后,我們可以像之前運行 nginx:v2 那樣來運行這個鏡像拙毫,其結(jié)果會和 nginx:v2 一樣依许。
鏡像構(gòu)建上下文(Context)
如果注意,會看到 docker build
命令最后有一個 .
缀蹄。.
表示當(dāng)前目錄峭跳,而 Dockerfile
就在當(dāng)前目錄,因此不少初學(xué)者以為這個路徑是在指定 Dockerfile
所在路徑缺前,這么理解其實是不準(zhǔn)確的蛀醉。如果對應(yīng)上面的命令格式,你可能會發(fā)現(xiàn)衅码,這是在指定 上下文路徑拯刁。那么什么是上下文呢?
首先我們要理解 docker build
的工作原理逝段。Docker 在運行時分為 Docker 引擎(也就是服務(wù)端守護(hù)進(jìn)程)和客戶端工具垛玻。Docker 的引擎提供了一組 REST API,被稱為 Docker Remote API奶躯,而如 docker
命令這樣的客戶端工具帚桩,則是通過這組 API 與 Docker 引擎交互,從而完成各種功能巫糙。因此朗儒,雖然表面上我們好像是在本機執(zhí)行各種 docker
功能,但實際上参淹,一切都是使用的遠(yuǎn)程調(diào)用形式在服務(wù)端(Docker 引擎)完成醉锄。也因為這種 C/S 設(shè)計,讓我們操作遠(yuǎn)程服務(wù)器的 Docker 引擎變得輕而易舉浙值。
當(dāng)我們進(jìn)行鏡像構(gòu)建的時候恳不,并非所有定制都會通過 RUN
指令完成,經(jīng)常會需要將一些本地文件復(fù)制進(jìn)鏡像开呐,比如通過 COPY
指令烟勋、ADD
指令等。而 docker build
命令構(gòu)建鏡像筐付,其實并非在本地構(gòu)建卵惦,而是在服務(wù)端,也就是 Docker 引擎中構(gòu)建的瓦戚。那么在這種客戶端/服務(wù)端的架構(gòu)中沮尿,如何才能讓服務(wù)端獲得本地文件呢?
這就引入了上下文的概念较解。當(dāng)構(gòu)建的時候畜疾,用戶會指定構(gòu)建鏡像上下文的路徑,docker build
命令得知這個路徑后印衔,會將路徑下的所有內(nèi)容打包啡捶,然后上傳給 Docker 引擎。這樣 Docker 引擎收到這個上下文包后奸焙,展開就會獲得構(gòu)建鏡像所需的一切文件瞎暑。
如果在 Dockerfile
中這么寫:
COPY ./package.json /app/
這并不是要復(fù)制執(zhí)行 docker build 命令所在的目錄下的 package.json,也不是復(fù)制 Dockerfile 所在目錄下的 package.json与帆,而是復(fù)制 上下文(context) 目錄下的 package.json金顿。
因此,COPY 這類指令中的源文件的路徑都是相對路徑鲤桥。這也是初學(xué)者經(jīng)常會問的為什么 COPY ../package.json /app 或者 COPY /opt/xxxx /app 無法工作的原因揍拆,因為這些路徑已經(jīng)超出了上下文的范圍,Docker 引擎無法獲得這些位置的文件茶凳。如果真的需要那些文件嫂拴,應(yīng)該將它們復(fù)制到上下文目錄中去。
現(xiàn)在就可以理解剛才的命令 docker build -t nginx:v3 . 中的這個 .贮喧,實際上是在指定上下文的目錄筒狠,docker build 命令會將該目錄下的內(nèi)容打包交給 Docker 引擎以幫助構(gòu)建鏡像。
如果觀察 docker build 輸出箱沦,我們其實已經(jīng)看到了這個發(fā)送上下文的過程:
$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
...
理解構(gòu)建上下文對于鏡像構(gòu)建是很重要的辩恼,避免犯一些不應(yīng)該的錯誤。比如有些初學(xué)者在發(fā)現(xiàn) COPY /opt/xxxx /app 不工作后,于是干脆將 Dockerfile 放到了硬盤根目錄去構(gòu)建灶伊,結(jié)果發(fā)現(xiàn) docker build 執(zhí)行后疆前,在發(fā)送一個幾十 GB 的東西,極為緩慢而且很容易構(gòu)建失敗聘萨。那是因為這種做法是在讓 docker build 打包整個硬盤竹椒,這顯然是使用錯誤。
一般來說米辐,應(yīng)該會將 Dockerfile 置于一個空目錄下胸完,或者項目根目錄下。如果該目錄下沒有所需文件翘贮,那么應(yīng)該把所需文件復(fù)制一份過來赊窥。如果目錄下有些東西確實不希望構(gòu)建時傳給 Docker 引擎,那么可以用 .gitignore 一樣的語法寫一個 .dockerignore狸页,該文件是用于剔除不需要作為上下文傳遞給 Docker 引擎的锨能。
那么為什么會有人誤以為 . 是指定 Dockerfile 所在目錄呢?這是因為在默認(rèn)情況下肴捉,如果不額外指定 Dockerfile 的話腹侣,會將上下文目錄下的名為 Dockerfile 的文件作為 Dockerfile。
這只是默認(rèn)行為齿穗,實際上 Dockerfile 的文件名并不要求必須為 Dockerfile傲隶,而且并不要求必須位于上下文目錄中,比如可以用 -f ../Dockerfile.php 參數(shù)指定某個文件作為 Dockerfile窃页。
當(dāng)然跺株,一般大家習(xí)慣性的會使用默認(rèn)的文件名 Dockerfile,以及會將其置于鏡像構(gòu)建上下文目錄中脖卖。
其它 docker build 的用法
直接用 Git repo 進(jìn)行構(gòu)建
或許你已經(jīng)注意到了乒省,docker build 還支持從 URL 構(gòu)建,比如可以直接從 Git repo 中構(gòu)建:
# $env:DOCKER_BUILDKIT=0
# export DOCKER_BUILDKIT=0
$ docker build -t hello-world https://github.com/docker-library/hello-world.git#master:amd64/hello-world
Step 1/3 : FROM scratch
--->
Step 2/3 : COPY hello /
---> ac779757d46e
Step 3/3 : CMD ["/hello"]
---> Running in d2a513a760ed
Removing intermediate container d2a513a760ed
---> 038ad4142d2b
Successfully built 038ad4142d2b
這行命令指定了構(gòu)建所需的 Git repo畦木,并且指定分支為 master袖扛,構(gòu)建目錄為 /amd64/hello-world/,然后 Docker 就會自己去 git clone 這個項目十籍、切換到指定分支蛆封、并進(jìn)入到指定目錄后開始構(gòu)建。
用給定的 tar 壓縮包構(gòu)建
$ docker build http://server/context.tar.gz
如果所給出的 URL 不是個 Git repo勾栗,而是個 tar 壓縮包惨篱,那么 Docker 引#擎會下載這個包,并自動解壓縮围俘,以其作為上下文砸讳,開始構(gòu)建琢融。
從標(biāo)準(zhǔn)輸入中讀取 Dockerfile 進(jìn)行構(gòu)建
docker build - < Dockerfile
或
cat Dockerfile | docker build -
如果標(biāo)準(zhǔn)輸入傳入的是文本文件,則將其視為 Dockerfile簿寂,并開始構(gòu)建漾抬。這種形式由于直接從標(biāo)準(zhǔn)輸入中讀取 Dockerfile 的內(nèi)容,它沒有上下文陶耍,因此不可以像其他方法那樣可以將本地文件 COPY 進(jìn)鏡像之類的事情奋蔚。
從標(biāo)準(zhǔn)輸入中讀取上下文壓縮包進(jìn)行構(gòu)建
$ docker build - < context.tar.gz
如果發(fā)現(xiàn)標(biāo)準(zhǔn)輸入的文件格式是 gzip她混、bzip2 以及 xz 的話烈钞,將會使其為上下文壓縮包,直接將其展開坤按,將里面視為上下文毯欣,并開始構(gòu)建。
3. 參考
(1) 利用 commit 理解鏡像構(gòu)成 https://yeasy.gitbook.io/docker_practice/image/commit
(2)使用 Dockerfile 定制鏡像
https://yeasy.gitbook.io/docker_practice/image/build