在 Docker 上開發應用 - 編寫 Dockerfile 的最佳實踐

在 Docker 上開發應用 - 編寫 Dockerfile 的最佳實踐

2018年02月25日 13:01:04 閱讀數:673 標籤: dockerdockerfile最佳實踐 更多php

我的分類: Dockerpython

原文地址nginx

Docker 能夠經過從 Dockerfile 中讀取指令來自動構建鏡像,Dockerfile 是一個文本文件,其中包含了按順序排列的構建指定鏡像所需的所有命令。Dockerfiles 採用特殊格式,使用一系列特別的指令。能夠在 Dockerfile 參考頁面 學習這些基礎知識。若是對於編寫 Dockerfile 你仍是新手,那麼接着往下看吧。git

本文檔介紹了由 Docker 公司和 Docker 社區推薦的用於構建高效鏡像的最佳實踐和方法。要查看更多實踐和建議,請點擊 Dockerfile for buildpack-depsgithub

注意:要查看 Dockerfile 命令的詳情,點擊 Dockerfile 參考頁面 。golang

1. 通常準則和建議

1.1 容器應該精簡 ephemeral

由 Dockerfile 定義的映像生成的容器應儘量精簡。意思是說,在容器被中止和銷燬,而且創建和配置完成一個新的容器時,有絕對最少的設置和配置。 你可能須要查看 十二要素應用宣言 的 Processes 部分(譯文在 這裏),以瞭解以這種無狀態方式運行容器的動機。sql

原文: 
The container produced by the image your Dockerfile defines should be as ephemeral as possible. By 「ephemeral,」 we mean that it can be stopped and destroyed and a new one built and put in place with an absolute minimum of set-up and configuration. You may want to take a look at the Processes section of the 12 Factor app methodology to get a feel for the motivations of running containers in such a stateless fashion.docker

1.2 使用 .dockerignore 文件

執行 docker build 命令時你所在的當前工做目錄被稱爲構建上下文,Dockerfile 文件必須在這個構建上下文中。默認狀況下,Dockerfile 被假設在當前目錄中,可是能夠經過 -f 標誌指定一個不一樣位置。無論 Dockerfile 文件位於何處,當前目錄中的全部文件和目錄都會做爲構建上下文發送到 Docker 守護進程。無心中包含了構建鏡像不須要的文件會產生更大的構建上下文和更大的鏡像大小。這些反過來又會增長構建時間、獲取和上傳鏡像的時間以及容器的運行時間。要查看構建上下文有多大,在構建 Dockerfile 時查找相似下面的消息。shell

Sending build context to Docker daemon  187.8MB
  • 1

可使用 .dockerignore 文件排除與構建無關的文件而不重構源代碼庫。該文件支持相似 .gitignore 文件的排除模式。有關建立此文件的信息,參考 這裏數據庫

1.3 使用多段構建

若是 Docker 版本是 17.05 或更高,那就可使用 多段構建 來大幅下降最終鏡像的大小,而無需在構建期間跳過 through hoops 來減小中間層的數量或刪除中間文件。

鏡像僅由最終一個階段構建,大部分時間既有利於構建緩存,又能使鏡像圖層最小化。(Images being built by the final stage only, you can most of the time benefit both the build cache and minimize images layers.)

你的構建階段可能包含多個層,下面例子從最不常見的變動到最多見的變動排序:

  • 安裝構建應用程序所需的工具

  • 安裝或更新庫和依賴

  • 產生應用

一個 Go 應用程序的 Dockerfile 示例:

FROM golang:1.9.2-alpine3.6 AS build
# Install tools required to build the project
# We need to run `docker build --no-cache .` to update those dependencies
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep

# Gopkg.toml and Gopkg.lock lists project dependencies
# These layers are only re-built when Gopkg files are updated
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
# Install library dependencies
RUN dep ensure -vendor-only

# Copy all project and build it
# This layer is rebuilt when ever a file has changed in the project directory
COPY . /go/src/project/
RUN go build -o /bin/project

# This results in a single layer image
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

1.4 避免安裝無用包

要下降複雜性、依賴、文件大小和構建時間,就要避免安裝額外的或不須要的包。例如在數據庫鏡像中不須要文本編輯器。

