偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

編寫(xiě)Dockerfiles的優(yōu)秀實(shí)踐

開(kāi)發(fā) 前端
本文檔介紹了構(gòu)建高效鏡像的優(yōu)秀實(shí)踐和方法。Docker通過(guò)從Dockerfile(按順序包含構(gòu)建給定鏡像所需的所有命令的文本文件)讀取命令來(lái)自動(dòng)構(gòu)建鏡像。Dockerfile遵循特定的格式和一組命令,您可以在Dockerfile reference中找到這些命令。

本文檔介紹了構(gòu)建高效鏡像的優(yōu)秀實(shí)踐和方法。

[[276943]]

Docker通過(guò)從Dockerfile(按順序包含構(gòu)建給定鏡像所需的所有命令的文本文件)讀取命令來(lái)自動(dòng)構(gòu)建鏡像。Dockerfile遵循特定的格式和一組命令,您可以在Dockerfile reference中找到這些命令。

Docker鏡像由只讀層組成,每個(gè)只讀層表示Dockerfile指令。這些層被堆疊起來(lái),每一層都是前一層變化的增量。考慮一下這個(gè)Dockerfile: 

  1. FROM ubuntu:18.04 
  2. COPY . /app 
  3. RUN make /app 
  4. CMD python /app/app.py 

每一個(gè)指令會(huì)創(chuàng)建一個(gè)層:

  • FROM從docker image ubuntu:18.04 創(chuàng)建層 。
  • COPY從Docker 客戶端添加文件到當(dāng)前目錄。
  • RUN使用make 命令構(gòu)建應(yīng)用。
  • CMD指定在容器里運(yùn)行的命令。

當(dāng)您運(yùn)行一個(gè)鏡像并生成一個(gè)容器時(shí),您將在底層之上添加一個(gè)新的可寫(xiě)層("容器層")。對(duì)正在運(yùn)行的容器所做的所有更改,例如寫(xiě)入新文件、修改現(xiàn)有文件和刪除文件,都被寫(xiě)入這個(gè)可寫(xiě)容器層。

通用概覽和建議

創(chuàng)建臨時(shí)容器

Dockerfile定義的鏡像應(yīng)該生成盡可能"短暫"的容器。所謂"臨時(shí)性",是指容器可以停止和銷毀,然后用絕對(duì)最小的設(shè)置和配置重新構(gòu)建和替換。

理解構(gòu)建上下文

當(dāng)您發(fā)出docker構(gòu)建命令時(shí),當(dāng)前工作目錄稱為構(gòu)建上下文。默認(rèn)情況下, Dockerfile在當(dāng)前目錄,但是您可以使用file標(biāo)志(-f)指定一個(gè)不同的位置。無(wú)論Dockerfile實(shí)際位于何處,當(dāng)前目錄中文件和目錄的所有遞歸內(nèi)容都作為構(gòu)建上下文發(fā)送到Docker守護(hù)進(jìn)程。

構(gòu)建上下文:

為構(gòu)建上下文創(chuàng)建一個(gè)目錄并將cd放入其中。將"hello"寫(xiě)入一個(gè)名為hello的文本文件中,并創(chuàng)建一個(gè)運(yùn)行cat的Dockerfile。從構(gòu)建上下文中構(gòu)建鏡像(.): 

  1. mkdir myproject && cd myproject 
  2. echo "hello" > hello 
  3. echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > Dockerfile 
  4. docker build -t helloapp:v1 . 

將Dockerfile和hello移到單獨(dú)的目錄中,并構(gòu)建鏡像的第二個(gè)版本(不依賴于上一個(gè)構(gòu)建的緩存)。使用-f指向Dockerfile并指定構(gòu)建上下文的目錄: 

  1. mkdir -p dockerfiles context 
  2. mv Dockerfile dockerfiles && mv hello context 
  3. docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context 

無(wú)意中包含了構(gòu)建鏡像所不需要的文件,會(huì)導(dǎo)致構(gòu)建上下文和鏡像大小變大。這可以增加構(gòu)建鏡像的時(shí)間、拖放鏡像的時(shí)間和容器運(yùn)行時(shí)大小。要查看構(gòu)建上下文的大小,請(qǐng)?jiān)跇?gòu)建Dockerfile時(shí)查看類似這樣的消息: 

  1. Sending build context to Docker daemon 187.8MB 

通過(guò)stdin使用Dockerfile 管道

Docker能夠通過(guò)使用本地或遠(yuǎn)程構(gòu)建上下文通過(guò)stdin管道傳輸Dockerfile來(lái)構(gòu)建鏡像。通過(guò)stdin管道傳輸Dockerfile對(duì)于執(zhí)行一次性構(gòu)建非常有用,不需要將Dockerfile寫(xiě)入磁盤(pán),或者在生成Dockerfile的情況下,不應(yīng)該在生成后保存Dockerfile。

