Dockerfile指令
官方文檔地址:https://docs.docker.com/engine/reference/builder/
Dockerfile是一個文本格式的配置文件,其內容包含眾多指令,用戶可以使用它快速的創建自定義鏡像。
部分指令列表
| 指令 | 作用 | 備注 |
|---|---|---|
| FROM | 指定基礎鏡像 | 任何Dockerfile中的第一條指令都必須是FROM 一個文件中可以存在多個FROM指令。 |
| LABEL | 為鏡像添加元數據標簽信息。 | |
| ARG | 定義創建鏡像過程中使用的變量,編譯成功后不存在。 | 在運行docker build時,指定--builder-arg為變量賦值。 |
| ENV | 設置環境變量 | 在鏡像啟動時也會存在。 docker run --env key=value 會覆蓋同名變量 |
| WORKDIR | 指定工作目錄 | 可以使用多個,建議使用絕對路徑。 |
| VOLUME | 創建數據卷掛載點 | 例如VOLUME ["/data/a","/data/b"] |
| EXPOSE | 聲明鏡像內服務監聽的端口 | 只起聲明作用,不會自動完成映射。 |
| USER | 指定運行容器時的用戶名或ID | 后續RUN指令也會以這個用戶身份運行。 |
| STOPSIGNAL | 指定所創建鏡像啟動的容器接收退出的信號值 |
COPY/ADD
COPY src dst / ADD src dst
其相同點是:作用都是復制內容到鏡像中;dst不存在時會創建;路徑支持正則。
不同點在於:
- ADD的src可以是相對於Dockerfile的相對路徑;也可以是一個URL;還可以是一個tar文件(自動解壓)。
- COPY的src只能是文件或者目錄。
除了目的明確是要將一個URL或者tar文件作為src的情況下,其他情況推薦使用COPY。
CMD/ENTRYPOINT
命令格式
- exec方式:
["executable","param1","param2"](JSON數組,所以一定要是雙引號) - shell方式:
command param1 param2
CMD與ENTRYPOINT兩種方式都支持,但推薦使用exec方式。
作用
在任意一個Dockerfile下:
- 只有最后一條CMD才會被執行。用來指定容器啟動時默認執行的命令及參數。但是會被docker run命令行參數覆蓋。
- 只有最后一條ENTRYPOINT才會被執行。用來配置容器啟動時的執行命令。並且會將docker run的命令行參數或者CMD的所有參數追加在ENTRYPOINT指令后作為參數。運行時可以被--entrypoint參數覆蓋。
偽代碼
finallyCommand = ""
if exists(ENTRYPOINT) {
finallyCommand = ENTRYPOINT
if exists(dockerRunCommandParam){
// 情景一
finallyCommand += dockerRunCommandParam
}elseif exists(CMD) {
// 情景二。除了CMD指令本身,包括后面JSON數組所有
finallyCommand += CMDParams
}
// 情景三
}elseif exists(dockerRunCommandParam){
// 情景四
finallyCommand = dockerRunCommandParams
}elseif exists(CMD){
// 情景五
finallyCommand = CMD
}
總結起來一句話:有ENTRYPOINT,CMD和docker run指定的命令都作為參數追加到ENTRYPOINT的參數后;沒有ENTRYPOINT,docker run指定了命令就執行,否則執行CMD(如果有的話)。
實操
Dockerfile內容、構建鏡像
FROM alpine:latest
ENTRYPOINT ["/bin/echo","entrypoint"]
-> [feifei@ffmac.local] [~/work/docker] docker build -t test .
// 情景三
-> [feifei@ffmac.local] [~/work/docker] docker run --rm test
entrypoint
// 情景一
-> [feifei@ffmac.local] [~/work/docker] docker run --rm test 12 34
entrypoint 12 34
Dockerfile添加CMD指令、構建鏡像
FROM alpine:latest
ENTRYPOINT ["/bin/echo","entrypoint"]
CMD ["/bin/echo","cmd"]
-> [feifei@ffmac.local] [~/work/docker] docker build -t test .
// 情景二:CMD中的 "/bin/echo" 沒有被當做命令執行,而是與"cmd"一起成為了ENTRYPOINT的參數。
-> [feifei@ffmac.local] [~/work/docker] docker run --rm test
entrypoint /bin/echo cmd
Dockerfile刪除ENTRYPOINT指令、構建鏡像
FROM alpine:latest
CMD ["/bin/echo","cmd"]
-> [feifei@ffmac.local] [~/work/docker] docker build -t test .
// 情景五
-> [feifei@ffmac.local] [~/work/docker] docker run --rm test
cmd
// 情景四
-> [feifei@ffmac.local] [~/work/docker] docker run --rm test echo 33
33
RUN
exec方式:RUN ["executable","param1","param2"]
shell方式:RUN command param1 param2
每條RUN指令將在當前鏡像基礎上執行指令,並且提交為新的鏡像層。當命令較長時可以使用\來換行
ONBUILD
ONBUILD [INSTRUCTION]
作為父鏡像被使用時自動執行的命令。對孫子鏡像無效。可用於自動編譯,檢查等。
假如父鏡像ParentImage的Dockerfile中有如下指令:
ONBUILD RUN mkdir /root/test
使用docker build基於ParentImage創建子鏡像ChildImage的時候,會先執行ParentImage中的ONBUILD指令。等價於在ChildImage中添加了:
RUN mkdir /root/test
HEALTHCHECK
健康檢查
HEALTHCHECK [OPTIONS] CMD command :根據執行命令返回值判斷,0是成功,1是不健康。
HEALTHCHECK NONE :禁止健康檢查
OPTIONS參數如下:
- --interval=DURATION (default: 30s) 多長時間檢查一次
- --timeout=DURATION (default: 30s) 檢查一次的超時時間
- --start-period=DURATION (default: 0s) 開始階段時長,此階段內第一次成功之前的失敗不計入retries。
- --retries=N (default: 3) 確定最終失敗的重試次數
構建鏡像
docker build
docker build [OPTIONS] PATH | URL | -
該命令默認讀取指定路徑下的Dockerfile(也可使用 -f 指定),並將該路徑下的所有數據作為上下文發送給Docker服務端。逐條執行指令,生成鏡像。
選擇父鏡像
一般情況下都需要使用FROM來指定父鏡像,所以父鏡像會影響到新生成鏡像的大小和功能。
通常有兩種鏡像可作為父鏡像:基礎鏡像和普通鏡像(由第三方創建,基於基礎鏡像)。基礎鏡像一般是基於scratch或者Dockerfile中不存在FROM指令。
多步驟構建
對於編譯型語言通常需要編譯環境和運行環境兩個鏡像:
- 編譯環境鏡像:包括完整的編譯環境、依賴庫等,體積較大。作用是將代碼編譯為二進制程序。
- 運行環境鏡像:運行二進制程序,而不需要與編譯環境,體積比較小。
app.go
package main
import "fmt"
func main() {
fmt.Println("Hello World")
}
Dockerfile中使用兩次FROM
FROM golang:1.14-alpine as builder
WORKDIR /go/src/test
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -o app app.go
FROM alpine:latest
COPY --from=builder /go/src/test/app /root/ #此處 --from=builder 也可以改為 --from=0
CMD ["/root/app"]
構建、運行
-> [feifei@ffmac.local] [~/work/docker] docker build -t hello .
-> [feifei@ffmac.local] [~/work/docker] docker run --rm hello
Hello World
小技巧
清理untagged鏡像
多階段構建會生成編譯環境鏡像,也會將之前的同名鏡像變為<none>:<none>,清理此類鏡像可執行以下命令:
docker rmi $(docker images --filter dangling=true -q)
COPY --from指定其他鏡像
COPY指令的--from選項也可以指定本地或者遠程倉庫的鏡像
准備一個Dockerfile,內容是從ubuntu鏡像中COPY一個文件到新鏡像的/root/下
FROM alpine:latest
COPY --from=ubuntu:latest /bin/bash /root/
CMD ["/bin/ls","/root/"]
dokcer build 構建鏡像
-> [feifei@ffmac.local] [~/work/docker] docker build -t test .
Sending build context to Docker daemon 19.46kB
Step 1/3 : FROM alpine:latest
---> f70734b6a266
Step 2/3 : COPY --from=ubuntu:latest /bin/bash /root/
latest: Pulling from library/ubuntu
d51af753c3d3: Pull complete
fc878cd0a91c: Pull complete
6154df8ff988: Pull complete
fee5db0ff82f: Pull complete
Digest: sha256:747d2dbbaaee995098c9792d99bd333c6783ce56150d1b11e333bbceed5c54d7
Status: Downloaded newer image for ubuntu:latest
---> Using cache
---> 082f95ffaa69
Step 3/3 : CMD ["/bin/ls","/root/"]
---> Using cache
---> 76fa9d9a1abd
Successfully built 76fa9d9a1abd
Successfully tagged test:latest
dokcer run 運行鏡像
-> [feifei@ffmac.local] [~/work/docker] docker run --rm test
bash
注意事項
- 使用
.dockerignore文件,避免發送不必要的數據 - 提供注釋和維護者信息
- 正確使用鏡像版本號
- 每條RUN都會提交為新的鏡像層,盡量合並RUN指令
- 刪除緩存和臨時文件
