Dockerfile命令詳解

這篇博客主要介紹了Dockerfile典型的基本結構和它支持的衆多指令,並具體講解如何通過這些指令來編寫定製鏡像的Dockerfile,以及如何生成鏡像。

一、Docker常用指令詳解
1)FROM:指定基礎鏡像
  FROM指令用於指定其後構建的新鏡像所使用的基礎鏡像。如果本地不存在,則Docker會默認去Docker Hub上去下載指定的鏡像。FROM指令必須是Dockerfile文件中的首條命令,啓動構建流程之後,Docker將基於該鏡像構建新的鏡像,FROM後面的命令也是基於此處指明的這個基礎鏡像。
FROM語法格式爲:

  FROM <image>或
  FROM <image>:<tag>或
  FROM <image>:<digest>

通過FROM指定的鏡像,可以是任何有效的基礎鏡像。FROM有以下限制:
  1、FROM必須是Dockerfile中的第一條非註釋命令;
  2、在一個Dockerfile文件中創建多個鏡像時,FROM可以出現多次。只需在每個新命令FROM
    之前,記錄提交上次的鏡像ID;
  3、tag或digest是可選的,如果不使用這兩個值時,默認會使用latest版本的基礎鏡像;

2)RUN:執行命令
  在鏡像的構建過程中執行特定的命令,並生成一箇中間鏡像。格式如下:

  #shell格式
  RUN <command>
  #exec格式
  RUN ["executable","param1","param2"]

1、RUN命令將在當前的image中執行任意合法命令並提交執行的結果。命令執行提交後,就
  會自動執行Dockerfile中的下一個指令。
  2、層級RUN指令和生成提交是符合Docker核心理念的理念的做法。它允許像版本控制那樣,
    在任意一個點,對image鏡像進行定製化構建。
  3、RUN指令創建的中間的鏡像會被緩存,並會在下次構建中使用。如果不想使用這些緩存
    鏡像,可以在構建時指定–no-cache參數,如:docker build --no-cache。
  4、每條RUN指令將在當前鏡像的基礎上執行指定的命令,並提交爲新的鏡像。當命令較長時
    可以使用""來換行。

3)CMD:啓動容器
  CMD用於指定在容器啓動時所要執行的命令。CMD有以下三種格式:

  CMD ["executable","param1","param2"]
  CMD ["param1","param2"]
  CMD command param1 param2

省略可執行文件的exec格式,這種寫法使CMD的參數當做ENTRYPOINT的默認參數,此時ENTRYPOINT也應該是exec格式,具體與ENTRYPOINT的組合使用,參考ENTRYPOINT。
注意:
  1、與RAN指令的區別:RAN在構建的時候執行,並生成一個新的鏡像,CMD在容器運行的時
   候執行,在構建時不進行任何操作;
  2、每個Dockerfile中只能有一條CMD命令。如果指定了多條命令,那麼也只有最後一條會被
   執行。如果用戶啓動容器時手動指定了運行的命令(作爲RUN的參數),則會覆蓋掉CMD
   指定的命令。

4)LABEL:添加元數據
  LABEL用於爲鏡像添加元數據,元數據以鍵值對的形式指定:

 LABEL <key>=<value> <key>=<value> <key>=<value> ...

使用LABEL指定元數據時,一條LABEL可以指定一條或者多條元數據,指定多條元數據時不同的元數據之間通過空格分隔。推薦將所有的元數據通過一條LABEL來指定一些元數據:

  LABEL version="1.0"  description="這是一個Web服務器" by="IT筆錄"

指定之後可以通過docker inspect來查看LABEL信息:

  docker inspect itbilu/test
  "Labels": {
      "version": "1.0",
      "description": "這是一個Web服務器",
      "by": "IT筆錄"
  },

5)EXPOSE:設置監聽的端口
  爲構建的鏡像設置監聽的端口,是容器在運行時監聽。格式:

  EXPOSE <post> [<post>...]
  #例如:EXPOSE 22 80 8443

注意:該指令只是起到聲明的作用,並不會自動的完成端口映射。在啓動容器時需要使用-P選項,Docker主機會隨機自動分配一個宿主機的臨時端口轉發到指定的端口;使用-p(注意大小寫),則可以具體的指定哪個宿主機的本地端口與容器的端口做映射關係。

6)ENV:設置環境變量
  指定環境變量,在鏡像生成過程中會被後續RUN指令使用,在鏡像啓動的容器中也會存在。

  ENV <key> <value>
  ENV <key1>=<value1> <key2>=<value2> ...

7)COPY:複製文件
  格式如下:

  COPY <源路徑>...<目標路徑>
  COPY ["<源路徑1>",..."<目標路徑>"]

複製本地主機的源地址(爲Dockerfile所在目錄的相對路徑、文件或目錄)下的內容到鏡像的目的路徑下。目標路徑不存在時,則會自動創建。當使用本地目錄時,推薦使用COPY

8)ADD:添加文件
  格式如下:

  ADD <源路徑>...<目標路徑>
  ADD ["<源路徑1>",..."<目標路徑>"]

該命令將複製指定的源路徑下的內容到容器的目的路徑下去。ADD指令和COPY指令的格式和性質基本是一致的。但是在COPY基礎上增加了一些功能。例如:源路徑可以是互聯網上的一個URL地址,這種情況下,Docker引擎會試圖去下載這個鏈接文件到目標路徑中去。