為了方便起見(jiàn),本節(jié)中的示例使用here文檔【http://tldp.org/LDP/abs/html/here-docs.html】,但是可以使用在stdin上提供Dockerfile的任何方法。

例如: 下面的命令是等價(jià)的: 

  1. echo -e 'FROM busybox\nRUN echo "hello world"' | docker build - 
  2. docker build -<<EOF 
  3. FROM busybox 
  4. RUN echo "hello world" 
  5. EOF 

您可以用您喜歡的方法或者最適合您用例的方法來(lái)替代這些例子。

使用STDIN中的DOCKERFILE構(gòu)建鏡像,而不發(fā)送構(gòu)建上下文

使用此語(yǔ)法可以從stdin中用Dockerfile構(gòu)建映像,而不需要發(fā)送額外的文件作為構(gòu)建上下文。連字符(-)占據(jù)路徑的位置,指示Docker從stdin而不是目錄中讀取構(gòu)建上下文(其中只包含Dockerfile): 

  1. docker build [OPTIONS] – 

下面的示例使用通過(guò)stdin傳遞的Dockerfile構(gòu)建一個(gè)鏡像。沒(méi)有文件作為構(gòu)建上下文發(fā)送到守護(hù)進(jìn)程。 

  1. docker build -t myimage:latest -<<EOF 
  2. FROM busybox 
  3. RUN echo "hello world" 
  4. EOF 

在Dockerfile不需要將文件復(fù)制到鏡像中的情況下,省略構(gòu)建上下文是非常有用的,并且可以提高構(gòu)建速度,因?yàn)闆](méi)有文件被發(fā)送到守護(hù)進(jìn)程。

注意:如果使用這種語(yǔ)法,嘗試構(gòu)建使用COPY或ADD的Dockerfile將會(huì)失敗。下面的例子說(shuō)明了這一點(diǎn): 

  1. create a directory to work in 
  2. mkdir example 
  3. cd example 
  4. create an example file 
  5. touch somefile.txt 
  6. docker build -t myimage:latest -<<EOF 
  7. FROM busybox 
  8. COPY somefile.txt . 
  9. RUN cat /somefile.txt 
  10. EOF 
  11. # observe that the build fails 
  12. ... 
  13. Step 2/3 : COPY somefile.txt . 
  14. COPY failed: stat /var/lib/docker/tmp/docker-builder249218248/somefile.txt: no such file or directory 

使用STDIN中的DOCKERFILE從本地構(gòu)建上下文構(gòu)建

使用此語(yǔ)法可以使用本地文件系統(tǒng)上的文件構(gòu)建映像,但要使用stdin中的Dockerfile。語(yǔ)法使用(-f或--file)選項(xiàng)指定要使用的Dockerfile,使用連字符(-)作為文件名,指示Docker從stdin中讀取Dockerfile: 

  1. docker build [OPTIONS] -f- PATH 

下面這個(gè)例子我們用當(dāng)前目錄作為構(gòu)建上下文,并且構(gòu)建鏡像用到的Dockerfile是通過(guò)stdin傳進(jìn)去的。例子在這里【http://tldp.org/LDP/abs/html/here-docs.html】 

  1. create a directory to work in 
  2. mkdir example 
  3. cd example 
  4. create an example file 
  5. touch somefile.txt 
  6. # build an image using the current directory as context, and a Dockerfile passed through stdin 
  7. docker build -t myimage:latest -f- . <<EOF 
  8. FROM busybox 
  9. COPY somefile.txt . 
  10. RUN cat /somefile.txt 
  11. EOF 

使用STDIN中的DOCKERFILE從遠(yuǎn)程構(gòu)建上下文構(gòu)建

使用此語(yǔ)法,使用來(lái)自遠(yuǎn)程git存儲(chǔ)庫(kù)的文件(使用來(lái)自stdin的Dockerfile)構(gòu)建一個(gè)鏡像。語(yǔ)法使用(-f或--file)選項(xiàng)指定要使用的Dockerfile,使用連字符(-)作為文件名,指示Docker從stdin中讀取Dockerfile: 

  1. docker build [OPTIONS] -f- PATH 

當(dāng)您希望從不包含Dockerfile的存儲(chǔ)庫(kù)構(gòu)建鏡像,或者希望使用自定義Dockerfile構(gòu)建鏡像,而不需要維護(hù)存儲(chǔ)庫(kù)的分支時(shí),這種語(yǔ)法非常有用。

