Dockerfile文件萬字全面解析

Dockerfile是一個文本文件,包含一些Docker指令。執行docker build,Docker就會執行Dockerfile裏面的指令,來自動建立鏡像。python

用法

Dockerfile裏面的指令能夠訪問context這些文件。linux

context是遞歸的,PATH包含全部子目錄,URL包含全部子模塊。nginx

例子,把當前目錄當作context,git

$ docker build .

Sending build context to Docker daemon  6.51 MB
...

build是由Docker daemon(守護進程)來運行,而不是CLI。github

build會把整個context發給daemon。因此最好把context設置爲空目錄,把Dockerfile放進去。只添加須要的文件,爲了提升build性能,還能夠添加.dockerignore來排除一些文件和目錄。golang

Warning!不要用系統根目錄/做爲PATH,否則會把根目錄下全部東西都傳給Docker daemon。web

通常會把Dockerfile放在context根目錄下,也可使用-f來指定其餘路徑,docker

$ docker build -f /path/to/a/Dockerfile .

指定鏡像存放倉庫可使用-tshell

$ docker build -t shykes/myapp .

支持多個,apache

$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .

Docker daemon在執行Dockfile的指令前,會作檢查,若是有語法錯誤會報錯,

$ docker build -t test/myapp .

Sending build context to Docker daemon 2.048 kB
Error response from daemon: Unknown instruction: RUNCMD

Docker daemon執行指令,是一個一個執行,一個一個提交的。執行結束會生成鏡像ID。自動清理context。

RUN cd /tmp是無效的,由於daemon是獨立執行每條指令的,不會做用到後面的指令。

爲了加速build過程,Docker會重複使用中間鏡像(緩存),在console日誌中能夠看到Using cache

$ 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

cache來源於以前本地build過的鏡像,或者使用docker load加載的鏡像。

若是想直接指定一個鏡像做爲cache,可使用--cache-from

格式

# Comment
INSTRUCTION arguments

#開頭是註釋或者parser directive(提示解析器作特殊處理)。

指令是忽略大小寫的,不過爲了和參數區分,通常全大寫。

Dockerfile從上往下順序執行指令,第一條指令必須是FROM,定義build的parent image(父鏡像)。沒有parent的鏡像叫base image。

參數裏面的#就不是註釋了,是參數的一部分,

# Comment
RUN echo 'we are running some # of cool things'

註釋在Dockerfile指令執行前,會被移除。如下是等價的,

RUN echo hello \
# comment
world
RUN echo hello \
world

注意,註釋不支持換行符\

註釋和指令前面的空格會被忽略,如下是等價的,

# this is a comment-line
    RUN echo hello
RUN echo world
# this is a comment-line
RUN echo hello
RUN echo world

可是參數裏面的空格,是會被保留的,

RUN echo "\
     hello\
     world"

Parser directives

# directive=value

Parser directives是一種特殊的註釋,用來提示解析器作特殊處理。

可是Parser directives並不會添加layers到build中,也不會被識別爲build step。

若是註釋、空行、或者指令被運行後,Docker就不會再識別Parser directives了,因此必須把Parser directives放在Dockerfile的最前面的最前面。

Parser directives是忽略大小寫的,不過通常約定爲全小寫。同時約定隨後跟一個空行。

Parser directives不支持換行符。

如下是一些無效示例,

無效--換行符

# direc \
tive=value

無效--出現了2次

# directive=value1
# directive=value2

FROM ImageName

無效--在指令以後就是普通的註釋

FROM ImageName
# directive=value

無效--在普通註釋以後也變成了普通註釋

# About my dockerfile
# directive=value
FROM ImageName

無效--未知命令會被視爲普通註釋,普通註釋以後也是普通註釋

# unknowndirective=value
# knowndirective=value

Parser directives同一行的空格會被忽略,如下是等價的,

#directive=value
# directive =value
#	directive= value
# directive = value
#	  dIrEcTiVe=value

目前支持2個Parser directives,

  • syntax,依賴BuildKit
  • escape

escape

反斜槓(默認)

# escape=\

或者反引號

# escape=`

用來指定轉義符。這個在Windows系統頗有用,由於\在Windows是路徑分隔符。

好比,

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>

