廣州做企業(yè)網(wǎng)站找哪家公司好網(wǎng)絡(luò)營銷推廣方法和手段
目標
-
更快的構(gòu)建速度
-
更小的Docker鏡像大小
-
更少的Docker鏡像層
-
充分利用鏡像緩存
-
增加Dockerfile可讀性
-
讓Docker容器使用起來更簡單
總結(jié)
-
編寫.dockerignore文件
-
容器只運行單個應(yīng)用
-
將多個RUN指令合并為一個
-
基礎(chǔ)鏡像的標簽不要用latest
-
每個RUN指令后刪除多余文件
-
選擇合適的基礎(chǔ)鏡像(alpine版本最好)
-
設(shè)置WORKDIR和CMD
-
使用ENTRYPOINT (可選)
-
在entrypoint腳本中使用exec
-
COPY與ADD優(yōu)先使用前者
-
合理調(diào)整COPY與RUN的順序
-
設(shè)置默認的環(huán)境變量,映射端口和數(shù)據(jù)卷
-
使用LABEL設(shè)置鏡像元數(shù)據(jù)
-
添加HEALTHCHECK
-
多階段構(gòu)建
示例
示例Dockerfile犯了幾乎所有的錯(當然我是故意的)。接下來,我會一步步優(yōu)化它。假設(shè)我們需要使用Docker運行一個Node.js應(yīng)用,下面就是它的Dockerfile(CMD指令太復(fù)雜了,所以我簡化了,它是錯誤的,僅供參考)。
FROM ubuntu
ADD . /app
RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y nodejs ssh mysql
RUN cd /app && npm install
# this should start three processes, mysql and ssh
# in the background and node app in foreground
# isn't it beautifully terrible? <3
CMD mysql & sshd & npm start
構(gòu)建鏡像:
docker build -t wtf .
你能發(fā)現(xiàn)上面Dockerfile所有的錯誤嗎? 不能? 那接下來讓我們一步一步完善它。
[root@wq dockerfile]# docker build -f mydockerfile-centos #dockerfile文件 -t mycentos:0.1 #鏡像名稱 . #點
優(yōu)化
1. 編寫.dockerignore文件
構(gòu)建鏡像時,Docker需要先準備context
?,將所有需要的文件收集到進程中。默認的context
包含Dockerfile目錄中的所有文件,但是實際上,我們并不需要.git目錄,node_modules目錄等內(nèi)容。.dockerignore
?的作用和語法類似于?.gitignore
,可以忽略一些不需要的文件,這樣可以有效加快鏡像構(gòu)建時間,同時減少Docker鏡像的大小。示例如下:
.git/node_modules/
2. 容器只運行單個應(yīng)用
從技術(shù)角度講,你可以在Docker容器中運行多個進程。你可以將數(shù)據(jù)庫,前端,后端,ssh,supervisor都運行在同一個Docker容器中。但是,這會讓你非常痛苦:
-
非常長的構(gòu)建時間(修改前端之后,整個后端也需要重新構(gòu)建)
-
非常大的鏡像大小
-
多個應(yīng)用的日志難以處理(不能直接使用stdout,否則多個應(yīng)用的日志會混合到一起)
-
橫向擴展時非常浪費資源(不同的應(yīng)用需要運行的容器數(shù)并不相同)
-
僵尸進程問題 - 你需要選擇合適的init進程
因此,建議大家為每個應(yīng)用構(gòu)建單獨的Docker鏡像,然后使用 Docker Compose 運行多個Docker容器。
現(xiàn)在,我從Dockerfile中刪除一些不需要的安裝包,另外,SSH可以用docker exec替代。示例如下:
FROM ubuntu
ADD . /app
RUN apt-get update
RUN apt-get upgrade -y
# we should remove ssh and mysql, and use
# separate container for database
RUN apt-get install -y nodejs # ssh mysql
RUN cd /app && npm install
CMD npm start
3. 將多個RUN指令合并為一個
Docker鏡像是分層的,下面這些知識點非常重要:
-
Dockerfile中的每個指令都會創(chuàng)建一個新的鏡像層。
-
鏡像層將被緩存和復(fù)用
-
當Dockerfile的指令修改了,復(fù)制的文件變化了,或者構(gòu)建鏡像時指定的變量不同了,對應(yīng)的鏡像層緩存就會失效
-
某一層的鏡像緩存失效之后,它之后的鏡像層緩存都會失效
-
鏡像層是不可變的,如果我們再某一層中添加一個文件,然后在下一層中刪除它,則鏡像中依然會包含該文件(只是這個文件在Docker容器中不可見了)。
Docker鏡像類似于洋蔥。它們都有很多層。為了修改內(nèi)層,則需要將外面的層都刪掉。記住這一點的話,其他內(nèi)容就很好理解了。
現(xiàn)在,我們將所有的RUN指令合并為一個。同時把apt-get upgrade
刪除,因為它會使得鏡像構(gòu)建非常不確定(我們只需要依賴基礎(chǔ)鏡像的更新就好了)
FROM ubuntu
ADD . /app
RUN apt-get update \ && apt-get install -y nodejs \&& cd /app \&& npm install
CMD npm start
記住一點,我們只能將變化頻率一樣的指令合并在一起。將node.js安裝與npm模塊安裝放在一起的話,則每次修改源代碼,都需要重新安裝node.js,這顯然不合適。因此,正確的寫法是這樣的:
FROM ubuntu
RUN apt-get update && apt-get install -y nodejs
ADD . /app
RUN cd /app && npm install
CMD npm start
4. 基礎(chǔ)鏡像的標簽不要用latest
當鏡像沒有指定標簽時,將默認使用latest
?標簽。因此,?FROM ubuntu
?指令等同于FROM ubuntu:latest
。當時,當鏡像更新時,latest標簽會指向不同的鏡像,這時構(gòu)建鏡像有可能失敗。如果你的確需要使用最新版的基礎(chǔ)鏡像,可以使用latest標簽,否則的話,最好指定確定的鏡像標簽。
示例Dockerfile應(yīng)該使用16.04
作為標簽。
FROM ubuntu:16.04 # it's that easy!
RUN apt-get update && apt-get install -y nodejs
ADD . /app
RUN cd /app && npm install
CMD npm start
5. 每個RUN指令后刪除多余文件
假設(shè)我們更新了apt-get源,下載,解壓并安裝了一些軟件包,它們都保存在/var/lib/apt/lists/
目錄中。但是,運行應(yīng)用時Docker鏡像中并不需要這些文件。我們最好將它們刪除,因為它會使Docker鏡像變大。
示例Dockerfile中,我們可以刪除/var/lib/apt/lists/
目錄中的文件(它們是由apt-get update生成的)。
FROM ubuntu:16.04
RUN apt-get update \ && apt-get install -y nodejs \# added lines&& rm -rf /var/lib/apt/lists/*
ADD . /app
RUN cd /app && npm install
CMD npm start
6. 選擇合適的基礎(chǔ)鏡像(alpine版本最好)
在示例中,我們選擇了ubuntu
作為基礎(chǔ)鏡像。但是我們只需要運行node程序,有必要使用一個通用的基礎(chǔ)鏡像嗎?node
鏡像應(yīng)該是更好的選擇。
FROM node
ADD . /app
# we don't need to install node
# anymore and use apt-get
RUN cd /app && npm install
CMD npm start
更好的選擇是alpine版本的node
鏡像。alpine是一個極小化的Linux發(fā)行版,只有4MB,這讓它非常適合作為基礎(chǔ)鏡像。
FROM node:7-alpine
ADD . /app
RUN cd /app && npm install
CMD npm start
apk是Alpine的包管理工具。它與apt-get
有些不同,但是非常容易上手。另外,它還有一些非常有用的特性,比如no-cache
和?--virtual
選項,它們都可以幫助我們減少鏡像的大小。
7. 設(shè)置WORKDIR和 CMD
WORKDIR指令可以設(shè)置默認目錄,也就是運行RUN
?/?CMD
?/?ENTRYPOINT
指令的地方。
CMD指令可以設(shè)置容器創(chuàng)建是執(zhí)行的默認命令。另外,你應(yīng)該講命令寫在一個數(shù)組中,數(shù)組中每個元素為命令的每個單詞(參考官方文檔)。
FROM node:7-alpine
WORKDIR /app
ADD . /app
RUN npm install
CMD ["npm", "start"]
8. 使用ENTRYPOINT (可選)
ENTRYPOINT指令并不是必須的,因為它會增加復(fù)雜度。ENTRYPOINT
是一個腳本,它會默認執(zhí)行,并且將指定的命令當成參數(shù)接收。它通常用于構(gòu)建可執(zhí)行的Docker鏡像。entrypoint.sh如下:
示例Dockerfile:
FROM node:7-alpine
WORKDIR /app
ADD . /app
RUN npm install
ENTRYPOINT ["./entrypoint.sh"]
CMD ["start"]
可以使用如下命令運行該鏡像:
_# 運行開發(fā)版本_docker run our-app dev
_# 運行生產(chǎn)版本_docker run our-app start
_# 運行bash_docker run -it our-app /bin/bash
9. 在entrypoint腳本中使用exec
在前文的entrypoint腳本中,我使用了exec
命令運行node應(yīng)用。不使用exec
的話,我們則不能順利地關(guān)閉容器,因為SIGTERM信號會被bash腳本進程吞沒。exec
命令啟動的進程可以取代腳本進程,因此所有的信號都會正常工作。
這里擴展介紹一下docker容器的停止過程:
(1). 對于容器來說,init
?系統(tǒng)不是必須的,當你通過命令?docker stop mycontainer
?來停止容器時,docker CLI 會將?TERM
?信號發(fā)送給 mycontainer 的?PID
?為 1 的進程。
-
如果 PID 1 是 init 進程?- 那么 PID 1 會將 TERM 信號轉(zhuǎn)發(fā)給子進程,然后子進程開始關(guān)閉,最后容器終止。
-
如果沒有 init 進程- 那么容器中的應(yīng)用進程(Dockerfile 中的
ENTRYPOINT
或CMD
指定的應(yīng)用)就是 PID 1,應(yīng)用進程直接負責(zé)響應(yīng)TERM
信號。這時又分為兩種情況:-
應(yīng)用不處理 SIGTERM?- 如果應(yīng)用沒有監(jiān)聽?
SIGTERM
?信號,或者應(yīng)用中沒有實現(xiàn)處理?SIGTERM
?信號的邏輯,應(yīng)用就不會停止,容器也不會終止。 -
容器停止時間很長?- 運行命令?
docker stop mycontainer
?之后,Docker 會等待?10s
,如果?10s
?后容器還沒有終止,Docker 就會繞過容器應(yīng)用直接向內(nèi)核發(fā)送?SIGKILL
,內(nèi)核會強行殺死應(yīng)用,從而終止容器。
-
(2).如果容器中的進程沒有收到?SIGTERM
?信號,很有可能是因為應(yīng)用進程不是?PID 1
,PID 1 是?shell
,而應(yīng)用進程只是?shell
?的子進程。而 shell 不具備?init
?系統(tǒng)的功能,也就不會將操作系統(tǒng)的信號轉(zhuǎn)發(fā)到子進程上,這也是容器中的應(yīng)用沒有收到?SIGTERM
?信號的常見原因。
問題的根源就來自?Dockerfile
,例如:
FROM alpine:3.7
COPY popcorn.sh .
RUN chmod +x popcorn.sh
ENTRYPOINT ./popcorn.sh
CMD ["start"]
ENTRYPOINT
?指令使用的是 **shell 模式**,這樣 Docker 就會把應(yīng)用放到?shell
?中運行,因此?shell
?是 PID 1。
解決方案有以下幾種:
方案 1:使用 exec 模式的 ENTRYPOINT 指令
與其使用 shell 模式,不如使用 exec 模式,例如:
FROM alpine:3.7
COPY popcorn.sh .
RUN chmod +x popcorn.sh
ENTRYPOINT ["./popcorn.sh"]
這樣 PID 1 就是?./popcorn.sh
,它將負責(zé)響應(yīng)所有發(fā)送到容器的信號,至于?./popcorn.sh
?是否真的能捕捉到系統(tǒng)信號,那是另一回事。
舉個例子,假設(shè)使用上面的 Dockerfile 來構(gòu)建鏡像,popcorn.sh
?腳本每過一秒打印一次日期:
#!/bin/shwhile true
dodatesleep 1
done
構(gòu)建鏡像并創(chuàng)建容器:
docker build -t truek8s/popcorn .
docker run -it --name corny --rm truek8s/popcorn
打開另外一個終端執(zhí)行停止容器的命令,并計時:
time docker stop corny
因為?popcorn.sh
?并沒有實現(xiàn)捕獲和處理?SIGTERM
?信號的邏輯,所以需要 10s 左右才能停止容器。要想解決這個問題,就要往腳本中添加信號處理代碼,讓它捕獲到?SIGTERM
?信號時就終止進程:
#!/bin/sh
# catch the TERM signal and then exit
trap "exit" TERM
while true
dodatesleep 1
done
注意:下面這條指令與 shell 模式的 ENTRYPOINT 指令是等效的:
ENTRYPOINT ["/bin/sh", "./popcorn.sh"]
方案 2:直接使用 exec 命令
如果你就想使用?shell
?模式的 ENTRYPOINT 指令,也不是不可以,只需將啟動命令追加到?exec
?后面即可,例如:
FROM alpine:3.7
COPY popcorn.sh .
RUN chmod +x popcorn.sh
ENTRYPOINT exec ./popcorn.sh
這樣?exec
?就會將 shell 進程替換為?./popcorn.sh
?進程,PID 1 仍然是?./popcorn.sh
。
方案 3:使用 init 系統(tǒng)
如果容器中的應(yīng)用默認無法處理?SIGTERM
?信號,又不能修改代碼,這時候方案 1 和 2 都行不通了,只能在容器中添加一個?init
?系統(tǒng)。init 系統(tǒng)有很多種,這里推薦使用 tini,它是專用于容器的輕量級 init 系統(tǒng),使用方法也很簡單:
-
安裝?
tini
-
將?
tini
?設(shè)為容器的默認應(yīng)用 -
將?
popcorn.sh
?作為?tini
?的參數(shù)
具體的 Dockerfile 如下:
FROM alpine:3.7
COPY popcorn.sh .
RUN chmod +x popcorn.sh
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--", "./popcorn.sh"]
現(xiàn)在 ``` tini
就是 PID 1,它會將收到的系統(tǒng)信號轉(zhuǎn)發(fā)給子進程 ```
popcorn.sh
10. COPY與ADD優(yōu)先使用前者
COPY指令非常簡單,僅用于將文件拷貝到鏡像中。ADD相對來講復(fù)雜一些,可以用于下載遠程文件以及解壓壓縮包(參考官方文檔)。
FROM node:7-alpine
WORKDIR /app
COPY . /app
RUN npm install
ENTRYPOINT ["./entrypoint.sh"]
CMD ["start"]
11. 合理調(diào)整COPY與RUN的順序
我們應(yīng)該把變化最少的部分放在Dockerfile的前面,這樣可以充分利用鏡像緩存。
在構(gòu)建鏡像的時候,docker 會按照dockerfile
中的指令順序來一次執(zhí)行。每一個指令被執(zhí)行的時候 docker 都會去緩存中檢查是否有已經(jīng)存在的鏡像可以復(fù)用,而不是去創(chuàng)建一個新的鏡像復(fù)制。
如果不想使用構(gòu)建緩存,可以使用docker build
參數(shù)選項--no-cache=true
來禁用構(gòu)建緩存。在使用鏡像緩存時,要弄清楚緩存合適生效,何時失效。構(gòu)建緩存最基本規(guī)則如下:
-
如果引用的父鏡像在構(gòu)建緩存中,下一個命令將會和所有從該父進程派生的子鏡像做比較,如果有子鏡像使用相同的命令,那么緩存命中,否則緩存失效。
-
在大部分情況下,通過比較
Dockerfile
中的指令和子鏡像已經(jīng)足夠了。但是有些指令需要進一步的檢查。 -
對于
ADD
和COPY
指令, 文件的內(nèi)容會被檢查,并且會計算每一個文件的校驗碼。但是文件最近一次的修改和訪問時間不在校驗碼的考慮范圍內(nèi)。在構(gòu)建過程中,docker 會比對已經(jīng)存在的鏡像,只要有文件內(nèi)容和元數(shù)據(jù)發(fā)生變動,那么緩存就會失效。 -
除了
ADD
和COPY
指令,鏡像緩存不會檢查容器中文件來判斷是否命中緩存。例如,在處理RUN apt-get -y update
命令時,不會檢查容器中的更新文件以確定是否命中緩存,這種情況下只會檢查命令字符串是否相同。
示例中,源代碼會經(jīng)常變化,則每次構(gòu)建鏡像時都需要重新安裝NPM模塊,這顯然不是我們希望看到的。因此我們可以先拷貝package.json
,然后安裝NPM模塊,最后才拷貝其余的源代碼。這樣的話,即使源代碼變化,也不需要重新安裝NPM模塊。
FROM node:7-alpine
WORKDIR /app
COPY package.json /app
RUN npm install
COPY . /app
ENTRYPOINT ["./entrypoint.sh"]
CMD ["start"]
同樣舉一反三,Python項目的時候,我們同樣可以先拷貝requerements.txt,然后進行pip install requerements.txt,最后再進行COPY 代碼。
ROM python:3.6
# 創(chuàng)建 app 目錄
WORKDIR /app
# 安裝 app 依賴
COPY src/requirements.txt ./
RUN pip install -r requirements.txt
# 打包 app 源碼
COPY src /app
EXPOSE 8080
CMD [ "python", "server.py" ]
## 12. 設(shè)置默認的環(huán)境變量,映射端口和數(shù)據(jù)卷 運行Docker容器時很可能需要一些環(huán)境變量。在Dockerfile設(shè)置默認的環(huán)境變量是一種很好的方式。另外,我們應(yīng)該在Dockerfile中設(shè)置映射端口和數(shù)據(jù)卷。示例如下: ```dockerfile FROM node:7-alpine ENV PROJECT_DIR=/app WORKDIRPROJECT_DIR ? RUN npm install ? COPY .MEDIA_DIR ? EXPOSE $APP_PORT ENTRYPOINT ["./entrypoint.sh"] ? CMD ["start"] ``` [ENV](https://docs.docker.com/engine/reference/builder/#env)指令指定的環(huán)境變量在容器中可以使用。如果你只是需要指定構(gòu)建鏡像時的變量,你可以使用[ARG](https://docs.docker.com/engine/reference/builder/#arg)指令。
13. 使用LABEL設(shè)置鏡像元數(shù)據(jù)
使用LABEL指令,可以為鏡像設(shè)置元數(shù)據(jù),例如鏡像創(chuàng)建者或者鏡像說明。舊版的Dockerfile語法使用MAINTAINER指令指定鏡像創(chuàng)建者,但是它已經(jīng)被棄用了。有時,一些外部程序需要用到鏡像的元數(shù)據(jù),例如nvidia-docker需要用到com.nvidia.volumes.needed
。示例如下:
FROM node:7-alpine
LABEL maintainer "jakub.skalecki@example.com"
...
14. 添加HEALTHCHECK
運行容器時,可以指定--restart always
選項。這樣的話,容器崩潰時,Docker守護進程(docker daemon)會重啟容器。對于需要長時間運行的容器,這個選項非常有用。但是,如果容器的確在運行,但是不可(陷入死循環(huán),配置錯誤)用怎么辦?使用HEALTHCHECK指令可以讓Docker周期性的檢查容器的健康狀況。我們只需要指定一個命令,如果一切正常的話返回0,否則返回1。對HEALTHCHECK感興趣的話,可以參考這篇博客。示例如下:
FROM node:7-alpine
LABEL maintainer "jakub.skalecki@example.com"
ENV PROJECT_DIR=/app
WORKDIR $PROJECT_DIR
COPY package.json $PROJECT_DIR
RUN npm install
COPY . $PROJECT_DIR
ENV MEDIA_DIR=/media \ NODE_ENV=production \APP_PORT=3000
VOLUME $MEDIA_DIR
EXPOSE $APP_PORT
HEALTHCHECK CMD curl --fail http://localhost:$APP_PORT || exit 1
ENTRYPOINT ["./entrypoint.sh"]
CMD ["start"]
當請求失敗時,curl --fail
?命令返回非0狀態(tài)。
15. 多階段構(gòu)建
參考文檔《https://docs.docker.com/develop/develop-images/multistage-build/》
在docker不支持多階段構(gòu)建的年代,我們構(gòu)建docker鏡像時通常會采用如下兩種方法:
方法A.將所有的構(gòu)建過程編寫在同一個Dockerfile中,包括項目及其依賴庫的編譯、測試、打包等流程,可能會有如下問題:
-
- Dockerfile可能會特別臃腫
-
- 鏡像層次特別深
-
- 存在源碼泄露的風(fēng)險
方法B.事先在外部將項目及其依賴庫編譯測試打包好后,再將其拷貝到構(gòu)建目錄中執(zhí)行構(gòu)建鏡像。
方法B較方法A略顯優(yōu)雅一些,而且可以很好地規(guī)避方法A存在的風(fēng)險點,但仍需要我們編寫兩套或多套Dockerfile或者一些腳本才能將其兩個階段自動整合起來,例如有多個項目彼此關(guān)聯(lián)和依賴,就需要我們維護多個Dockerfile,或者需要編寫更復(fù)雜的腳本,導(dǎo)致后期維護成本很高。
為解決以上問題,**Docker v17.05 開始支持多階段構(gòu)建 (multistage builds)**。使用多階段構(gòu)建我們就可以很容易解決前面提到的問題,并且只需要編寫一個 Dockerfile。
你可以在一個 Dockerfile 中使用多個 FROM 語句。每個 FROM 指令都可以使用不同的基礎(chǔ)鏡像,并表示開始一個新的構(gòu)建階段。你可以很方便的將一個階段的文件復(fù)制到另外一個階段,在最終的鏡像中保留下你需要的內(nèi)容即可。
默認情況下,構(gòu)建階段是沒有命令的,我們可以通過它們的索引來引用它們,第一個 FROM 指令從0開始,我們也可以用AS指令為構(gòu)建階段命名。
案例1
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
通過?docker build
?構(gòu)建后,最終結(jié)果是產(chǎn)生與之前相同大小的 Image,但復(fù)雜性顯著降低。您不需要創(chuàng)建任何中間 Image,也不需要將任何編譯結(jié)果臨時提取到本地系統(tǒng)。
哪它是如何工作的呢?關(guān)鍵就在?COPY --from=0
?這個指令上。Dockerfile 中第二個 FROM 指令以 alpine:latest 為基礎(chǔ)鏡像開始了一個新的構(gòu)建階段,并通過?COPY --from=0
?僅將前一階段的構(gòu)建文件復(fù)制到此階段。前一構(gòu)建階段中產(chǎn)生的 Go SDK 和任何中間層都會在此階段中被舍棄,而不是保存在最終 Image 中。
使用多階段構(gòu)建一個python應(yīng)用。
案例2
默認情況下,構(gòu)建階段是未命名的。您可以通過一個整數(shù)值來引用它們,默認是從第 0 個 FROM 指令開始的。為了方便管理,您也可以通過向 FROM 指令添加 as NAME 來命名您的各個構(gòu)建階段。下面的示例就通過命名各個構(gòu)建階段并在 COPY 指令中使用名稱來訪問指定的構(gòu)建階段。
這樣做的好處就是即使稍后重新排序 Dockerfile 中的指令,COPY 指令一樣能找到對應(yīng)的構(gòu)建階段。
FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
案例3
停在特定的構(gòu)建階段
構(gòu)建鏡像時,不一定需要構(gòu)建整個 Dockerfile 中每個階段,您也可以指定需要構(gòu)建的階段。比如:您只構(gòu)建 Dockerfile 中名為 builder 的階段
$ docker build --target builder -t alexellis2/href-counter:latest .
此功能適合以下場景:
-
調(diào)試特定的構(gòu)建階段。
-
在 Debug 階段,啟用所有程序調(diào)試模式或調(diào)試工具,而在生產(chǎn)階段盡量精簡。
-
在 Testing 階段,您的應(yīng)用程序使用測試數(shù)據(jù),但在生產(chǎn)階段則使用生產(chǎn)數(shù)據(jù)。
案例4
使用外部鏡像作為構(gòu)建階段
使用多階段構(gòu)建時,您不僅可以從 Dockerfile 中創(chuàng)建的鏡像中進行復(fù)制。您還可以使用?COPY --from
?指令從單獨的 Image 中復(fù)制,支持使用本地 Image 名稱、本地或 Docker 注冊中心可用的標記或標記 ID。
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
案例5
把前一個階段作為一個新的階段
在使用 FROM 指令時,您可以通過引用前一階段停止的地方來繼續(xù)。同樣,采用此方式也可以方便一個團隊中的不同角色,如何使用類似流水線的方式,一級一級提供基礎(chǔ)鏡像,同樣更方便快速的復(fù)用團隊其他人的基礎(chǔ)鏡像。例如:
FROM alpine:latest as builder
RUN apk --no-cache add build-base
FROM builder as build1
COPY source1.cpp source.cpp
RUN g++ -o /binary source.cpp
FROM builder as build2
COPY source2.cpp source.cpp
RUN g++ -o /binary source.cpp
# ---- 基礎(chǔ) python 鏡像 ----
FROM python:3.6 AS base
# 創(chuàng)建 app 目錄
WORKDIR /app
# ---- 依賴 ----
FROM base AS dependencies
COPY gunicorn_app/requirements.txt ./
# 安裝 app 依賴
RUN pip install -r requirements.txt
# ---- 復(fù)制文件并 build ----
FROM dependencies AS build
WORKDIR /app
COPY . /app
# 在需要時進行 Build 或 Compile
# --- 使用 Alpine 發(fā)布 ----
FROM python:3.6-alpine3.7 AS release
# 創(chuàng)建 app 目錄
WORKDIR /app
COPY --from=dependencies /app/requirements.txt ./
COPY --from=dependencies /root/.cache /root/.cache
# 安裝 app 依賴
RUN pip install -r requirements.txt
COPY --from=build /app/ ./
CMD ["gunicorn", "--config", "./gunicorn_app/conf/gunicorn_config.py", "gunicorn_app:app"]