來自docker官方網址:https://docs.docker.com/engine/reference/builder/linux
docker可以從Dockerfile中讀取指令並自動構建一個鏡像。Dockerfile是一個文本文檔,它包含用戶能夠在命令行上調用的全部命令來組裝一個鏡像。使用docker構建,用戶能夠建立一個連續執行多個命令行指令的自動化構建。nginx
這個頁面描述了你能夠在Dockerfile中使用的命令。讀完此頁後,請參閱Dockerfile最佳實踐(Dockerfile
Best Practices)以得到面向tip的指南。git
翻譯這篇文章前咱們先闡述一個重要的概念,那就是上下文(context):docker構建時會將上下文的全部內容(.dockerignore中指定的內容除外)發送到daemon中進行處理。那麼如何指定這個上下文呢?咱們看一個命令來更容易的理解:github
docker build -t xxxxx .
注意這行命令後面有一個.(小點),這個小點的做用不是指定dockerfile的位置,指定dockerfile的位置是用-f參數來指定的,那你覺得這個小點是幹嗎的?他就是用來指定構建鏡像的上下文的。golang
理解了以上的例子後,咱們再看看COPY這個命令的真是含義:docker
COPY ./package.json /app/
COPY是將源目標複製到鏡像中的一個命令,那麼上面這個./package.json是指什麼路徑呢?這並非要複製執行 docker build
命令所在的目錄下的 package.json
,也不是複製 Dockerfile
所在目錄下的 package.json
,而是複製 上下文(context) 目錄下的 package.json
。shell
docker構建命令(docker build
)從Dockerfile和上下文構建一個映像。構建的上下文是位於指定位置路徑或URL的文件集。路徑是本地文件系統上的一個目錄。URL是一個Git存儲庫位置。apache
上下文會被遞歸的處理。所以,路徑包含任何子目錄,URL包含存儲庫及其子模塊。這個例子顯示了一個使用當前目錄做爲上下文的構建命令:json
$ docker build . Sending build context to Docker daemon 6.51 MB ...
構建由Docker守護進程運行,而不是由CLI運行。構建過程要作的第一件事是將整個上下文(遞歸地)發送給守護進程。在大多數狀況下,最好從一個空目錄做爲上下文開始,並將Dockerfile保存在該目錄中。而且只添加構建Dockerfile所需的文件。ubuntu
警告:不要使用根目錄/做爲路徑,由於它會致使構建將硬盤驅動器的所有內容傳輸到Docker守護進程。
要在構建上下文中使用文件,Dockerfile引用一條指令中指定的文件,例如一條COPY指令。要提升構建的性能,能夠經過向上下文目錄添加.dockerignore文件來排除文件和目錄。有關如何建立.dockerignore文件的信息,請參閱此頁上的文檔。 create a .dockerignore
file
一般,Dockerfile的文件名稱默認就是Dockerfile(首字母D大寫),位於上下文的根目錄中。在docker build中使用-f標誌指向文件系統中任何位置的Dockerfile。
docker build -f /path/to/a/Dockerfile .
您能夠指定一個存儲庫(repository)和用於保存新映像的標記(tag),這會在鏡像構建成功以後應用:
docker build -t shykes/myapp .
要在構建以後將映像標記到多個存儲庫中,請在運行構建命令時添加多個-t參數:
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守護進程將自動清理您發送的上下文。
注意,每條指令都是獨立運行的,而且會建立一個新鏡像——所以,運行cd /tmp不會對接下來的指令產生任何影響。
只要有可能,Docker將重用中間鏡像(緩存),以顯著加快Docker的構建過程。這由控制檯輸出中的Using cache消息表示。(有關更多信息,請參閱Dockerfile最佳實踐指南中的Build cache部分 Build cache section):
$ 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
構建緩存僅用於具備本地父鏈(parent chain)的映像。這意味着這些映像是由之前的構建建立的,或者整個映像鏈是用docker加載的。若是但願使用特定映像的構建緩存,可使用--cache-from選項指定它。使用--cache-from指定的鏡像不須要父鏈,能夠從其餘倉庫(registries)獲取。
完成構建以後,就能夠考慮瀏覽Pushing a repository to its registry了。
從18.09版本開始,Docker支持一個新的moby/buildkit項目提供的後端來執行構建(build)。與舊的實現相比,BuildKit後端提供了許多優勢。例如,BuildKit能夠:
檢測並跳過未使用的構建階段(stage)
並行化構建獨立的構建階段
在構建之間只會增量地傳輸構建上下文中更改的文件
檢測並跳過在構建上下文中傳輸未使用的文件
使用帶有許多新特性的外部Dockerfile實現
避免API其他部分的反作用(中間鏡像和容器)
爲自動修剪(prune)設置構建緩存的優先級
要使用BuildKit後端,您須要在調用docker構建以前在CLI上設置一個環境變量DOCKER_BUILDKIT=1。
要了解基於BuildKit的構建可用的實驗性Dockerfile語法,請參考BuildKit存儲庫中的文檔: refer to the documentation in the BuildKit repository.
下面是Dockerfile的格式:
# 註釋
指令 參數
指令不區分大小寫。可是,習慣上它們是大寫的,以便更容易地將它們與參數區分開。
Docker按順序在Dockerfile中運行指令。Dockerfile必須以「FROM」指令開頭。它可能會在解析器指令、註釋和全局做用域的參數以後。FROM指令指定要從中構建的父映像。FROM以前可能只有一個或多個ARG指令,它們聲明在Dockerfile的FROM行中使用的參數。
Docker將以#開頭的行做爲註釋,行中任何位置的#標記都被視爲註釋,除非該行是有效的解析器指令。這容許這樣的語句:
# Comment RUN echo 'we are running some # of cool things'
註釋中不支持行延續字符。
解析器指令是可選的,而且影響Dockerfile中後續行的處理方式。解析器指令不向構建中添加層,也不會顯示爲構建步驟。解析器指令被編寫爲形式# 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=[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後端時該指令纔有用。
syntax指令定義用於構建當前Dockerfile的構建器的位置。BuildKit後端容許無縫地使用構建器的外部實現,這些構建器以Docker鏡像的形式分發,並在容器沙箱環境中執行。
自定義Dockerfile的實現容許你:
.dockerignore文件的主旨是要「排除」一些髮網daemon的文件,可是裏面也有一些邏輯是不排除的,你要先明確這一點。
在docker CLI將上下文發送到docker守護進程以前,它在上下文的根目錄中查找一個名爲.dockerignore的文件。若是該文件存在,CLI將修改上下文以排除與其中模式匹配的文件和目錄。這有助於避免沒必要要地向守護進程發送大型或敏感的文件和目錄,並可能使用ADD或COPY將它們添加到映像中。
CLI將.dockerignore文件解釋爲一個新行分隔的模式列表,相似於Unix shell的文件globs。爲了進行匹配,上下文的根被認爲是工做目錄和根目錄。例如,模式/foo/bar和foo/bar都排除了路徑的foo子目錄或位於URL的git存儲庫根目錄中名爲bar的文件或目錄。二者都不排斥其餘任何東西。
若是.dockerignore文件中的一行以第1列中的#開始,那麼這一行將被視爲註釋,並在CLI解釋以前被忽略。
# comment */temp* */*/temp* temp?
該文件致使如下構建行爲:
規則 | 行爲 |
---|---|
# comment |
忽略掉. |
*/temp* |
排除在根目錄的任何直接子目錄中名稱以temp開頭的文件和目錄。例如,排除了普通文件/somedir/temporary.txt,以及/somedir/temp目錄。 |
*/*/temp* |
從根目錄下兩層的任何子目錄中排除以temp開頭的文件和目錄。例如,/somedir/subdir/temporary.txt被排除。 |
temp? |
排除根目錄中名稱爲temp的單字符擴展名的文件和目錄。例如,排除/tempa和/tempb。 |
使用Go語言的filepath的Match方法來匹配規則。預處理步驟刪除開頭和結尾的空白並使用Go語言的filepath.Clean方法消除.和. .。預處理後爲空的行將被忽略。
依賴於filepath.Match提供的規則,Docker使用一個特殊的通配符**來匹配任意數量的目錄,好比:**/*.go這個會匹配全部目錄中.go擴展名的文件,包括構建的根目錄。
以!(感嘆號)開始的行可用於排除的例外狀況。下面是一個例子,.dockerignore文件使用這個機制:
*.md
!README.md
除了README.md外全部的markdown文件都被排除在上下文外了。
放置!的地方影響以下行爲:.dockerignore中與特定文件匹配的最後一行決定它是被包含仍是被排除。考慮下面的例子:
*.md !README*.md README-secret.md
首先全部的markdown文件都被排除了。
在!README*.md下面還有一行 README-secret.md那麼結果就是除了這個README-secret.md的全部README開頭的markdown文件不會被排除。
在來看下面這個例子:
*.md README-secret.md !README*.md
首先全部的markdown文件都被排除了
而後第二行README-secret.md這個能夠不寫,由於第一行已經指定了規則,它也符合第一行的規則。
第三行又將全部README開頭的markdown文件包含了進來,也就是說第二行根本不會起做用了,它指定的這個文件由於第三行的規則會被包括進去。
甚至可使用.dockerignore文件來排除Dockerfile和.dockerignore文件。這些文件仍然被髮送到守護進程,由於守護進程須要它們來完成本身的工做。可是ADD和COPY指令不會將它們複製到鏡像。
最後,你可能但願指定要在上下文中包含哪些文件,而不是要排除哪些文件。要實現這一點,將*指定爲第一個模式,而後是一個或多個模式!例外模式。
note:做爲歷史緣由你應該瞭解,.模式被忽略了。
FROM <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]
FROM指令初始化一個新的構建階段,併爲後續指令設置基本鏡像。所以,一個有效的Dockerfile必須以FROM指令開始。鏡像能夠是任何有效的鏡像—從公共存儲庫(docker hub)中提取鏡像尤爲容易。
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在構建階段以外,所以在FROM以後的任何指令中都不能使用它。若要使用在第一個以前聲明的ARG的默認值,請使用在構建階段內沒有值的ARG指令:
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
RUN有兩種格式:
#shellform,命令在shell中運行,默認是Linux上的/bin/sh -c或Windows上的cmd /S /C RUN <command> #execform RUN ["executable", "param1", "param2"]
RUN指令將在當前鏡像之上的新層中執行任何命令並提交結果。生成的提交鏡像將用於Dockerfile中的下一步。
分層RUN指令和生成提交符合Docker的核心概念,其中提交很廉價,能夠從鏡像歷史中的任何點建立容器,很像源代碼控制。
execform格式的RUN指令在避免shell字符串轉換和在一個不包含指定的可執行shell的基礎鏡像上運行RUN指令成爲可能。
shellform格式下的默認shell能夠經過SHELL命令來修改。
在shellform的格式下,可使用\(反斜槓)將單個運行指令延續到下一行。例如,考慮這兩行:
RUN /bin/bash -c 'source $HOME/.bashrc; \ echo $HOME'
與下面這一行是等同的:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
note:使用exec form格式時,能夠將目標shell傳入,這可使你使用一個不一樣的shell,而不是‘/bin/sh’。以下例:
RUN ["/bin/bash", "-c", "echo hello"]
note:exec from格式命令會被解析成json數組,這意味着你必須使用雙引號而不是單引號來括住數組中的每個元素。
與shellform不一樣,execform不調用命令shell。這意味着不會發生正常的shell處理。例如,RUN ["echo", "$HOME"]不會對$HOME執行變量替換。若是想要shell處理,那麼要麼使用shellform,要麼直接執行shell,例如:RUN ["sh", "-c", "echo $HOME"]。當使用execform並直接執行shell時(如shellform的狀況),執行環境變量擴展的是shell,而不是docker。
在JSON格式中(execform會被看成json處理,還記得嗎),必須轉義反斜槓。這在以反斜槓爲路徑分隔符的windows系統特別相關。下面這行代碼因爲不是有效的JSON,將被視爲shellform進行處理,並以一種意外的方式失敗:運行["c:\windows\system32\tasklist。本例的正確語法是:RUN ["c:\\windows\\system32\\tasklist.exe"]
RUN指令的緩存不會在下一次構建期間自動失效。像RUN apt-get distt -upgrade -y這樣的指令的緩存將在下一個構建過程當中重用。RUN指令的緩存能夠經過使用--no-cache標誌來失效,例如docker build --no-cache。
從 Dockerfile
Best Practices guide 查看更多信息。
運行指令的緩存能夠經過添加指令失效。詳見下文below。
關於RUN指令已提交的ISSUE
Issue 783 is about file permissions problems that can occur when using the AUFS file system. You might notice it during an attempt to rm
a file, for example.
For systems that have recent aufs version (i.e., dirperm1
mount option can be set), docker will attempt to fix the issue automatically by mounting the layers with dirperm1
option. More details on dirperm1
option can be found at aufs
man page
If your system doesn’t have support for dirperm1
, the issue describes a workaround.
CMD有三種格式:
#exec form, 推薦格式 CMD ["executable","param1","param2"] #做爲ENTRYPOINT的默認參數 CMD ["param1","param2"] #shell form CMD command param1 param2 (shell form)
Dockerfile中只能存在一個CMD,若是你有多個CMD,那麼最後那個CMD命令會生效。
CMD的主要用途是爲執行容器提供缺省值。這些缺省值能夠包括可執行文件,也能夠省略可執行文件,在這種狀況下,您還必須指定一個ENTRYPOINT指令。
NOTE:若是CMD用於爲ENTRYPOINT指令提供默認參數,那麼應該使用JSON數組格式指定CMD和ENTRYPOINT指令。
NOTE:exec form格式的會被看成JSON數組來對待,這意味着你必須在數組裏面使用雙引號而不是單引號。
NOTE:exec form不會像shell form那樣會調用shell命令,這意味着不會發生普通的shell處理,好比CMD [ "echo", "$HOME" ]這條命令不會在$HOME上發生變量替換。若是想要shell處理,那麼要麼使用shell form,要麼直接執行shell,好比:CMD [ "sh", "-c", "echo $HOME" ]。當使用exec form並直接執行shell時(如shell form的狀況),執行環境變量擴展的是shell,而不是docker。
在shell或exec格式中使用時,CMD指令設置在運行鏡像時要執行的命令。
若是你使用的是shell form的格式,CMD會在/bin/sh -c裏執行:
FROM ubuntu CMD echo "This is a test." | wc -
若是不想使用shell來運行命令,那麼必須將該命令表示爲JSON數組,並給出可執行文件的完整路徑。這種數組形式是CMD的推薦格式。任何附加參數必須單獨表示爲數組中的字符串:
FROM ubuntu CMD ["/usr/bin/wc","--help"]
若是但願容器每次都運行相同的可執行文件,那麼應該考慮將ENTRYPOINT與CMD結合使用。查看這裏: ENTRYPOINT。
若是用戶指定docker運行的參數,那麼他們將覆蓋CMD中指定的默認值。
不要將RUN與CMD混淆。RUN實際運行命令並提交結果;CMD在構建時不執行任何操做,可是爲鏡像指定想要的命令。
LABEL <key>=<value> <key>=<value> <key>=<value> ...
LABEL指令將元數據添加到圖像中。LABEL是鍵值對。要在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."
一個鏡像能夠有多個標籤。能夠在一行中指定多個標籤。在Docker 1.10以前,這下降了最終鏡像的大小,但如今再也不是這樣了。你可能仍然選擇以如下兩種方式在單個LABEL指令中指定多個標籤:
LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \ multi.label2="value2" \ other="value3"
FROM行中指定的基礎鏡像或父鏡像中的LABEL會被你的鏡像繼承,若是一個LABEL已經存在可是有不一樣的值,那麼最近使用的LABEL的值會覆蓋以前聲明的那個值。
使用docker inspect命令來查看LABEL:
"Labels": { "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" },
LABEL指令是這個指令的替代。
不描述這個指令了,不推薦用,若是你有新項目,那麼使用LABEL來代替它,沒有必要在使用這個指令。
EXPOSE <port> [<port>/<protocol>...]
EXPOSE指令通知Docker容器在運行時監聽指定的網絡端口。能夠指定端口監聽TCP仍是UDP,若是沒有指定協議,則默認爲TCP。
EXPOSE指令實際上並不發佈端口。它的功能相似於構建鏡像的人員和運行容器的人員之間的一種文檔類型,關於要發佈哪些端口。也就是說這個命令就是一個提示做用。要在運行容器時實際發佈端口,可使用docker run上的-p標誌來發布和映射一個或多個端口,或者使用-p標誌來發布全部公開的端口並將它們映射到高階端口。
默認狀況下EXPOST假設使用的TCP協議,你能夠指定其餘協議:
EXPOSE 80/udp
要在相同端口上暴露兩個端口,能夠指定兩行:
EXPOSE 80/tcp EXPOSE 80/udp
在這種狀況下,若是在docker run中使用-P,那麼TCP和UDP端口將分別公開一次。請記住,-P在主機上使用臨時的高階主機端口,所以TCP和UDP的端口將不相同。
不管EXPOSE作了怎麼樣的設置,你均可以在運行鏡像時經過-p標誌來覆蓋他們:
docker run -p 80:80/tcp -p 80:80/udp ...
要在主機系統上設置端口重定向,請參閱 using the -P flag.。docker network命令支持在容器之間建立通訊網絡,而不須要公開或發佈特定的端口,由於鏈接到docker network的容器能夠經過任何端口相互通訊。有關詳細信息,請參閱 overview of this feature。
ENV <key> <value>
ENV <key>=<value> ...
ENV指令將環境變量<key>設置爲值<value>。此值將位於構建階段中全部後續指令的環境中,也能夠內聯地替換( 詳見上文環境替換)許多指令。
ENV指令有兩種形式。第一種形式是ENV <key> <value>,它將把單個變量設置爲一個值。第一個空格以後的整個字符串將被視爲<value>—包括空白字符。該值將被解釋爲其餘環境變量,所以若是沒有轉義,引號字符將被刪除。
第二種形式,ENV <key>=<value>…,容許同時設置多個變量。注意,第二種形式在語法中使用了等號(=),而第一種形式沒有。與命令行解析同樣,引號和反斜槓可用於在值中包含空格。
例子:
ENV myName="John Doe" myDog=Rex\ The\ Dog \ myCat=fluffy
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
上面這兩個例子會在最終的鏡像中產生同樣的結果。
當從結果鏡像運行容器時,使用ENV設置的環境變量將保持不變。可使用docker inspect查看這些值,並使用docker run --env <key>=<value>更改它們。
NOTE:環境持久性可能會致使意想不到的反作用。例如,設置ENV DEBIAN_FRONTEND noninteractive可能會使基於debian的圖像上的apt-get使用者感到困惑。要設置單個命令的值,使用RUN <key>=<value> <command>。
ADD有兩種格式:
ADD [--chown=<user>:<group>] <src>... <dest>
#包含空格的路徑須要此格式 ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
NOTE--chown特性只支持用於構建Linux容器的Dockerfiles,不能用於Windows容器。因爲用戶和組全部權概念不能在Linux和Windows之間轉換,所以使用/etc/passwd和/etc/group將用戶名和組名轉換爲IDs,這限制了該特性只能用於基於Linux os的容器。
ADD指令從<src>複製新的文件、目錄或遠程文件url,並將它們添加到路徑<dest>的鏡像文件系統中。
能夠指定多個<src>資源,可是若是它們是文件或目錄,它們的路徑將被解釋爲相對於構建上下文的源。
每個<src>能夠指定通配符,這個通配符會被GO語言的 filepath.Match 提供的邏輯進行處理。好比:
ADD hom* /mydir/ # 添加全部以"hom"開頭的文件 ADD hom?.txt /mydir/ # ? 會被一個單獨的字符代替, 好比, "home.txt"
<dest>
表示一個絕對路徑,獲取是相對於WORKDIR的路徑,它用來表示資源會被拷貝到目標容器中的位置。
ADD test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/ ADD test /absoluteDir/ # adds "test" to /absoluteDir/
在添加包含特殊字符(如[和])的文件或目錄時,須要轉義那些遵循Golang規則的路徑,以防止它們被視爲匹配模式。例如,添加一個名爲arr[0].txt的文件,使用如下:
ADD arr[[]0].txt /mydir/ # copy a file named "arr[0].txt" to /mydir/
全部新建立的文件和目錄的UID和GID都是0,除非可選的--chown標誌指定了一個給定的用戶名、groupname或UID/GID組合來請求添加內容的特定全部權--chown標誌的格式容許在任何組合中使用用戶名和groupname字符串或直接整數UID和GID。提供沒有groupname的用戶名或沒有GID的UID將使用與GID相同的數字UID。若是提供了用戶名或groupname,則將使用容器的根文件系統/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標誌中使用了用戶名或組名,那麼在添加操做時構建將失敗。使用數字id不須要查找,也不依賴於容器根文件系統的內容。
在<src>是遠程文件URL的狀況下,目的地的權限爲600。若是正在檢索的遠程文件具備HTTP Last-Modified標頭,則來自該標頭的時間戳將用於設置目標文件的時間。可是,與添加過程當中處理的任何其餘文件同樣,在肯定文件是否更改以及是否應該更新緩存時,並不包括mtime。
NOTE:若是經過STDIN (docker build - < somefile)傳遞Dockerfile進行構建,則沒有構建上下文,所以Dockerfile只能包含基於URL的ADD指令。您還能夠經過STDIN (docker build - < archive.tar.gz)傳遞壓縮的歸檔文件,歸檔文件根目錄下的Dockerfile和歸檔文件的其他部分將用做構建的上下文。注:STDIN是標準輸入的意思。
NOTE:若是你的URL文件使用身份驗證進行保護,那麼您將須要在容器中使用RUN wget、RUN curl或其餘工具,由於ADD指令不支持身份驗證。
NOTE:若是<src>的內容發生了變化,那麼第一個遇到的ADD指令將使Dockerfile中的全部後續指令的緩存無效。這包括使RUN指令的緩存無效。有關更多信息,請參閱Dockerfile最佳實踐指南:Dockerfile
Best Practices guide 。
ADD指令遵循下列規則:
NOTE:目錄自己不是複製的,只是它的內容。
不管目標路徑上存在什麼
COPY有兩種格式:
COPY [--chown=<user>:<group>] <src>... <dest>
#該格式是用於路徑中有空格的狀況 COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
COPY指令基本與ADD指令差很少,差異就在ADD指令會在識別到壓縮文件後對文件進行解壓縮,COPY不會。COPY的內容就不翻譯了,之後有變化再說。
ENTRYPOINT有兩種格式:
#(exec form, 推薦的格式) ENTRYPOINT ["executable", "param1", "param2"] #(shell form) ENTRYPOINT command param1 param2
ENTRYPOINT容許你配置一個可看成可執行程序運行的容器。
例如,下面的命令啓動一個nginx容器,使用默認的內容,而且監聽80端口:
docker run -i -t --rm -p 80:80 nginx
docker run <image>的命令行參數將追加到以exec form格式執行的ENTRYPOINT指令中的全部元素以後,並將覆蓋使用CMD指定的全部元素。這容許參數被傳遞到入口點,也就是說,docker run <image> -d將把-d參數傳遞給入口點。你可使用docker run - ENTRYPOINT標誌覆蓋ENTRYPOINT指令。
shell form格式的能夠防止使用任何CMD或run命令行參數,可是有一個缺點,即你的ENTRYPOINT
將做爲/bin/sh -c的子命令啓動,該命令不傳遞信號。這意味着可執行文件不是容器的PID 1,也不會收到Unix信號,因此你的可執行文件不會收到來自docker stop <container>的終止信號。
只有最後的ENTRYPOINT
會生效。
您可使用ENTRYPOINT的exec form格式來設置至關穩定的默認命令和參數,而後使用CMD的任何一種形式來設置更可能更改的其餘默認值。
FROM ubuntu ENTRYPOINT ["top", "-b"]#相對穩定的命令和參數 CMD ["-c"]#可能會更改的參數
當你是用上面的Dockfile運行容器,你能夠看到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
你可使用docker stop測試優雅地請求top關閉。
下面的Dockerfile展現瞭如何使用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"]
若是須要爲單個可執行文件編寫啓動腳本,可使用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
NOTE:您可使用--ENTRYPOINT覆蓋ENTRYPOINT設置,但這隻能將二進制文件設置爲exec(不使用sh -c)。
NOTE:exec form的格式的命令會被解析爲json數組,意味着你必須將數組的元素用雙引號括住而不是單引號。
NOTE:與shell form不一樣,exec form不調用命令shell。這意味着不會發生正常的shell處理。例如,ENTRYPOINT ["echo", "$HOME"]不會對$HOME執行變量替換。若是您想要shell處理,那麼要麼使用shell form,要麼直接執行shell,例如:ENTRYPOINT ["sh", "-c", "echo $HOME"]。當使用exec form並直接執行shell時(如shell form的狀況),執行環境變量擴展的是shell,而不是docker。
之後再說
CMD和ENTRYPOINT指令都定義了在運行容器時執行什麼命令。這裏有一些規則描述他們的協做關係。
下表顯示了對不一樣的ENTRYPOINT/ CMD組合執行的命令:
無ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [「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 |
NOTE:若是CMD是從基礎鏡像定義的,那麼設置ENTRYPOINT將把CMD重置爲空值。在這種狀況下,必須在當前鏡像中定義CMD才能得到一個值。
VOLUME ["/data"]
VOLUME指令使用指定的名稱建立一個掛載點,並將其標記爲持有來自本機主機或其餘容器的外部掛載卷。該值能夠是一個JSON數組,VOLUME ["/var/log/"],也能夠是一個有多個參數的普通字符串,好比VOLUME /var/log或VOLUME /var/log/ var/db。有關經過Docker客戶端的更多信息/示例和安裝說明,請參閱經過卷文檔共享目錄: Share Directories via Volumes.。
docker run命令使用存在於基本鏡像中指定位置的任何數據初始化新建立的卷。例如,考慮如下Dockerfile片斷:
FROM ubuntu RUN mkdir /myvol RUN echo "hello world" > /myvol/greeting VOLUME /myvol
這個Dockerfile會產生一個鏡像,這個鏡像會致使docker運行時在/myvol處建立一個新的掛載點,並將greeting文件複製到新建立的卷中。
關於Dockerfile中的卷,請記住如下幾點。
後續翻譯
WORKDIR /path/to/workdir
WORKDIR指令爲Dockerfile中的任何RUN、CMD、ENTRYPOINT、COPY和ADD指令設置工做目錄。若是WORKDIR不存在,即便在後續的Dockerfile指令中不使用它,也會建立它。
WORKDIR指令能夠在Dockerfile中屢次使用。若是提供了一個相對路徑,它將相對於前面的WORKDIR指令的路徑。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
這個Dockerfile中的最後一個pwd命令的輸出是/a/b/c。
WORKDIR指令能夠解析以前使用ENV設置的環境變量。只能使用在Dockerfile中顯式設置的環境變量。例如:
ENV DIRPATH /path WORKDIR $DIRPATH/$DIRNAME RUN pwd
這個Dockerfile中的最後一個pwd命令的輸出是/path/$DIRNAME