Docker中的Dockerfile命令詳解FROM RUN COPY ADD ENTRYPOINT...


Dockerfile指令

這些建議旨在幫助您創建高效且可維護的Dockerfile。

FROM

FROM指令的Dockerfile引用

盡可能使用當前的官方圖像作為圖像的基礎。我們推薦Alpine圖像,因為它是嚴格控制的並且尺寸小(目前小於5 MB),同時仍然是完整的Linux發行版。

標簽

了解對象標簽

您可以為圖像添加標簽,以幫助按項目組織圖像,記錄許可信息,幫助實現自動化或出於其他原因。對於每個標簽,添加LABEL以一個或多個鍵值對開頭的行。以下示例顯示了不同的可接受格式。內容包括解釋性意見。

必須引用帶空格的字符串必須轉義空格。內引號字符(")也必須進行轉義。

# Set one or more individual labels

LABEL com.example.version="0.0.1-beta"

LABEL vendor1="ACME Incorporated"

LABEL vendor2=ZENITH\ Incorporated

LABEL com.example.release-date="2015-02-12"

LABEL com.example.version.is-production=""

圖像可以有多個標簽。在Docker 1.10之前,建議將所有標簽組合到一條LABEL指令中,以防止創建額外的層。這不再是必需的,但仍然支持組合標簽。

# 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參考中的LABEL

RUN

RUN指令的Dockerfile參考

RUN在使用反斜杠分隔的多行上拆分長或復雜語句,以使您Dockerfile更具可讀性,可理解性和可維護性。

APT-GET的

可能最常見的用例RUN是應用程序apt-get。因為它安裝了包,所以該RUN apt-get命令有幾個需要注意的問題。

避免RUN apt-get upgrade和dist-upgrade,因為父圖像中的許多“基本”包無法在非特權容器內升級 。如果父圖像中包含的包已過期,請與其維護人員聯系。如果您知道有特定包,foo需要更新,請使用 apt-get install -y foo自動更新。

始終在同一 聲明中結合RUN apt-get update使用。例如:apt-get installRUN

RUN apt-get update && apt-get install -y \

    package-bar \

    package-baz \

    package-foo

apt-get update在RUN語句中單獨使用會導致緩存問題,並且后續apt-get install指令會失敗。例如,假設你有一個Dockerfile:

FROM ubuntu:18.04

RUN apt-get update

RUN apt-get install -y curl

構建映像后,所有層都在Docker緩存中。假設您稍后apt-get install通過添加額外包修改:

FROM ubuntu:18.04

RUN apt-get update

RUN apt-get install -y curl nginx

Docker將初始和修改的指令視為相同,並重用前面步驟中的緩存。其結果是,apt-get update在執行,因為編譯使用緩存的版本。因為apt-get update沒有運行,您的構建有可能得到的一個過時的版本curl和 nginx包。

使用RUN apt-get update && apt-get install -y確保您的Dockerfile安裝最新的軟件包版本,無需進一步編碼或手動干預。這種技術被稱為“緩存破壞”。您還可以通過指定包版本來實現緩存清除。這稱為版本固定,例如:

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並確保安裝新版本。列出每行的包也可以防止包重復中的錯誤。

此外,當您通過刪除/var/lib/apt/lists它來清理apt緩存時會減小圖像大小,因為apt緩存不存儲在圖層中。由於RUN語句以#開頭apt-get update,因此包緩存始終在刷新之前刷新apt-get install。

官方Debian和Ubuntu映像自動運行apt-get clean,因此不需要顯式調用。

使用管道

某些RUN命令依賴於使用管道符(|)將一個命令的輸出傳遞到另一個命令的能力,如下例所示:

RUN wget -O - https://some.site | wc -l > /number

Docker使用/bin/sh -c解釋器執行這些命令,解釋器僅評估管道中最后一個操作的退出代碼以確定成功。在上面的示例中,只要wc -l命令成功,即使wget命令失敗,此構建步驟也會成功並生成新映像。

如果您希望命令因管道中任何階段的錯誤而失敗,請預先set -o pipefail &&確定意外錯誤,以防止構建無意中成功。例如:

RUN set -o pipefail && wget -O - https://some.site | wc -l > /number

並非所有shell都支持該-o pipefail選項。

在諸如dash基於Debian的映像上的shell的情況下,考慮使用exec形式RUN明確選擇支持該pipefail選項的shell 。例如:

RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]