9)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參數將被傳遞給入口點。

 ENTRYPOINT ["/usr/bin/nginx"]

完整構建代碼:

  [root@docker mynginx]# vim Dockerfile 
  #Version:0.0.3
  FROM ubuntu:16.04
  MAINTAINER 江江 "[email protected]"
  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 -it  itbilu/test -g "daemon off;"

在運行容器時,我們使用了-g “daemon off;”,這個參數將會被傳遞給ENTRYPOINT,最終在容器中執行的命令爲"/usr/sbin/nginx -g daemon off;"

10)VOLUME:定義匿名卷
  VOLUME用於創建掛載點,即向基於所構建的鏡像創始的容器添加捲:

 VOLUME ["/data"]

一個卷可以存在於一個或多個容器的指定目錄,該目錄可以繞過聯合文件系統,並具有以下功能:
  1、卷可以容器間共享和重用;
  2、容器並不一定要和其他容器共享卷;
  3、修改卷後會立即生效;
  4、對卷的修改不會對鏡像產生影響;
  5、卷會一直存在,知道沒有任何容器再使用它;
  6、VOLUME讓我們可以將源代碼、數據或其他內容添加到鏡像中,而又不提交到鏡像中,並使我們的多個容器間可以共享這些內容。

11)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參數覆蓋Dockerfile文件構建時所設置的工作目錄。

12)USER:指定當前用戶
  指定運行容器時的用戶名或UID,後續的RUN等指令也會使用指定的用戶身份。語法格式:

  USER daemon

使用USER指定用戶時,可以使用用戶名、UID或GID。或者兩者的組合。以下都是合法的指定:

  USER  user
  USER  user:group
  USER  uid
  USER  uid:gid
  USER  user:gid
  USER  uid:group

13)ARG
  指定一些鏡像內使用的參數(例如版本號信息等),這些參數在執行docker build命令時才以–build-arg<varname>=<value>格式傳入。語法格式爲:

  ARG <name>[=<default value>]
  docker build --build-arg site=itiblu.com -t itbilu/test

14)ONBUILD
  配置當前所創建的鏡像作爲其他鏡像的基礎鏡像時,所執行的創建操作指令。語法格式爲:

  ONBUILD [INSTRUCTION]

例如:Dockerfile使用如下的內容創建了鏡像image-A:

  [...]
  ONBUILD ADD . /app/src
  ONBUILD RUN /usr/local/bin/python-build --dir /app/src
  [...]

如果基於image-A創建新的鏡像時,新的Dockerfile中使用FROM image-A指定基礎鏡像,會自動執行ONBUILD指令的內容,等價於在後面添加了兩條指令:

  FROM image-A
  ADD . /app/src
  RUN /usr/local/bin/python-build --dir /app/src
  [...]

使用ONBUILD指令的鏡像,推薦在標籤中著名,例如:ruby:1.9-onbuild。

15)STOPSIGNAL
  STOPSIGNAL用於設置停止容器所要發送的系統調用信號:

  STOPSIGNAL signal

所使用的信號必須是內核系統調用表中的合法的值,如:SIGKILL(kill -l可查看所有信號)。

16)SHELL
  指定其他命令使用shell時的默認shell類型。

  SHELL ["executable","parameters"]

默認值爲["/bin/sh","-c"]
對於Windows系統,建議在Dockerfile開頭添加# escape=來指定轉移信息。

二、Dockfile示例
構建Nginx運行環境:

# 指定基礎鏡像
FROM sameersbn/ubuntu:14.04.20161014

# 維護者信息
MAINTAINER [email protected]

# 設置環境
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環境:

# 指定基於的基礎鏡像
FROM ubuntu:13.10  

# 維護者信息
MAINTAINER zhangjiayang "[email protected]"  
  
# 鏡像的指令操作
# 獲取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.ou

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

三、原則和建議:
  首先,要儘量吃透每個指令的含義和執行效果,自己多編寫一些簡單的例子進行測試,弄清楚了再編寫正式的Dockerfile。此外,Docker Hub官方倉庫中提供了大量的優秀鏡像和對應的Dockerfile,可以通過閱讀它們來學習如何編寫高效的Dockerfile。
  1、容器輕量化。從鏡像中產生的容器應該儘量輕量化,能在足夠短的時間內停止、銷燬、重
    新生成並替換原來的容器。
  2、使用.gitignore。在大部分情況下,Dockerfile會和構建所需的文件放在同一個目錄中,爲了
    提高構建的性能,應該使用.gitignore來過濾掉不需要的文件和目錄。
  3、爲了減少鏡像的大小,減少依賴,僅安裝需要的軟件包。
  4、一個容器只做一件事情。解耦複雜的應用,分成多個容器,而不是所有東西都放在一個容
    器內運行。如一個Python Web應用,可能需要Server、DB、Cache、MQ、Log等幾個
    容器。一個更加極端的說法:One process per conatiner。
  5、減少鏡像的圖層。不要多個Label、ENV等標籤。
  6、對續行的參數按照字母表排序,特別是使用apt-get install -y安裝包的時候。
  7、使用構建緩存。如果不想使用緩存,可以在構建的時候使用參數–no-cache=true來強制重
    新生成中間鏡像。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章