使用escape能夠替換\爲`

# 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>

環境替換

環境變量(使用ENV指令來定義環境變量)可以用在指令中做爲變量,被Dockerfile解釋。還能夠處理轉義符,以便在語句中照字面值地包含variable-like語法。

使用$variable_name${variable_name}來引用環境變量。

可使用雙括弧和下劃線來命名,如${foo}_bar。同時支持bash修飾符,

  • ${variable:-word} set variable後就是set的值,沒有set variable值就是word
  • ${variable:+word} set variable後值就是word,沒有set variable就是空字符串

word既能夠是string,也能夠是另一個環境變量。

能夠在變量前加轉義符,好比\$foo\${foo}會被分別轉義爲$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,由於上一條指令賦值的hello。

ghi的值纔會是bye。

.dockerignore file

.dockerignore文件位於context根目錄,會把匹配到的文件和目錄排除在context以外。

這樣就能夠在使用ADDCOPY命令時,避免把一些大文件或者敏感信息文件和目錄,發送到Docker daemon。

context是由PATHURL定義的,因此.dockerignore文件會匹配這2個路徑。

/foo/bar == foo/bar

示例,

# comment
*/temp*
*/*/temp*
temp?
Rule Behavior
# comment 註釋忽略
*/temp* 排除root的子目錄下,temp開頭的文件和目錄。
/somedir/temporary.txt/somedir/temp
*/*/temp* 排除root的二層目錄下,temp開頭的文件和目錄。
/somedir/subdir/temporary.txt
temp? 排除root下, temp+1個字符的文件和目錄。
/tempa/tempb

匹配遵循Go語言的filepath.Match規則。

Docker還支持**,匹配任意數量的目錄(包括0)。如**/*.go排除.go結尾的,包括context root下全部目錄。

若是排除了一堆文件後,想只包含其中幾個文件,可使用異常規則!

示例,排除.md結尾的文件,包含README.md

*.md
!README.md

README-secret.md不會被排除,由於!README*.md能匹配到README-secret.md,又把README-secret.md包含進來了。

.dockerignore文件甚至能夠排除Dockerfile.dockerignore,然而並無什麼卵用,這些文件仍是會被髮送到Docker daemon,只是ADDCOPY命令不會把它們複製到鏡像了。

FROM

FROM指令初始化一個新的buid stage,爲後面的指令設置Parent Image。

FROM [--platform=<platform>] <image> [AS <name>]

FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

--platform,用來定義image的平臺,如linux/amd64, linux/arm64, 或者windows/amd64,這樣就能支持多平臺鏡像。

tag digest是可選的,都不填時,默認用最新的tag。若是找不到tag,builder就會報錯。

AS name能夠給image取個別名,在後續FROMCOPY --from=<name|index>指令中可使用這個別名。

能夠在一個Dockerfile文件中使用多個FROM。每一個FROM都會把上個指令建立的狀態清除。因此在每一個新的FROM指令以前,記錄commit輸出的最後一個image ID。

ARG是惟一能在FROM以前的指令。

好比--platform,默認狀況下,會使用build請求的默認平臺。也可使用全局build參數,經過automatic platform ARGs(依賴BuildKit)來強制把stage指定爲本地build平臺(--platform=$BUILDPLATFORM),而後用它來在stage中cross-compile目標平臺。

FROMARG怎麼結合使用呢?

FROM指令支持出如今第一個FROM以前的ARG聲明的變量。

ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD  /code/run-app

FROM extras:${CODE_VERSION}
CMD  /code/run-extras

FROM以前聲明的ARG是在build stage以外的,因此它不能用在FROM後的任何指令中。若是要用,可使用在build stage中的不帶value的ARG指令,

ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

RUN

  • RUN <command> (shell 格式,Linux /bin/sh -c Windowscmd /S /C)
  • RUN ["executable", "param1", "param2"] (exec 格式)

RUN指令會在當前鏡像之上的新layer中執行命令,commit結果,commit後的鏡像會在Dockerfile的下一個step中使用。

RUN指令的commits符合Docker理念,commit is cheap,containers能夠從image歷史中任何記錄建立,就像source control。

可使用不一樣的SHELL

shell格式

RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'

exec格式

RUN ["/bin/bash", "-c", "echo hello"]

shell格式會調用command shell,而exec格式不會,因此exec中$HOME是沒用的,要用的話直接執行shell RUN [ "sh", "-c", "echo $HOME" ]

注意,exec格式被解析爲JSON數組,因此只能用雙引號。還需注意反斜槓,

錯誤

RUN ["c:\windows\system32\tasklist.exe"]

正確

RUN ["c:\\windows\\system32\\tasklist.exe"]

默認是會啓動RUN的緩存的,好比RUN apt-get dist-upgrade -y會在下次build的時候複用。可使用docker build --no-cache來禁用緩存。

使用ADDCOPY指令也能夠禁用RUN緩存。

CMD

CMDRUN是不一樣的。RUN指令是在build過程當中執行command和commit結果。CMD在build時不會執行任何command,而是爲image定義command,在container(鏡像建立的容器)啓動的時候執行。

  • CMD ["executable","param1","param2"] (exec 格式,首選)
  • CMD ["param1","param2"] (ENTRYPOINT默認參數)
  • CMD command param1 param2 (shell 格式)

一個Dockerfile只能有一個CMD指令,若是有多個,只有最後一個生效。

shell格式會調用command shell,而exec格式不會,因此exec中$HOME是沒用的,要用的話直接執行shell RUN [ "sh", "-c", "echo $HOME" ]

注意,exec格式被解析爲JSON數組,因此只能用雙引號。還需注意反斜槓。

若是想要container每次運行相同的可執行文件,須要結合 ENTRYPOINT使用。

若是docker run定義了參數,那麼會覆蓋CMD定義。

LABEL

LABEL <key>=<value> <key>=<value> <key>=<value> ...

LABEL用來給image添加metadata,是key-value鍵值對的形式。

示例,

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."

一個image能夠有多個label,一個label能夠有多個鍵值對,如下是等價的,

LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

label會隨着image繼承,從base image或parent image繼承到當前image。

重複的label,會用最新的覆蓋舊的。

可使用命令查看image的labels,

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 已經棄用了,直接使用LABLE

LABEL maintainer="SvenDowideit@home.org.au"

EXPOSE

EXPOSE <port> [<port>/<protocol>...]

EXPOSE定義了container監聽的網絡端口,支持TCP和UDP,默認TCP。

EXPOSE並不真正的發佈端口,而只是一種預約義。

真正發佈是在docker run的時候,使用-p-P來發布。

-p發佈一個或多個端口,-P發佈所有,並映射到高位端口。

示例,默認TCP,能夠定義UDP,

EXPOSE 80/udp

也能夠同時定義TCP和UDP,

EXPOSE 80/tcp
EXPOSE 80/udp

若是這裏docker run使用了-P,將會暴露一次TCP端口和一次UDP端口,因爲會映射到高位端口,它們的端口會不同。

使用-p指定端口,

docker run -p 80:80/tcp -p 80:80/udp ...

也可使用docker network來建立網絡在container之間通訊而不須要暴露任何端口。由於container可使用任何端口通訊。

ENV

ENV <key> <value>
ENV <key>=<value> ...

ENV用來設置環境變量。有2種形式,如下是等價的,

ENV myName="John Doe" myDog=Rex\ The\ Dog \
    myCat=fluffy
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

可使用docker inspect來查看環境變量。也可使用docker run --env <key>=<value>來修改環境變量。

ENV的做用域除了build,還包括container running。有時候會有反作用,好比ENV DEBIAN_FRONTEND noninteractive,全部操做都是非交互式的,無需向用戶請求輸入,直接運行命令。可能會使apt-get用戶誤認爲是一個Debian-based image。正確的作法是爲command添加單獨的環境變量,如RUN apt-get install -y python3

ADD

ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

ADD有2種形式,第2種是爲了支持路徑包含空格,因此加了雙引號。

--chown只適用於Linux container,對Windows無效。

ADD的做用是從<src>複製新文件,目錄或者遠程文件URLs,而後添加到<desc>所在的image文件系統。

src若是是文件和目錄,那麼就是相對路徑,相對於build的context。同時支持通配符,遵循Golang的filepath.Match規則。

示例,添加全部以"hom"開頭的文件,

ADD hom* /mydir/

?匹配單個字符,

ADD hom?.txt /mydir/

<dest>是絕對路徑,或者WORKDIR的相對路徑。

示例,絕對路徑,

ADD test.txt /absoluteDir/

相對路徑,<WORKDIR>/relativeDir/

ADD test.txt relativeDir/

若是路徑種包含特殊字符(如[]),那麼須要進行轉義,

示例,添加一個文件arr[0].txt

ADD arr[[]0].txt /mydir/

針對Linux,可使用--chown定義username、groupname或者UID/GID,默認新文件和目錄會被設置爲UID爲0,GID爲0。

若是隻設置username不設置groupname,或只設置UID不設置GID,GID會使用和UID相同的數值。

username和groupname會被container's root filesystem /etc/passwd and /etc/group 轉換爲UID/GID。若是container沒有這2個文件,在設置了username/groupname後,就會報錯。能夠經過設置UID/GID來避免。

示例,

ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/

若是build使用STDIN (docker build - < somefile),就沒有build context,就只能用ADDURL。也能夠在使用STDIN時添加壓縮包 (docker build - < archive.tar.gz),壓縮包根目錄的Dockerfile和其餘壓縮包會當作build context。

若是src是一個遠程文件URL,就會須要600權限(Linux)。若是遠程文件有HTTP Last-Modified header,header的timestamp會用來設置到dest文件的mtime。可是mtime不會反映文件是否修改和緩存是否應該更新。

若是URL文件須要受權,ADD是不支持的,須要使用RUN wget, RUN curl,或者container裏面的其餘工具。

ADD遵循如下規則:

  • <src> 必須在build的context 中;不能 ADD ../something /something添加context父目錄的東西。由於 docker build 的第一步是把context,目錄及其子目錄發送到docker daemon。
  • 若是<src> 是URL,<dest> 沒有以斜槓結尾,那麼文件從直接從URL下載後,而後直接複製到 <dest>
  • 若是 <src> 是URL,<dest> 是以斜槓結尾的,那麼會從URL解析出文件名,下載到<dest>/<filename>。好比, ADD http://example.com/foobar dest/ 會建立文件 dest/foobar。URL必須是明確的路徑,以保證能找到合適的文件名(http://example.com 是無效的)。
  • 若是 <src> 是目錄,那麼整個目錄都會被複制,包括文件系統的metadata。(目錄自己不復制,只是內容)
  • 若是 <src>是本地壓縮包(如gzip, bzip2 or xz),那麼會被解壓成目錄。遠程URL是不會解壓的。解壓至關於執行了 tar -x,若是dest路徑下有文件衝突,會被重命名爲「2」。(壓縮包不是根據文件名判斷的,而是根據內容,好比一個空文件命名爲.tar.gz,是不會被解壓複製的)
  • 若是 <src> 是任何其餘文件,就會隨同它的metadata一塊兒複製。此時 <dest> 以斜槓 /結尾的話,就會被認爲是一個目錄,<src>的內容會被寫到<dest>/base(<src>)
  • 若是<src>定義的是多個資源,不管是直接仍是通配符匹配到的, <dest> 必須是一個目錄,且以斜槓/結尾。
  • 若是 <dest> 不以斜槓結尾,那麼就會被認爲是一個普通文件,那麼<src> 會被寫到<dest>
  • 若是 <dest> 不存在,那麼path中的全部未建立的目錄都會自動建立。

若是src內容改變了,在第一次遇到ADD指令後,會禁用後續全部指令的緩存,包括RUN指令的緩存。

COPY

COPYADD區別在於ADD能夠添加遠程URLS,COPY不能。

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

COPY有2種形式,第2種是爲了支持路徑包含空格,因此加了雙引號。

--chown只適用於Linux container,對Windows無效。

COPY的做用是從<src>複製新文件,目錄,而後添加到<desc>所在的image文件系統。

src若是是文件和目錄,那麼就是相對路徑,相對於build的context。同時支持通配符,遵循Golang的filepath.Match規則。

示例,添加全部以"hom"開頭的文件,

COPY hom* /mydir/

?匹配單個字符,

COPY hom?.txt /mydir/

<dest>是絕對路徑,或者WORKDIR的相對路徑。

示例,絕對路徑,

COPY test.txt /absoluteDir/

相對路徑,<WORKDIR>/relativeDir/

COPY test.txt relativeDir/

若是路徑種包含特殊字符(如[]),那麼須要進行轉義,

示例,添加一個文件arr[0].txt

COPY arr[[]0].txt /mydir/

針對Linux,可使用--chown定義username、groupname或者UID/GID,默認新文件和目錄會被設置爲UID爲0,GID爲0。

若是隻設置username不設置groupname,或只設置UID不設置GID,GID會使用和UID相同的數值。

username和groupname會被container's root filesystem /etc/passwd and /etc/group 轉換爲UID/GID。若是container沒有這2個文件,在設置了username/groupname後,就會報錯。能夠經過設置UID/GID來避免。

示例,

COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/
COPY --chown=1 files* /somedir/
COPY --chown=10:11 files* /somedir/

若是build使用STDIN (docker build - < somefile),就沒有build context,就不能用COPY

COPY支持--from=<name|index>,用來指定src爲以前buid的image(經過FROM .. AS <name>建立的)來替換build context。既能夠是name也能夠是index數字(全部使用FROM指令創建的build stages)。若是經過name找不到build stage,就會去找同名的image。

COPY遵循如下規則:

  • <src> 必須在build的context 中;不能 COPY ../something /something添加context父目錄的東西。由於 docker build 的第一步是把context,目錄及其子目錄發送到docker daemon。
  • 若是 <src> 是目錄,那麼整個目錄都會被複制,包括文件系統的metadata。(目錄自己不復制,只是內容)
  • 若是 <src> 是任何其餘文件,就會隨同它的metadata一塊兒複製。此時 <dest> 以斜槓 /結尾的話,就會被認爲是一個目錄,<src>的內容會被寫到<dest>/base(<src>)
  • 若是<src>定義的是多個資源,不管是直接仍是通配符匹配到的, <dest> 必須是一個目錄,且以斜槓/結尾。
  • 若是 <dest> 不以斜槓結尾,那麼就會被認爲是一個普通文件,那麼<src> 會被寫到<dest>
  • 若是 <dest> 不存在,那麼path中的全部未建立的目錄都會自動建立。

若是src內容改變了,在第一次遇到COPY指令後,會禁用後續全部指令的緩存,包括RUN指令的緩存。

ENTRYPOINT

exec 格式

ENTRYPOINT ["executable", "param1", "param2"]

shell 格式

ENTRYPOINT command param1 param2

ENTRYPOINT用來配置container做爲可執行文件來運行。

示例,使用默認內容啓動nginx,監聽80端口,

$ docker run -i -t --rm -p 80:80 nginx

docker run <image>的命令行參數,會被添加到exec格式中的全部元素以後,並覆蓋CMD指令定義的元素。這樣就能夠把參數傳遞給entry point,也就是docker run <image> -d會把-d傳遞給entry point。可使用docker run --entrypoint來覆蓋ENTRYPOINT指令(可是隻能把binary設置爲exec,不能用sh -c)。

shell格式會禁用掉CMD或者run命令行參數,可是有個缺點就是,ENTRYPOINT就不是做爲/bin/sh -c的子命令來啓動的了,也就是不能傳遞signals。也就意味着可執行文件,不是container的PID 1,也不會接收Unix signals(一種軟件中斷)。這樣可執行文件就不會接收來自docker stop <container>SIGTERM

只有Dockerfile的最後一個ENTRYPOINT纔會生效。

ENTRYPOINT Exec示例

FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

當運行container,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 -b -H,其中top -bENTRYPOINT設置的,-H是docker命令行參數,添加到了ENTRYPOINT後面,覆蓋了CMD-c。

而後能夠優雅地使用docker stop test請求top shut down。

示例,使用ENTRYPOINT在前臺運行Apache(也就是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"]

若是想編寫單個可執行文件的啓動腳本,可使用execgosu命令,來確保可執行文件可以接收到Unix signals。

#!/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 "$@"

最後,若是在shutdown的時候須要作一些額外的清理(或者和其餘containers交互),或者是多個協調而不是單個可執行文件,就可能須要確保ENTRYPOINT腳本可以接收Unix signals,傳遞,而後作更多工做,

#!/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來運行這個image,那麼就可使用docker execdocker top來驗證container處理,而後使用腳本中止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

shell格式會調用command shell,而exec格式不會,因此exec中$HOME是沒用的,要用的話直接執行shell RUN [ "sh", "-c", "echo $HOME" ]

注意,exec格式被解析爲JSON數組,因此只能用雙引號。還需注意反斜槓。

ENTRYPOINT Shell示例

ENTRYPOINT定義一個簡單的string,而後它就會在/bin/sh -c中執行。shell格式使用shell processing來替代shell environment variables,而後會忽略任何CMDdocker run命令行參數。爲了確保docker stop能直接signal任何運行的ENTRYPOINT可執行文件,記住使用exec開始,

FROM ubuntu
ENTRYPOINT exec top -b

運行這個image時,你會看到單個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

若是忘了在ENTRYPOINT前添加exec

FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1

運行(爲下一步設置一個name),

$ 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

你就會看到ENTRYPOINT定義的top不是PID 1

若是執行docker stop test,container就不會乾淨地退出。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

real 10.19s超時。

CMD和ENTRYPOINT如何結合使用

CMDENTRYPOINT指令都定義了運行container時,哪些命令會執行。他們的結合有一些規則,

  1. Dockerfile應該定義至少一個CMDENTRYPOINT
  2. 若是使用container做爲可執行文件,應該定義ENTRYPOINT
  3. 若是須要給ENTRYPOINT 定義默認參數,或者在container中執行ad-hoc(臨時)命令,應該使用CMD
  4. 以可選參數運行container時會覆蓋CMD

下面這個表格展現了CMDENTRYPOINT指令的不一樣組合

No ENTRYPOINT ENTRYPOINT exec_entry p1_entry ENTRYPOINT [「exec_entry」, 「p1_entry」]
No CMD error, not allowed /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是從base image定義的,那麼設置ENTRYPOINT會重置CMD爲空值。此時若是要使用CMD,必須在當前image從新定義。

VOLUME

VOLUME ["/data"]

VOLUME指令用來建立掛載點,把container掛載到native host(宿主機)或其餘container。

value能夠是JSON array,如VOLUME ["/var/log/"],也能夠是string,如VOLUME /var/logVOLUME /var/log /var/db

docker run命令會用base image中定義的location中存在的任何數據,來初始化新建立的volumn。

示例,

FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol

docker run會在/myvol建立一個掛載點,而後把greeting複製到新建立的volumn。

遵循規則,

  • **基於Windows的containers **: volumn的目標路徑必須是如下之一:
    • 不存在的或者空的目錄
    • C:之外的驅動
  • 在Dockerfile裏面修改volumn: 在volumn已經被聲明以後的任何build steps嘗試修改volumn數據,都會被忽略。
  • JSON formatting: 要用雙引號,不要用單引號.
  • 在container run-time纔會聲明主機目錄(掛載點): 掛載點是依賴主機的。由於主機目錄不能保證對全部主機都是有用的,爲了保證image的可移植性,不能在Dockerfile中掛載主機目錄,而是必須在建立或運行container的時候。VOLUME指令也不支持host-dir這樣的參數。

USER

USER <user>[:<group>]

USER <UID>[:<GID>]

USER指令用於RUN, CMDENTRYPOINT指令執行時指定user name / group。USER指令能夠設置user name(或UID),可選用user group(或GID)。

若是定義了user group,那麼這個user就只有這個group的membership,任何其餘配置的group memberships都會被忽略。

若是user沒有primary group,那麼image(或者下一條指令)就會以root group運行。

在Windows,若是不是內建帳號,必須先建立。能夠在Dockerfile中調用net user命令,

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

WORKDIRRUN, CMD, ENTRYPOINT, COPY and ADD指令設置工做目錄。

若是WORKDIR不存在,即便後面的Dockerfile不會用到,它仍然會被建立。

WORKDIR指令能夠在Dockerfile中定義屢次。若是是相對路徑,那麼就是相對於上一條WORKDIR指令的路徑。

示例,

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

pwd的結果是/a/b/c

WORKDIR能夠引用ENV定義的環境變量,示例,

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

pwd的結果是/path/$DIRNAME

ARG

ARG <name>[=<default value>]

ARG指令定義變量,用戶能夠在使用docker build命令帶參數--build-arg <varname>=<value>,在build-time傳遞這個變量給builder。若是用戶指定了一個build參數而沒有在Dockerfile中定義,build會報warning,

[Warning] One or more build-args [foo] were not consumed.

一個Dockerfile能夠包含一個或多個ARG指令。

示例,

FROM busybox
ARG user1
ARG buildno
# ...

警告!不建議使用build-time變量來傳遞私密數據,如github keys,用戶認證信息等。由於image的任何用戶均可以使用docker history查看build-time變量。

默認值

ARG指令能夠設置默認值(可選),

FROM busybox
ARG user1=someuser
ARG buildno=1
# ...

若是ARG指令有默認值,在build-time沒有值傳遞,那麼builder會用這個默認值。

範圍

ARG指令是在它被定義那一行生效的,而不是命令行被使用的時候,或者其餘地方。

示例,

FROM busybox
USER ${user:-some_user}
ARG user
USER $user
# ...

用戶build這個文件,調用,

$ docker build --build-arg user=what_user .

第2行的USER結果爲some_user由於user變量是在第3行定義的。

第4行的USER結果爲what_user,由於user變量已經被定義了,在命令行傳遞了what_user值。

ARG指令定義以前,任何變量使用結果都是空string。

ARG定義的build stage結束時,ARG指令就超出範圍了。爲了在多個stages使用同一個arg,每一個stage都必須包括ARG指令,

FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS

FROM busybox
ARG SETTINGS
RUN ./run/other $SETTINGS

使用ARG變量

可使用ARGENV指令來爲RUN指令定義變量。ENV定義的環境變量始終都會覆蓋ARG定義的同名變量。

示例,

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
RUN echo $CONT_IMG_VER

假設使用這條命令build image,

$ docker build --build-arg CONT_IMG_VER=v2.0.1 .

RUN會使用v1.0.0而不是ARG傳遞的v2.0.1。這個行爲有點相似於shell腳本,一個局部變量會覆蓋經過參數傳遞的變量,或者從環境定義繼承的變量。

仍是上面的例子,定義不一樣的ENV會把ARGENV結合的更好用,

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER

不像ARGENV的值會在build image中持久化。若是不用--build-arg build,

$ docker build .

用這個Dockerfile,CONT_IMG_VER仍然會持久化在這個image,它的值是v1.0.0,由於在第3行用ENV定義了默認值。

在這個示例中,經過ENV指令,能夠把命令行參數傳遞進來,而後持久化到最終的image,實現了變量擴展。變量擴展只支持Dockerfile指令的一部分指令。

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • FROM
  • LABEL
  • STOPSIGNAL
  • USER
  • VOLUME
  • WORKDIR
  • ONBUILD (結合以上指令使用)

預約義ARGs

Docker有一些預約義的ARG變量,你能夠不使用ARG指令,直接用這些變量。

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy

直接在命令行使用,

--build-arg <varname>=<value>

默認這些預約義的變量是不會輸出到docker history中的。這樣能夠下降在HTTP_PROXY變量中意外泄露敏感認證信息的風險。

示例,使用--build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com來build Dockerfile,

FROM ubuntu
RUN echo "Hello World"

HTTP_PROXY變量不會輸出到docker history,也不會被緩存。若是代理服務器變成了http://user:pass@proxy.sfo.example.com,後續的build不會致使cache miss。

可使用ARG來覆蓋這個默認行爲,

FROM ubuntu
ARG HTTP_PROXY
RUN echo "Hello World"

當build這個Dockerfile的時候,HTTP_PROXY會存到docker history中,若是它的值改變了,會把build緩存禁用掉。

對緩存的影響

ARG變量並不會像ENV持久化到image,可是會以相似的方式,影響到build緩存。若是Dockerfile定義了一個ARG變量,這個變量和前一個build不同,那麼在第一次用這個變量的時候會發生"cache miss"(不是定義的時候)。尤爲是,全部ARG後面的RUN指令通常都會使用ARG變量,這樣就會致使cache miss。可是全部預約義ARGs是沒有影響cache的,除非是在Dockerfile中有一個同名的ARG指令。

示例,2個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個示例在第2行都不會cache miss,第3行會cache miss。ARG CONT_IMG_VER會致使RUN那一行被認爲是執行了CONT_IMG_VER=<value> echo hello,因此若是<value>改變了,就cache miss了。

另一個示例,

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER $CONT_IMG_VER
RUN echo $CONT_IMG_VER

第3行會發生cache miss。由於ENV引用的ARG變量經過命令行改變了。另外,在這個示例中,ENV會致使image包含這個value(ENV會持久化到image中)。

若是ENVARG指令重複,

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER hello
RUN echo $CONT_IMG_VER

第3行就不會發生cache miss,由於CONT_IMG_VER的值是常量(hello)。所以第4行RUN指令用到的環境變量和值在build之間不會改變。

ONBUILD

ONBUILD <INSTRUCTION>

ONBUILD指令會在image中添加一個trigger,這個trigger會在image做爲base的時候觸發。trigger會在下游的 build context中執行,就像在下游的Dockerfile 中,在 FROM指令以後,它就已經被當即嵌入了。

任何build指令均可以註冊爲trigger。

若是你build一個image,這個image會做爲base來build其餘images,這就頗有用。好比,一個應用build環境或者一個deamon自定義配置。

示例,若是一個image是可複用的Python應用builder(用來build新的應用image),那麼它須要把應用源碼添加到一個特定目錄,而後調用build腳本。此時ADDRUN指令是沒法訪問應用源碼的,每一個應用build的源碼也可能不同。你能夠簡單地,給應用開發者提供Dockerfile樣本文件來複制粘貼到他們的應用中,但這是低效、易出錯和困難去作更新的,由於這個和「應用定義」代碼混淆了。

可使用ONBUILD指令來提早註冊指令,在下個build stage再運行。

過程以下,

  1. 當碰到ONBUILD 指令,builder就會添加trigger到正在build的image的metadata。這條指令不會影響當前build。
  2. 在build的最後,全部的triggers都會被存儲到image的manifest,在key OnBuild下面。能夠用 docker inspect 命令查看。
  3. 而後image可能會被用來做爲新build的base,使用 FROM 指令。 FROM 指令在處理時,下游builder會查找 ONBUILD triggers,而後按它們註冊的順序執行。若是有trigger失敗了,FROM指令就會中斷,build失敗。若是triggers都成功了,那麼FROM會完成,build成功。
  4. Triggers會在執行後,從最後一個image中清除。也就是說,它們是不會隨着「父子」build繼承的。

好比你可能會添加這樣的內容,

ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src

注意,1.鏈式ONBUILD ONBUILD是不容許的。2.ONBUILD可能不會trigger FROMMAINTAINER指令。

STOPSIGNAL

STOPSIGNAL signal

STOPSIGNAL指令設置system call signal,發送到container退出。signal能夠是有效的unsigned number(匹配kernel’s syscall table裏的position,好比9),也能夠是SIGNAME(好比SIGKILL)。

HEALTHCHECK

2種格式,

  • HEALTHCHECK [OPTIONS] CMD command (經過運行container裏面的命令來檢查container)
  • HEALTHCHECK NONE (禁用健康檢查,從base image繼承)

HEALTHCHECK 指令用來告訴Docker怎樣測試container是否還在工做。好比雖然server一直在運行,可是實際上已經死循環了,沒法處理新鏈接了。

當container定義了健康檢查,就會把健康狀態添加到status中。status初始化是starting。不管健康檢查何時經過,它都會變爲healthy(不管以前是什麼狀態)。在必定數量的連續失敗後,它會變爲unhealthy

第一種格式的OPTION能夠是,

  • --interval=DURATION (default: 30s)
  • --timeout=DURATION (default: 30s)
  • --start-period=DURATION (default: 0s)
  • --retries=N (default: 3)

在container開始後的interval seconds ,會運行健康檢查。每一個健康檢查完成後,等待interval seconds再次運行。

若是健康檢查運行的時候超過了timeout seconds,就認爲失敗。

失敗的次數若是達到了retries的值,就認爲unhealthy

start period指定了container須要啓動的時間。在這期間探針失敗(Probe failure)不會記做重試次數。可是,若是在這期間健康檢查經過了,那麼container就認爲已經啓動了,這以後的失敗(all consecutive failures)就會記做重試次數。

一個Dockerfile只能有一個HEALTHCHECK 指令。若是有多個,那麼只有最後一個HEALTHCHECK 生效。

第1種格式的command既能夠是shell命令(如,HEALTHCHECK CMD /bin/check-running),也能夠是exec數組。

command的退出狀態反應了container的健康狀態,

  • 0: success - the container is healthy and ready for use
  • 1: unhealthy - the container is not working correctly
  • 2: reserved - do not use this exit code

示例,每5分鐘檢查1次,以確保web服務器能在3秒內爲網站首頁提供服務,

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

爲了幫助debug失敗探針(failing probes),任何寫到stdout或stderr輸出文本(UTF-8編碼)都會被存儲到健康狀態,而且可使用docker inspect查詢。並且輸出應該簡短(目前只有最開始的4096 bytes會被存儲)。

當container的健康狀態改變了,會用新的狀態生成一個health_status事件。

SHELL

SHELL ["executable", "parameters"]

SHELL指令容許重寫shell格式命令的默認shell。Linux的默認shell是["/bin/sh", "-c"],Windows的默認shell是["cmd", "/S", "/C"]SHELL指令必須在Dockfile中寫成JSON格式。

SHELL指令在Windows特別有用,由於Windows有2個經常使用的不一樣的原生shell,cmdpowershell,也有可選用的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格式的RUN CMDENTRYPOINT出如今Dcokerfile中時,SHELL指令能影響這些指令。

示例,Windows上常見的模式,能夠經過使用SHELL指令進行簡化,

RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"

docker調用的命令,

cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"

這個有點低效,有2個緣由。首先,有一個沒必要要的cmd.exe命令行處理器(aka shell)被調用了。其次,shell格式的RUN指令須要額外的前綴命令powershell -command

爲了更高效,有2種機制。其一是使用JSON格式,

RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]

JSON格式是清晰的,不會使用沒必要要的cmd.exe。可是須要雙引號和轉義符,顯得有點冗餘。

。其二是用SHELL指令和shell格式,這樣能夠給Windows用戶更天然的語法,特別是和escape parser directive結合使用的時候,

# 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:\docker\build\shell> 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:\docker\build\shell>

SHELL指令也能被用來修改shell操做方式。好比在Windows用SHELL cmd /S /C /V:ON|OFF,能夠修改延遲環境變量擴展語義。

SHELL指令也能夠用在Linux上,可選的shell有zsh, csh, tcsh等。

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.

如下內容可查看參考資料進一步閱讀。

  • BuildKit(第三方工具)
  • Parser directives的命令syntax(依賴BuildKit)
  • RUN已知bug(Issue 783
  • External implementation features(依賴BuildKit)
  • Automatic platform ARGs in the global scope(依賴BuildKit)

參考資料

https://docs.docker.com/engine/reference/builder/

下一篇《Dockerfile最佳實踐》,歡迎持續關注哦。

版權申明:本文爲博主原創文章,轉載請保留原文連接及做者。


專一測試,堅持原創,只作精品。

相關文章
相關標籤/搜索