CMD

CMD指令的Dockerfile參考

該CMD指令應用於運行圖像包含的軟件以及任何參數。CMD應該幾乎總是以形式使用CMD ["executable", "param1", "param2"…]。因此,如果圖像是用於服務的,例如Apache和Rails,那么你可以運行類似的東西CMD ["apache2","-DFOREGROUND"]。實際上,建議將這種形式的指令用於任何基於服務的圖像。

在大多數其他情況下,CMD應該給出一個交互式shell,例如bash,python和perl。例如,CMD ["perl", "-de0"],CMD ["python"],或CMD ["php", "-a"]。使用此表單意味着當您執行類似的操作時 docker run -it python,您將被放入可用的shell中,隨時可以使用。 CMD應該很少的方式使用CMD ["param", "param"]會同ENTRYPOINT,除非你和你預期的用戶已經非常熟悉如何ENTRYPOINT 工作的。

EXPOSE

EXPOSE指令的Dockerfile參考

該EXPOSE指令指示容器偵聽連接的端口。因此,您應該為您的應用程序使用通用的傳統端口。例如,包含Apache Web服務器EXPOSE 80的圖像將使用,而包含MongoDB的圖像將使用EXPOSE 27017,依此類推。

對於外部訪問,您的用戶可以docker run使用一個標志來執行,該標志指示如何將指定端口映射到他們選擇的端口。對於容器鏈接,Docker為從接收容器返回源的路徑提供環境變量(即MYSQL_PORT_3306_TCP)。

ENV

ENV指令的Dockerfile參考

要使新軟件更易於運行,您可以使用ENV更新PATH容器安裝的軟件的 環境變量。例如,ENV PATH /usr/local/nginx/bin:$PATH確保CMD ["nginx"] 正常工作。

該ENV指令對於提供特定於您希望容納的服務的必需環境變量也很有用,例如Postgres PGDATA。

最后,ENV還可以用於設置常用的版本號,以便更容易維護版本顛簸,如以下示例所示:

ENV PG_MAJOR 9.3

ENV PG_VERSION 9.3.4

RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …

ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

與在程序中使用常量變量(與硬編碼值相對)類似,此方法允許您更改單個ENV指令以自動神奇地破壞容器中的軟件版本。

每ENV行創建一個新的中間層,就像RUN命令一樣。這意味着即使您在將來的圖層中取消設置環境變量,它仍然會在此圖層中保留,並且可以轉儲其值。您可以通過創建如下所示的Dockerfile來測試它,然后構建它。

FROM alpine

ENV ADMIN_USER="mark"

RUN echo $ADMIN_USER > ./mark

RUN unset ADMIN_USER

$ docker run --rm test sh -c 'echo $ADMIN_USER'

 

mark

要防止這種情況,並且確實取消設置環境變量,請使用RUN帶有shell命令的命令,在單個圖層中設置,使用和取消設置變量all。您可以使用;或分隔命令&&。如果您使用第二種方法,並且其中一個命令失敗,則docker build也會失敗。這通常是一個好主意。使用\Linux Dockerfiles作為行繼續符可以提高可讀性。您還可以將所有命令放入shell腳本中,並讓RUN命令運行該shell腳本。

FROM alpine

RUN export ADMIN_USER="mark" \

    && echo $ADMIN_USER > ./mark \

    && unset ADMIN_USER

CMD sh

$ docker run --rm test sh -c 'echo $ADMIN_USER'

 

ADD COPY

一般而言,雖然ADD並且COPY在功能上類似,但是COPY 是優選的。那是因為它更透明ADD。COPY僅支持將本地文件基本復制到容器中,同時ADD具有一些功能(如僅限本地的tar提取和遠程URL支持),這些功能並不是很明顯。因此,最好的用途ADD是將本地tar文件自動提取到圖像中,如ADD rootfs.tar.xz /。

如果您有多個Dockerfile步驟使用上下文中的不同文件,則COPY它們是單獨的,而不是一次性完成。這可確保每個步驟的構建緩存僅在特定所需文件更改時失效(強制重新執行該步驟)。

例如:

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

對於不需要ADDtar自動提取功能的其他項目(文件,目錄),您應該始終使用COPY。

ENTRYPOINT

ENTRYPOINT指令的Dockerfile參考

最好的用法ENTRYPOINT是設置圖像的主命令,允許該圖像像該命令一樣運行(然后CMD用作默認標志)。

