?
This document uses PHP Chinese website manual Release
Docker可以通過從Dockerfile
,一個(gè)文本文件,包含所有命令,按照順序,需要生成給定的圖像。Dockerfile
S堅(jiān)持特定的格式,并使用一組特定的指令。您可以學(xué)習(xí)Dockerfile引用一頁。如果你剛開始寫作Dockerfile
你應(yīng)該從那兒開始。
本文檔涵蓋Docker公司和Docker社區(qū)推薦的最佳做法和方法,以創(chuàng)建易于使用、有效的產(chǎn)品。Dockerfile
我們強(qiáng)烈建議您遵循這些建議%28事實(shí)上,如果您正在創(chuàng)建一個(gè)官方形象,您必堅(jiān)持這些實(shí)踐。
構(gòu)建包-depsDockerfile
...
注意:有關(guān)此處提到的任何Dockerfile命令的更詳細(xì)說明,請?jiān)L問Dockerfile引用一頁。
由圖像生成的容器Dockerfile
定義應(yīng)該盡可能的短暫。所謂“短暫”,我們的意思是,它可以被停止和摧毀,一個(gè)新的建立和安置的絕對最小的設(shè)置和配置。您可能想看看過程12要素應(yīng)用程序方法中的一節(jié),以了解以這樣一種無狀態(tài)方式運(yùn)行容器的動(dòng)機(jī)。
在大多數(shù)情況下,最好將每個(gè)Dockerfile放在一個(gè)空目錄中。然后,只向該目錄添加構(gòu)建Dockerfile所需的文件。若要提高生成的性能,可以通過添加.dockerignore
文件也放在那個(gè)目錄下。此文件支持類似于.gitignore
檔案。有關(guān)創(chuàng)建一個(gè)的信息,請參見.dockerignore文件...
為了減少復(fù)雜性、依賴性、文件大小和構(gòu)建時(shí)間,您應(yīng)該避免僅僅因?yàn)椤皳碛小鳖~外的或不必要的包而安裝它們。例如,不需要在數(shù)據(jù)庫映像中包含文本編輯器。
將應(yīng)用程序解耦到多個(gè)容器中,可以更容易地進(jìn)行水平擴(kuò)展和重用容器。例如,Web應(yīng)用程序堆??赡苡扇齻€(gè)單獨(dú)的容器組成,每個(gè)容器都有自己獨(dú)特的映像,以解耦的方式管理Web應(yīng)用程序、數(shù)據(jù)庫和內(nèi)存中的緩存。
您可能聽說過“每個(gè)容器應(yīng)該有一個(gè)過程”。雖然這個(gè)咒語有良好的意圖,但不一定每個(gè)容器只應(yīng)該有一個(gè)操作系統(tǒng)進(jìn)程。除了容器現(xiàn)在可以由init進(jìn)程生成,一些程序可能會(huì)自動(dòng)產(chǎn)生額外的進(jìn)程。例如,芹菜可以生成多個(gè)工作進(jìn)程,或阿帕奇可能會(huì)為每個(gè)請求創(chuàng)建一個(gè)進(jìn)程。雖然“每個(gè)容器一個(gè)進(jìn)程”通常是一個(gè)好的經(jīng)驗(yàn)法則,但它并不是一個(gè)硬和快速的規(guī)則。用你最好的判斷來保持容器盡可能的干凈和模塊化。
如果容器相互依賴,則可以使用碼頭集裝箱網(wǎng)絡(luò)以確保這些容器能夠通信。
您需要找到可讀性(以及長期可維護(hù)性)Dockerfile
與最小化其使用的層數(shù)之間的平衡。對您使用的圖層數(shù)量保持戰(zhàn)略性和謹(jǐn)慎。
只要有可能,可以通過對多行參數(shù)進(jìn)行字母數(shù)字排序來簡化以后的更改。這將幫助您避免包的重復(fù),并使列表更容易更新。這也使PRs更容易閱讀和審查。在反斜杠%28之前添加空格\
%29也有幫助。
下面是一個(gè)來自buildpack-deps
圖像*
RUN apt-get update && apt-get install -y \ bzr \ cvs \ git \ mercurial \ subversion
在構(gòu)建圖像碼頭的過程中,您將逐步了解Dockerfile
按照指定的順序執(zhí)行每個(gè)。在檢查每條指令時(shí),Docker將在其緩存中尋找一個(gè)可以重用的現(xiàn)有映像,而不是創(chuàng)建一個(gè)新的%28重復(fù)%29映像。如果您根本不想使用緩存,則可以使用--no-cache=true
選項(xiàng)的docker build
命令。
但是,如果您確實(shí)讓Docker使用它的緩存,那么非常重要的是要了解它什么時(shí)候會(huì),并且不會(huì)找到匹配的映像。碼頭工人將遵循的基本規(guī)則概述如下:
從緩存中已經(jīng)存在的父映像開始,將下一條指令與從該基本映像派生的所有子映像進(jìn)行比較,以查看其中一個(gè)是使用完全相同的指令生成的。否則,緩存將失效。
在大多數(shù)情況下,只需比較Dockerfile
其中一個(gè)孩子的圖像就足夠了。然而,某些指示需要更多的檢查和解釋。
為ADD
和COPY
說明,檢查圖像中文件%28s%29的內(nèi)容,并計(jì)算每個(gè)文件的校驗(yàn)和。在這些校驗(yàn)和中不考慮文件%28s%29的最后修改和最后訪問次數(shù)。在緩存查找過程中,將校驗(yàn)和與現(xiàn)有圖像中的校驗(yàn)和進(jìn)行比較。如果文件%28s%29中有任何更改,如內(nèi)容和元數(shù)據(jù),則緩存無效。
除了ADD
和COPY
命令時(shí),緩存檢查將不會(huì)查看容器中的文件以確定緩存匹配。例如,當(dāng)處理RUN apt-get -y update
命令不會(huì)檢查容器中更新的文件以確定是否存在緩存命中。在這種情況下,僅使用命令字符串本身來查找匹配項(xiàng)。
一旦緩存失效,所有后續(xù)Dockerfile
命令將生成新圖像,緩存將不被使用。
下面,您將找到關(guān)于編寫各種可用說明的最佳方法的建議,以便在Dockerfile
...
FROM指令的Dockerfile引用
只要有可能,使用當(dāng)前的官方存儲(chǔ)庫作為你形象的基礎(chǔ)。我們推薦Debian圖像因?yàn)樗欠浅?yán)格控制和保持最小的%28目前低于150 MB%29,同時(shí)仍然是一個(gè)完整的發(fā)行版。
理解對象標(biāo)簽
您可以向圖像中添加標(biāo)簽,以幫助按項(xiàng)目組織圖像、記錄許可信息、幫助自動(dòng)化或其他原因。對于每個(gè)標(biāo)簽,添加一行以LABEL
和一個(gè)或多個(gè)鍵值對。下面的示例顯示了不同的可接受格式。解釋性評論包括內(nèi)聯(lián)。
注*如果字符串包含空格,則必須引用它或這些空間必須逃掉。如果字符串包含內(nèi)部引號字符%28
"
%29,也要逃離他們。
# Set one or more individual labels LABEL com.example.version="0.0.1-beta"LABEL vendor="ACME Incorporated"LABEL com.example.release-date="2015-02-12"LABEL com.example.version.is-production=""# Set multiple labels on one line LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"# Set multiple labels at once, using line-continuation characters to break long lines LABEL vendor=ACME\ Incorporated \ com.example.is-beta= \ com.example.is-production="" \ com.example.version="0.0.1-beta" \ com.example.release-date="2015-02-12"
見理解對象標(biāo)簽有關(guān)可接受的標(biāo)簽鍵和值的指南。有關(guān)查詢標(biāo)簽的信息,請參考與管理對象的標(biāo)簽...
運(yùn)行指令的Dockerfile引用
一如既往,讓你Dockerfile
更易讀,更易理解,更易維護(hù),分裂更長或更復(fù)雜RUN
語句在用反斜杠分隔的多行上。
可能是最常見的用例RUN
是apt-get
...RUN apt-get
命令,因?yàn)樗惭b包,所以有幾個(gè)問題需要注意。
你應(yīng)該避免RUN apt-get upgrade
或dist-upgrade
,因?yàn)閬碜愿赣诚竦脑S多“基本”包不會(huì)在非特權(quán)容器中升級。如果父映像中包含的包過期,則應(yīng)與其維護(hù)人員聯(lián)系.。如果你知道有一個(gè)特別的包裹,foo
,需要更新,使用apt-get install -y foo
自動(dòng)更新。
總是結(jié)合RUN apt-get update
帶著apt-get install
同RUN
聲明,例如:
RUN apt-get update && apt-get install -y \ package-bar \ package-baz \ package-foo
使用apt-get update
獨(dú)處RUN
語句導(dǎo)致緩存問題和后續(xù)事件。apt-get install
指令失敗。例如,假設(shè)您有一個(gè)Dockerfile:
FROM ubuntu:14.04 RUN apt-get update RUN apt-get install -y curl
生成圖像后,所有層都在Docker緩存中。假設(shè)您稍后修改apt-get install
通過添加額外的包:
FROM ubuntu:14.04 RUN apt-get update RUN apt-get install -y curl nginx
Docker將初始指令和修改后的指令視為相同,并重用前面步驟中的緩存。因此,apt-get update
是不執(zhí)行是因?yàn)闃?gòu)建使用緩存的版本。因?yàn)?code>apt-get update如果沒有運(yùn)行,您的生成可能會(huì)獲得過時(shí)版本的curl
和nginx
包裹。
使用RUN apt-get update && apt-get install -y
確保您的Dockerfile安裝最新的包版本,不再進(jìn)行編碼或手動(dòng)干預(yù)。這種技術(shù)被稱為“緩存破壞”。您還可以通過指定包版本來實(shí)現(xiàn)高速緩存破壞。例如,這稱為版本釘扎:
RUN apt-get update && apt-get install -y \ package-bar \ package-baz \ package-foo=1.3.*
版本釘扎強(qiáng)制構(gòu)建檢索特定版本,而不管緩存中的是什么。這種技術(shù)還可以減少由于所需包中意外的更改而導(dǎo)致的故障。
下面是一個(gè)結(jié)構(gòu)良好的RUN
說明所有apt-get
建議。
RUN apt-get update && apt-get install -y \ aufs-tools \ automake \ build-essential \ curl \ dpkg-sig \ libcap-dev \ libsqlite3-dev \ mercurial \ reprepro \ ruby1.9.1 \ ruby1.9.1-dev \ s3cmd=1.1.* \ && rm -rf /var/lib/apt/lists/*
大s3cmd
說明指定版本1.1.*
如果圖像以前使用舊版本,則指定新版本將導(dǎo)致apt-get update
并確保新版本的安裝。在每一行上列出包還可以防止包復(fù)制中的錯(cuò)誤。
此外,當(dāng)您通過刪除APT緩存來清除/var/lib/apt/lists
減少圖像大小,因?yàn)锳PT緩存沒有存儲(chǔ)在一個(gè)層中。因?yàn)?code>RUN語句以apt-get update
之前總是刷新包緩存。apt-get install
...
注*Debian和Ubuntu的官方圖片自動(dòng)運(yùn)行
apt-get clean
,因此不需要顯式調(diào)用。
一些RUN
命令依賴于使用管道字符%28將一個(gè)命令的輸出管道輸送到另一個(gè)命令的能力|
%29,如下例所示:
RUN wget -O - https://some.site | wc -l > /number
Docker使用/bin/sh -c
解釋器,它只計(jì)算管道中最后一個(gè)操作的退出代碼以確定成功。在上面的示例中,此構(gòu)建步驟成功并生成一個(gè)新映像,只要wc -l
命令成功,即使wget
命令失敗。
如果您希望命令由于管道中任何階段的錯(cuò)誤而失敗,請預(yù)先準(zhǔn)備set -o pipefail &&
若要確保意外錯(cuò)誤阻止生成意外成功,請執(zhí)行以下操作。例如:
RUN set -o pipefail && wget -O - https://some.site | wc -l > /number
注*并非所有shell都支持
-o pipefail
選擇。在這種情況下,%28,例如dash
shell是基于debian的圖像%29上的默認(rèn)shell,請考慮使用主管形式RUN
若要顯式選擇確實(shí)支持pipefail
選擇。例如:
RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]
CMD指令的Dockerfile引用
大CMD
指令應(yīng)該用來運(yùn)行你的圖像所包含的軟件,以及任何參數(shù)。CMD
幾乎總是以CMD [“executable”, “param1”, “param2”…]
因此,如果映像是用于服務(wù)(如Apache和Rails),則可以運(yùn)行以下內(nèi)容CMD ["apache2","-DFOREGROUND"]
事實(shí)上,對于任何基于服務(wù)的圖像,都推薦使用這種形式的指令。
在其他大多數(shù)情況下,CMD
應(yīng)該被賦予一個(gè)交互式的shell,例如bash、python和perl。例如,CMD ["perl", "-de0"]
,,,CMD ["python"]
,或CMD [“php”, “-a”]
.使用此表單意味著當(dāng)您執(zhí)行以下操作時(shí)docker run -it python
,你會(huì)掉進(jìn)一個(gè)可用的殼里,準(zhǔn)備好了。CMD
應(yīng)很少以下列方式使用:CMD [“param”, “param”]
與ENTRYPOINT
,除非您和您的預(yù)期用戶已經(jīng)非常熟悉ENTRYPOINT
起作用了。
公開指令的Dockerfile引用
大EXPOSE
指示容器將偵聽連接的端口。因此,應(yīng)用程序應(yīng)該使用通用的傳統(tǒng)端口。例如,包含apache web服務(wù)器的映像將使用EXPOSE 80
,而包含MongoDB的圖像將使用EXPOSE 27017
諸若此類
對于外部訪問,用戶可以執(zhí)行docker run
帶有指示如何將指定端口映射到他們選擇的端口的標(biāo)志。對于容器鏈接,Docker為從收件人容器返回到源%28 ie的路徑提供環(huán)境變量,MYSQL_PORT_3306_TCP
29%。
ENV指令的Dockerfile引用
為了使新軟件更容易運(yùn)行,您可以使用ENV
若要更新PATH
容器安裝的軟件的環(huán)境變量。例如,ENV PATH /usr/local/nginx/bin:$PATH
將確保CMD [“nginx”]
只是起作用了。
大ENV
指令對于提供特定于您希望容器化的服務(wù)所需的環(huán)境變量也很有用,例如Postgres的PGDATA
...
最后,ENV
還可以用來設(shè)置常用的版本號,以便更容易維護(hù)版本凸起,如下面的示例所示:
ENV PG_MAJOR 9.3ENV PG_VERSION 9.3.4RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && … ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
類似于程序%28中有常量變量,而不是硬編碼值%29,這種方法允許您更改單個(gè)ENV
指令,自動(dòng)-神奇地碰撞版本的軟件在您的容器。
添加指令的Dockerfile引用
復(fù)制指令的Dockerfile引用
盡管ADD
和COPY
在功能上是相似的,一般來說,COPY
是首選。那是因?yàn)樗?code>ADD...COPY
只支持將本地文件基本復(fù)制到容器中,而ADD
有一些特性%28,如本地只提取焦油和遠(yuǎn)程URL支持%29,這不是立即明顯。因此,對ADD
是本地tar文件自動(dòng)提取到圖像中,如ADD rootfs.tar.xz /
...
如果你有多重Dockerfile
使用與上下文不同的文件的步驟,COPY
他們是單獨(dú)的,而不是一次性的。這將確保每個(gè)步驟的生成緩存僅無效%28,如果特殊需要的文件更改,強(qiáng)制步驟重新運(yùn)行%29。
例如:
COPY requirements.txt /tmp/RUN pip install --requirement /tmp/requirements.txt COPY . /tmp/
類的緩存失效減少。RUN
步驟,如果你把COPY . /tmp/
在它之前。
因?yàn)閳D像大小很重要,所以使用ADD
強(qiáng)烈建議從遠(yuǎn)程URL獲取包;您應(yīng)該使用curl
或wget
相反。這樣,您就可以在解壓縮后刪除不再需要的文件,并且不必在圖像中添加另一層。例如,您應(yīng)該避免這樣做:
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
相反,做一些如下的事情:
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
對于不需要ADD
tar自動(dòng)提取功能的其他項(xiàng)目(文件,目錄),您應(yīng)該始終使用COPY
。
入口點(diǎn)指令的Dockerfile引用
最好的用法ENTRYPOINT
是設(shè)置圖像的主要命令,允許該圖像像該命令一樣運(yùn)行(然后CMD
用作默認(rèn)標(biāo)志)。
讓我們從命令行工具的圖像示例開始s3cmd
*
ENTRYPOINT ["s3cmd"]CMD ["--help"]
現(xiàn)在可以像這樣運(yùn)行映像,以顯示命令的幫助:
$ docker run s3cmd
或者使用正確的參數(shù)來執(zhí)行命令:
$ docker run s3cmd ls s3://mybucket
這很有用,因?yàn)閳D像名可以作為對二進(jìn)制文件的引用加倍,如上面的命令所示。
大ENTRYPOINT
指令也可以與輔助腳本結(jié)合使用,允許它以類似于上面命令的方式工作,即使啟動(dòng)工具可能需要多個(gè)步驟。
例如,郵政總局官方形象使用以下腳本作為其ENTRYPOINT
*
#!/bin/bashset -eif [ "$1" = 'postgres' ]; then chown -R postgres "$PGDATA" if [ -z "$(ls -A "$PGDATA")" ]; then gosu postgres initdb fi exec gosu postgres "$@"fi exec "$@"
注*此腳本使用大
exec
巴什命令因此,最終運(yùn)行的應(yīng)用程序?qū)⒊蔀槿萜鞯腜ID 1。這允許應(yīng)用程序接收發(fā)送到容器的任何Unix信號。見ENTRYPOINT
了解更多細(xì)節(jié)。
將助手腳本復(fù)制到容器中,并通過ENTRYPOINT
在集裝箱啟動(dòng)時(shí):
COPY ./docker-entrypoint.sh /ENTRYPOINT ["/docker-entrypoint.sh"]
此腳本允許用戶以多種方式與Postgres交互。
它只需啟動(dòng)Postgres:
$ docker run postgres
或者,它可以用于運(yùn)行Postgres并將參數(shù)傳遞給服務(wù)器:
$ docker run postgres postgres --help
最后,它還可以用來啟動(dòng)一個(gè)完全不同的工具,比如Bash:
$ docker run --rm -it postgres bash
卷指令的Dockerfile引用
大VOLUME
指令應(yīng)用于公開由??科魅萜鲃?chuàng)建的任何數(shù)據(jù)庫存儲(chǔ)區(qū)、配置存儲(chǔ)區(qū)或文件/文件夾。強(qiáng)烈鼓勵(lì)您使用VOLUME
對于圖像中的任何可變和/或用戶可用部分。
用戶指令的Dockerfile引用
如果服務(wù)可以在沒有特權(quán)的情況下運(yùn)行,請使用USER
若要更改為非根用戶,請執(zhí)行以下操作。首先,在Dockerfile
像這樣RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres
...
注:圖像中的用戶和組得到一個(gè)不確定的UID/GID,因?yàn)椤跋乱徊健盪ID/GID將被分配,而不管圖像重建如何。因此,如果它是關(guān)鍵的,您應(yīng)該分配一個(gè)顯式的UID/GID。注*由于未解決的缺陷在Go存檔/tar包處理稀疏文件時(shí),試圖在Docker容器中創(chuàng)建具有足夠大的UID的用戶會(huì)導(dǎo)致磁盤耗盡
/var/log/faillog
在容器層中填充NUL%28\0%29個(gè)字符。通過--no-log-init
要用戶添加的標(biāo)志可以解決此問題。Debian/Ubuntuadduser
包裝器不支持--no-log-init
標(biāo)志,應(yīng)避免。
您應(yīng)該避免安裝或使用sudo
由于它具有不可預(yù)知的TTY和信號轉(zhuǎn)發(fā)行為,因此可能會(huì)導(dǎo)致比它解決的更多的問題。如果您絕對需要類似于sudo
%28E.。如果將守護(hù)進(jìn)程初始化為root,但以非root%29的形式運(yùn)行它,則可以使用“天哪”...
最后,為了減少層數(shù)和復(fù)雜度,避免切換。USER
頻繁地來回走動(dòng)。
WORKDIR指令的Dockerfile引用
為了清晰可靠,您應(yīng)該始終使用絕對路徑WORKDIR
同時(shí),你也應(yīng)該用WORKDIR
而不是像RUN cd … && do-something
,它們很難閱讀、故障排除和維護(hù)。
ONBUILD指令的Dockerfile引用
安ONBUILD
命令之后執(zhí)行Dockerfile
構(gòu)建完成。ONBUILD
在派生的任何子映像中執(zhí)行。FROM
當(dāng)前圖像。想想ONBUILD
命令作為父級指令。Dockerfile
給孩子Dockerfile
...
執(zhí)行Docker生成ONBUILD
在子命令之前的命令Dockerfile
...
ONBUILD
對于將要構(gòu)建的圖像是有用的。FROMONBUILD
中生成用該語言編寫的任意用戶軟件的語言堆棧映像。Dockerfile
,如你所見魯比氏ONBUILD
變體...
圖像ONBUILD
應(yīng)該得到一個(gè)單獨(dú)的標(biāo)記,例如:ruby:1.9-onbuild
或ruby:2.0-onbuild
...
放的時(shí)候要小心ADD
或COPY
在ONBUILD
如果新構(gòu)建的上下文缺少要添加的資源,“onbuild”映像將災(zāi)難性地失敗。如上面所建議的那樣,添加一個(gè)單獨(dú)的標(biāo)記將有助于緩解這種情況,因?yàn)樗试SDockerfile
作者做出選擇。
Dockerfile引用
更多關(guān)于基本圖像的信息
有關(guān)自動(dòng)生成的更多信息
創(chuàng)建官方存儲(chǔ)庫的指導(dǎo)方針