下面的示例使用來(lái)自stdin的Dockerfile構(gòu)建一個(gè)鏡像,并添加hello.c文件從git 倉(cāng)里庫(kù)【https://github.com/docker-library/hello-world】 

  1. docker build -t myimage:latest -f- https://github.com/docker-library/hello-world.git <<EOF 
  2. FROM busybox 
  3. COPY hello.c . 
  4. EOF 
  5. Note: 

當(dāng)使用遠(yuǎn)程Git存儲(chǔ)庫(kù)作為構(gòu)建上下文構(gòu)建鏡像時(shí),Docker在本地 執(zhí)行存儲(chǔ)庫(kù)的Git clone,并將這些文件作為構(gòu)建上下文發(fā)送給守護(hù)進(jìn)程。該特性要求git安裝在運(yùn)行docker構(gòu)建命令的主機(jī)上。

使用.dockerignore忽略不需要的文件

要排除與構(gòu)建不相關(guān)的文件(不需要調(diào)整資源庫(kù)),請(qǐng)使用.dockerignore文件。該文件支持類似于.gitignore文件的排除模式。 更多信息請(qǐng)查看【https://docs.docker.com/engine/reference/builder/#dockerignore-file】

使用多級(jí)構(gòu)建

多階段構(gòu)建允許您大幅度減小最終映像的大小,而不必費(fèi)力地減少中間層和文件的數(shù)量。

因?yàn)殓R像是在構(gòu)建過(guò)程的最后階段構(gòu)建的,所以可以通過(guò)利用構(gòu)建緩存最小化鏡像層?!緃ttps://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache】

例如,如果您的構(gòu)建包含多個(gè)層,您可以將它們排序從更改頻率較低的層(以確保構(gòu)建緩存可重用)到更改頻率較高的層:

  • 安裝構(gòu)建應(yīng)用程序所需的工具
  • 安裝或者更改依賴的庫(kù)
  • 生成應(yīng)用

下面是一個(gè)構(gòu)建golang應(yīng)用的Dockerfile 文件: 

  1. FROM golang:1.11-alpine AS build 
  2. # Install tools required for project 
  3. # Run `docker build --no-cache .` to update dependencies 
  4. RUN apk add --no-cache git 
  5. RUN go get github.com/golang/dep/cmd/dep 
  6. # List project dependencies with Gopkg.toml and Gopkg.lock 
  7. # These layers are only re-built when Gopkg files are updated 
  8. COPY Gopkg.lock Gopkg.toml /go/src/project/ 
  9. WORKDIR /go/src/project/ 
  10. # Install library dependencies 
  11. RUN dep ensure -vendor-only 
  12. # Copy the entire project and build it 
  13. # This layer is rebuilt when a file changes in the project directory 
  14. COPY . /go/src/project/ 
  15. RUN go build -o /bin/project 
  16. # This results in a single layer image 
  17. FROM scratch 
  18. COPY --from=build /bin/project /bin/project 
  19. ENTRYPOINT ["/bin/project"
  20. CMD ["--help"

不安裝不必要的包

為了減少?gòu)?fù)雜、依賴、文件尺寸和構(gòu)建時(shí)間,避免安裝額外的和不需要的包。一個(gè)高水準(zhǔn)的Dockerfile必須要注意這些細(xì)節(jié)。

解耦

每個(gè)容器應(yīng)該只有一個(gè)關(guān)注點(diǎn)。將應(yīng)用程序解耦到多個(gè)容器可以更容易地水平伸縮和重用容器。例如,web應(yīng)用程序棧可能由三個(gè)獨(dú)立的容器組成,每個(gè)容器都有自己獨(dú)特的鏡像,以解耦的方式管理web應(yīng)用程序、數(shù)據(jù)庫(kù)和內(nèi)存緩存。

限制每個(gè)容器只運(yùn)行一個(gè)進(jìn)程是一個(gè)很好的經(jīng)驗(yàn)法則。但是,這并不準(zhǔn)確。因?yàn)楹芏鄳?yīng)用都會(huì)有很多進(jìn)程。比如,Celery就會(huì)有很多worker進(jìn)程。Apache每個(gè)request就會(huì)有一個(gè)進(jìn)程。容器自己也有init進(jìn)程。

所以,用你的嚴(yán)謹(jǐn)和專業(yè)來(lái)保持容器盡可能的干凈和模塊化。如果容器彼此依賴,可以使用Docker容器網(wǎng)絡(luò)來(lái)確保這些容器能夠通信。

保存最小數(shù)量的層

在老一點(diǎn)的docker版本中,保持層數(shù)的最少是非常重要的,因?yàn)橐WC性能。