1.5 每一個容器只解決一個問題

將應用程序解耦爲多個容器使得橫向擴展和重用容器變得更容易。例如,一個 Web 應用程序堆棧可能由三個獨立的容器組成,每一個容器都有其獨特的鏡像,以解耦的方式管理 Web 應用程序、數據庫和內存中的緩存。

你可能聽過這句話「每一個容器一個進程」。雖然這個口頭禪的意圖很好,但並不必定每一個容器只有一個操做系統進程。除了如今可使用 init 進程建立容器 以外,一些程序可能會自行產生其餘進程。例如,Celery 能夠派生多個工做進程,或者 Apache 可能會爲每一個請求建立一個進程。 雖然「每一個容器一個進程」是一個很好的經驗法則,但它並非硬性規定。 盡你最大的努力使容器保持乾淨和模塊化。

若是容器互相依賴,可使用 Docker 容器網絡 來確保容器之間的通訊。

1.6 最小化層數

在 Docker 17.05 甚至 1.10 以前,最小化鏡像的層數是很重要的。下面的改善措施緩解了這個需求:

  • Docker 1.10 及更高版本中,只有 RUN、COPY 和 ADD 命令會建立層。其餘命令建立臨時的中間層鏡像,不會在構建時增長體積。

  • Docker 17.05 及更高版本,增長了分段構建功能,使得能夠只複製所需的項目文件到最終的鏡像中。這讓你能夠在中間層構建過程當中添加工具和調試信息,而不會增大最終鏡像的體積。

1.7 排序多行參數

只要有可能,經過按字母數字順序排列多行參數來簡化後面的更改。這有助於避免軟件包重複並使列表更容易更新。這也使得 PR 更容易閱讀和審覈。在反斜槓(\)以前添加空格也有幫助。

這是來自 buildpack-deps 鏡像 的例子:

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.8 構建緩存

在構建鏡像的過程當中,Docker 會按照指定的順序執行 Dockerfile 文件中的指令。檢查完全部指令後,Docker 會從緩存中尋找可用的鏡像,而不是建立一個新鏡像。若是不想使用緩存,能夠在執行 docker build 命令是添加 --no-cache=true選項。

然而,若是容許 Docker 使用緩存,就須要理解它什麼時候能,什麼時候不能,找到匹配的鏡像。Docker 遵照的基本規則以下:

  • 從緩存中已經存在的父鏡像開始,將下一條指令與從該基本鏡像派生的全部子鏡像進行比較,以查看是否使用徹底相同的指令構建了其中的一個子鏡像。若是沒有則緩存失效。

  • 大多數狀況下,簡單的將 Dockerfile 中的指令和子鏡像中的一個進行比較就足夠了。然而,部分指令須要更多的檢查和解釋。

  • 對於 ADD 和 COPY 指令,鏡像中的文件內容都須要檢查併爲每一個文件計算校驗和 checksum。這些校驗和中不考慮文件的最後編輯時間和最後訪問時間。在緩存查找過程當中,將校驗和與現有鏡像中的校驗和進行比較。若是文件中的內容有任何更改,如內容和元數據,則緩存將失效。

  • 除了 ADD 和 COPY 指令,緩存檢查時不會經過檢查容器中的文件來決定緩存是否匹配。例如在處理 RUN apt-get -y update 命令時,不會經過檢查容器中更新過的文件來決定緩存是否命中。此時只會對比命令字符串是否相同來尋找匹配的緩存。

一旦關閉緩存,全部後續的 Dockerfile 命令都會生成新鏡像,不使用緩存。

2. The Dockerfile instructions

這些建議能夠幫助你寫出高效的、容易維護的 Dockerfile。

FROM

FROM 指令的 Dockerfile 參考資料

只要有可能,使用官方倉庫做爲鏡像的基礎。推薦使用 Alpine 鏡像,由於它的控制很是嚴格,而且保持最小(目前低於5 MB),同時仍然是完整的發行版。

LABEL

理解 labels 對象

能夠給鏡像添加標籤,來幫助項目組織鏡像、記錄許可信息、幫助自動化或出於其餘緣由。對於每一個標籤,添加一行以 LABEL 開頭並帶有一個或多個鍵值對的行。下面示例顯示了多種支持的格式。解釋性意見包含在內。

