[TOC]
# [Dockerfile參考](https://docs.docker.com/engine/reference/builder)
Docker可以通過閱讀Docker中的指令來自動構建映像`Dockerfile`。A`Dockerfile`是一個文本文檔,其中包含用戶可以在命令行上調用以組裝圖像的所有命令。使用`docker build`用戶可以創建自動構建,該構建連續執行多個命令行指令。
本頁描述您可以在中使用的命令`Dockerfile`。閱讀完此頁面后,請參考[`Dockerfile`最佳實踐](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/)以獲取有關技巧的指南。
## Usage
[docker build](https://docs.docker.com/engine/reference/commandline/build/)命令從一個構建的圖像`Dockerfile`和一個*上下文*。構建的上下文是指定位置`PATH`或的文件集`URL`。這`PATH`是本地文件系統上的目錄。該`URL`是一個Git倉庫的位置。
上下文是遞歸處理的。因此,a`PATH`包括任何子目錄,并且a包括`URL`存儲庫及其子模塊。此示例顯示了一個使用當前目錄作為上下文的構建命令:
~~~
$ docker build .
Sending build context to Docker daemon 6.51 MB
...
~~~
構建是由Docker守護程序而不是CLI運行的。構建過程要做的第一件事是將整個上下文(遞歸)發送到守護程序。在大多數情況下,最好從一個空目錄作為上下文開始,并將您的Dockerfile保留在該目錄中。僅添加構建Dockerfile所需的文件。
> **警告**
>
> 不要用你的根目錄下,`/`作為`PATH`因為它會導致生成到您的硬盤驅動器的全部內容傳輸到docker護進程。
要在構建上下文中使用文件,`Dockerfile`引用指的是指令(例如,`COPY`指令)中指定的文件。要提高構建的性能,請通過將`.dockerignore`文件添加到上下文目錄來排除文件和目錄。有關如何[創建`.dockerignore`文件的信息,](https://docs.docker.com/engine/reference/builder/#dockerignore-file)請參閱此頁面上的文檔。
傳統上,`Dockerfile`稱為,`Dockerfile`并且位于上下文的根中。您可以使用`-f`標志with`docker build`指向文件系統中任何位置的Dockerfile。
~~~
$ docker build -f /path/to/a/Dockerfile .
~~~
如果構建成功,則可以指定存儲新映像的存儲庫和標記:
~~~
$ docker build -t shykes/myapp .
~~~
要在構建后將映像標記到多個存儲庫中,請在`-t`運行`build`命令時添加多個參數:
~~~
$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
~~~
在Docker守護程序運行中的指令之前`Dockerfile`,它會對進行初步驗證,`Dockerfile`如果語法不正確,則會返回錯誤:
~~~
$ docker build -t test/myapp .
Sending build context to Docker daemon 2.048 kB
Error response from daemon: Unknown instruction: RUNCMD
~~~
Docker守護程序以`Dockerfile`一對一的方式運行指令,如有必要,將每個指令的結果提交到新映像,然后最終輸出新映像的ID。Docker守護程序將自動清理您發送的上下文。
請注意,每條指令都是獨立運行的,并會導致創建新的映像-因此`RUN cd /tmp`對下一條指令不會有任何影響。
Docker將盡可能重用中間映像(緩存),以`docker build`顯著加速該過程。這由`Using cache`控制臺輸出中的消息指示。(有關詳細信息,請參閱[`Dockerfile`最佳做法指南](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/):
~~~
$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step 1/4 : FROM alpine:3.2
---> 31f630c65071
Step 2/4 : MAINTAINER SvenDowideit@home.org.au
---> Using cache
---> 2a1c91448f5f
Step 3/4 : RUN apk update && apk add socat && rm -r /var/cache/
---> Using cache
---> 21ed6e7fbb73
Step 4/4 : CMD env | grep _TCP= | (sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' && echo wait) | sh
---> Using cache
---> 7ea8aef582cc
Successfully built 7ea8aef582cc
~~~
構建緩存僅用于具有本地父鏈的圖像。這意味著這些圖像是由以前的版本創建的,或者整個圖像鏈都已加載`docker load`。如果您希望使用特定映像的構建緩存,則可以使用`--cache-from`選項指定它。指定帶有的圖像`--cache-from`不需要具有父鏈,并且可以從其他注冊表中提取。
完成構建后,您就可以研究[*將存儲庫推送到其注冊表了*](https://docs.docker.com/engine/tutorials/dockerrepos/#/contributing-to-docker-hub)。
## BuildKit
從版本18.09開始,Docker支持由[moby / buildkit](https://github.com/moby/buildkit)項目提供的用于執行構建的新后端。與舊的實現相比,BuildKit后端提供了許多好處。例如,BuildKit可以:
* 檢測并跳過執行未使用的構建階段
* 并行構建獨立的構建階段
* 兩次構建之間僅增量傳輸構建上下文中的已更改文件
* 在構建上下文中檢測并跳過傳輸未使用的文件
* 使用具有許多新功能的外部Dockerfile實現
* 避免其他API(中間圖像和容器)的副作用
* 優先考慮構建緩存以進行自動修剪
要使用BuildKit后端,您需要`DOCKER_BUILDKIT=1`在CLI上設置環境變量,然后再調用`docker build`。
要了解基于BuildKit的構建可用的實驗性Dockerfile語法,[請參閱BuildKit存儲庫中的文檔](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md)。
## Format
這是`Dockerfile`的格式:
~~~
# Comment
INSTRUCTION arguments
~~~
該指令不區分大小寫。但是,慣例是將它們大寫以更輕松地將它們與參數區分開。
Docker`Dockerfile`按順序運行指令。一個`Dockerfile`**必須以開始`FROM`的指令**。這可能在[解析器指令](https://docs.docker.com/engine/reference/builder/#parser-directives),[注釋](https://docs.docker.com/engine/reference/builder/#format)和全局范圍的[ARG之后](https://docs.docker.com/engine/reference/builder/#arg)。該`FROM`指令指定要從中構建[*父圖像*](https://docs.docker.com/glossary/#parent_image)。`FROM`只能在一個或多個`ARG`指令之前,這些指令聲明在中的`FROM`行中使用的參數`Dockerfile`。
該碼頭工人對待線*開始*以`#`作為注釋,除非該行是一個有效的[解析器指令](https://docs.docker.com/engine/reference/builder/#parser-directives)。`#`一行中其他任何地方的標記都被視為參數。這允許如下語句:
~~~
# Comment
RUN echo 'we are running some # of cool things'
~~~
在執行Dockerfile指令之前刪除注釋行,這意味著以下示例中的注釋不會由執行`echo`命令的Shell處理,并且以下兩個示例是等效的:
~~~
RUN echo hello \
# comment
world
~~~
~~~
RUN echo hello \
world
~~~
注釋中不支持換行符。
> **注意空白**
>
> 為了向后兼容,將忽略但不鼓勵在注釋(`#`)和指令(例如`RUN`)之前的前導空白。在這些情況下,不保留前導空格,因此以下示例是等效的:
>
> ~~~
> # this is a comment-line
> RUN echo hello
> RUN echo world
>
> ~~~
>
> ~~~
> # this is a comment-line
> RUN echo hello
> RUN echo world
>
> ~~~
>
> 但是請注意,指令*參數*中的空白(例如跟隨其后的命令`RUN`)被保留,因此以下示例使用指定的前導空白打印“ hello world”:
>
> ~~~
> RUN echo "\
> hello\
> world"
>
> ~~~
## Parser directives
解析器指令是可選的,并且會影響`Dockerfile`處理a中后續行的方式。解析器指令不會在構建中添加圖層,也不會顯示為構建步驟。解析器指令以形式寫為特殊類型的注釋`# directive=value`。單個指令只能使用一次。
處理完注釋,空行或生成器指令后,Docker不再尋找解析器指令。而是將格式化為解析器指令的任何內容都視為注釋,并且不會嘗試驗證它是否可能是解析器指令。因此,所有解析器指令必須位于的最頂部`Dockerfile`。
解析器指令不區分大小寫。但是,約定是小寫的。約定還應在任何解析器指令之后包含一個空白行。解析器偽指令不支持行繼續字符。
由于這些規則,以下示例均無效:
由于行繼續而無效:
~~~
# direc \
tive=value
~~~
由于出現兩次而無效:
~~~
# directive=value1
# directive=value2
FROM ImageName
~~~
由于在生成器指令之后出現,因此被視為注釋:
~~~
FROM ImageName
# directive=value
~~~
由于出現在不是解析器指令的注釋之后,因此被視為注釋:
~~~
# About my dockerfile
# directive=value
FROM ImageName
~~~
由于未被識別,未知指令被視為注釋。另外,由于在不是解析器指令的注釋之后出現,所以已知指令被視為注釋。
~~~
# unknowndirective=value
# knowndirective=value
~~~
解析器指令中允許非換行空格。因此,以下各行都被相同地對待:
~~~
#directive=value
# directive =value
# directive= value
# directive = value
# dIrEcTiVe=value
~~~
支持以下解析器指令:
* `syntax`
* `escape`
## syntax
~~~
# syntax=[remote image reference]
~~~
例如:
~~~
# syntax=docker/dockerfile
# syntax=docker/dockerfile:1.0
# syntax=docker.io/docker/dockerfile:1
# syntax=docker/dockerfile:1.0.0-experimental
# syntax=example.com/user/repo:tag@sha256:abcdef...
~~~
只有在[BuildKit公司](https://docs.docker.com/engine/reference/builder/#buildkit)使用后端
syntax指令定義用于生成當前Dockerfile的Dockerfile生成器的位置。BuildKit后端允許無縫地使用作為Docker映像分發并在容器沙盒環境中執行的構建器的外部實現。
自定義Dockerfile實現允許您:
* 無需更新守護進程即可自動修復錯誤
* 確保所有用戶使用相同的實現來構建Dockerfile
* 使用最新功能而不更新守護程序
* 嘗試新的實驗或第三方功能
### Official releases
Docker分發可用于在下構建Dockerfiles的映像的官方版本`docker/dockerfile`Docker Hub上的存儲庫。有兩個通道可以發布新的圖像:穩定的和實驗的。
穩定通道遵循語義版本控制。例如:
* `docker/dockerfile:1.0.0`\-只允許不可變版本 `1.0.0`
* `docker/dockerfile:1.0`\-允許版本 `1.0.*`
* `docker/dockerfile:1`\-允許版本 `1.*.*`
* `docker/dockerfile:latest`\-穩定頻道的最新版本
實驗性的通道在發布時使用穩定通道中的主要和次要組件的增量版本控制。例如:
* `docker/dockerfile:1.0.1-experimental`\-只允許不可變版本`1.0.1-實驗`
* `docker/dockerfile:1.0-experimental`\-之后的最新實驗版本 `one`
* `docker/dockerfile:experimental`\-實驗頻道最新版本
你應該選擇最適合你需要的頻道。如果您只想修復錯誤,您應該使用`docker/dockerfile:1.0`. 如果您想從實驗特性中獲益,您應該使用實驗頻道。如果您使用的是實驗性的通道,較新的版本可能無法向后兼容,因此建議使用不可變的完整版本變體。
有關主版本和夜間功能版本,請參閱中的說明[源存儲庫](https://github.com/moby/buildkit/blob/master/README.md)
## escape
~~~
# escape=\ (backslash)
~~~
Or
~~~
# escape=` (backtick)
~~~
這個`escape`指令設置用于轉義 `Dockerfile文件`. 如果未指定,則默認轉義字符為`\`.
轉義符既用于轉義行中的字符,也用于轉義換行符。這允許`Dockerfile`跨越多行的指令。請注意,無論`逃跑`解析器指令包含在`Dockerfile`,*在中不執行轉義`RUN`命令,除了在行尾*
將轉義符設置為`is especially useful on`Windows`, where`\`is the directory path separator.`與[Windows PowerShell](https://technet.microsoft.com/en-us/library/hh847755.aspx).
考慮下面的例子,它會以不明顯的方式失敗`Windows`. 第二次 `\`在第二行末尾,將被解釋為新行的轉義,而不是從第一行轉義的目標`\`. 同樣 `\`在第三行的末尾,假設它實際上是作為一條指令來處理的,會導致它被視為行的延續。此dockerfile的結果是第二行和第三行被視為一條指令:
~~~
FROM microsoft/nanoserver
COPY testfile.txt c:\\
RUN dir c:\
~~~
結果:
~~~
PS C:\John> docker build -t cmd .
Sending build context to Docker daemon 3.072 kB
Step 1/2 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/2 : COPY testfile.txt c:\RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS C:\John>
~~~
解決上述問題的一種方法是使用`/`作為兩個目標`復制`說明,以及`dir`. 然而,這種語法充其量是令人困惑的,因為它對于上的路徑來說是不自然的`窗戶`,最壞的情況是,由于不是所有命令都打開,所以容易出錯`Windows`支持 `/`作為路徑分隔符
通過添加`escape`解析器指令,如下所示 `Dockerfile文件`對上的文件路徑使用自然平臺語義,如預期的那樣成功`Windows`:
~~~
# escape=`
FROM microsoft/nanoserver
COPY testfile.txt c:\
RUN dir c:\
~~~
結果:
~~~
PS C:\John> docker build -t succeeds --no-cache=true .
Sending build context to Docker daemon 3.072 kB
Step 1/3 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/3 : COPY testfile.txt c:\
---> 96655de338de
Removing intermediate container 4db9acbb1682
Step 3/3 : RUN dir c:\
---> Running in a2c157f842f5
Volume in drive C has no label.
Volume Serial Number is 7E6D-E0F7
Directory of c:\
10/05/2016 05:04 PM 1,894 License.txt
10/05/2016 02:22 PM <DIR> Program Files
10/05/2016 02:14 PM <DIR> Program Files (x86)
10/28/2016 11:18 AM 62 testfile.txt
10/28/2016 11:20 AM <DIR> Users
10/28/2016 11:20 AM <DIR> Windows
2 File(s) 1,956 bytes
4 Dir(s) 21,259,096,064 bytes free
---> 01c7f3bef04f
Removing intermediate container a2c157f842f5
Successfully built 01c7f3bef04f
PS C:\John>
~~~
## Environment replacement
環境變量(用[這個`ENV`陳述](https://docs.docker.com/engine/reference/builder/#env))也可以在某些指令中用作要由 `Dockerfile文件`. 還可以處理轉義,以便將類似變量的語法逐字包含到語句中。
環境變量在`Dockerfile`或者`$variable_名稱`或`${variable_name}`. 它們被同等對待,大括號語法通常用于解決變量名沒有空格的問題,例如`${foo}\u條`.
這個`${variable_name}`語法也支持一些標準`猛擊`以下指定的修飾符:
* `${variable:-word}`表示如果`變量`則結果將為該值。如果`variable`當時還沒有設定`單詞`結果就是這樣
* `${variable:+word}`表示如果`變量`就這樣定下來了`word`,否則結果為空字符串。
在所有情況下,`word`可以是任何字符串,包括其他環境變量。
通過添加一個`\`變量前:`\$福`或`\${foo}`例如,將轉換為`$福`和`${foo}`文字
示例(解析后的表示將顯示在`#`):
~~~
FROM busybox
ENV FOO=/bar
WORKDIR ${FOO} # WORKDIR /bar
ADD . $FOO # ADD . /bar
COPY \$FOO /quux # COPY $FOO /quux
~~~
中的以下指令列表支持環境變量`Dockerfile`:
* `ADD`
* `COPY`
* `ENV`
* `EXPOSE`
* `FROM`
* `LABEL`
* `STOPSIGNAL`
* `USER`
* `VOLUME`
* `WORKDIR`
* `ONBUILD`(與上述支持的指令之一結合使用時)
環境變量替換將在整個指令中為每個變量使用相同的值。換句話說,在這個例子中:
~~~
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc
~~~
會導致`def`有價值的`hello`,不是`bye`. 然而, `ghi`將具有值`bye`因為它不是同一指令的一部分`abc`到`bye`.
##
## .dockerignore file
在docker CLI將上下文發送到docker守護程序之前,它將查找名為`.dockerignore`在上下文的根目錄中。如果此文件存在,CLI將修改上下文以排除與其中模式匹配的文件和目錄。這有助于避免不必要地向守護進程發送大的或敏感的文件和目錄,并可能使用將它們添加到映像中`添加`或`COPY`.
CLI解釋`.dockerignore`文件作為一個換行分隔的模式列表,類似于unixshell的fileglobs。為了進行匹配,上下文的根目錄被認為是工作目錄和根目錄。例如,模式`/食品/酒吧`和`foo/bar`兩者都排除名為`酒吧`在`foo`的子目錄`路徑`或位于git存儲庫的根目錄中`URL`. 也不排除任何其他因素。
如果有人排隊`.dockerignore`文件以開頭 `#`在第1列中,這一行被視為注釋,在被CLI解釋之前被忽略。
這里有一個例子`.dockerignore`文件:
~~~gitignore
# comment
*/temp*
*/*/temp*
temp?
~~~
此文件導致以下生成行為:
| 規則 | 行為 |
| --- | --- |
| `# comment` | 忽略 |
| `*/temp*` | 排除名稱以開頭的文件和目錄`temp`在根的任何直接子目錄中。例如,普通文件`/somedir/temporary.txt`被排除,目錄也是`/somedir/temp`. |
| `*/*/temp*` | 排除以開頭的文件和目錄`temp`從根目錄下兩級的任何子目錄。例如,`/somedir/subdir/temporary.txt`被排除在外 |
| `temp?` | 排除根目錄中名稱擴展名為一個字符的文件和目錄`temp`. 例如, `我是你嗎/`和`/tempb`被排除在外 |
匹配是用Go完成的[文件路徑匹配](http://golang.org/pkg/path/filepath#Match)規則。預處理步驟刪除前導空格和尾隨空格并消除`.`和 `..`使用Go的元素[文件路徑。清除](http://golang.org/pkg/path/filepath/#Clean). 預處理后空白的行將被忽略。
除了Go的filepath.Match規則之外,Docker還支持一個特殊的通配符字符串`**`匹配任意數量的目錄(包括零)。例如,`**/*走吧`將排除以結尾的所有文件`.go`可以在所有目錄中找到,包括生成上下文的根目錄。
以開頭的行`!`(感嘆號)可用于排除例外。下面是一個例子 `.dockerignore公司`使用此機制的文件:
~~~gitignore
*.md
!README.md
~~~
所有markdown文件*除了*`README.md`從上下文中排除
安置`!`異常規則影響行為:最后一行 `.dockerignore`與特定文件匹配的值將決定是否包含或排除該文件。考慮以下示例:
~~~gitignore
*.md
!README*.md
README-secret.md
~~~
上下文中不包括除自述文件以外的任何降價文件`README-secret.md`.
現在考慮這個例子:
~~~gitignore
*.md
README-secret.md
!README*.md
~~~
包括所有自述文件。中間線沒有效果,因為`!README*.md`比賽`readme-secret.md`最后一名
你甚至可以使用`.dockerignore`要排除的文件 `Dockerfile文件`和`.dockerignore`文件夾。這些文件仍然被發送到守護進程,因為它需要它們來完成它的工作。但是`添加`和`COPY`說明不會將它們復制到映像中。
最后,您可能希望指定在上下文中包括哪些文件,而不是排除哪些文件。為此,請指定`*`作為第一個模式,后面跟著一個或多個 `!`異常模式
> **注意**
>
>由于歷史原因`.`被忽略
## FROM
~~~
FROM [--platform=<platform>] <image> [AS <name>]
~~~
Or
~~~
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
~~~
Or
~~~
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
~~~
這個`FROM`指令初始化新的生成階段并設置[*基本圖像*](https://docs.docker.com/glossary/#base_image)有關后續說明。因此,一個有效的`Dockerfile`必須以`從`說明。圖像可以是任何有效的圖像,尤其是從**繪制圖像**從[*公共存儲庫*](https://docs.docker.com/docker-hub/repos/).
* `ARG`是唯一可以放在前面的指令`從`在`Dockerfile`. 看到了嗎[了解ARG和FROM如何交互](https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact).
* `FROM`可以在一個 `Dockerfile文件`創建多個映像或將一個生成階段用作另一個生成階段的依賴項。只需在每次更新之前記錄提交輸出的最后一個圖像ID`FROM`說明。每個`從`指令清除先前指令創建的任何狀態。
* 可選地,可以通過添加`AS name`致`從`說明。該名稱可用于后續`FROM`和`COPY --from=<name>`說明引用在此階段構建的映像。
* 這個`tag`或`消化`值是可選的。如果忽略其中任何一個,則構建器假定`latest`默認情況下標記。如果找不到`標簽`價值觀
可選的`--platform`標志可用于指定圖像的平臺,以防`從`引用多平臺映像。例如,`linux/amd64`,`linux/arm64`,或`windows/amd64`. 默認情況下,使用生成請求的目標平臺。例如,可以在該標志的值中使用全局生成參數[自動平臺參數](https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope)允許您強制階段到本機構建平臺(`--platform=$BUILDPLATFORM`),并在階段內使用它交叉編譯到目標平臺。
### Understand how ARG and FROM interact
`FROM`指令支持由`ARG`出現在第一個指令之前的指令`FROM`.
~~~
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
FROM extras:${CODE_VERSION}
CMD /code/run-extras
~~~
一個`ARG`在a之前聲明`FROM`在生成階段之外,因此不能在`FROM`. 使用的默認值`ARG`在第一個`FROM`使用`ARG`生成階段內沒有值的指令:
~~~
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
~~~
## RUN
RUN有兩種形式:
* `RUN <command>`(*殼*窗體中,命令在shell中運行,默認情況下是`/bin/sh -c`在Linux或 `命令/序號`在Windows上)
* `RUN ["executable", "param1", "param2"]`(*執行官*形式)
這個`RUN`指令將在當前圖像頂部的新層中執行任何命令并提交結果。生成的提交映像將用于 `Dockerfile文件`.
分層`RUN`指令和生成提交符合Docker的核心概念,在Docker中提交很便宜,并且可以從映像歷史的任何一點創建容器,就像源代碼管理一樣。
這個*執行官*形式可以避免貝殼串咀嚼,并`RUN`使用不包含指定的shell可執行文件的基映像的命令。
的默認shell*殼*可以使用`SHELL`命令
在*殼*您可以使用`\`(反斜杠)將單個運行指令繼續到下一行。例如,考慮以下兩行
~~~
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'
~~~
它們一起相當于這一行:
~~~
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
~~~
要使用除‘/bin/sh‘以外的其他shell,請使用*執行官*通過所需外殼的形狀。例如:
~~~
RUN ["/bin/bash", "-c", "echo hello"]
~~~
> **注意**
>
> 這個*執行官*表單被解析為JSON數組,這意味著您必須在單詞周圍使用雙引號(“),而不是單引號(')。
不像*殼*形式上*執行官*窗體不調用命令shell。這意味著正常的shell處理不會發生。例如,`RUN [ "echo", "$HOME" ]`不會對執行變量替換`$家`. 如果需要shell處理,則使用*殼*直接形成或執行shell,例如:`RUN [ "sh", "-c", "echo $HOME" ]`. 當使用exec form并直接執行shell時(如shell form的情況),執行環境變量擴展的是shell,而不是docker。
> **注意**
>
> 在*JSON格式*窗體中,必須避免反斜杠。這在反斜杠是路徑分隔符的Windows上特別相關。否則,以下行將被視為*殼*由于不是有效的JSON,并以意外方式失敗:
>
> ~~~
> RUN ["c:\windows\system32\tasklist.exe"]
>
> ~~~
>
> 此示例的正確語法是:
>
> ~~~
> RUN ["c:\\windows\\system32\\tasklist.exe"]
>
> ~~~
緩存`RUN`指令不會在下一個構建期間自動失效。像這樣的指令的緩存`RUN apt get dist upgrade-y`將在下一個生成期間重用。的緩存`RUN`指令可以通過使用`--無緩存`比如說國旗`docker build --no-cache`.
見[`Dockerfile`最佳實踐指南](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/)了解更多信息
的緩存`RUN`指令可以通過以下方式失效[`ADD`](https://docs.docker.com/engine/reference/builder/#add)和[`COPY`](https://docs.docker.com/engine/reference/builder/#copy)說明的緩存`RUN`指令不會在下一個構建期間自動失效。像這樣的指令的緩存`運行apt get dist upgrade-y`將在下一個生成期間重用。的緩存`RUN`指令可以通過使用`--無緩存`比如說國旗`docker build --no-cache`.
見[`Dockerfile`最佳實踐指南](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/)了解更多信息
的緩存`RUN`指令可以通過以下方式失效[`ADD`](https://docs.docker.com/engine/reference/builder/#add)和[`COPY`](https://docs.docker.com/engine/reference/builder/#copy)說明
### Known issues (RUN)
* * [第783期](https://github.com/docker/docker/issues/783)是關于使用AUFS文件系統時可能出現的文件權限問題。你可能會注意到`rm`例如一個文件
對于具有最新aufs版本的系統(即。,`dirperm1`可通過掛載選項自動設置掛載問題) `直管1`選項。更多詳細信息`dirperm1`選項可在[`aufs`手冊頁](https://github.com/sfjro/aufs3-linux/tree/aufs3.18/Documentation/filesystems/aufs)
如果您的系統不支持`dirperm1`,該問題描述了一種解決方法。
## CMD
這個`CMD`指令有三種形式:
* `CMD ["executable","param1","param2"]`(*執行官*表格,這是首選表格)
* `CMD ["param1","param2"]`(作為*入口點的默認參數*)
* `CMD command param1 param2`(*殼*形式)
只能有一個`CMD`a中的指令 `Dockerfile文件`. 如果你列出不止一個`CMD`那就只剩下最后一個了 `命令`將生效
\*\*a的主要目的`CMD`為正在執行的容器提供默認值。\*\*這些默認值可以包括可執行文件,也可以省略可執行文件,在這種情況下,必須指定`入口點`還有指導
如果`CMD`用于為`入口點`指令,兩個`CMD`和`入口點`指令應使用JSON數組格式指定。
> **注意**
>
> 這個*執行官*表單被解析為JSON數組,這意味著您必須在單詞周圍使用雙引號(“),而不是單引號(')。
不像*殼*形式上*執行官*窗體不調用命令shell。這意味著正常的shell處理不會發生。例如,`CMD [ "echo", "$HOME" ]`不會對執行變量替換`$家`. 如果需要shell處理,則使用*殼*直接形成或執行shell,例如:`CMD [ "sh", "-c", "echo $HOME" ]`. 當使用exec form并直接執行shell時(如shell form的情況),執行環境變量擴展的是shell,而不是docker。
在shell或exec格式中使用時`CMD`指令設置運行映像時要執行的命令。
如果你使用*殼*形式`CMD`,然后`<command>`將在中執行`/bin/sh -c`:
~~~
FROM ubuntu
CMD echo "This is a test." | wc -
~~~
如果你想的話**運行您的**`<command>`**沒有外殼**必須將JSON\*數組的格式設置為完整的JSON\*格式,然后將此命令的格式設置為express`CMD`.\*\*任何附加參數都必須在數組中單獨表示為字符串:
~~~
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
~~~
如果希望容器每次都運行相同的可執行文件,則應考慮使用`ENTRYPOINT`與 `命令`. 看到了嗎[*入口點*](https://docs.docker.com/engine/reference/builder/#entrypoint).
如果用戶指定參數`docker run`然后它們將重寫中指定的默認值 `命令`.
> **注意**
>
> 不要混淆`RUN`具有 `命令`.`RUN`實際運行命令并提交結果; `命令`不在生成時執行任何操作,但指定映像的預期命令。
## LABEL
~~~
LABEL <key>=<value> <key>=<value> <key>=<value> ...
~~~
這個`LABEL`指令將元數據添加到圖像中。A`標簽`是一個鍵值對。在`LABEL`值,請像在命令行分析中那樣使用引號和反斜杠。一些用法示例:
~~~
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
~~~
一個圖像可以有多個標簽。可以在一行上指定多個標簽。在Docker1.10之前,這降低了最終圖像的大小,但現在已經不是這樣了。您仍然可以選擇在一條指令中指定多個標簽,方法有以下兩種:
~~~
LABEL multi.label1="value1" multi.label2="value2" other="value3"
~~~
~~~
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
~~~
基本圖像或父圖像中包含的標簽(中的圖像`FROM`(行)由您的圖像繼承。如果標簽已經存在,但具有不同的值,則最近應用的值將覆蓋以前設置的任何值。
要查看圖像的標簽,請使用`docker image inspect`命令。你可以使用`--格式`選擇只顯示標簽;
~~~
docker image inspect --format='' myimage
~~~
~~~
{
"com.example.vendor": "ACME Incorporated",
"com.example.label-with-value": "foo",
"version": "1.0",
"description": "This text illustrates that label-values can span multiple lines.",
"multi.label1": "value1",
"multi.label2": "value2",
"other": "value3"
}
~~~
## MAINTAINER (已棄用)
~~~
MAINTAINER <name>
~~~
這個`MAINTAINER`指令設置*作者*生成圖像的字段。這個`LABEL`指令是這個版本的一個更靈活的版本,您應該使用它,因為它可以設置您需要的任何元數據,并且可以很容易地查看,例如使用`碼頭工人檢查`. 設置與`MAINTAINER`您可以使用的字段:
~~~
LABEL maintainer="SvenDowideit@home.org.au"
~~~
然后可以從`docker inspect`還有其他的標簽
## EXPOSE
~~~
EXPOSE <port> [<port>/<protocol>...]
~~~
這個`EXPOSE`指令通知Docker容器在運行時偵聽指定的網絡端口。可以指定端口是偵聽TCP還是UDP,如果未指定協議,則默認為TCP。
這個`EXPOSE`指令并不實際發布端口。它的功能是構建映像的人和運行容器的人之間的一種文檔類型,關于要發布哪些端口。要在運行容器時實際發布端口,請使用`-p`標志打開`docker run`發布和映射一個或多個端口,或`-P`用于發布所有公開端口并將它們映射到高階端口的標志。
默認情況下,`EXPOSE`假設為TCP。也可以指定UDP:
~~~
EXPOSE 80/udp
~~~
要同時在TCP和UDP上公開,請包括兩行:
~~~
EXPOSE 80/tcp
EXPOSE 80/udp
~~~
在這種情況下,如果您使用`-P`具有`docker run`,對于TCP和UDP,端口將公開一次。記住這一點`-P`在主機上使用短暫的高順序主機端口,因此對于TCP和UDP,端口將不同。
不管`EXPOSE`設置,可以在運行時使用`-p`旗子。例如
~~~
docker run -p 80:80/tcp -p 80:80/udp ...
~~~
要在主機系統上設置端口重定向,請參閱[使用-P標志](https://docs.docker.com/engine/reference/run/#expose-incoming-ports). 這個`docker network`命令支持創建用于容器之間通信的網絡,而無需公開或發布特定端口,因為連接到網絡的容器可以通過任何端口彼此通信。有關詳細信息,請參見[此功能概述](https://docs.docker.com/engine/userguide/networking/).
## ENV
~~~
ENV <key>=<value> ...
~~~
該`ENV`指令將環境變量`<key>`設置為 value`<value>`。此值將在構建階段的所有后續指令的環境中,并且也可以在許多中[內聯替換](https://docs.docker.com/engine/reference/builder/#environment-replacement)。該值將被解釋為其他環境變量,因此如果未轉義引號字符將被刪除。與命令行解析一樣,引號和反斜杠可用于在值中包含空格。
例子:
~~~
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy
~~~
該`ENV`指令允許`<key>=<value> ...`一次設置多個變量,下面的示例將在最終圖像中產生相同的凈結果:
~~~
ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
MY_CAT=fluffy
~~~
`ENV`當容器從生成的映像運行時,使用設置的環境變量將持續存在。您可以使用 來查看值`docker inspect`,并使用 更改它們`docker run --env <key>=<value>`。
環境變量持久性可能會導致意外的副作用。例如,設置會`ENV DEBIAN_FRONTEND=noninteractive`更改 的行為`apt-get`,并可能使用戶對您的圖像感到困惑。
如果僅在構建期間需要環境變量,而不是在最終映像中,請考慮為單個命令設置一個值:
~~~
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...
~~~
或者使用[`ARG`](https://docs.docker.com/engine/reference/builder/#arg),它不會保留在最終圖像中:
~~~
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ...
~~~
> **替代語法**
>
> 該`ENV`指令還允許使用替代語法`ENV <key> <value>`,省略`=`.例如:
>
> ~~~
> ENV MY_VAR my-value
>
> ~~~
>
> 此語法不允許在單個`ENV`指令中設置多個環境變量,并且可能會造成混淆。例如,以下內容設置了一個(`ONE`)帶有 value 的環境變量 `"TWO= THREE=world"`:
>
> ~~~
> ENV ONE TWO= THREE=world
>
> ~~~
>
> 支持替代語法以實現向后兼容性,但由于上述原因不鼓勵使用,并且可能會在未來版本中刪除。
## ADD
ADD有兩種形式:
~~~
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
~~~
包含空格的路徑需要后一種形式。
> **筆記**
>
> 該`--chown`功能僅在用于構建 Linux 容器的 Dockerfile 上受支持,不適用于 Windows 容器。由于用戶和組所有權概念不Linux和Windows,使用之間進行轉換`/etc/passwd`,并`/etc/group`用于轉換的用戶和組名ID的限制此功能僅適用于基于Linux操作系統的容器是可行的。
該`ADD`指令從中復制新文件、目錄或遠程文件 URL`<src>`,并將它們添加到路徑 處的圖像文件系統中`<dest>`。
`<src>`可以指定多個資源,但如果它們是文件或目錄,則它們的路徑被解釋為相對于構建上下文的源。
每個都`<src>`可能包含通配符,匹配將使用 Go 的[filepath.Match](http://golang.org/pkg/path/filepath#Match)規則完成。例如:
添加所有以“hom”開頭的文件:
~~~
ADD hom* /mydir/
~~~
在下面的示例中,`?`被替換為任何單個字符,例如“home.txt”。
~~~
ADD hom?.txt /mydir/
~~~
`<dest>`是一個絕對路徑,或相對于一個路徑`WORKDIR`,到其中的源將在目標容器內進行復制。
下面的示例使用相對路徑,并將“test.txt”添加到`<WORKDIR>/relativeDir/`:
~~~
ADD test.txt relativeDir/
~~~
而此示例使用絕對路徑,并將“test.txt”添加到`/absoluteDir/`
~~~
ADD test.txt /absoluteDir/
~~~
添加包含特殊字符(如`[`和`]`)的文件或目錄時,您需要按照 Golang 規則對這些路徑進行轉義,以防止它們被視為匹配模式。例如,要添加名為 的文件`arr[0].txt`,請使用以下命令;
~~~
ADD arr[[]0].txt /mydir/
~~~
所有新文件和目錄都使用 0 的 UID 和 GID 創建,除非可選`--chown`標志指定給定的用戶名、組名或 UID/GID 組合以請求添加內容的特定所有權。該`--chown`標志的格式允許用戶名和組名字符串或直接整數 UID 和 GID 的任意組合。提供不帶組名的用戶名或不帶 GID 的 UID 將使用與 GID 相同的數字 UID。如果提供了用戶名或組名,容器的根文件系統`/etc/passwd`和`/etc/group`文件將分別用于執行從名稱到整數 UID 或 GID 的轉換。以下示例顯示了`--chown`標志的有效定義:
~~~
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/
~~~
如果容器根文件系統不包含`/etc/passwd`或`/etc/group`文件,并且在`--chown`標志中使用了用戶名或組名,則構建`ADD`操作將失敗。使用數字 ID 不需要查找,也不依賴于容器根文件系統內容。
在 where`<src>`是遠程文件 URL的情況下,目標將具有 600 的權限。如果正在檢索的遠程文件具有 HTTP`Last-Modified`標頭,則來自該標頭的時間戳將用于設置`mtime`目標文件上的 。但是,與在 期間處理的任何其他文件一樣`ADD`,`mtime`將不包括在確定文件是否已更改和緩存是否應更新的確定中。
> **筆記**
>
> 如果通過傳遞`Dockerfile`STDIN (`docker build - < somefile`) 進行構建,則沒有構建上下文,因此`Dockerfile`只能包含基于 URL 的`ADD`指令。您還可以通過 STDIN: (`docker build - < archive.tar.gz`)傳遞壓縮檔案,檔案`Dockerfile`的根目錄和檔案的其余部分將用作構建的上下文。
如果您的 URL 文件使用身份驗證保護,則需要使用`RUN wget`,`RUN curl`或使用容器內的其他工具,因為該`ADD`指令不支持身份驗證。
> **筆記**
>
> `ADD`如果 Dockerfile 的內容`<src>`已更改,則遇到的第一個指令將使 Dockerfile 中所有后續指令的緩存無效。這包括使`RUN`指令的緩存無效。有關更多信息,請參閱[`Dockerfile`最佳實踐指南 – 利用構建緩存](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache)。
`ADD`遵守以下規則:
* 該`<src>`路徑必須是內部*語境*的構建;你不能`ADD ../something /something`,因為 a 的第一步`docker build`是將上下文目錄(和子目錄)發送到 docker 守護進程。
* 如果`<src>`是 URL 并且`<dest>`不以斜杠結尾,則從 URL 下載文件并將其復制到`<dest>`.
* 如果`<src>`是 URL 并且`<dest>`確實以斜杠結尾,則從 URL 推斷文件名并將文件下載到`<dest>/<filename>`.例如,`ADD http://example.com/foobar /`將創建文件`/foobar`.URL 必須有一個重要的路徑,以便在這種情況下可以發現適當的文件名(`http://example.com`將不起作用)。
* 如果`<src>`是目錄,則復制目錄的全部內容,包括文件系統元數據。
> **筆記**
>
> 不會復制目錄本身,只會復制其內容。
* 如果`<src>`是可識別的壓縮格式(identity、gzip、bzip2 或 xz)的*本地*tar 存檔,則將其解壓縮為目錄。來自*遠程*URL 的資源**不會被**解壓縮。當一個目錄被復制或解包時,它的行為與 相同`tar -x`,結果是:
1. 目標路徑中存在的任何內容和
2. 源樹的內容,解決了有利于“2”的沖突。在逐個文件的基礎上。
> **筆記**
>
> 文件是否被識別為可識別的壓縮格式完全取決于文件的內容,而不是文件的名稱。例如,如果一個空文件恰好以`.tar.gz`this結尾,則不會將其識別為壓縮文件,**也不會**生成任何類型的解壓縮錯誤消息,而只會將該文件復制到目的地。
* 如果`<src>`是任何其他類型的文件,則將其與其元數據一起單獨復制。在這種情況下,如果`<dest>`以斜杠結尾`/`,它將被視為一個目錄,其內容`<src>`將被寫入`<dest>/base(<src>)`。
* 如果`<src>`直接指定了多個資源,或者由于使用了通配符,則`<dest>`必須是目錄,并且必須以斜杠結尾`/`。
* 如果`<dest>`不以斜杠結尾,則將其視為常規文件,并將其內容`<src>`寫入`<dest>`.
* 如果`<dest>`不存在,則創建它及其路徑中所有丟失的目錄。
## COPY
COPY 有兩種形式:
~~~
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
~~~
包含空格的路徑需要后一種形式
> **筆記**
>
> 該`--chown`功能僅在用于構建 Linux 容器的 Dockerfile 上受支持,不適用于 Windows 容器。由于用戶和組所有權概念不Linux和Windows,使用之間進行轉換`/etc/passwd`,并`/etc/group`用于轉換的用戶和組名ID的限制此功能僅適用于基于Linux操作系統的容器是可行的。
該`COPY`指令從中復制新文件或目錄`<src>`并將它們添加到容器的文件系統中的路徑`<dest>`。
`<src>`可以指定多個資源,但文件和目錄的路徑將被解釋為相對于構建上下文的源。
每個都`<src>`可能包含通配符,匹配將使用 Go 的[filepath.Match](http://golang.org/pkg/path/filepath#Match)規則完成。例如:
添加所有以“hom”開頭的文件:
~~~
COPY hom* /mydir/
~~~
在下面的示例中,`?`被替換為任何單個字符,例如“home.txt”。
~~~
COPY hom?.txt /mydir/
~~~
`<dest>`是一個絕對路徑,或相對于一個路徑`WORKDIR`,到其中的源將在目標容器內進行復制。
下面的示例使用相對路徑,并將“test.txt”添加到`<WORKDIR>/relativeDir/`:
~~~
COPY test.txt relativeDir/
~~~
而此示例使用絕對路徑,并將“test.txt”添加到`/absoluteDir/`
~~~
COPY test.txt /absoluteDir/
~~~
復制包含特殊字符(如`[`和`]`)的文件或目錄時,您需要按照 Golang 規則對這些路徑進行轉義,以防止它們被視為匹配模式。例如,要復制名為 的文件`arr[0].txt`,請使用以下命令;
~~~
COPY arr[[]0].txt /mydir/
~~~
所有新文件和目錄都使用 0 的 UID 和 GID 創建,除非可選`--chown`標志指定給定的用戶名、組名或 UID/GID 組合以請求復制內容的特定所有權。該`--chown`標志的格式允許用戶名和組名字符串或直接整數 UID 和 GID 的任意組合。提供不帶組名的用戶名或不帶 GID 的 UID 將使用與 GID 相同的數字 UID。如果提供了用戶名或組名,容器的根文件系統`/etc/passwd`和`/etc/group`文件將分別用于執行從名稱到整數 UID 或 GID 的轉換。以下示例顯示了`--chown`標志的有效定義:
~~~
COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/
COPY --chown=1 files* /somedir/
COPY --chown=10:11 files* /somedir/
~~~
如果容器根文件系統不包含`/etc/passwd`或`/etc/group`文件,并且在`--chown`標志中使用了用戶名或組名,則構建`COPY`操作將失敗。使用數字 ID 不需要查找并且不依賴于容器根文件系統內容。
> **筆記**
>
> 如果使用 STDIN (`docker build - < somefile`)構建,則沒有構建上下文,因此`COPY`無法使用。
可以選擇`COPY`接受一個標志`--from=<name>`,該標志可用于將源位置設置為前一個構建階段(使用`FROM .. AS <name>`),該階段將用于代替用戶發送的構建上下文。如果無法找到具有指定名稱的構建階段,則會嘗試使用具有相同名稱的圖像。
`COPY`遵守以下規則:
* 該`<src>`路徑必須是內部*語境*的構建;你不能`COPY ../something /something`,因為 a 的第一步`docker build`是將上下文目錄(和子目錄)發送到 docker 守護進程。
* 如果`<src>`是目錄,則復制目錄的全部內容,包括文件系統元數據。
> **筆記**
>
> 不會復制目錄本身,只會復制其內容。
* 如果`<src>`是任何其他類型的文件,則將其與其元數據一起單獨復制。在這種情況下,如果`<dest>`以斜杠結尾`/`,它將被視為一個目錄,其內容`<src>`將被寫入`<dest>/base(<src>)`。
* 如果`<src>`直接指定了多個資源,或者由于使用了通配符,則`<dest>`必須是目錄,并且必須以斜杠結尾`/`。
* 如果`<dest>`不以斜杠結尾,則將其視為常規文件,并將其內容`<src>`寫入`<dest>`.
* 如果`<dest>`不存在,則創建它及其路徑中所有丟失的目錄。
> **筆記**
>
> `COPY`如果 Dockerfile 的內容`<src>`已更改,則遇到的第一個指令將使 Dockerfile 中所有后續指令的緩存無效。這包括使`RUN`指令的緩存無效。有關更多信息,請參閱[`Dockerfile`最佳實踐指南 – 利用構建緩存](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache)。
## ENTRYPOINT
ENTRYPOINT 有兩種形式:
在*EXEC*的形式,這是優選的形式:
~~~
ENTRYPOINT ["executable", "param1", "param2"]
~~~
該*shell*形式:
~~~
ENTRYPOINT command param1 param2
~~~
An`ENTRYPOINT`允許您配置將作為可執行文件運行的容器。
例如,以下內容以默認內容啟動 nginx,偵聽端口 80:
~~~
$ docker run -i -t --rm -p 80:80 nginx
~~~
命令行參數 to`docker run <image>`將附加在*exec*表單中的所有元素之后`ENTRYPOINT`,并將覆蓋所有使用`CMD`.這允許將參數傳遞給入口點,即,`docker run <image> -d`將`-d`參數傳遞給入口點。您可以`ENTRYPOINT`使用`docker run --entrypoint`標志覆蓋指令。
所述*殼*形式防止任何`CMD`或`run`被使用命令行參數,但具有的缺點是你`ENTRYPOINT`將開始作為的子命令`/bin/sh -c`,其不通過信號。這意味著可執行文件將不是容器的`PID 1`\- 并且*不會*接收 Unix 信號 - 因此您的可執行文件不會收到`SIGTERM`來自`docker stop <container>`.
只有 中的最后一條`ENTRYPOINT`指令`Dockerfile`會起作用。
### Exec form ENTRYPOINT example
您可以使用*exec*形式`ENTRYPOINT`設置相當穩定的默認命令和參數,然后使用任一形式`CMD`設置更可能更改的其他默認值。
~~~
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
~~~
當您運行容器時,您可以看到這`top`是唯一的過程:
~~~
$ docker run -it --rm --name test top -H
top - 08:25:00 up 7:27, 0 users, load average: 0.00, 0.01, 0.05
Threads: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.1 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 2056668 total, 1616832 used, 439836 free, 99352 buffers
KiB Swap: 1441840 total, 0 used, 1441840 free. 1324440 cached Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 19744 2336 2080 R 0.0 0.1 0:00.04 top
~~~
要進一步檢查結果,您可以使用`docker exec`:
~~~
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 2.6 0.1 19752 2352 ? Ss+ 08:24 0:00 top -b -H
root 7 0.0 0.1 15572 2164 ? R+ 08:25 0:00 ps aux
~~~
您可以`top`使用`docker stop test`.
下面`Dockerfile`顯示了使用`ENTRYPOINT`來在前臺運行 Apache(即 as`PID 1`):
~~~
FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
~~~
如果您需要為單個可執行文件編寫啟動腳本,您可以使用`exec`和`gosu`命令確保最終的可執行文件接收到 Unix 信號:
~~~
#!/usr/bin/env 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 "$@"
~~~
最后,如果您需要在關閉時進行一些額外的清理(或與其他容器通信),或者正在協調多個可執行文件,您可能需要確保`ENTRYPOINT`腳本接收 Unix 信號,將它們傳遞,然后執行還有一些工作:
~~~
#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too
# USE the trap if you need to also do manual cleanup after the service is stopped,
# or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM
# start service in background here
/usr/sbin/apachectl start
echo "[hit enter key to exit] or run 'docker stop <container>'"
read
# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop
echo "exited $0"
~~~
如果使用 運行此映像`docker run -it --rm -p 80:80 --name test apache`,則可以使用`docker exec`、 或來檢查容器的進程`docker top`,然后讓腳本停止 Apache:
~~~
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.0 4448 692 ? Ss+ 00:42 0:00 /bin/sh /run.sh 123 cmd cmd2
root 19 0.0 0.2 71304 4440 ? Ss 00:42 0:00 /usr/sbin/apache2 -k start
www-data 20 0.2 0.2 360468 6004 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
www-data 21 0.2 0.2 360468 6000 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
root 81 0.0 0.1 15572 2140 ? R+ 00:44 0:00 ps aux
$ docker top test
PID USER COMMAND
10035 root {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054 root /usr/sbin/apache2 -k start
10055 33 /usr/sbin/apache2 -k start
10056 33 /usr/sbin/apache2 -k start
$ /usr/bin/time docker stop test
test
real 0m 0.27s
user 0m 0.03s
sys 0m 0.03s
~~~
> **筆記**
>
> 您可以使用 覆蓋`ENTRYPOINT`設置`--entrypoint`,但這只能將二進制文件設置為*exec*(不會`sh -c`使用)。
> **筆記**
>
> 在*EXEC*形式被解析為一個JSON陣列,這意味著必須使用雙引號(“)周圍的話不單引號(')。
與*shell*形式不同,*exec*形式不調用命令 shell。這意味著不會發生正常的 shell 處理。例如,`ENTRYPOINT [ "echo", "$HOME" ]`不會對 進行變量替換`$HOME`。如果你想要 shell 處理,那么要么使用*shell*形式,要么直接執行 shell,例如:`ENTRYPOINT [ "sh", "-c", "echo $HOME" ]`.當使用 exec 形式并直接執行 shell 時,就像 shell 形式一樣,是 shell 進行環境變量擴展,而不是 docker。
### Shell form ENTRYPOINT example
您可以為 指定一個純字符串,`ENTRYPOINT`它將在`/bin/sh -c`.這種形式將使用 shell 處理來替換 shell 環境變量,并且將忽略任何`CMD`或`docker run`命令行參數。為確保正確`docker stop`發出任何長時間運行的`ENTRYPOINT`可執行文件的信號,您需要記住以以下方式啟動它`exec`:
~~~
FROM ubuntu
ENTRYPOINT exec top -b
~~~
運行此映像時,您將看到單個`PID 1`進程:
~~~
$ docker run -it --rm --name test top
Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
CPU: 5% usr 0% sys 0% nic 94% idle 0% io 0% irq 0% sirq
Load average: 0.08 0.03 0.05 2/98 6
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
1 0 root R 3164 0% 0% top -b
~~~
哪個干凈地退出`docker stop`:
~~~
$ /usr/bin/time docker stop test
test
real 0m 0.20s
user 0m 0.02s
sys 0m 0.04s
~~~
如果您忘記添加`exec`到您的開頭`ENTRYPOINT`:
~~~
FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1
~~~
然后您可以運行它(為下一步命名):
~~~
$ docker run -it --name test top --ignored-param2
Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached
CPU: 9% usr 2% sys 0% nic 88% idle 0% io 0% irq 0% sirq
Load average: 0.01 0.02 0.05 2/101 7
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
1 0 root S 3168 0% 0% /bin/sh -c top -b cmd cmd2
7 1 root R 3164 0% 0% top -b
~~~
您可以從輸出中`top`看到指定`ENTRYPOINT`的不是`PID 1`。
如果您然后運行`docker stop test`,容器將不會干凈地退出 - 該`stop`命令將`SIGKILL`在超時后強制發送:
~~~
$ docker exec -it test ps aux
PID USER COMMAND
1 root /bin/sh -c top -b cmd cmd2
7 root top -b
8 root ps aux
$ /usr/bin/time docker stop test
test
real 0m 10.19s
user 0m 0.04s
sys 0m 0.03s
~~~
### Understand how CMD and ENTRYPOINT interact
無論`CMD`和`ENTRYPOINT`指令定義運行的容器中時什么命令得到執行。很少有規則可以描述他們的合作。
1. Dockerfile 應至少指定`CMD`或`ENTRYPOINT`命令之一。
2. `ENTRYPOINT`應在將容器用作可執行文件時進行定義。
3. `CMD`應該用作定義`ENTRYPOINT`命令的默認參數或在容器中執行臨時命令的一種方式。
4. `CMD`使用替代參數運行容器時將被覆蓋。
下表顯示了針對不同`ENTRYPOINT`/`CMD`組合執行的命令:
| ? | 沒有入口點 | 入口點 exec\_entry p1\_entry | 入口點 \[“exec\_entry”,“p1\_entry”\] |
| --- | --- | --- | --- |
| **沒有 CMD** | *錯誤,不允許* | /bin/sh -c exec\_entry p1\_entry | exec\_entry p1\_entry |
| **CMD \[“exec\_cmd”,“p1\_cmd”\]** | exec\_cmd p1\_cmd | /bin/sh -c exec\_entry p1\_entry | exec\_entry p1\_entry exec\_cmd p1\_cmd |
| **CMD \[“p1\_cmd”,“p2\_cmd”\]** | p1\_cmd p2\_cmd | /bin/sh -c exec\_entry p1\_entry | exec\_entry p1\_entry p1\_cmd p2\_cmd |
| **CMD exec\_cmd p1\_cmd** | /bin/sh -c exec\_cmd p1\_cmd | /bin/sh -c exec\_entry p1\_entry | exec\_entry p1\_entry /bin/sh -c exec\_cmd p1\_cmd |
> **筆記**
>
> 如果`CMD`從基本圖像定義,則設置`ENTRYPOINT`將重置`CMD`為空值。在這種情況下,`CMD`必須在當前圖像中定義一個值。
## VOLUME
~~~
VOLUME ["/data"]
~~~
該`VOLUME`指令創建一個具有指定名稱的掛載點,并將其標記為保存來自本機主機或其他容器的外部掛載卷。該值可以是 JSON 數組、`VOLUME ["/var/log/"]`或帶有多個參數的純字符串,例如`VOLUME /var/log`或`VOLUME /var/log /var/db`。有關通過 Docker 客戶端的更多信息/示例和安裝說明,請參閱[*通過卷共享目錄*](https://docs.docker.com/storage/volumes/)文檔。
該`docker run`命令使用基礎映像中指定位置存在的任何數據初始化新創建的卷。例如,考慮以下 Dockerfile 片段:
~~~
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
~~~
此 Dockerfile 會`docker run`生成一個映像,該映像會導致創建新的掛載點`/myvol`并將`greeting`文件復制到新創建的卷中。
### Notes about specifying volumes
請記住以下有關`Dockerfile`.
* **基于 Windows 的容器上的卷**:使用基于 Windows 的容器時,容器內卷的目標必須是以下之一:
* 一個不存在或空的目錄
* 驅動器以外的驅動器`C:`
* **從 Dockerfile 中更改卷**:如果任何構建步驟在聲明卷后更改了卷中的數據,則這些更改將被丟棄。
* **JSON 格式**:列表被解析為 JSON 數組。您必須用雙引號 (`"`) 而不是單引號 (`'`)將單詞括起來。
* **主機目錄在容器運行時聲明**:主機目錄(掛載點)本質上是依賴于主機的。這是為了保持圖像的可移植性,因為不能保證給定的主機目錄在所有主機上都可用。因此,您無法從 Dockerfile 中掛載主機目錄。該`VOLUME`指令不支持指定`host-dir`參數。您必須在創建或運行容器時指定掛載點。
## USER
~~~
USER <user>[:<group>]
~~~
或
~~~
USER <UID>[:<GID>]
~~~
所述`USER`指令集運行的圖像和用于任何時要使用的用戶名(或UID)和任選的所述用戶組(或GID)`RUN`,`CMD`和`ENTRYPOINT`它后面的指令`Dockerfile`。
> 請注意,為用戶指定組時,用戶將*僅*具有指定的組成員資格。任何其他配置的組成員資格都將被忽略。
> **警告**
>
> 當用戶沒有主要組時,映像(或下一個說明)將與該`root`組一起運行。
>
> 在 Windows 上,如果用戶不是內置帳戶,則必須先創建該用戶。這可以通過`net user`作為 Dockerfile 的一部分調用的命令來完成。
~~~
FROM microsoft/windowsservercore
# Create Windows user in the container
RUN net user /add patrick
# Set it for subsequent commands
USER patrick
~~~
## WORKDIR
~~~
WORKDIR /path/to/workdir
~~~
該`WORKDIR`指令集的工作目錄對任何`RUN`,`CMD`,`ENTRYPOINT`,`COPY`和`ADD`它后面的說明`Dockerfile`。如果`WORKDIR`不存在,即使它沒有在任何后續`Dockerfile`指令中使用,它也會被創建。
該`WORKDIR`指令可以在一個`Dockerfile`.如果提供了相對路徑,它將相對于前一條`WORKDIR`指令的路徑。例如:
~~~
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
~~~
最終`pwd`命令的輸出`Dockerfile`將是`/a/b/c`.
該`WORKDIR`指令可以解析先前使用`ENV`.您只能使用在`Dockerfile`.例如:
~~~
ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
~~~
最終`pwd`命令的輸出`Dockerfile`將是`/path/$DIRNAME`
## ARG
~~~
ARG <name>[=<default value>]
~~~
該`ARG`指令定義了一個變量,用戶可以在構建時`docker build`通過使用`--build-arg <varname>=<value>`標志的命令將其傳遞給構建器。如果用戶指定了未在 Dockerfile 中定義的構建參數,則構建會輸出警告。
~~~
[Warning] One or more build-args [foo] were not consumed.
~~~
一個 Dockerfile 可能包含一個或多個`ARG`指令。例如,以下是一個有效的 Dockerfile:
~~~
FROM busybox
ARG user1
ARG buildno
# ...
~~~
> **警告:**
>
> 不建議使用構建時變量來傳遞秘密,如 github 密鑰、用戶憑據等`docker history`。使用命令的圖像的任何用戶都可以看到構建時變量值。
>
> 請參閱[“使用 BuildKit 構建鏡像”](https://docs.docker.com/develop/develop-images/build_enhancements/#new-docker-build-secret-information)部分,了解在構建鏡像時使用機密的安全方法。
### Default values
一條`ARG`指令可以選擇包含一個默認值:
~~~
FROM busybox
ARG user1=someuser
ARG buildno=1
# ...
~~~
如果`ARG`指令具有默認值并且在構建時沒有傳遞任何值,則構建器使用默認值。
### Scope
一個`ARG`變量定義進入從在其上在限定的線效果`Dockerfile`不從參數對命令行或其他地方使用。例如,考慮這個 Dockerfile:
~~~
FROM busybox
USER ${user:-some_user}
ARG user
USER $user
# ...
~~~
用戶通過調用構建此文件:
~~~
$ docker build --build-arg user=what_user .
~~~
的`USER`第2次的計算結果為`some_user`作為`user`變量的后續行3.定義`USER`4個求值在線路到`what_user`作為`user`被定義并且`what_user`值在命令行上通過。在`ARG`指令定義之前,對變量的任何使用都會導致空字符串。
的`ARG`指令在它被定義的構建階段結束推移的范圍進行。要在多個階段使用 arg,每個階段都必須包含`ARG`指令。
~~~
FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS
FROM busybox
ARG SETTINGS
RUN ./run/other $SETTINGS
~~~
### Using ARG variables
您可以使用`ARG`或`ENV`指令來指定可用于`RUN`指令的變量。使用`ENV`指令定義的環境變量總是覆蓋`ARG`同名指令。考慮這個帶有`ENV`and`ARG`指令的Dockerfile。
~~~
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=v1.0.0
RUN echo $CONT_IMG_VER
~~~
然后,假設此映像是使用以下命令構建的:
~~~
$ docker build --build-arg CONT_IMG_VER=v2.0.1 .
~~~
在這種情況下,`RUN`指令使用`v1.0.0`而不是`ARG`用戶傳遞的設置:`v2.0.1`此行為類似于 shell 腳本,其中本地范圍的變量覆蓋作為參數傳遞或從環境繼承的變量,從定義的角度來看。
使用上面的例子,但不同的`ENV`規格,您可以創建更多的之間的互動非常有用`ARG`和`ENV`說明:
~~~
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER
~~~
與`ARG`指令不同,`ENV`值始終保留在構建的映像中。考慮一個沒有`--build-arg`標志的 docker 構建:
~~~
$ docker build .
~~~
使用此 Dockerfile 示例,`CONT_IMG_VER`仍保留在映像中,但其值將是指令`v1.0.0`在第 3 行中設置的默認值`ENV`。
本示例中的變量擴展技術允許您從命令行傳遞參數,并通過利用`ENV`指令將它們保留在最終圖像中。變量擴展僅支持[有限的 Dockerfile 指令集。](https://docs.docker.com/engine/reference/builder/#environment-replacement)
### Predefined ARGs
Docker 有一組預定義的`ARG`變量,您可以`ARG`在 Dockerfile 中沒有相應指令的情況下使用這些變量。
* `HTTP_PROXY`
* `http_proxy`
* `HTTPS_PROXY`
* `https_proxy`
* `FTP_PROXY`
* `ftp_proxy`
* `NO_PROXY`
* `no_proxy`
要使用這些,請使用`--build-arg`標志在命令行上傳遞它們,例如:
~~~
$ docker build --build-arg HTTPS_PROXY=https://my-proxy.example.com .
~~~
默認情況下,這些預定義變量從`docker history`.排除它們會降低意外泄漏`HTTP_PROXY`變量中敏感身份驗證信息的風險。
例如,考慮使用以下 Dockerfile 構建`--build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com`
~~~
FROM ubuntu
RUN echo "Hello World"
~~~
在這種情況下,`HTTP_PROXY`變量的值在中不可用`docker history`并且不會被緩存。如果您要更改位置,并且您的代理服務器更改為`http://user:pass@proxy.sfo.example.com`,則后續構建不會導致緩存未命中。
如果您需要覆蓋此行為,則可以通過`ARG`在 Dockerfile 中添加如下語句來實現:
~~~
FROM ubuntu
ARG HTTP_PROXY
RUN echo "Hello World"
~~~
構建此 Dockerfile 時,將`HTTP_PROXY`保留在 中`docker history`,更改其值會使構建緩存無效。
### Automatic platform ARGs in the global scope
此功能僅在使用[BuildKit](https://docs.docker.com/engine/reference/builder/#buildkit)后端時可用。
Docker 預定義了一組`ARG`變量,其中包含有關執行構建的節點的平臺(構建平臺)和生成的映像的平臺(目標平臺)的信息。可以使用`--platform`on 標志指定目標平臺`docker build`。
以下`ARG`變量是自動設置的:
* `TARGETPLATFORM`\- 構建結果的平臺。例如`linux/amd64`,`linux/arm/v7`,`windows/amd64`。
* `TARGETOS`\- TARGETPLATFORM 的操作系統組件
* `TARGETARCH`\- TARGETPLATFORM 的架構組件
* `TARGETVARIANT`\- TARGETPLATFORM 的變體組件
* `BUILDPLATFORM`\- 執行構建的節點的平臺。
* `BUILDOS`\- BUILDPLATFORM 的操作系統組件
* `BUILDARCH`\- BUILDPLATFORM 的架構組件
* `BUILDVARIANT`\- BUILDPLATFORM 的變體組件
這些參數在全局范圍內定義,因此在構建階段或您的`RUN`命令中不會自動可用。在構建階段公開這些參數之一,重新定義它沒有價值。
例如:
~~~
FROM alpine
ARG TARGETPLATFORM
RUN echo "I'm building for $TARGETPLATFORM"
~~~
### Impact on build caching
`ARG`變量不會像`ENV`變量那樣持久化到構建的映像中。但是,`ARG`變量確實以類似的方式影響構建緩存。如果 Dockerfile 定義了一個`ARG`變量,其值與之前的構建不同,那么“緩存未命中”發生在它第一次使用時,而不是它的定義。特別是,`RUN`指令后面的所有指令都隱式地`ARG`使用該`ARG`變量(作為環境變量),因此可能導致緩存未命中。所有預定義`ARG`變量免于緩存,除非有一個匹配`ARG`的語句中`Dockerfile`。
例如,考慮這兩個 Dockerfile:
~~~
FROM ubuntu
ARG CONT_IMG_VER
RUN echo $CONT_IMG_VER
~~~
~~~
FROM ubuntu
ARG CONT_IMG_VER
RUN echo hello
~~~
如果`--build-arg CONT_IMG_VER=<value>`在命令行中指定,在這兩種情況下,第 2 行的指定都不會導致緩存未命中;第 3 行確實會導致緩存未命中。`ARG CONT_IMG_VER`導致 RUN 行被標識為與 running 相同`CONT_IMG_VER=<value> echo hello`,因此如果`<value>`發生變化,我們會得到緩存未命中。
考慮同一命令行下的另一個示例:
~~~
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=$CONT_IMG_VER
RUN echo $CONT_IMG_VER
~~~
在此示例中,緩存未命中發生在第 3 行。發生未命中是因為`ENV`引用`ARG`變量中的變量值以及通過命令行更改了該變量。在此示例中,該`ENV`命令使圖像包含該值。
如果一條`ENV`指令覆蓋了一條`ARG`同名指令,比如這個 Dockerfile:
~~~
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=hello
RUN echo $CONT_IMG_VER
~~~
第 3 行不會導致緩存未命中,因為 的值為`CONT_IMG_VER`常數 (`hello`)。因此,在`RUN`(第 4 行)上使用的環境變量和值在構建之間不會改變。
## ONBUILD
~~~
ONBUILD <INSTRUCTION>
~~~
該`ONBUILD`指令將一個*觸發*指令添加到圖像中,以便稍后在該圖像用作另一個構建的基礎時執行。觸發器將在下游構建的上下文中執行,就好像它是`FROM`在下游的指令之后立即插入的`Dockerfile`。
任何構建指令都可以注冊為觸發器。
如果您正在構建將用作構建其他鏡像的基礎的鏡像,例如應用程序構建環境或可以使用用戶特定配置自定義的守護程序,這將非常有用。
例如,如果您的映像是可重用的 Python 應用程序構建器,則需要將應用程序源代碼添加到特定目錄中,并且可能需要*在*此*之后*調用構建腳本。您不能只調用`ADD`and`RUN`now,因為您還沒有訪問應用程序源代碼的權限,而且每個應用程序構建都會有所不同。您可以簡單地為應用程序開發人員提供一個樣板,`Dockerfile`以便將其復制粘貼到他們的應用程序中,但這效率低下、容易出錯且難以更新,因為它與特定于應用程序的代碼混合在一起。
解決方案是`ONBUILD`在下一個構建階段使用注冊高級指令以便稍后運行。
這是它的工作原理:
1. 當遇到`ONBUILD`指令時,構建器會向正在構建的圖像的元數據添加觸發器。該指令不會以其他方式影響當前構建。
2. 在構建結束時,所有觸發器的列表存儲在圖像清單中的鍵下`OnBuild`。可以使用`docker inspect`命令檢查它們。
3. 稍后,可以使用該`FROM`指令將該映像用作新構建的基礎。作為處理`FROM`指令的一部分,下游構建器查找`ONBUILD`觸發器,并按照它們注冊的順序執行它們。如果任何觸發器失敗,`FROM`指令就會中止,從而導致構建失敗。如果所有觸發器都成功,則`FROM`指令完成并且構建照常繼續。
4. 觸發器在執行后從最終圖像中清除。換句話說,它們不是由“孫子”構建繼承的。
例如,您可以添加如下內容:
~~~
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
~~~
> **警告**
>
> 不允許`ONBUILD`使用鏈接指令`ONBUILD ONBUILD`。
> **警告**
>
> 該`ONBUILD`指令可能不會觸發`FROM`或`MAINTAINER`指令。
## STOPSIGNAL
~~~
STOPSIGNAL signal
~~~
該`STOPSIGNAL`指令設置將發送到容器退出的系統調用信號。該信號可以是與內核系統調用表中的位置匹配的有效無符號數,例如 9,或格式為 SIGNAME 的信號名稱,例如 SIGKILL。
## HEALTHCHECK
該`HEALTHCHECK`指令有兩種形式:
* `HEALTHCHECK [OPTIONS] CMD command`(通過在容器內運行命令來檢查容器健康狀況)
* `HEALTHCHECK NONE`(禁用從基礎鏡像繼承的任何健康檢查)
該`HEALTHCHECK`指令告訴 Docker 如何測試容器以檢查它是否仍在工作。這可以檢測諸如 Web 服務器陷入無限循環并且無法處理新連接的情況,即使服務器進程仍在運行。
當容器指定了*健康檢查時*,除了其正常狀態之外,它還具有*健康*狀態。此狀態最初為`starting`。每當健康檢查通過時,它就會變成`healthy`(無論它之前處于什么狀態)。連續失敗一定次數后,變為`unhealthy`。
之前可以出現的選項`CMD`有:
* `--interval=DURATION`(默認值:`30s`)
* `--timeout=DURATION`(默認值:`30s`)
* `--start-period=DURATION`(默認值:`0s`)
* `--retries=N`(默認值:`3`)
運行狀況檢查將在容器啟動后首先運行**interval**seconds,然后在每次之前的檢查完成后再次運行**interval**seconds。
如果檢查的單次運行時間超過**timeout**秒,則認為檢查失敗。
它需要**重試**連續的健康檢查失敗才能考慮容器`unhealthy`。
**start period**為需要時間引導的容器提供初始化時間。在此期間的探測失敗將不計入最大重試次數。但是,如果在啟動期間健康檢查成功,則認為容器已啟動,所有連續失敗都將計入最大重試次數。
`HEALTHCHECK`一個 Dockerfile 中只能有一條指令。如果您列出多個,則只有最后一個`HEALTHCHECK`才會生效。
`CMD`關鍵字后的命令可以是 shell 命令(例如`HEALTHCHECK CMD /bin/check-running`)或*exec*數組(與其他 Dockerfile 命令一樣;`ENTRYPOINT`有關詳細信息,請參見 eg)。
命令的退出狀態指示容器的健康狀態。可能的值為:
* 0:成功 - 容器運行良好,可以使用
* 1:不健康 - 容器無法正常工作
* 2:reserved - 不要使用這個退出代碼
例如,每五分鐘左右檢查一次網絡服務器是否能夠在三秒鐘內為站點的主頁提供服務:
~~~
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
~~~
為了幫助調試失敗的探測,命令在 stdout 或 stderr 上寫入的任何輸出文本(UTF-8 編碼)都將存儲在健康狀態中,并且可以使用`docker inspect`.此類輸出應保持簡短(當前僅存儲前 4096 個字節)。
當容器的健康狀態發生變化時,`health_status`會生成具有新狀態的事件。
## SHELL
~~~
SHELL ["executable", "parameters"]
~~~
該`SHELL`指令允許覆蓋用于命令的*shell*形式的默認 shell。Linux 上的默認 shell 是`["/bin/sh", "-c"]`,Windows 上是`["cmd", "/S", "/C"]`.該`SHELL`指令*必須*以 JSON 格式寫入 Dockerfile。
該`SHELL`指令在 Windows 上特別有用,因為 Windows 有兩種常用且截然不同的本機 shell:`cmd`和`powershell`,以及可用的備用 shell,包括`sh`.
該`SHELL`指令可以出現多次。每條`SHELL`指令都會覆蓋所有先前的`SHELL`指令,并影響所有后續指令。例如:
~~~
FROM microsoft/windowsservercore
# Executed as cmd /S /C echo default
RUN echo default
# Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default
# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello
# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S", "/C"]
RUN echo hello
~~~
`SHELL`當在 Dockerfile 中使用它們的*shell*形式時,以下指令可能會受到該指令的影響:`RUN`,`CMD`和`ENTRYPOINT`.
以下示例是在 Windows 上找到的常見模式,可以使用`SHELL`指令進行精簡:
~~~
RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
~~~
docker 調用的命令將是:
~~~
cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
~~~
這是低效的,原因有二。首先,調用了一個不必要的 cmd.exe 命令處理器(又名 shell)。其次,*shell*形式`RUN`中的每條指令都需要一個額外的命令前綴。`powershell -command`
為了使這更有效,可以采用兩種機制之一。一種是使用 RUN 命令的 JSON 形式,例如:
~~~
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
~~~
雖然 JSON 形式是明確的并且不使用不必要的 cmd.exe,但它確實需要通過雙引號和轉義來更加冗長。另一種機制是使用`SHELL`指令和*外殼*形式,為 Windows 用戶提供更自然的語法,尤其是與`escape`解析器指令結合使用時:
~~~
# escape=`
FROM microsoft/nanoserver
SHELL ["powershell","-command"]
RUN New-Item -ItemType Directory C:\Example
ADD Execute-MyCmdlet.ps1 c:\example\
RUN c:\example\Execute-MyCmdlet -sample 'hello world'
~~~
導致:
~~~
PS E:\myproject> docker build -t shell .
Sending build context to Docker daemon 4.096 kB
Step 1/5 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/5 : SHELL powershell -command
---> Running in 6fcdb6855ae2
---> 6331462d4300
Removing intermediate container 6fcdb6855ae2
Step 3/5 : RUN New-Item -ItemType Directory C:\Example
---> Running in d0eef8386e97
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/28/2016 11:26 AM Example
---> 3f2fbf1395d9
Removing intermediate container d0eef8386e97
Step 4/5 : ADD Execute-MyCmdlet.ps1 c:\example\
---> a955b2621c31
Removing intermediate container b825593d39fc
Step 5/5 : RUN c:\example\Execute-MyCmdlet 'hello world'
---> Running in be6d8e63fe75
hello world
---> 8e559e9bf424
Removing intermediate container be6d8e63fe75
Successfully built 8e559e9bf424
PS E:\myproject>
~~~
該`SHELL`指令還可用于修改 shell 的運行方式。例如,`SHELL cmd /S /C /V:ON|OFF`在 Windows 上使用,可以修改延遲的環境變量擴展語義。
如果`SHELL`需要備用 shell,例如、和其他`zsh`,該指令也可以在 Linux 上使用。`csh tcsh`
## Dockerfile examples
您可以在下面看到 Dockerfile 語法的一些示例。
~~~
# Nginx
#
# VERSION 0.0.1
FROM ubuntu
LABEL Description="This image is used to start the foobar executable" Vendor="ACME Products" Version="1.0"
RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
~~~
~~~
# Firefox over VNC
#
# VERSION 0.3
FROM ubuntu
# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get update && apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'
EXPOSE 5900
CMD ["x11vnc", "-forever", "-usepw", "-create"]
~~~
~~~
# Multiple images example
#
# VERSION 0.1
FROM ubuntu
RUN echo foo > bar
# Will output something like ===> 907ad6c2736f
FROM ubuntu
RUN echo moo > oink
# Will output something like ===> 695d7793cbe4
# You'll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
# /oink.
~~~
有關 Dockerfile 的示例,請參閱:
* 在[“build images”](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
* 在[“開始”](https://docs.docker.com/get-started/)
* 該[語言特定的入門指南](https://docs.docker.com/language/)