為了減少這樣的限制,增加了一下的特性:

  • 只有指令RUN,COPY,ADD創(chuàng)建層。其他指令創(chuàng)建臨時(shí)中間鏡像,并且不增加構(gòu)建的大小
  • 在可能的情況下,使用多階段構(gòu)建,并且只將您需要的工件復(fù)制到最終鏡像中。這允許您在中間構(gòu)建階段包含工具和調(diào)試信息,而不需要增加最終映像的大小。

命令行參數(shù)排序

只要方便,可以通過(guò)對(duì)多行參數(shù)進(jìn)行字母數(shù)字排序來(lái)簡(jiǎn)化后面的更改。這有助于避免包的重復(fù),并使列表更容易更新。這也使得PRs更容易閱讀和審查。在反斜杠(\)之前添加空格也有幫助。

下面是一個(gè)參數(shù)排列的例子: 

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

利用構(gòu)建緩存

在構(gòu)建映像時(shí),Docker逐步讀取 Dockerfile中的指令,并且按照順序執(zhí)行。在檢查每條指令時(shí),Docker會(huì)在緩存中查找可以重用的現(xiàn)有鏡像,而不是創(chuàng)建一個(gè)新的(重復(fù)的)鏡像。

如果,你就是不想用cache,可以使用—no-cache=true來(lái)關(guān)閉在執(zhí)行docker build的時(shí)候。當(dāng)然,如果你開(kāi)啟了cacha,docker 在構(gòu)建是找到緩存,如果沒(méi)有匹配到,就創(chuàng)建新的鏡像。 Docker遵循的基本規(guī)則如下:

  • 從緩存中已經(jīng)存在的父鏡像開(kāi)始,將下一條指令與從該基本鏡像派生的所有子鏡像進(jìn)行比較,看看其中一條是否使用完全相同的指令構(gòu)建。否則,緩存將無(wú)效
  • 在大多數(shù)情況下,只需將Dockerfile中的指令與其中一個(gè)子鏡像進(jìn)行比較就足夠了。然而,某些指示需要更多的檢查和解釋。
  • 對(duì)于ADD和COPY指令,將檢查鏡像中文件的內(nèi)容,并且檢查和校驗(yàn)每個(gè)文件 。最后修改時(shí)間和最后訪問(wèn)時(shí)間不會(huì)被校驗(yàn)。在緩存查找期間,將校驗(yàn)和與現(xiàn)有鏡像中的校驗(yàn)和進(jìn)行比較。如果文件中有任何更改,比如內(nèi)容和元數(shù)據(jù),那么緩存將無(wú)效。
  • 除了ADD和COPY命令外,緩存檢查不會(huì)查看容器中的文件來(lái)確定緩存匹配。例如,在處理RUN apt-get -y update命令時(shí),不會(huì)檢查容器中更新的文件,以確定是否存在緩存命中。在這種情況下,僅使用命令字符串本身來(lái)查找匹配項(xiàng)。

一旦緩存失效,所有后續(xù)的Dockerfile命令都會(huì)生成新的鏡像,而緩存則不被使用。

Dockerfile 指令

這些建議旨在幫助您創(chuàng)建一個(gè)高效且可維護(hù)的Dockerfile。

FROM

只要可能,使用當(dāng)前的官方鏡像作為你的鏡像的基礎(chǔ)鏡像。我們推薦Alpine鏡像【https://hub.docker.com/_/alpine/】,因?yàn)榫帉?xiě)這個(gè)鏡像是非常嚴(yán)格的,并且很小(目前小于5 MB),但仍然是一個(gè)完整的Linux發(fā)行版。

LABEL

您可以將標(biāo)簽添加到鏡像中,以幫助按項(xiàng)目組織鏡像、記錄許可信息、幫助實(shí)現(xiàn)自動(dòng)化或出于其他原因。對(duì)于每個(gè)標(biāo)簽,用LABEL標(biāo)記開(kāi)始,用一個(gè)或者多個(gè)鍵值對(duì) 。下面的示例顯示了不同的可接受格式。解釋性注釋是內(nèi)聯(lián)的。

必須引用帶空格的字符串,否則必須轉(zhuǎn)義空格。內(nèi)部引號(hào)字符(")也必須轉(zhuǎn)義。

  1. Set one or more individual labels 
  2. LABEL com.example.version="0.0.1-beta" 
  3. LABEL vendor1="ACME Incorporated" 
  4. LABEL vendor2=ZENITH\ Incorporated 
  5. LABEL com.example.release-date="2015-02-12" 
  6. LABEL com.example.version.is-production="" 

一個(gè)鏡像可以有多個(gè)標(biāo)簽。在Docker 1.10之前,建議將所有標(biāo)簽合并到一個(gè)標(biāo)簽指令中,以防止創(chuàng)建額外的層。這不再需要,但是仍然支持組合標(biāo)簽。 

  1. Set multiple labels on one line 
  2. LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12" 

上面的這個(gè)例子還可以寫(xiě)成下面這樣: 

  1. Set multiple labels at once, using line-continuation characters to break long lines 
  2. LABEL vendor=ACME\ Incorporated \ 
  3. com.example.is-beta= \ 
  4. com.example.is-production="" \ 
  5. com.example.version="0.0.1-beta" \ 
  6. com.example.release-date="2015-02-12" 
  7. RUN 

使用反斜杠(\) 來(lái)分隔獨(dú)立的命令行可以使RUN命令更有可讀性、易于維護(hù)。

APT-GET

Apt-get 命令是很多Docker經(jīng)常使用的命令。因?yàn)?,他是安裝各種包必須使用的命令。

避免運(yùn)行apt-get升級(jí)和distl -upgrade,因?yàn)閬?lái)自父鏡像的許多"基本"包無(wú)法在非特權(quán)容器中升級(jí)。如果父鏡像中包含的包過(guò)期了,請(qǐng)聯(lián)系它的維護(hù)人員。如果您知道有一個(gè)特定的包foo需要更新,那么使用apt-get install -y foo自動(dòng)更新。

始終將RUN apt-get update與apt-get install組合在同一個(gè)RUN語(yǔ)句中。例如: 

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

在RUN語(yǔ)句中單獨(dú)使用apt-get update會(huì)導(dǎo)致緩存問(wèn)題,隨后的apt-get安裝指令會(huì)失敗。例如,假設(shè)您有一個(gè)Dockerfile: 

  1. FROM ubuntu:18.04 
  2. RUN apt-get update 
  3. RUN apt-get install -y curl 

當(dāng)構(gòu)建完鏡像后,所有的層都已經(jīng)被緩存了,假設(shè)之后你修改了apt-get install 增加了其他的包: 

  1. FROM ubuntu:18.04 
  2. RUN apt-get update 
  3. RUN apt-get install -y curl nginx 

Docker將初始指令和修改后的指令視為相同的,并重用前面步驟中的緩存。因此,apt-get更新不會(huì)執(zhí)行,因?yàn)闃?gòu)建使用緩存的版本。由于apt-get更新沒(méi)有運(yùn)行,您的構(gòu)建可能會(huì)得到一個(gè)過(guò)時(shí)版本的curl和nginx包。

使用RUN apt-get update && apt-get install -y確保您的Dockerfile安裝最新的包版本,而無(wú)需進(jìn)一步編碼或手動(dòng)干預(yù)。這種技術(shù)稱為"緩存破壞"。還可以通過(guò)指定包版本來(lái)實(shí)現(xiàn)緩存崩潰。這就是所謂的版本固定,例如: 

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

版本固定強(qiáng)制構(gòu)建以檢索特定版本,而不管緩存中的內(nèi)容是什么。這種技術(shù)還可以減少由于所需包中的意外更改而導(dǎo)致的故障。