注意:若是字符串中包含空格,則必須用雙引號引發來或轉義這個空格。若是字符串中包含雙引號,必須轉義。

# 設置一個或多個獨立的標籤
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=""
  • 1
  • 2
  • 3
  • 4
  • 5

鏡像能夠有多個標籤。在 Docker 1.10 版本以前,建議將全部的標籤合併到一個 LABEL 指令中,以防止建立額外的層。如今不須要這麼作了,可是仍然支持合併標籤。

# 在同一行中設置多個標籤
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"
  • 1
  • 2

上面的例子也可用下面的寫法:

# 一次設置多個標籤,並使用續行字符打斷很長的行
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"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

有關可以使用的標籤中鍵和值的信息,參閱 Understanding object labels。有關查詢 querying 標籤的信息,參閱 Managing labels on objects 中與過濾相關的項目。另請參閱 Dockerfile 參考中的 LABEL

RUN

RUN 指令的 Dockerfile 參考資料

將很長或很複雜的 RUN 語句用反斜線(\)切分爲多行可讓 Dockerfile 文件易讀、易理解而且易維護。

1. APT-GET 指令

RUN 最多見的用例多是 apt-get 應用程序。由於 RUN apt-get 命令會安裝軟件包,有幾個須要注意的問題。

應該避免使用 RUN apt-get upgrade 或 dist-upgrade,由於許多來自父鏡像的「essential」基本軟件包沒法在非特權容器內升級。若是父鏡像中的軟件包已過期,應聯繫其維護人員。若是你知道須要更新某個特定軟件包,好比「foo」,請使用 apt-get install -y foo 自動更新。

在同一個 RUN 語句中一同運行 apt-get update 和 apt-get install。例如:

RUN apt-get update && apt-get install -y \
        package-bar \
        package-baz \
        package-foo
  • 1
  • 2
  • 3
  • 4

RUN 語句中單獨使用 apt-get update 會致使緩存問題,並使後面的 apt-get install 指令執行失敗。例如,看下面的 Dockerfile:

FROM ubuntu:14.04
    RUN apt-get update
    RUN apt-get install -y curl
  • 1
  • 2
  • 3

上面的鏡像構建完成後,全部的層都會在 Docker 緩存中。假設後面會經過添加額外的包來變動 apt-get install 這條指令:

FROM ubuntu:14.04
    RUN apt-get update
    RUN apt-get install -y curl nginx
  • 1
  • 2
  • 3

此時 Docker 會認爲這個例子中的前兩步和上個例子的同樣,從而使用上個例子生成的緩存,致使 apt-get update 命令並未執行。apt-get update 沒有運行,因此後面可能會安裝的 curl 和 nginx 可能不是最新版本。

使用 RUN apt-get update && apt-get install -y 能夠確保 Dockerfile 安裝最新版本的包,無需進一步編碼或手動干預。這種技術被稱爲「緩存破壞」(cache busting)。 也能夠經過指定軟件包的版原本清除緩存。這被稱爲版本固定(version pinning),例如:

RUN apt-get update && apt-get install -y \
        package-bar \
        package-baz \
        package-foo=1.3.*
  • 1
  • 2
  • 3
  • 4

版本固定會強制構建時檢索特定的版本,而無論緩存中的內容。該技術還能夠減小因爲所需軟件包的意外更改而致使的故障。

下面是一個組織良好的 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/*
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

s3cmd 指定要安裝 1.1.* 版本。若是鏡像在以前使用的是舊的版本,指定新版本會致使 apt-get update 命令的緩存破壞,從而確保安裝的是這個指定的新版本。每一個包單獨出如今一行中,能夠防止出現包重複的錯誤。

此外,當經過刪除 /var/lib/apt/lists 目錄來清除 apt 的緩存時,能夠減少鏡像尺寸(由於 apt 緩存不會存入層)。這裏的 RUN 語句用 apt-get update 命令開頭,因此在執行 apt-get install 命令以前包緩存老是會獲得更新。

注意:官方的 Debian 和 Ubuntu 鏡像會 自動運行 apt-get clean,所以不須要顯式調用。

2. 使用管道

部分 RUN 命令藉助管道 pipe 將一個命令的輸出發送到另外一個命令。下面例子演示了管道符號 | 的使用:

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

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

預先設置 set -o pipefail && 命令,可使管道中的任何一步發生錯誤時,都會致使命令執行失敗,從而再也不構建鏡像。例如:

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

注意:並使是全部的 shell 都支持 -o pipefail 選項(好比 Debian 基礎鏡像中的默認 shell dash)。此時,可使用 RUN 的 exec 形式來顯式選擇一個支持 pipefail 選項的 shell。例如:

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

CMD

CMD 指令的 Dockerfile 參考資料

CMD 指令應該用來運行鏡像中的軟件,能夠有任意多個參數。格式爲:CMD [「executable」, 「param1」, 「param2」…]。所以,若是鏡像用來運行服務,例如 Apache 和 Rails,能夠經過 CMD ["apache2","-DFOREGROUND"] 來運行。事實上,全部的基於服務的鏡像都推薦使用這種命令格式。

大多數狀況下,CMD 須要交互式的 shell,例如 bash、Python 或 Perl。例如,CMD ["perl", "-de0"]CMD ["python"]或 CMD ["php", "-a"]。CMD 採用這種形式時,意味着當你執行相似 docker run -it python 這樣的命令時能夠直接進入到一個可用的 shell。除非您和您的預期用戶已經很是熟悉 ENTRYPOINT 的工做方式,不然 CMD 應該不多以 CMD ["param", "param"] 和 ENTRYPOINT 的方式使用。

EXPOSE

EXPOSE 指令的 Dockerfile 參考資料

EXPOSE 指令指示開啓容器的哪一個端口來監聽鏈接。應該爲應用程序使用通用的傳統端口。例如,包含 Apache Web 服務器的鏡像將使用EXPOSE 80,而包含 MongoDB 的映像將使用 EXPOSE 27017 等。

爲了使外部能夠訪問,用戶能夠在執行 docker run 命令時使用標誌將容器的某個端口映射到用戶選擇的端口。對於容器連接,Docker 爲從服務容器返回到源的路徑(即 MYSQL_PORT_3306_TCP)提供環境變量。(原文:For container linking, Docker provides environment variables for the path from the recipient container back to the source (ie, 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
  • 1
  • 2
  • 3
  • 4

跟程序中的常量(而不是硬編碼值)相似,此方法可以讓你更改單個 ENV 指令,以自動的地處理容器中的軟件版本。

跟 RUN 命令同樣,每一個 ENV 行會建立一個新的中間層。這意味着即便在後面的層中 unset 環境變量,這個值仍然會持久化在這個層中,其值可能會丟棄。能夠經過建立相似下面的 Dockerfile 而且構建鏡像來測試一下:

FROM alpine
ENV ADMIN_USER="mark"
RUN echo $ADMIN_USER > ./mark
RUN unset ADMIN_USER
CMD sh
  • 1
  • 2
  • 3
  • 4
  • 5
$ docker run --rm -it test sh echo $ADMIN_USER

mark
  • 1
  • 2
  • 3

在同一個層中使用帶 shell 命令的 RUN 命令來 set、use 和 unset 變量能夠避免這種狀況,而且確保完全 unset 環境變量。能夠經過分號 ; 或 && 來分隔命令。使用 && 時,任何一個命令執行失敗都會致使鏡像構建失敗。這是個好主意。使用反斜線 \ 做爲行繼續符號,能夠提升 Linux 中 Dockerfile 的可讀性。能夠把全部的命令放入一個 shell 腳本中,經過 RUN 命令直接運行這個腳本。

FROM alpine
RUN export ADMIN_USER="mark" \
    && echo $ADMIN_USER > ./mark \
    && unset ADMIN_USER
CMD sh
  • 1
  • 2
  • 3
  • 4
  • 5
$ docker run --rm -it test sh echo $ADMIN_USER
  • 1

ADD or COPY

ADD 指令的 Dockerfile 參考資料

COPY 指令的 Dockerfile 參考資料

ADD 和 COPY 在功能上類似,一般來講優先使用 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/
  • 1
  • 2
  • 3

上面的例子中,相比使用 COPY . /tmp/,用於 RUN 這一步的緩存更加不容易失效。

由於鏡像大小的考慮,很是不建議經過 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
  • 1
  • 2
  • 3

相反,使用這個例子:

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
  • 1
  • 2
  • 3
  • 4

對於其餘不須要 ADD 的 tar 文件自動解壓縮功能的時候,儘可能使用 COPY。

ENTRYPOINT

ENTRYPOINT 指令的 Dockerfile 參考資料

ENTRYPOINT 指令的最佳用途是設置鏡像的主命令,容許該鏡像像該命令同樣運行(而後使用 CMD 做爲默認標誌)。

下面的鏡像,ENTRYPOINT 設置爲命令行工具 s3cmd:

ENTRYPOINT ["s3cmd"]
CMD ["--help"]
  • 1
  • 2

如今要查看命令的幫助能夠這樣運行:

$ docker run s3cmd
  • 1

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

$ docker run s3cmd ls s3://mybucket
  • 1

這頗有用,由於如上面的命令所示,鏡像名稱能夠做爲對二進制文件的二次引用。

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 "$@"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注意:這個腳本使用了 exec 這個 Bash 命令,所以最終運行的應用程序稱爲容器的 PID 1。這會容許應用程序接受任何發送到容器的 Unix 信號。更多信息參考 ENTRYPOINT

輔助腳本被複制到容器中,而且在容器啓動時經過 ENTRYPOINT 運行:

COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
  • 1
  • 2

這個腳本容許用戶使用多種方式同 Postgres 交互。

能夠簡單的啓動 Postgres:

$ docker run postgres
  • 1

或者用來運行 Postgres 而且向服務器傳參數:

$ docker run postgres postgres --help
  • 1

最後,還能夠用來開啓徹底不一樣的工具,好比 Bash:

$ docker run --rm -it postgres bash
  • 1

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 很關鍵,就必須明確指定。

注意:因爲 Go archive/tar 包處理稀疏文件(sparse files)時存在 未解決的錯誤,試圖在 Docker 容器內建立具備足夠大UID的用戶可能致使磁盤耗盡,由於容器層中的 /var/log/faillog 文件會填滿 NUL(\0)字符。 將 --no-log-init 標誌傳遞給 useradd 能夠解決此問題。 Debian/Ubuntu 的 adduser 不支持 --no-log-init 標誌。

避免安裝或使用 sudo,由於它具備可能致使問題的不可預知的 TTY 和信號轉發行爲。 若是須要與 sudo 相似的功能,例如以 root 身份初始化守護程序,但將其做爲非 root 用戶運行),請考慮使用 gosu

最後,爲了減小層數和複雜性,避免頻繁切換 USER。

WORKDIR

WORKDIR 指令的 Dockerfile 參考資料

應該始終爲 WORKDIR 使用絕對路徑來保證清晰可靠。另外,應該使用 WORKDIR 而不是像 RUN CD ... && do-something這樣的繁瑣指令,這些指令很難讀懂、排除故障和維護。

ONBUILD

ONBUILD 指令的 Dockerfile 參考資料

ONBUILD 指令在所在的 Dockerfile 構建完成後執行。ONBUILD 在從當前鏡像派生的任何子鏡像中執行。能夠將 ONBUILD 命令看做父 Dockerfile 給子 Dockerfile 的指令。

Docker 構建時會在執行子 Dockerfile 的任何命令以前執行 ONBUILD 命令。

ONBUILD 命令在從指定鏡像構建新鏡像時頗有用。例如,能夠爲語言堆棧鏡像使用 ONBUILD,在 Dockerfile 中使用該語言編寫任意用戶軟件,就像在 Ruby 的 ONBUILD 變體中看到的同樣。

從 ONBUILD 構建的鏡像應該有一個獨立的標籤,例如:ruby:1.9-onbuild 或 ruby:2.0-onbuild

在 ONBUILD 中使用 ADD 或 COPY 時須要當心。若是新構建的上下文缺乏所需資源,或致使 ONBUILD 的鏡像構建失敗。按照上面的建議添加一個單獨的標籤,經過容許 Dockerfile 做者作出選擇能夠幫助緩解這種狀況。

3. 官方倉庫示例

這些官方倉庫具備示例性 Dockerfiles:

4. 附加資源

相關文章
相關標籤/搜索