讓我們從命令行工具的圖像示例開始s3cmd:

ENTRYPOINT ["s3cmd"]

CMD ["--help"]

現在可以像這樣運行圖像來顯示命令的幫助:

$ docker run s3cmd

或使用正確的參數執行命令:

$ docker run s3cmd ls s3://mybucket

這很有用,因為圖像名稱可以兼作二進制文件的引用,如上面的命令所示。

該ENTRYPOINT指令還可以與輔助腳本結合使用,允許它以與上述命令類似的方式運行,即使啟動該工具可能需要多個步驟。

例如,Postgres官方圖像 使用以下腳本作為其ENTRYPOINT:

#!/bin/bash

set -e

 

if [ "$1" = 'postgres' ]; then

    chown -R postgres "$PGDATA"

 

    if [ -z "$(ls -A "$PGDATA")" ]; then

        gosu postgres initdb

    fi

 

    exec gosu postgres "$@"

fi

 

exec "$@"

app配置為PID 1

此腳本使用的execbash命令 ,以使最終運行的應用程序成為容器的PID 1.這允許應用程序接收發送到所述容器任何Unix信號。如需更多信息,請參閱ENTRYPOINT參考

幫助程序腳本被復制到容器中並通過ENTRYPOINT容器啟動運行:

COPY ./docker-entrypoint.sh /

ENTRYPOINT ["/docker-entrypoint.sh"]

CMD ["postgres"]

該腳本允許用戶以多種方式與Postgres交互。

它可以簡單地啟動Postgres:

$ docker run postgres

或者,它可用於運行Postgres並將參數傳遞給服務器:

$ docker run postgres postgres --help

最后,它還可以用來啟動一個完全不同的工具,比如Bash:

$ docker run --rm -it postgres bash

VOLUME

VOLUME指令的Dockerfile參考

該 VOLUME指令應用於公開由docker容器創建的任何數據庫存儲區域,配置存儲或文件/文件夾。強烈建議您使用圖像VOLUME的任何可變和/或用戶可維修部分。

USER

USER指令的Dockerfile參考

如果服務可以在沒有權限的情況下運行,請使用USER更改為非root用戶。首先在Dockerfile類似的東西中創建用戶和組RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres。

考慮一個顯式的UID / GID

圖像中的用戶和組被分配了非確定性UID / GID,因為無論圖像重建如何,都會分配“下一個”UID / GID。因此,如果它很重要,您應該分配一個顯式的UID / GID。

由於Go存檔/ tar包處理稀疏文件時未解決的錯誤,嘗試在Docker容器內創建具有非常大的UID的用戶可能導致磁盤耗盡,因為/var/log/faillog容器層中填充了NULL(\ 0)字符。解決方法是將--no-log-init標志傳遞給useradd。Debian / Ubuntu adduser包裝器不支持此標志。

避免安裝或使用,sudo因為它具有可能導致問題的不可預測的TTY和信號轉發行為。如果您絕對需要類似的功能sudo,例如將守護程序初始化root為非運行它root,請考慮使用“gosu”

最后,為了減少層次和復雜性,避免USER頻繁地來回切換。

WORKDIR

WORKDIR指令的Dockerfile參考

為了清晰和可靠,您應該始終使用絕對路徑 WORKDIR。此外,您應該使用難以閱讀,排除故障和維護WORKDIR的擴散指令RUN cd … && do-something。

ONBUILD

ONBUILD指令的Dockerfile參考

一個ONBUILD命令將當前執行后Dockerfile構建完成。ONBUILD在任何導出FROM當前圖像的子圖像中執行。將該ONBUILD命令視為父母Dockerfile給孩子的指令Dockerfile。

Docker構建ONBUILD在子代中的任何命令之前執行命令Dockerfile。

ONBUILD對於將要構建FROM給定圖像的圖像非常有用。例如,您將使用ONBUILD一個語言堆棧映像來構建在該語言中編寫的任意用戶軟件 Dockerfile,正如您在Ruby的ONBUILD變體中所看到的那樣。

構建的圖像ONBUILD應該獲得單獨的標記,例如:ruby:1.9-onbuild或ruby:2.0-onbuild。

把時要小心,ADD或COPY在ONBUILD。如果新構建的上下文缺少正在添加的資源,則“onbuild”映像將發生災難性故障。如上所述,添加單獨的標記有助於通過允許Dockerfile作者做出選擇來緩解這種情況。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM