Docker(三):Dockerfile 命令详解
上一篇文章Docker(二):Dockerfile 使用介紹介紹了 Dockerfile 的使用,這篇文章我們來繼續了解 Dockerfile ,學習 Dockerfile 各種命令的使用。
Dockerfile 指令詳解
1 FROM 指定基礎鏡像
FROM 指令用于指定其后構建新鏡像所使用的基礎鏡像。FROM 指令必是 Dockerfile 文件中的首條命令,啟動構建流程后,Docker 將會基于該鏡像構建新鏡像,FROM 后的命令也會基于這個基礎鏡像。
FROM語法格式為:
FROM <image>- 1
或
FROM <image>:<tag>- 1
或
FROM <image>:<digest>- 1
通過 FROM 指定的鏡像,可以是任何有效的基礎鏡像。FROM 有以下限制:
- FROM 必須 是 Dockerfile 中第一條非注釋命令
- 在一個 Dockerfile 文件中創建多個鏡像時,FROM 可以多次出現。只需在每個新命令 FROM 之前,記錄提交上次的鏡像 ID。
- tag 或 digest 是可選的,如果不使用這兩個值時,會使用 latest 版本的基礎鏡像
2 RUN 執行命令
在鏡像的構建過程中執行特定的命令,并生成一個中間鏡像。格式:
#shell格式 RUN <command> #exec格式 RUN ["executable", "param1", "param2"]- 1
- 2
- 3
- 4
- RUN 命令將在當前 image 中執行任意合法命令并提交執行結果。命令執行提交后,就會自動執行 Dockerfile 中的下一個指令。
- 層級 RUN 指令和生成提交是符合 Docker 核心理念的做法。它允許像版本控制那樣,在任意一個點,對 image 鏡像進行定制化構建。
- RUN 指令創建的中間鏡像會被緩存,并會在下次構建中使用。如果不想使用這些緩存鏡像,可以在構建時指定?--no-cache?參數,如:docker build --no-cache。
3 COPY 復制文件
格式:
COPY <源路徑>... <目標路徑> COPY ["<源路徑1>",... "<目標路徑>"]- 1
- 2
和 RUN 指令一樣,也有兩種格式,一種類似于命令行,一種類似于函數調用。COPY 指令將從構建上下文目錄中 <源路徑> 的文件/目錄復制到新的一層的鏡像內的<目標路徑>位置。比如:
COPY package.json /usr/src/app/- 1
<源路徑>可以是多個,甚至可以是通配符,其通配符規則要滿足 Go 的 filepath.Match 規則,如:
COPY hom* /mydir/ COPY hom?.txt /mydir/- 1
- 2
<目標路徑>可以是容器內的絕對路徑,也可以是相對于工作目錄的相對路徑(工作目錄可以用 WORKDIR 指令來指定)。目標路徑不需要事先創建,如果目錄不存在會在復制文件前先行創建缺失目錄。
此外,還需要注意一點,使用 COPY 指令,源文件的各種元數據都會保留。比如讀、寫、執行權限、文件變更時間等。這個特性對于鏡像定制很有用。特別是構建相關文件都在使用 Git 進行管理的時候。
4 ADD 更高級的復制文件
ADD 指令和 COPY 的格式和性質基本一致。但是在 COPY 基礎上增加了一些功能。比如<源路徑>可以是一個 URL,這種情況下,Docker 引擎會試圖去下載這個鏈接的文件放到<目標路徑>去。
在構建鏡像時,復制上下文中的文件到鏡像內,格式:
ADD <源路徑>... <目標路徑> ADD ["<源路徑>",... "<目標路徑>"]- 1
- 2
注意?
如果 docker 發現文件內容被改變,則接下來的指令都不會再使用緩存。關于復制文件時需要處理的/,基本跟正常的 copy 一致
5 ENV 設置環境變量
格式有兩種:
ENV <key> <value> ENV <key1>=<value1> <key2>=<value2>...- 1
- 2
這個指令很簡單,就是設置環境變量而已,無論是后面的其它指令,如 RUN,還是運行時的應用,都可以直接使用這里定義的環境變量。
ENV VERSION=1.0 DEBUG=on \NAME="Happy Feet"- 1
- 2
這個例子中演示了如何換行,以及對含有空格的值用雙引號括起來的辦法,這和 Shell 下的行為是一致的。
6 EXPOSE
為構建的鏡像設置監聽端口,使容器在運行時監聽。格式:
EXPOSE <port> [<port>...]- 1
EXPOSE 指令并不會讓容器監聽 host 的端口,如果需要,需要在 docker run 時使用?-p、-P?參數來發布容器端口到 host 的某個端口上。
7 VOLUME 定義匿名卷
VOLUME用于創建掛載點,即向基于所構建鏡像創始的容器添加卷:
VOLUME ["/data"]- 1
一個卷可以存在于一個或多個容器的指定目錄,該目錄可以繞過聯合文件系統,并具有以下功能:
- 卷可以容器間共享和重用
- 容器并不一定要和其它容器共享卷
- 修改卷后會立即生效
- 對卷的修改不會對鏡像產生影響
- 卷會一直存在,直到沒有任何容器在使用它
VOLUME 讓我們可以將源代碼、數據或其它內容添加到鏡像中,而又不并提交到鏡像中,并使我們可以多個容器間共享這些內容。
8 WORKDIR 指定工作目錄
WORKDIR用于在容器內設置一個工作目錄:
WORKDIR /path/to/workdir- 1
通過WORKDIR設置工作目錄后,Dockerfile 中其后的命令 RUN、CMD、ENTRYPOINT、ADD、COPY 等命令都會在該目錄下執行。?
如,使用WORKDIR設置工作目錄:
- 1
- 2
- 3
- 4
在以上示例中,pwd 最終將會在?/a/b/c?目錄中執行。在使用 docker run 運行容器時,可以通過-w參數覆蓋構建時所設置的工作目錄。
9 USER 指定當前用戶
USER 用于指定運行鏡像所使用的用戶:
USER daemon- 1
使用USER指定用戶時,可以使用用戶名、UID 或 GID,或是兩者的組合。以下都是合法的指定試:
USER user USER user:group USER uid USER uid:gid USER user:gid USER uid:group- 1
- 2
- 3
- 4
- 5
- 6
使用USER指定用戶后,Dockerfile 中其后的命令 RUN、CMD、ENTRYPOINT 都將使用該用戶。鏡像構建完成后,通過 docker run 運行容器時,可以通過?-u?參數來覆蓋所指定的用戶。
10 CMD
CMD用于指定在容器啟動時所要執行的命令。CMD 有以下三種格式:
CMD ["executable","param1","param2"] CMD ["param1","param2"] CMD command param1 param2- 1
- 2
- 3
省略可執行文件的 exec 格式,這種寫法使 CMD 中的參數當做 ENTRYPOINT 的默認參數,此時 ENTRYPOINT 也應該是 exec 格式,具體與 ENTRYPOINT 的組合使用,參考 ENTRYPOINT。
注意?
與 RUN 指令的區別:RUN 在構建的時候執行,并生成一個新的鏡像,CMD 在容器運行的時候執行,在構建時不進行任何操作。
11 ENTRYPOINT
ENTRYPOINT 用于給容器配置一個可執行程序。也就是說,每次使用鏡像創建容器時,通過 ENTRYPOINT 指定的程序都會被設置為默認程序。ENTRYPOINT 有以下兩種形式:
ENTRYPOINT ["executable", "param1", "param2"] ENTRYPOINT command param1 param2- 1
- 2
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"]- 1
完整構建代碼:
# 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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
使用docker build構建鏡像,并將鏡像指定為 itbilu/test:
docker build -t="itbilu/test" .- 1
構建完成后,使用itbilu/test啟動一個容器:
docker run -i -t itbilu/test -g "daemon off;"- 1
在運行容器時,我們使用了?-g "daemon off;",這個參數將會被傳遞給 ENTRYPOINT,最終在容器中執行的命令為?/usr/sbin/nginx -g "daemon off;"。
12 LABEL
LABEL用于為鏡像添加元數據,元數以鍵值對的形式指定:
LABEL <key>=<value> <key>=<value> <key>=<value> ...- 1
使用LABEL指定元數據時,一條LABEL指定可以指定一或多條元數據,指定多條元數據時不同元數據之間通過空格分隔。推薦將所有的元數據通過一條LABEL指令指定,以免生成過多的中間鏡像。?
如,通過LABEL指定一些元數據:
- 1
指定后可以通過docker inspect查看:
docker inspect itbilu/test "Labels": {"version": "1.0","description": "這是一個Web服務器","by": "IT筆錄" },- 1
- 2
- 3
- 4
- 5
- 6
13 ARG
ARG用于指定傳遞給構建運行時的變量:
ARG <name>[=<default value>]- 1
如,通過ARG指定兩個變量:
ARG site ARG build_user=IT筆錄- 1
- 2
以上我們指定了 site 和 build_user 兩個變量,其中 build_user 指定了默認值。在使用 docker build 構建鏡像時,可以通過?--build-arg <varname>=<value>?參數來指定或重設置這些變量的值。
docker build --build-arg site=itiblu.com -t itbilu/test .- 1
這樣我們構建了 itbilu/test 鏡像,其中site會被設置為 itbilu.com,由于沒有指定 build_user,其值將是默認值 IT 筆錄。
14 ONBUILD
ONBUILD用于設置鏡像觸發器:
ONBUILD [INSTRUCTION]- 1
當所構建的鏡像被用做其它鏡像的基礎鏡像,該鏡像中的觸發器將會被鑰觸發。?
如,當鏡像被使用時,可能需要做一些處理:
- 1
- 2
- 3
- 4
15 STOPSIGNAL
STOPSIGNAL用于設置停止容器所要發送的系統調用信號:
STOPSIGNAL signal- 1
所使用的信號必須是內核系統調用表中的合法的值,如:SIGKILL。
16 SHELL
SHELL用于設置執行命令(shell式)所使用的的默認 shell 類型:
SHELL ["executable", "parameters"]- 1
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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
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"]- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
構建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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
原則與建議
- 容器輕量化。從鏡像中產生的容器應該盡量輕量化,能在足夠短的時間內停止、銷毀、重新生成并替換原來的容器。
- 使用?.gitignore。在大部分情況下,Dockerfile 會和構建所需的文件放在同一個目錄中,為了提高構建的性能,應該使用?.gitignore?來過濾掉不需要的文件和目錄。
- 為了減少鏡像的大小,減少依賴,僅安裝需要的軟件包。
- 一個容器只做一件事。解耦復雜的應用,分成多個容器,而不是所有東西都放在一個容器內運行。如一個 Python Web 應用,可能需要 Server、DB、Cache、MQ、Log 等幾個容器。一個更加極端的說法:One process per container。
- 減少鏡像的圖層。不要多個 Label、ENV 等標簽。
- 對續行的參數按照字母表排序,特別是使用apt-get install -y安裝包的時候。
- 使用構建緩存。如果不想使用緩存,可以在構建的時候使用參數--no-cache=true來強制重新生成中間鏡像。
總結
以上是生活随笔為你收集整理的Docker(三):Dockerfile 命令详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Docker(一):Docker入门教程
- 下一篇: Docker(四):Docker 三剑客