使用Dockerfile創建鏡像
Dockerfile是一個文本格式的配置文件,用戶可以使用Dockerfile來快速創建自定義的鏡像。
1、基本結構
Dockerfile由一行行命令語句組成,並且支持以#開頭的注釋行。
一般而言,Dockerfile主體內容分為四部分:基礎鏡像鏡像、維護者信息、鏡像操作指令和容器啟動時執行指令
2、指令說明
Dockerfile中指令的一般格式為:INSTRUCTION arguments,包括配置指令(配置鏡像信息)和操作指令(具體執行操作)
分類 | 指令 | 說明 |
---|---|---|
配置指令 | ARG | 定義創建鏡像過程中使用的變量 |
FROM | 指定所創建鏡像的基礎鏡像 | |
LABEL | 為生成的鏡像添加元數據標簽信息 | |
EXPOSE | 聲明鏡像內服務監聽的端口 | |
ENV | 指定環境變量 | |
ENTRYPOINT | 指定鏡像的默認入口命令 | |
VOLUME | 創建一個數據卷掛載點 | |
USER | 指定運行容器時的用戶名或UID | |
WORKDIR | 配置工作目錄 | |
ONBUILD | 創建子鏡像時指定自動執行的操作指令 | |
STOPSIGNAL | 指定退出的信號值 | |
HEALTHCHECK | 配置所啟動容器如何進行健康檢查 | |
SHELL | 指定默認shell類型 | |
操作指令 | RUN | 運行指定命令 |
CMD | 啟動容器時指定默認執行的命令 | |
ADD | 添加內容到鏡像 | |
COPY | 復制內容到鏡像 |
2.1、配置指令介紹
ARG
定義創建鏡像過程中使用的變量
格式:
ARG <name>[=<default value>]
在執行docker build時,可以通過-build-args[=]來為變量賦值。當鏡像編譯成功后,ARG指定的變量將不再存在(ENV指定的變量將在鏡像中保留)
Docker內置了一些鏡像創建變量,用戶可以直接使用而無須聲明,包括(不區分大小寫)HTTP_PROXY、HTTPS_PROXY、FTP_PROXY、NO_PROXY。
FROM
指定所創建鏡像的基礎鏡像
格式:
FROM <image> [AS <name>] 或 FROM <image>:<tag> [AS <name>] 或 FROM <image>@<digest> [AS <name>]
任何Dockerfile中第一條指令必須為FROM指令。並且,如果在同一個Dockerfile中創建多個鏡像時,可以使用多個FROM鏡像(每個鏡像一次)。
為了保證鏡像精簡,可以選用體積較小的鏡像如Alpine或Debain作為基礎鏡像。
LABEL
LABEL指令可以為生成的鏡像添加元數據標簽信息。這些信息可以用來輔助過濾出特定鏡像。
格式:
LABEL <key>=<value> <key>=<value> <key>=<value>...
EXPOSE
聲明鏡像內服務監聽的端口。
格式:
EXPOSE <port> [<port>/<protocol>...]
eg:
EXPOSE 22 80 8443
注意:該指令只是起到聲明作用,並不會自動完成端口映射。
如果要映射端口出來,在啟動容器時可以使用-P參數(Docker主機會自動分配一個宿主機的臨時端口)或-p HOST_PORT:CONTAINER_PORT參數(具體指定所映射的本地端口)
ENV
指定環境變量,在鏡像生成過程中會被后續RUN指令使用,在鏡像啟動的容器中也會存在。
格式:
ENV <key> <value> 或 ENV <key>=<value>
eg:
ENV APP_VERSION=1.0.0
ENV APP_HOME=/usr/local/app
ENV PATH $PATH:/usr/local/bin
指令指定的環境變量在運行時可以被覆蓋掉,如docker run --env
注意:當一條ENV指令中同時為多個環境變量賦值且值也是從環境變量讀取時,會為變量都賦值后再更新。
如下面的指令,最終結果為:key1=value1 key2=value2
ENV key1=value2
ENV key1=value1 key2=${keys1}
ENTRYPOINT
指定鏡像的默認入口命令,該入口命令會在啟動容器時作為根命令執行,所有傳入值作為該命令的參數。
支持以下兩種格式:
#exec調用執行
ENTRYPOINT ["executable","param1","param2"]、
#shell中執行
ENTRYPOINT command param1 param2
此時,CMD指令指定值將作為根命令的參數。
每個Dockerfile中只能有一個ENTRYPOINT,當指定多個時,只有最后一個生效。
在運行時,可以被--entrypoint參數覆蓋掉,如:docker run --entrypoint(即覆蓋Dockerfile文件中ENTRYPOINT指令)
VOLUME
創建一個數據掛載點
格式:
VOLUME ["/data"]
運行容器時可以從本地主機或其他容器掛載數據卷,一般用來存放數據庫和需要保持的數據等。
USER
指定運行容器時的用戶名或UID,后續的RUN等指令也會使用指定的用戶身份。
格式:
USER daemon
當服務不需要管理員權限時,可以通過該命令指定運行用戶,並且可以在Dockerfile中創建所需要的用戶,例如:
RUN groupadd -r postgres && useradd --no-log-init -r -g postgress postgres
如要臨時獲取管理員權限可以使用goru命令。
WORKDIR
為后續的RUN、CMD、ENTRYPOINT指令配置工作目錄。
格式:
WORKDIR /path/to/workdir
可以使用多個WORKDIR指令,后續命令如果參數是相對路徑,則會基於之前命令指定的路徑。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
#最終路徑為:/a/b/c
因此,為了避免出錯,推薦WORKDIR指令中只使用絕對路徑
ONBUILD
指定當基於所生成鏡像創建子鏡像時,自動執行的操作指令。
格式:
ONBUILD [INSTRUCYION]
例如,使用如下的Dockerfile創建父鏡像ParentImage,指定ONBUILD指令:
#Dockerfile for ParentImage
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
使用docker build命令創建子鏡像ChildImage時(FROM ParentImage),會首先執行ParentImage中配置的ONBUILD指令:
#Dockerfile for ChildImage
FROM ParentImage
等價於在ChildImage的Dockerfile中添加了如下指令:
[...]
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
...
由於ONBUILD指令是隱式執行的,推薦在使用它的鏡像標簽中進行標注,如:ruby:2.1-onbuild
ONBUILD指令在創建專門用於自動編譯、檢查等操作的基礎鏡像時,十分有用。
STOPSIGNAL
指定所創建鏡像啟動的容器接收退出的信號值
格式:
STOPSIGNAL signal
HEALTHCHECK
配置所啟動容器如何進行健康檢查(如何判斷健康與否)。
格式有兩種:
#根據所執行命令返回值是否為0來判斷
HEALTHCHECK [OPTIONS] CMD command
#禁止基礎鏡像中的健康檢查
HEALTHCHECK NONE
OPTION支持如下參數:
- -interval=DURATION(default:30s):過多久檢查一次
- -timeout=DURATION(default:30s):每次檢查等待結果的超時
- -retries=N(default:3):如果失敗了,重試幾次才最終確定失敗。
SHELL
指定其他命令使用shell時的默認shell類型
格式:
SHELL ["executable","parameters"]
默認值為["/bin/sh","-C"]
注意:對於windows系統,shell路徑中使用了“\”作為分隔符,建議在Dockerfile開頭添加”#escape=“來指定轉義符。
2.2、操作指令介紹
RUN
運行指定命令
格式:
RUN <command> 或 RUN ["executable","param1","param2"]
注意:后者指令會被解析為JSON數組,因此必須使用雙引號。前者默認將在shell終端中運行命令,即/bin/sh -c;后者則使用exec執行,不會啟動shell環境。
指定使用其他終端類型可以通過exec方式實現,如:
RUN ["/bin/bsah","-c",“echo hello world"]
每條RUN指令將在當前鏡像基礎上執行指定命令,並提交為新的鏡像層。當命令較長時,可用\來換行,如:
RUN apt-get update \
&& apt-get install -y python3 libbz2-dev \
&& rm -rf /var/cache/apt \
&& rm -rf /var/lib/apt/list/*
CMD
CMD指令用來指定啟動容器時默認執行的命令。
支持三種格式:
#推薦方式
CMD ["executable","param1","param2"]
#在默認的shell中執行,提供給需要交互的應用
CMD command param1 param2
#提供給ENTRYPOINT的默認參數
CMD ["param1","param2"]
每個Dockerfile只能有一條CMD命令。如果指定了多條,只有最后一條會被執行。
如果用戶啟動容器時手動指定了運行的命令(作為run命令的參數),則會覆蓋掉CMD指定的命令
ADD
添加內容到鏡像
格式:
ADD <src> <dest>
#該命令將復制指定的<src>路徑下內容到容器中的<dest>路徑下。
其中src可以是Dockerfile所在目錄的一個相對路徑(文件或目錄),也可以是一個URL,還可以是一個tar文件(自動解壓為目錄);dest可以是鏡像內絕對路徑,或者相對於WORKDIR的相對路徑。
COPY
復制內容到鏡像
格式:
COPY <src> <dest>
復制本地主機的src(Dockerfile所在目錄的相對路徑、文件或目錄)下內容到鏡像中的dest。目標路徑不存在時,會自動創建
COPY和ADD指令功能相似,當使用本地目錄為源目錄時,推薦使用COPY。
3、Dockerfile示例
FROM node
MAINTAINER Taolei
RUN git clone -q https://github.com/docker-in-practice/todo.git
WORKDIR todo
RUN npm install > /dev/null
EXPOSE 8000
CMD ["npm","start"]
構建過程如下:
[root@aliyun docker]# pwd
/home/docker
#構建一個鏡像
[root@aliyun docker]# docker build .
Sending build context to Docker daemon 2.048kB
Step 1/7 : FROM node
latest: Pulling from library/node
0e29546d541c: Pull complete
9b829c73b52b: Pull complete
cb5b7ae36172: Pull complete
6494e4811622: Pull complete
6f9f74896dfa: Pull complete
f2930ff7fb60: Pull complete
c1d26179dd86: Pull complete
1fa56dd41537: Pull complete
321141c774e9: Pull complete
Digest: sha256:36aca218a5eb57cb23bc790a030591382c7664c15a384e2ddc2075761ac7e701
Status: Downloaded newer image for node:latest
---> a283f62cb84b
Step 2/7 : MAINTAINER Taolei
---> Running in c367d3410334
Removing intermediate container c367d3410334
---> e7567eaaf35c
Step 3/7 : RUN git clone -q https://github.com/docker-in-practice/todo.git
---> Running in 9191275f23b1
Removing intermediate container 9191275f23b1
---> 5d80658a509b
Step 4/7 : WORKDIR todo
---> Running in f297028141cf
Removing intermediate container f297028141cf
---> a81464358a14
Step 5/7 : RUN npm install > /dev/null
---> Running in 19d637151604
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE package: 'minifyify@4.4.0',
npm WARN EBADENGINE required: { node: '0.10.x' },
npm WARN EBADENGINE current: { node: 'v17.3.0', npm: '8.3.0' }
npm WARN EBADENGINE }
npm WARN deprecated graceful-fs@2.0.3: please upgrade to graceful-fs 4 for compatibility with current and future versions of Node.js
npm WARN deprecated buffer-browserify@0.2.5: Package not maintained. Recent browserify uses https://github.com/feross/buffer
npm WARN deprecated mkdirp@0.3.5: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
npm WARN deprecated mkdirp@0.3.5: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
npm WARN deprecated minimatch@0.3.0: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN deprecated minimatch@0.3.0: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN deprecated react-tools@0.11.2: react-tools is deprecated. For more information, visit https://fb.me/react-tools-deprecated
npm notice
npm notice New minor version of npm available! 8.3.0 -> 8.6.0
npm notice Changelog: <https://github.com/npm/cli/releases/tag/v8.6.0>
npm notice Run `npm install -g npm@8.6.0` to update!
npm notice
Removing intermediate container 19d637151604
---> d1b737e8b68f
Step 6/7 : EXPOSE 8000
---> Running in 27157638d7d6
Removing intermediate container 27157638d7d6
---> 6fafd82e1672
Step 7/7 : CMD ["npm","start"]
---> Running in fe6d70c5ec05
Removing intermediate container fe6d70c5ec05
---> eafa085c10fa
Successfully built eafa085c10fa
步驟說明:
- docker會上傳docker build指定目錄下的文件和目錄
- 每個構建步驟從1開始按順序編號,並與命令一起輸出
- 每個命令會導致一個新鏡像被創建出來,其鏡像ID在此輸出
- 為節省空間,在繼續前每個中間容器會被移除
- 構建的調試信息在此輸出
- 此次構建的最終鏡像ID,可用於打標簽
打標簽
#打標簽前
[root@aliyun docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> eafa085c10fa 7 minutes ago 1.1GB
busybox latest beae173ccac6 3 months ago 1.24MB
node latest a283f62cb84b 3 months ago 993MB
#打標簽時
[root@aliyun docker]# docker tag eafa085c10fa todoapp
#打標簽后
[root@aliyun docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
todoapp latest eafa085c10fa 8 minutes ago 1.1GB
busybox latest beae173ccac6 3 months ago 1.24MB
node latest a283f62cb84b 3 months ago 993MB
4、Dockerfile創建鏡像過程
編寫完Dockerfile之后,可使用docker build命令來創建鏡像。基本格式為:
docker build [OPTIONS] PATH | URL | -
創建過程:
該命令將讀取指定路徑下(包括子目錄)的Dockerfile,並將該路徑下所有數據作為上下文發送給docker服務端。docker服務端在校驗Dockerfile格式通過后,逐條執行其中定義的指令,碰到RUN、ADD和COPY指令會生成一層新的鏡像。最后如果創建鏡像成功,會返回最終鏡像的ID。
如果上下文過大,會導致發送大量數據給服務端,延緩創建過程。故而除非是生成鏡像所必須的文件,不然不要放在上下文路徑下。