下面是一個(gè)格式良好的運(yùn)行指令,演示了所有apt-get 的優(yōu)秀實(shí)踐。 

  1. RUN apt-get update && apt-get install -y \ 
  2. aufs-tools \ 
  3. automake \ 
  4. build-essential \ 
  5. curl \ 
  6. dpkg-sig \ 
  7. libcap-dev \ 
  8. libsqlite3-dev \ 
  9. mercurial \ 
  10. reprepro \ 
  11. ruby1.9.1 \ 
  12. ruby1.9.1-dev \ 
  13. s3cmd=1.1.* \ 
  14. && rm -rf /var/lib/apt/lists/* 

s3cmd指定了一個(gè)新的版本。如果之前的鏡像安裝的是一個(gè)舊的版本。apt-get update 會(huì)導(dǎo)致緩存失效,從而安裝新的版本。

在這樣的條件下,當(dāng)你清除apt緩存并且移除/var/lib/apt/lists 目錄,來(lái)減小文件尺寸。當(dāng)RUN 聲明以apt-get update開(kāi)始,在執(zhí)行apt-get install的時(shí)候,緩存依然會(huì)被刷新。

注:

Debian和ubuntu的官方鏡像會(huì)自動(dòng)運(yùn)行apt-get clecn命令。所以不需要顯示調(diào)用。

使用管道

有些運(yùn)行命令依賴于使用管道字符(|)將一個(gè)命令的輸出管道到另一個(gè)命令的能力,如下例所示: 

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

Docker使用/bin/sh -c解釋器執(zhí)行這些命令,解釋器只計(jì)算管道中最后一個(gè)操作的退出代碼來(lái)確定是否成功。在上面的示例中,只要wc -l命令成功,即使wget命令失敗,這個(gè)構(gòu)建步驟就會(huì)成功并生成一個(gè)新映像。

如果您希望命令在管道中的任何階段由于錯(cuò)誤而失敗,請(qǐng)預(yù)先設(shè)置-o pipefail &&,以確保意外錯(cuò)誤防止構(gòu)建意外成功。例如: 

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

注:

不是所有的shell都支持 –o pipfail 選項(xiàng)

在基于debian的鏡像上使用dash shell的情況下,可以考慮使用exec形式的RUN顯式地選擇一個(gè)支持pipefail選項(xiàng)的shell。例如: 

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

CMD

CMD指令應(yīng)該用于運(yùn)行鏡像所包含的軟件,以及任何參數(shù)。CMD幾乎總是以CMD["executable"、"param1"、"param2"…]的形式使用。因此,如果鏡像是用于服務(wù)的,比如Apache和Rails,您將運(yùn)行類似CMD ["apache2","-DFOREGROUND "]的東西。實(shí)際上,對(duì)于任何基于服務(wù)的鏡像,都推薦使用這種形式的指令。

在大多數(shù)其他情況下,應(yīng)該為CMD提供一個(gè)交互式shell,如bash、python和perl。例如,CMD ["perl"、"-de0"], CMD ("python"),或CMD ("php","-a")。使用這種形式意味著,當(dāng)您執(zhí)行像docker run - python這樣的東西時(shí),您將被放入一個(gè)可用的shell中,準(zhǔn)備就緒。CMD應(yīng)該很少與ENTRYPOINT一起以CMD ["param", "param"]的方式使用,除非您和您的預(yù)期用戶已經(jīng)非常熟悉ENTRYPOINT的工作方式。

EXPOSE

EXPOSE指令指示容器監(jiān)聽(tīng)連接的端口。因此,您應(yīng)該為您的應(yīng)用程序使用公共的、傳統(tǒng)的端口。例如,包含Apache web服務(wù)器的鏡像使用 80端口,而包含MongoDB的映像將使用 27017 端口,以此類推。

對(duì)于外部訪問(wèn),用戶可以使用一個(gè)標(biāo)志執(zhí)行docker run,該標(biāo)志指示如何將指定的端口映射到他們選擇的端口。對(duì)于容器鏈接,Docker為從接收容器返回到源容器的路徑提供了環(huán)境變量(即MYSQL_PORT_3306_TCP)。

ENV

為了使新軟件更容易運(yùn)行,可以使用ENV更新容器安裝的軟件的PATH環(huán)境變量。例如,ENV PATH /usr/local/nginx/bin:$PATH確保CMD ["nginx"]正常工作。

ENV指令對(duì)于提供特定于您希望封裝的服務(wù)的所需環(huán)境變量也很有用,比如Postgres的PGDATA。

最后,ENV還可以用來(lái)設(shè)置常用的版本號(hào),以便更容易維護(hù)版本,如下例所示: 

  1. ENV PG_MAJOR 9.3 
  2. ENV PG_VERSION 9.3.4 
  3. RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && … 
  4. ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH 

類似于在程序中使用常量變量(而不是硬編碼值),這種方法允許您更改單個(gè)ENV指令,從而自動(dòng)地在容器中神奇地彈出軟件版本。

每個(gè)ENV行創(chuàng)建一個(gè)新的中間層,就像RUN命令一樣。這意味著,即使您在未來(lái)的層中取消了環(huán)境變量的設(shè)置,它仍然保留在這個(gè)層中,并且它的值可以被轉(zhuǎn)儲(chǔ)。您可以通過(guò)創(chuàng)建一個(gè)Dockerfile(如下所示)來(lái)測(cè)試它,然后構(gòu)建它。 

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

為了防止這種情況發(fā)生,并真正取消對(duì)環(huán)境變量的設(shè)置,可以使用一個(gè)帶有shell命令的RUN命令,在一個(gè)單層中設(shè)置、使用和取消對(duì)變量的設(shè)置。你可以用;和& &。如果使用第二種方法,并且其中一個(gè)命令失敗,docker構(gòu)建也會(huì)失敗。這通常是個(gè)好主意。使用\作為L(zhǎng)inux Dockerfiles的行延續(xù)字符可以提高可讀性。您還可以將所有命令放入shell腳本中,并讓RUN命令運(yùn)行該shell腳本。 

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

ADD 或者COPY

雖然ADD和COPY在功能上是相似的,但是一般來(lái)說(shuō),COPY是首選的。這是因?yàn)樗華DD更透明,COPY只支持將本地文件基本復(fù)制到容器中,而ADD的一些特性(比如只本地的tar提取和遠(yuǎn)程URL支持)不是很有效。因此,ADD的最佳用途是將本地tar文件自動(dòng)提取到映像中,如ADD rootfs.tar.xz / 。

如果有多個(gè)Dockerfile步驟使用與上下文不同的文件,請(qǐng)分別復(fù)制它們,而不是一次全部復(fù)制。這確保只有在特定需要的文件發(fā)生更改時(shí),每個(gè)步驟的構(gòu)建緩存才會(huì)失效(強(qiáng)制重新運(yùn)行該步驟)。

例如: 

  1. COPY requirements.txt /tmp/ 
  2. RUN pip install --requirement /tmp/requirements.txt 
  3. COPY . /tmp/ 

將COPY . /tmp/放到RUN前面,會(huì)使緩存失效???

由于鏡像的大小很重要,因此強(qiáng)烈反對(duì)使用ADD從遠(yuǎn)程url獲取包;您應(yīng)該使用curl或wget來(lái)代替。這樣,你可以刪除你不再需要的文件后,他們已經(jīng)被提取出來(lái),你不需要添加另一層在您的鏡像。例如,你應(yīng)該避免做以下事情: 

  1. ADD http://example.com/big.tar.xz /usr/src/things/ 
  2. RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things 
  3. RUN make -C /usr/src/things all 
  4. 我們用下面的命令取代: 
  5. RUN mkdir -p /usr/src/things \ 
  6. && curl -SL http://example.com/big.tar.xz \ 
  7. | tar -xJC /usr/src/things \ 
  8. && make -C /usr/src/things all 

如果不需要提取tar (文件、目錄)的話,應(yīng)該始終使用COPY。

ENTRYPOINT

ENTRYPOINT的最佳用法是設(shè)置鏡像的主命令,允許像運(yùn)行該命令一樣運(yùn)行該鏡像(然后使用CMD作為默認(rèn)標(biāo)志)。

讓我們從命令行工具s3cmd的鏡像示例開(kāi)始:

ENTRYPOINT ["s3cmd"]

CMD ["--help"]

現(xiàn)在,這個(gè)鏡像可以像這樣運(yùn)行: 

  1. $ docker run s3cmd 

也可以傳參數(shù)執(zhí)行: 

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

這很有用,因?yàn)殓R像的名字可以同時(shí)作為對(duì)二進(jìn)制文件的引用,如上面的命令所示。

ENTRYPOINT指令也可以與helper腳本結(jié)合使用,允許它以類似于上面命令的方式運(yùn)行,即使在啟動(dòng)工具時(shí)可能需要不止一個(gè)步驟。

例如,Postgres官方鏡像使用以下腳本作為其入口點(diǎn): 

  1. #!/bin/bash 
  2. set -e 
  3. if [ "$1" = 'postgres' ]; then 
  4. chown -R postgres "$PGDATA" 
  5. if [ -z "$(ls -A "$PGDATA")" ]; then 
  6. gosu postgres initdb 
  7. fi 
  8. exec gosu postgres "$@" 
  9. fi 
  10. exec "$@" 

注:

設(shè)置應(yīng)用的PID為1,這樣,PG會(huì)結(jié)構(gòu)linux的任何信號(hào)。

helper腳本被復(fù)制到容器中,并在容器開(kāi)始時(shí)通過(guò)ENTRYPOINT運(yùn)行: 

  1. COPY ./docker-entrypoint.sh / 
  2. ENTRYPOINT ["/docker-entrypoint.sh"
  3. CMD ["postgres"

這個(gè)腳本允許用戶以多種方式與Postgres交互。

它可以簡(jiǎn)單地啟動(dòng)Postgres: 

  1. $ docker run postgres 

或者,它可以用來(lái)運(yùn)行Postgres并將參數(shù)傳遞給服務(wù)器: 

  1. $ docker run postgres postgres –help 

最后,它也可以用來(lái)啟動(dòng)一個(gè)完全不同的工具,如Bash: 

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

VOLUME

卷指令應(yīng)該用于公開(kāi)由docker容器創(chuàng)建的任何數(shù)據(jù)庫(kù)存儲(chǔ)區(qū)域、配置存儲(chǔ)或文件/文件夾。強(qiáng)烈建議對(duì)鏡像的任何可變和或用戶可服務(wù)的部分使用VOLUME。

USER

如果服務(wù)可以在沒(méi)有特權(quán)的情況下運(yùn)行,請(qǐng)使用USER將其更改為非root用戶。首先在Dockerfile中創(chuàng)建用戶和組,使用類似于RUN groupadd -r postgres && useradd——no-log-init -r -g postgres postgres的東西。

鏡像中的用戶和組被分配一個(gè)不確定的UID/GID,因?yàn)?quot;下一個(gè)"UID/GID被分配,而不考慮鏡像的重建。因此,如果它是必須要使用的,您應(yīng)該分配一個(gè)顯式的UID/GID。

由于Go archive/tar包在處理稀疏文件時(shí)存在一個(gè)未解決的bug,試圖在Docker容器中創(chuàng)建一個(gè)UID非常大的用戶可能會(huì)導(dǎo)致磁盤(pán)耗盡,因?yàn)槿萜鲗又械?var/log/faillog中填充了NULL(\0)字符。一個(gè)解決方案是將——no-log-init標(biāo)志傳遞給useradd。Debian/Ubuntu adduser包裝器不支持這個(gè)標(biāo)志。

避免安裝或使用sudo,因?yàn)樗哂胁豢深A(yù)知的TTY和信號(hào)轉(zhuǎn)發(fā)行為,可能會(huì)導(dǎo)致問(wèn)題。如果您絕對(duì)需要類似于sudo的功能,比如將守護(hù)進(jìn)程初始化為根進(jìn)程,但以非根進(jìn)程的形式運(yùn)行它,那么可以考慮使用"gosu"。

最后,為了減少層次和復(fù)雜性,避免頻繁地來(lái)回切換用戶。

WORKER

為了清晰和可靠,您應(yīng)該始終為您的WORKDIR使用絕對(duì)路徑。此外,您應(yīng)該使用WORKDIR,而不是像RUN cd…&& do-something這樣的指令,這些指令很難閱讀、排除故障和維護(hù)。

ONBUILD

ONBUILD命令在當(dāng)前Dockerfile構(gòu)建完成后執(zhí)行。ONBUILD在從當(dāng)前鏡像派生的任何子鏡像中執(zhí)行。將ONBUILD命令看作是父Dockerfile給子Dockerfile的一條指令。

Docker構(gòu)建在子Dockerfile中的任何命令之前執(zhí)行ONBUILD命令。

ONBUILD對(duì)于將從給定鏡像構(gòu)建的鏡像非常有用。例如,您可以對(duì)一個(gè)語(yǔ)言堆棧鏡像使用ONBUILD,該鏡像可以在Dockerfile中構(gòu)建用該語(yǔ)言編寫(xiě)的任意用戶軟件,正如您可以在Ruby的ONBUILD變體中看到的那樣。

使用ONBUILD構(gòu)建的鏡像應(yīng)該有一個(gè)單獨(dú)的標(biāo)記,例如:ruby:1.9-onbuild或ruby:2.0-onbuild。

在ONBUILD中添加或復(fù)制時(shí)要小心。如果新構(gòu)建的上下文缺少正在添加的資源,則"onbuild"鏡像將災(zāi)難性地失敗。如上面建議的那樣,添加一個(gè)單獨(dú)的標(biāo)記,通過(guò)允許Dockerfile作者做出選擇,可以幫助緩解這種情況。

 

責(zé)任編輯:華軒 來(lái)源: 今日頭條
相關(guān)推薦

2023-07-04 15:56:08

DevOps開(kāi)發(fā)測(cè)試

2020-06-01 09:40:06

開(kāi)發(fā)ReactTypeScript

2022-08-19 09:01:59

ReactTS類型

2024-01-15 08:00:00

開(kāi)發(fā)API文檔集成

2023-01-27 14:53:03

2021-12-04 23:10:02

Java代碼開(kāi)發(fā)

2020-02-25 20:55:20

JavaScript開(kāi)發(fā) 技巧

2023-04-14 08:10:59

asyncawait

2022-08-25 11:38:38

GolangDockerfile

2020-03-09 14:10:48

代碼開(kāi)發(fā)工具

2021-08-17 15:00:10

BEC攻擊網(wǎng)絡(luò)攻擊郵件安全

2021-07-06 14:17:16

MLOps機(jī)器學(xué)習(xí)AI

2022-12-21 08:20:01

2021-04-15 08:08:48

微前端Web開(kāi)發(fā)

2019-11-27 10:55:36

云遷移云計(jì)算云平臺(tái)

2014-07-29 13:55:10

程序員代碼

2022-02-28 15:56:14

零信任企業(yè)

2025-01-08 12:36:52

2020-11-24 10:32:16

CIO首席信息官工具

2022-10-10 14:53:00

云安全云計(jì)算云平臺(tái)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)