上一篇文章Docker(二):Dockerfile 使用介紹介紹了 Dockerfile 的使用,這篇文章我們來繼續瞭解 Dockerfile ,學習 Dockerfile 各種命令的使用。
Dockerfile 指令詳解
1 FROM 指定基礎映象
FROM 指令用於指定其後構建新映象所使用的基礎映象。FROM 指令必是 Dockerfile 檔案中的首條命令,啟動構建流程後,Docker 將會基於該映象構建新映象,FROM 後的命令也會基於這個基礎映象。
FROM語法格式為:
FROM <image>
或
FROM <image>:<tag>
或
FROM <image>:<digest>
通過 FROM 指定的映象,可以是任何有效的基礎映象。FROM 有以下限制:
- FROM 必須 是 Dockerfile 中第一條非註釋命令
- 在一個 Dockerfile 檔案中建立多個映象時,FROM 可以多次出現。只需在每個新命令 FROM 之前,記錄提交上次的映象 ID。
- tag 或 digest 是可選的,如果不使用這兩個值時,會使用 latest 版本的基礎映象
2 RUN 執行命令
在映象的構建過程中執行特定的命令,並生成一箇中間映象。格式:
#shell格式
RUN <command>
#exec格式
RUN ["executable", "param1", "param2"]
- RUN 命令將在當前 image 中執行任意合法命令並提交執行結果。命令執行提交後,就會自動執行 Dockerfile 中的下一個指令。
- 層級 RUN 指令和生成提交是符合 Docker 核心理念的做法。它允許像版本控制那樣,在任意一個點,對 image 映象進行定製化構建。
- RUN 指令建立的中間映象會被快取,並會在下次構建中使用。如果不想使用這些快取映象,可以在構建時指定
--no-cache
引數,如:docker build --no-cache
。
3 COPY 複製檔案
格式:
COPY <源路徑>... <目標路徑>
COPY ["<源路徑1>",... "<目標路徑>"]
和 RUN 指令一樣,也有兩種格式,一種類似於命令列,一種類似於函式呼叫。COPY 指令將從構建上下文目錄中 的檔案/目錄複製到新的一層的映象內的<目標路徑>
位置。比如:
COPY package.json /usr/src/app/
<源路徑>
可以是多個,甚至可以是萬用字元,其萬用字元規則要滿足 Go 的 filepath.Match 規則,如:
COPY hom* /mydir/
COPY hom?.txt /mydir/
<目標路徑>
可以是容器內的絕對路徑,也可以是相對於工作目錄的相對路徑(工作目錄可以用 WORKDIR 指令來指定)。目標路徑不需要事先建立,如果目錄不存在會在複製檔案前先行建立缺失目錄。
此外,還需要注意一點,使用 COPY 指令,原始檔的各種後設資料都會保留。比如讀、寫、執行許可權、檔案變更時間等。這個特性對於映象定製很有用。特別是構建相關檔案都在使用 Git 進行管理的時候。
4 ADD 更高階的複製檔案
ADD 指令和 COPY 的格式和性質基本一致。但是在 COPY 基礎上增加了一些功能。比如<源路徑>
可以是一個 URL,這種情況下,Docker 引擎會試圖去下載這個連結的檔案放到<目標路徑>
去。
在構建映象時,複製上下文中的檔案到映象內,格式:
ADD <源路徑>... <目標路徑>
ADD ["<源路徑>",... "<目標路徑>"]
注意
如果 docker 發現檔案內容被改變,則接下來的指令都不會再使用快取。關於複製檔案時需要處理的/,基本跟正常的 copy 一致
5 ENV 設定環境變數
格式有兩種:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
這個指令很簡單,就是設定環境變數而已,無論是後面的其它指令,如 RUN,還是執行時的應用,都可以直接使用這裡定義的環境變數。
ENV VERSION=1.0 DEBUG=on \
NAME="Happy Feet"
這個例子中演示瞭如何換行,以及對含有空格的值用雙引號括起來的辦法,這和 Shell 下的行為是一致的。
6 EXPOSE
為構建的映象設定監聽埠,使容器在執行時監聽。格式:
EXPOSE <port> [<port>...]
EXPOSE 指令並不會讓容器監聽 host 的埠,如果需要,需要在 docker run 時使用 -p
、-P
引數來發布容器埠到 host 的某個埠上。
7 VOLUME 定義匿名卷
VOLUME用於建立掛載點,即向基於所構建映象創始的容器新增捲:
VOLUME ["/data"]
一個卷可以存在於一個或多個容器的指定目錄,該目錄可以繞過聯合檔案系統,並具有以下功能:
- 卷可以容器間共享和重用
- 容器並不一定要和其它容器共享卷
- 修改卷後會立即生效
- 對卷的修改不會對映象產生影響
- 卷會一直存在,直到沒有任何容器在使用它
VOLUME 讓我們可以將原始碼、資料或其它內容新增到映象中,而又不併提交到映象中,並使我們可以多個容器間共享這些內容。
8 WORKDIR 指定工作目錄
WORKDIR用於在容器內設定一個工作目錄:
WORKDIR /path/to/workdir
通過WORKDIR設定工作目錄後,Dockerfile 中其後的命令 RUN、CMD、ENTRYPOINT、ADD、COPY 等命令都會在該目錄下執行。
如,使用WORKDIR設定工作目錄:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
在以上示例中,pwd 最終將會在 /a/b/c
目錄中執行。在使用 docker run 執行容器時,可以通過-w
引數覆蓋構建時所設定的工作目錄。
9 USER 指定當前使用者
USER 用於指定執行映象所使用的使用者:
USER daemon
使用USER指定使用者時,可以使用使用者名稱、UID 或 GID,或是兩者的組合。以下都是合法的指定試:
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
使用USER指定使用者後,Dockerfile 中其後的命令 RUN、CMD、ENTRYPOINT 都將使用該使用者。映象構建完成後,通過 docker run 執行容器時,可以通過 -u
引數來覆蓋所指定的使用者。
10 CMD
CMD用於指定在容器啟動時所要執行的命令。CMD 有以下三種格式:
CMD ["executable","param1","param2"]
CMD ["param1","param2"]
CMD command param1 param2
省略可執行檔案的 exec 格式,這種寫法使 CMD 中的引數當做 ENTRYPOINT 的預設引數,此時 ENTRYPOINT 也應該是 exec 格式,具體與 ENTRYPOINT 的組合使用,參考 ENTRYPOINT。
注意
與 RUN 指令的區別:RUN 在構建的時候執行,並生成一個新的映象,CMD 在容器執行的時候執行,在構建時不進行任何操作。
11 ENTRYPOINT
ENTRYPOINT 用於給容器配置一個可執行程式。也就是說,每次使用映象建立容器時,通過 ENTRYPOINT 指定的程式都會被設定為預設程式。ENTRYPOINT 有以下兩種形式:
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
ENTRYPOINT 與 CMD 非常類似,不同的是通過docker run
執行的命令不會覆蓋 ENTRYPOINT,而docker run
命令中指定的任何引數,都會被當做引數再次傳遞給 ENTRYPOINT。Dockerfile 中只允許有一個 ENTRYPOINT 命令,多指定時會覆蓋前面的設定,而只執行最後的 ENTRYPOINT 指令。
docker run
執行容器時指定的引數都會被傳遞給 ENTRYPOINT ,且會覆蓋 CMD 命令指定的引數。如,執行docker run <image> -d
時,-d 引數將被傳遞給入口點。
也可以通過docker run --entrypoint
重寫 ENTRYPOINT 入口點。如:可以像下面這樣指定一個容器執行程式:
ENTRYPOINT ["/usr/bin/nginx"]
完整構建程式碼:
# Version: 0.0.3
FROM ubuntu:16.04
MAINTAINER 何民三 "cn.liuht@gmail.com"
RUN apt-get update
RUN apt-get install -y nginx
RUN echo 'Hello World, 我是個容器' \
> /var/www/html/index.html
ENTRYPOINT ["/usr/sbin/nginx"]
EXPOSE 80
使用docker build構建映象,並將映象指定為 itbilu/test:
docker build -t="itbilu/test" .
構建完成後,使用itbilu/test啟動一個容器:
docker run -i -t itbilu/test -g "daemon off;"
在執行容器時,我們使用了 -g "daemon off;"
,這個引數將會被傳遞給 ENTRYPOINT,最終在容器中執行的命令為 /usr/sbin/nginx -g "daemon off;"
。
12 LABEL
LABEL用於為映象新增後設資料,元數以鍵值對的形式指定:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
使用LABEL指定後設資料時,一條LABEL指定可以指定一或多條後設資料,指定多條後設資料時不同後設資料之間通過空格分隔。推薦將所有的後設資料通過一條LABEL指令指定,以免生成過多的中間映象。
如,通過LABEL指定一些後設資料:
LABEL version="1.0" description="這是一個Web伺服器" by="IT筆錄"
指定後可以通過docker inspect檢視:
docker inspect itbilu/test
"Labels": {
"version": "1.0",
"description": "這是一個Web伺服器",
"by": "IT筆錄"
},
13 ARG
ARG用於指定傳遞給構建執行時的變數:
ARG <name>[=<default value>]
如,通過ARG指定兩個變數:
ARG site
ARG build_user=IT筆錄
以上我們指定了 site 和 build_user 兩個變數,其中 build_user 指定了預設值。在使用 docker build 構建映象時,可以通過 --build-arg <varname>=<value>
引數來指定或重設定這些變數的值。
docker build --build-arg site=itiblu.com -t itbilu/test .
這樣我們構建了 itbilu/test 映象,其中site會被設定為 itbilu.com,由於沒有指定 build_user,其值將是預設值 IT 筆錄。
14 ONBUILD
ONBUILD用於設定映象觸發器:
ONBUILD [INSTRUCTION]
當所構建的映象被用做其它映象的基礎映象,該映象中的觸發器將會被鑰觸發。
如,當映象被使用時,可能需要做一些處理:
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
15 STOPSIGNAL
STOPSIGNAL用於設定停止容器所要傳送的系統呼叫訊號:
STOPSIGNAL signal
所使用的訊號必須是核心系統呼叫表中的合法的值,如:SIGKILL。
16 SHELL
SHELL用於設定執行命令(shell式)所使用的的預設 shell 型別:
SHELL ["executable", "parameters"]
SHELL在Windows環境下比較有用,Windows 下通常會有 cmd 和 powershell 兩種 shell,可能還會有 sh。這時就可以通過 SHELL 來指定所使用的 shell 型別:
FROM microsoft/windowsservercore
# Executed as cmd /S /C echo default
RUN echo default
# Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default
# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello
# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S"", "/C"]
RUN echo hello
Dockerfile 使用經驗
Dockerfile 示例
構建Nginx執行環境
# 指定基礎映象
FROM sameersbn/ubuntu:14.04.20161014
# 維護者資訊
MAINTAINER sameer@damagehead.com
# 設定環境
ENV RTMP_VERSION=1.1.10 \
NPS_VERSION=1.11.33.4 \
LIBAV_VERSION=11.8 \
NGINX_VERSION=1.10.1 \
NGINX_USER=www-data \
NGINX_SITECONF_DIR=/etc/nginx/sites-enabled \
NGINX_LOG_DIR=/var/log/nginx \
NGINX_TEMP_DIR=/var/lib/nginx \
NGINX_SETUP_DIR=/var/cache/nginx
# 設定構建時變數,映象建立完成後就失效
ARG BUILD_LIBAV=false
ARG WITH_DEBUG=false
ARG WITH_PAGESPEED=true
ARG WITH_RTMP=true
# 複製本地檔案到容器目錄中
COPY setup/ ${NGINX_SETUP_DIR}/
RUN bash ${NGINX_SETUP_DIR}/install.sh
# 複製本地配置檔案到容器目錄中
COPY nginx.conf /etc/nginx/nginx.conf
COPY entrypoint.sh /sbin/entrypoint.sh
# 執行指令
RUN chmod 755 /sbin/entrypoint.sh
# 允許指定的埠
EXPOSE 80/tcp 443/tcp 1935/tcp
# 指定網站目錄掛載點
VOLUME ["${NGINX_SITECONF_DIR}"]
ENTRYPOINT ["/sbin/entrypoint.sh"]
CMD ["/usr/sbin/nginx"]
構建tomcat 環境
Dockerfile檔案
# 指定基於的基礎映象
FROM ubuntu:13.10
# 維護者資訊
MAINTAINER zhangjiayang "zhangjiayang@sczq.com.cn"
# 映象的指令操作
# 獲取APT更新的資源列表
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe"> /etc/apt/sources.list
# 更新軟體
RUN apt-get update
# Install curl
RUN apt-get -y install curl
# Install JDK 7
RUN cd /tmp && curl -L 'http://download.oracle.com/otn-pub/java/jdk/7u65-b17/jdk-7u65-linux-x64.tar.gz' -H 'Cookie: oraclelicense=accept-securebackup-cookie; gpw_e24=Dockerfile' | tar -xz
RUN mkdir -p /usr/lib/jvm
RUN mv /tmp/jdk1.7.0_65/ /usr/lib/jvm/java-7-oracle/
# Set Oracle JDK 7 as default Java
RUN update-alternatives --install /usr/bin/java java /usr/lib/jvm/java-7-oracle/bin/java 300
RUN update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/java-7-oracle/bin/javac 300
# 設定系統環境
ENV JAVA_HOME /usr/lib/jvm/java-7-oracle/
# Install tomcat7
RUN cd /tmp && curl -L 'http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.8/bin/apache-tomcat-7.0.8.tar.gz' | tar -xz
RUN mv /tmp/apache-tomcat-7.0.8/ /opt/tomcat7/
ENV CATALINA_HOME /opt/tomcat7
ENV PATH $PATH:$CATALINA_HOME/bin
# 復件tomcat7.sh到容器中的目錄
ADD tomcat7.sh /etc/init.d/tomcat7
RUN chmod 755 /etc/init.d/tomcat7
# Expose ports. 指定暴露的埠
EXPOSE 8080
# Define default command.
ENTRYPOINT service tomcat7 start && tail -f /opt/tomcat7/logs/catalina.out
tomcat7.sh
命令檔案
export JAVA_HOME=/usr/lib/jvm/java-7-oracle/
export TOMCAT_HOME=/opt/tomcat7
case $1 in
start)
sh $TOMCAT_HOME/bin/startup.sh
;;
stop)
sh $TOMCAT_HOME/bin/shutdown.sh
;;
restart)
sh $TOMCAT_HOME/bin/shutdown.sh
sh $TOMCAT_HOME/bin/startup.sh
;;
esac
exit 0
原則與建議
- 容器輕量化。從映象中產生的容器應該儘量輕量化,能在足夠短的時間內停止、銷燬、重新生成並替換原來的容器。
- 使用
.gitignore
。在大部分情況下,Dockerfile 會和構建所需的檔案放在同一個目錄中,為了提高構建的效能,應該使用.gitignore
來過濾掉不需要的檔案和目錄。 - 為了減少映象的大小,減少依賴,僅安裝需要的軟體包。
- 一個容器只做一件事。解耦複雜的應用,分成多個容器,而不是所有東西都放在一個容器內執行。如一個 Python Web 應用,可能需要 Server、DB、Cache、MQ、Log 等幾個容器。一個更加極端的說法:One process per container。
- 減少映象的圖層。不要多個 Label、ENV 等標籤。
- 對續行的引數按照字母表排序,特別是使用
apt-get install -y
安裝包的時候。 - 使用構建快取。如果不想使用快取,可以在構建的時候使用引數
--no-cache=true
來強制重新生成中間映象。
參考
Dockerfile reference
使用Dockerfile構建Docker映象
Docker映象構建檔案Dockerfile及相關命令介紹
深入Dockerfile(一): 語法指南
Docker — 從入門到實踐