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