Dockerfile 是一個文本格式的配置文件一罩,風(fēng)格卻像是一段代碼,用戶可以通過編寫 Dockerfile 來創(chuàng)建自定義鏡像撇簿。
本文將從 Dockerfile 的基本格式入手聂渊,羅列其支持的指令并進行介紹,并說明如何通過這些指令來構(gòu)建我們想要的鏡像四瘫。最后還會講下一些編寫 Dockerfile 的經(jīng)驗汉嗽。
1. 基礎(chǔ)結(jié)構(gòu)
Docker 由一行行命令語句和以 # 開頭的注釋語句構(gòu)成。主體內(nèi)容分為四個部分:基礎(chǔ)鏡像信息找蜜、維護者信息饼暑、鏡像操作指令和容器啟動時執(zhí)行指令。
下面從一個簡單的 Dockerfile 例子開始說明:
# escape=\ (backslash)
# This dockerfile uses the ubuntu:xenial image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu:xenial
# Maintainer: docker_user <docker_user at email.com> (@docker_user)
LABEL maintainer docker_user<docker_user@email.com>
# Command to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ xenial main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# Command when creating a new container
CMD /usr/sbin/nginx
1.1 解析器指令
這個例子中洗做,第一行是解析器命令弓叛,而不是普通的注釋,它指定了這個 Dockerfile 文本的轉(zhuǎn)義字符诚纸。
# escape=\ (backslash)
或者 # escape=` (backtick)
撰筷,默認(rèn)轉(zhuǎn)義字符是 \ 。
轉(zhuǎn)義字符既用于轉(zhuǎn)義一行中的字符畦徘,也用于轉(zhuǎn)義換行符毕籽。這使得一條 Dockerfile 指令可以跨越多行抬闯。注意,無論解析器指令 escape 是否包含在 Dockerfile 中影钉,轉(zhuǎn)義都不會在 RUN 指令中執(zhí)行画髓,除非要轉(zhuǎn)義的字符出現(xiàn)在行末尾。
在Windows環(huán)境下平委,將轉(zhuǎn)義字符設(shè)置為反引號而不是 \ 將非常有用奈虾,這樣,\ 就可以保持其作為目錄路徑分隔符的原有的含義廉赔,與 Windows PowerShell 中保持一致肉微。
考慮下面的這個例子,它在Windows上可能會非常詭異地執(zhí)行失敗蜡塌。第二行末尾的第二個 \ 將被視為用來轉(zhuǎn)義換行符的轉(zhuǎn)義字符碉纳,而不是第一個 \ 的轉(zhuǎn)義目標(biāo)。類似地馏艾,假設(shè)第三行末尾的 \ 本來是要作為一條指令進行處理的劳曹,但是它卻將被視為行延續(xù)字符。這個 Dockerfile 的結(jié)果是琅摩,第二行和第三行被認(rèn)為是一條指令:
FROM microsoft/nanoserver
COPY testfile.txt c:\\
RUN dir c:\
解決方式就是指定轉(zhuǎn)義字符為 `
铁孵。
更多關(guān)于解析器指令的資料可以參考 Docker 官方文檔
1.2 主體部分
一般而言,大部分鏡像會包含一個 FROM
標(biāo)簽指定基礎(chǔ)鏡像信息房资,即要構(gòu)建的新鏡像是基于哪個舊鏡像構(gòu)建的蜕劝。接下來是使用 LABEL 指令說明維護者信息,后面則是鏡像操作指令轰异,需要注意的是每運行一條 RUN 指令岖沛,鏡像添加新的一層,并提交搭独,最后是 CMD 指令婴削,指定容器啟動時的操作命令。
2. 指令說明
Dockerfile 中指令的一般格式是 INSTRUCTION arguments牙肝,類型有配置指令(配置鏡像信息)和操作指令(具體執(zhí)行操作)唉俗。
2.1 配置指令
2.1.1 ARG
定義了創(chuàng)建鏡像過程中的變量。
格式為 ARG <name>[=<default value>]惊奇。
在執(zhí)行 docker build 時互躬,可以通過 -build-arg[=] 來為變量賦值。當(dāng)鏡像編譯成功后颂郎, ARG 變量將不再存在(ENV 變量將一直保留在環(huán)境變量中)吼渡。
Docker 中內(nèi)置了一些鏡像創(chuàng)建變量,可以直接使用(無需區(qū)分大小寫):HTTP_PROXY乓序、hTTPS_PROXY寺酪、FTP_PROXY坎背、NO_PROXY。
2.1.2 FROM
指定所創(chuàng)建鏡像的基礎(chǔ)鏡像寄雀。
格式為 FROM <image> [AS <name>] 或 FROM <image>:<tag> [AS <name>] 或 FROM <image>@<digest> [AS <name>]得滤。
任何 Dockerfile 的第一條指令(不算解析器指令),必須是 FROM 指令盒犹。如果在同一個 Dockerfile 中有多個鏡像懂更,可以使用多個 FROM 指令。
例子為
ARG VERSION=9.3
FROM debian:${VERSION}
2.1.3 LABEL
LABEL 指令可以為生成的鏡像添加元數(shù)據(jù)標(biāo)簽信息急膀。之后可以使用 docker ps -f lable key=value
快速過濾出特定鏡像沮协。
格式為 LABEL <key>=<value> <key>=<value> <key>=<value>....
例如:
LABEL author="wean" date="2019-1-16"
LABEL description="This is a mult-line \
text"
2.1.4 EXPOSE
聲明鏡像內(nèi)服務(wù)監(jiān)聽的端口。
格式為 EXPOSE <port> [<port>/<protocol>...]
例如:
EXPOSE 22 80 443
需要注意的是卓嫂,該指令只是起到聲明的作用慷暂,并不會自動完成端口映射。
如果要映射端口出來晨雳,在啟動容器時可以使用 -P 或 -p 參數(shù)行瑞,可以參考 操作 Docker 容器,查看端口映射的具體操作餐禁。
2.1.5 ENV
指定環(huán)境變量血久,在編譯鏡像過程中會被后續(xù) RUN 指令使用,在鏡像啟動后也會存在于環(huán)境變量中坠宴。
例如:
ENV APP_VERSION=1.0.0
ENV PATH $PATH:/usr/local/bin
指令指定的環(huán)境變量在啟動時也可以覆蓋掉:docker run --env <key>=<value> built_image
注意當(dāng)一條 ENV 指令中同時為多個環(huán)境變量賦值并且值也是從環(huán)境變量讀取時洋魂;會為變量先賦值后更新绷旗。如下面指令喜鼓,最終結(jié)果是 key1=value1 key2=value2
ENV key1=value2
ENV key1=value1 key2=${key1}
2.1.6 ENTRYPOINT
指定鏡像的默認(rèn)入口命令,該命令會在啟動容器時作為跟命令執(zhí)行衔肢,所有傳入值作為該命令的參數(shù)庄岖。
支持兩種格式:
- ENTRYPOINT ["executable", "param1", "param2"]: exec 調(diào)用執(zhí)行
- ENTRYPOINT command param1 param2: shell 調(diào)用執(zhí)行。
此時角骤,若是指定了 CMD 指令或者 docker run 指定了啟動命令隅忿,則會作為 ENTRYPOINT 根命令的參數(shù)。
每個 Dockerfile 中只能有一個 ENTRYPOINT邦尊,當(dāng)指定多個時背桐,只有最后一個生效。
在運行時蝉揍,可以被 --entrypoint 參數(shù)覆蓋掉链峭。
關(guān)于 ENRTYPOINT 和 CMD 的更多差異可以參考 https://www.cnblogs.com/lienhua34/p/5170335.html。
2.1.7 VOLUME
創(chuàng)建數(shù)據(jù)卷掛載點又沾,掛載到本地的一個隨機目錄下弊仪。
格式為 VOLUME ["/data"]熙卡。
2.1.8 USER
指定運行容器時的用戶名或者 UID,后續(xù)的 RUN 指令也會默認(rèn)使用特定的用戶身份励饵。
格式為 USER daemon驳癌。
當(dāng)服務(wù)不需要管理員權(quán)限時,可以在 Dockerfile 中創(chuàng)建需要所需要的用戶役听,并通過 USER 命令指定該運行用戶颓鲜。例如:
RUN groupadd -r postgres && useradd --no-log-init -r -g postgress postgress
后續(xù)需要獲取臨時管理員權(quán)限可以使用 gosu 命令。
2.1.9 WORKDIR
為后續(xù)的 RUN典予、CMD灾杰、ENTRYPOINT 指令配置工作目錄。
格式為 WORKDIR /path/to/workdir
可以使用多個 WORKDIR 指令熙参,但是后續(xù)指令如果是相對路徑艳吠,基于之前的指令指定的路徑。
例如:
WORKDIR /a
WORKDIR b
結(jié)果是路徑 /a/b孽椰,所以推薦要是需要寫多個 WORKDIR昭娩,則每個 WORKDIR 都寫絕對路徑。
2.1.10 ONBUILD
指定當(dāng)基于所生成的鏡像創(chuàng)建子鏡像時黍匾,自動執(zhí)行的操作指令栏渺。
格式為 ONBUILD [INSTRUCTION]
由由于這個命令是隱式執(zhí)行的,子鏡像創(chuàng)建過程中察覺不到锐涯,所以應(yīng)該在鏡像標(biāo)簽中進行標(biāo)注磕诊,例如 ruby:2.1-onbuild。
這個指令在創(chuàng)建專門用于自動編譯纹腌、檢查等操作的基礎(chǔ)鏡像時十分有用霎终。
2.1.11 STOPSIGNAL
默認(rèn)的stop-signal是SIGTERM,在docker stop的時候會給容器內(nèi)PID為1的進程發(fā)送這個signal升薯,通過--stop-signal可以設(shè)置自己需要的signal莱褒,主要的目的是為了讓容器內(nèi)的應(yīng)用程序在接收到signal之后可以先做一些事情,實現(xiàn)容器的平滑退出涎劈,如果不做任何處理广凸,容器將在一段時間之后強制退出,會造成業(yè)務(wù)的強制中斷蛛枚,這個時間默認(rèn)是10s谅海。
格式為 STOPSIGNAL signal
2.1.12 HEALTHCHECK
配置所啟動容器如何進行健康檢查(如何判斷健康與否)。格式有兩種:
- HEALTHCHECK [OPTIONS] CMD command:根據(jù)所執(zhí)行命令返回值是否為 0 來判斷
- HEALTHCHECK NONE: 禁止基礎(chǔ)鏡像中的健康檢查
OPTIONS 支持如下參數(shù):
- -interval=DURATION (default: 30s): 過多久檢查一次蹦浦。
- -timeout=DURATION (default: 30s): 每次檢查的超時時間扭吁。
- retries=N (default: 3): 如果失敗了,重試幾次才最終確定失敗。
2.1.13 SHELL
指定其他命令使用 shell 時的默認(rèn) shell 類型智末。
SHELL ["executable", "parameters"]谅摄。
默認(rèn)值是 ["/bin/sh", "-c"]。
2.2 操作指令
2.2.1 RUN
運行指定命令系馆。
格式為 RUN <command> 或 RUN ["executable", "param1", "param2"]送漠。命令過長可以使用轉(zhuǎn)義字符換行,比如默認(rèn)的
每條 RUN 指令會生成一個新的鏡像層由蘑。
2.2.2 CMD
CMD 容器用來指定容器啟動時默認(rèn)執(zhí)行的命令闽寡。支持三種格式:
- CMD ["executable", "param1", "param2"]: 相當(dāng)于執(zhí)行 executable param1 param2,推薦方式
- CMD command parame1 param2: 在默認(rèn)的 shell 中執(zhí)行尼酿,提供給需要交互的應(yīng)用
- CMD ["param1", "param2"]: 提供給 ENTRYPOINT 的默認(rèn)參數(shù)
每個鏡像只能有一個 CMD爷狈,多條會覆蓋,可以在運行 (run) 容器時通過參數(shù)覆蓋
2.2.3 ADD
添加內(nèi)容到鏡像裳擎。格式為 ADD <src> <dest>涎永。
該命令復(fù)制指定的 <src> 下的內(nèi)容到容器中的 <dest> 路徑下。
其中 <src> 可以是 Dockerfile 所在目錄的一個相對路徑(文件或目錄)鹿响;也可以是一個 URL羡微;還可以是一個 tar 文件(會自動解壓為目錄)。<dest> 可以是鏡像內(nèi)的絕對路徑惶我,或者相對于工作目錄 WORKDIR 下的相對路徑妈倔。且路徑支持正則格式。
2.2.4 COPY
COPY 指令與 ADD 相似绸贡,語義上 CPOY 更貼切盯蝴,推薦使用 COPY。
3. 創(chuàng)建鏡像
編寫完 Dockerfile 后就可以通過 docker [image] build [OPTIONS] PATH|URL|-
命令來創(chuàng)建鏡像听怕。
要注意的點有:
- 碰到 ADD捧挺、COPY 和 RUN 指令會生成一層新的鏡像
- 執(zhí)行命令的過程中會把 Dockerfile 文件所在的上下文都發(fā)給服務(wù)端。因此應(yīng)該避免無關(guān)的文件存在上下文叉跛,或者使用 .dockerignore 文件松忍。
- 非上下文路徑的 Dockerfile 使用 -f 選項來指定其路徑
- 要指定生成鏡像的標(biāo)簽信息蒸殿,可以使用 -t 選項筷厘。該選項可以重復(fù)使用多次來添加多個標(biāo)簽。
3.1 OPTIONS 命令選項
選項 | 說明 |
---|---|
-add-host list | 添加自定義的主機名到 IP 的映射 |
-build-arg list | 添加創(chuàng)建時的變量 |
-cache-from strings | 使用指定鏡像作為緩存源 |
-cgroup-parent string | 繼承上層 cgroup |
-compress | 使用 gzip 來壓縮創(chuàng)建上下文數(shù)據(jù) |
-cpu-period int | 分配的 CFS 調(diào)度器時長 |
-cpu-quota int | CPU 調(diào)度器總份額 |
-c, -cpu-shares int | CPU 權(quán)重 |
-cpuset-cpus string | 多 CPU 允許使用的 CPU |
-cpuset-mems string | 多 CPU 允許使用的內(nèi)存 |
-disable-content-trust | 不進行鏡像校驗宏所,默認(rèn)為真 |
-f酥艳,-file string | Dockerfile 名稱 |
-force-rm | 總是刪除中間過程的容器 |
-iidfile string | 將鏡像 ID 寫入到文件 |
-isolation string | 容器的隔離機制 |
-label list | 配置鏡像的元數(shù)據(jù) |
-m,-memory bytes | 限制使用內(nèi)存量 |
-memory-swap bytes | 限制內(nèi)存和緩存的總量 |
-network string | 指定 RUN 命令時的網(wǎng)絡(luò)模式 |
-no-cache | 創(chuàng)建鏡像時不適用緩存 |
-platform string | 指定平臺類型 |
-pull | 總是嘗試獲取鏡像的最新版本 |
-q爬骤,-quiet | 不打印創(chuàng)建過程中的日志信息 |
-rm | 創(chuàng)建成功后自動刪除中間過程容器充石,默認(rèn)為真 |
-security-opt strings | 指定安全相關(guān)的選項 |
-shm-size bytes | /dev/shm 的大小 |
-squash | 將新創(chuàng)建的多層擠壓放到一層中 |
-stream | 持續(xù)獲取創(chuàng)建的上下文 |
-t,-tag list | 指定鏡像的標(biāo)簽列表 |
-target string | 指定創(chuàng)建的目標(biāo)階段 |
-ulimit ulimit | 指定 ulimit 變量 |
3.2 選擇父鏡像
從這個鏡像的繼承圖來看霞玄,我們可以選擇兩種鏡像作為父鏡像骤铃,一種是基礎(chǔ)鏡像(灰色)拉岁,另一種是普通鏡像(白色)。
3.3 使用 .dockerignore 文件
從前面我們可以知道惰爬,Dockerfile build 的過程中會把上下文一起發(fā)給服務(wù)端喊暖,如果有不想發(fā)送的文件(加快傳輸過程),可以使用一下模式忽略文件:
- /temp
- //temp*
- tmp?
- ~*
- Dockerfile
- !README.md
其中 * 表示任意多的字符撕瞧, 陵叽? 代表單個字符, 丛版! 代表不匹配(即不忽略指定的路徑或文件)巩掺。
3.4 多步驟創(chuàng)建
從 17.05 版本開始,Docker 支持多步驟鏡像創(chuàng)建页畦,用來精簡最終生成的鏡像的大小胖替。
舉個簡單的例子,制作一個 Java 程序鏡像豫缨,分成兩個步驟創(chuàng)建刊殉,第一步用包含 maven 庫及 JDK 的編譯鏡像編譯程序,第二部把生成的 jar 文件復(fù)制到運行鏡像中州胳,打包運行鏡像就得到精簡的鏡像记焊。
以 Go 語言為例,提供一個例子:
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, Docker")
}
FROM golang:1.9 as builder
RUN mkdir -p /go/src/test
WORKDIR /go/src/test
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR/root/
COPY --from=builder /go/src/test/app .
CMD ["./app"]
編譯鏡像栓撞,可以看到最終大小只有 6.83 MB
>docker build -t wean2018/test-multistage:latest .
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
wean2018/test-multistage latest 2e2eed03a664 53 seconds ago 6.83MB