企業——Dockerfile的編寫及Dockerfile的優化

一.什麼是Dockerfile?

  Dockerfile是一個包含用於組合映像的命令的文本文檔。可使用在命令行中調用任何命令。 Docker經過讀取 Dockerfile 中的指令自動生成映像。docker build 命令用於從 Dockerfile 構建映像。能夠在 docker build 命令中使用 -f 標誌指向文件系統中任何位置的Dockerfile。html

  例如:docker build -f /path/to/a/Dockerfilemysql

 

 

二.Dockerfile的基本結構和相應的命令說明

  Dockerfile 通常分爲四部分:基礎鏡像信息、維護者信息、鏡像操做指令和容器啓動時執行指令,## 爲 Dockerfile 中的註釋信息。linux

  Docker以從上到下的順序運行Dockerfile的指令。爲了指定基本映像,第一條指令必須是FROM。注意:以#字符開頭,被視爲註釋。能夠在Docker文件中使用RUN(執行)、CMD(命令)、FROM(指定基本的映像信息)、EXPOSE、ENV等指令。nginx

 

1. FROM:指定基礎鏡像,必須爲第一個命令

  格式:   web

    FROM <image>  FROM <image>:<tag>  FROM <image>@<digest>sql

  示例:docker

    FROM  mysql:5.6  ##注意:tag或digest是可選的,若是不使用這兩個值時,會使用latest版本的基礎鏡像shell

 

2.MAINTAINER: 維護者信息

  格式:數據庫

    MAINTAINER  <name>ubuntu

  示例:

    MAINTAINER  wangfang  MAINTAINER  wang@qq.com  MAINTAINER  wangfang  <wang@qq.com>

 

3.RUN:構建鏡像時執行的命令

  RUN用於在鏡像容器中執行命令,具備兩種命令執行的方式:

  (1)shell執行:RUN <command>

  (2)exec執行:RUN ["executable", "param1", "param2"]  示例:RUN ["/etc/execfile", "arg1", "arg1"]

 

4.ADD:將本地文件添加到容器中,tar類型文件會自動解壓(網絡壓縮資源不會被解壓),能夠訪問網絡資源

  格式:

   ADD <src>... <dest>       ADD ["<src>",... "<dest>"]  ##用於支持包含空格的路徑

  示例:

   ADD hom* /mydir/      ##添加全部以"hom"開頭的文件

   ADD hom?.txt /mydir/      ## ? 替代一個單字符。例如:"home.txt"

   ADD test relativeDir/       ## 添加 "test" 到 `WORKDIR`/relativeDir/

   ADD test /absoluteDir/    ##添加 "test" 到 /absoluteDir/

 

5.COPY:功能相似ADD,可是是不會自動解壓文件,也不能訪問網絡資源

6.CMD:構建容器後調用,也就是在容器啓動時才進行調用。

  格式:

   CMD ["executable","param1","param2"] (執行可執行文件,優先)      CMD ["param1","param2"] (設置了ENTRYPOINT,則直接調用ENTRYPOINT添加參數)      CMD command param1 param2 (執行shell內部命令)

  示例:

   CMD echo "This is a test." | wc - CMD ["/usr/bin/wc","--help"]  ##注意:CMD不一樣於RUN,CMD用於指定在容器啓動時所要執行的命令,而RUN用於指定鏡像構建時所要執行的命令。

 

7.ENTRYPOINT:配置容器,使其可執行化。配合CMD可省去"application",只使用參數。

  格式:

  ENTRYPOINT ["executable", "param1", "param2"] (可執行文件, 優先)

  ENTRYPOINT command param1 param2 (shell內部命令)

  示例:

  FROM ubuntu ENTRYPOINT ["top", "-b"] CMD ["-c"]    ##注意:ENTRYPOINT與CMD很是相似,不一樣的是經過 docker run 執行的命令不會覆蓋ENTRYPOINT,而 docker run 命令中指定的任何參數,都會被當作參數再次傳遞給ENTRYPOINT。Dockerfile中只容許有一個ENTRYPOINT命令,多指定時會覆蓋前面的設置,而只執行最後的ENTRYPOINT指令

 

8.ENV:設置環境變量

  格式:

  ENV <key> <value>    ##<key>以後的全部內容均會被視爲其<value>的組成部分,所以,一次只能設置一個變量

  ENV <key>=<value> ...    ##能夠設置多個變量,每一個變量爲一個"<key>=<value>"的鍵值對,若是<key>中包含空格,可使用\來進行轉義,也能夠經過""來進行標示;另外,反斜線也能夠用於續行

  示例:

   ENV myName wangfang      ENV myDog Rourou The Dog     ENV myCat=kitty

 

9.EXPOSE:指定於外界交互的端口

  格式:

   EXPOSE <port> [<port>...]

  示例:

   EXPOSE 80 443     EXPOSE 8080    EXPOSE 11211/tcp 11211/udp    ##注意:EXPOSE並不會讓容器的端口訪問到主機。要使其可訪問,須要在 docker run 運行容器時經過 -p 來發布這些端口,或經過 -p 參數來發布EXPOSE導出的全部端口

 

10.WORKDIR:工做目錄,相似於cd命令

  格式:

   WORKDIR /path/to/workdir    ## WORKDIR至關於linux中的命令 cd

  示例:

  WORKDIR /a (這時工做目錄爲/a)       WORKDIR b (這時工做目錄爲/a/b)      WORKDIR c (這時工做目錄爲/a/b/c)
  注意:經過WORKDIR設置工做目錄後,Dockerfile中其後的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都會在該目錄下執行。在使用 docker run 運行容器時,能夠經過 -w 參數覆蓋構建時所設置的工做目錄。

 

11.VOLUME:可實現掛載功能,能夠將內地文件夾或者其餘容器種得文件夾掛在到這個容器種

   格式:

  volume  ["/data"]

  示例:

  volume  ["/avr/log/message"]

  volume  /var/log

  volume  /var/log  /etc/passwd

 

==========================================================================

 Dockerfile文件中CMD和EMTRYPOINT的區別?

  ENTRYPOINT 容器啓動後執行的命令,讓容器執行表現的像一個可執行程序同樣,與CMD的區別是:ENTRYPOINT 不會被 docker run 覆 蓋 , 會把 docker run 後面的參數,如:$name 等,看成傳遞給 ENTRYPOINT 指令的參數。Dockerfile 中只能指定一個 ENTRYPOINT,若是指定了不少,只有最後一個有效。docker run 命令的 -entrypoint 參數能夠把指定的參數繼續傳遞給 ENTRYPOINT。

  下面進行相應的測試,利用結果感受這兩個參數的不一樣:

(1)測試 ENTRYPOINT:

  docker load -i busybox.tar   ##導入一個鏡像
  cd /tmp/docker/
  mkdir test
  cd test/
  pwd
    /tmp/docker/test
  vim Dockerfile
    FROM busybox
    ENV name world
    CMD echo "hello,$name"

  docker build -t busybox:v1 .    ##建立鏡像
  docker run --rm busybox:v1    ##利用鏡像建立新的容器
    hello,world

(2)測試CMD:

  vim Dockerfile
    FROM busybox
    ENV name world
    CMD ["/bin/echo","hello,$name"]


  docker build -t busybox:v2 .
  docker run --rm busybox:v2
    hello,$name

(3)使CMD不會被覆蓋掉的方法

  vim Dockerfile
    FROM busybox
    ENV name world
    CMD ["/bin/sh","-c","echo hello, $name"]    ##這個命令在底層實際執行過程當中,調用 /bin/sh -c  echo "hello world"

  docker build -t busybox:v3 .
  docker run --rm busybox:v3
    hello, world

 

  vim Dockerfile
    FROM busybox
    ENTRYPOINT ["/bin/echo", "hello"]
    CMD ["world"]
  docker build -t busybox:v4 .

  docker run --rm busybox:v4
    hello world
  docker run --rm busybox:v4 westos
    hello westos

==========================================================================

 

 

三.Dockerfile的編寫  (這裏安裝的是HTTP服務)

1.導入鏡像

  docker load -i rhel7.tar

 

2.編寫相應的 Dockerfile 文件

  pwd
    /tmp/docker
  vim Dockerfile
    FROM rhel7    ##源鏡像是rhel7,最好將名爲rhel7的鏡像放在本地

         ENV HOSTNAME server1
         COPY dvd.repo /etc/yum.repos.d/dvd.repo     ##倉庫沒有yum源,將真機的yum源cp到容器裏
    RUN rpmdb --rebuilddb && yum install -y httpd && yum clean all  ##執行命令安裝httpd並清除yum緩存,rpmdb 命令用於初始化和重建rpm數據庫,--rebuilddb:從已安裝的包頭文件,反向重建RPM數據庫
    EXPOSE 80      ##定義端口爲80
    CMD ["/usr/share/httpd","-D","FOREGROUND"]  ### 打開apach服務, -D 是全局文件/etc/sysconfig/httpd中的打開參數

 

3.編寫yum.repo(在上面的真機的路徑下面配置相應的文件)

  vim yum.repo

    [rhel7.3]
    name=rhel7.3
    baseurl=http://172.25.254.1/rhel7.3    ##這裏是網絡yum源,本地的yum源用的是 file:/// 格式。
    gpgcheck=0

 

4.封裝鏡像,並測試可否正常使用

  以前都是用公共倉庫中的鏡像,直接用命令search 尋找相應的鏡像,而後進行相應的拉取,最後直接利用鏡像建立容器就能夠了。如今使用的Dockerfile,就至關因而要用最基礎的鏡像配置(自定義)咱們本身的鏡像,而後也能夠上傳到私有倉庫或者共有倉庫當中。就至關於用剛纔編寫的 Dockerfile 文件所用的基礎的鏡像,在基礎的鏡像上添加了許多的新的應用,就好比這裏添加的HTTP的功能,而後將這個新的添加了功能的這個容器封裝成新的鏡像,而後用新封裝的鏡像去建立容器,這樣,這個建立的容器就能夠,具備HTTP的服務。

 

  docker build -t rhel7:v1 . (注意後面 有個點表示當前目錄)  ##使用當前目錄的 Dockerfile 建立鏡像,標籤爲 rhel7:v1

  docker run -d --name vm1 rhel7:v1    ##利用剛纔新建立的封裝的鏡像建立須要HTTP的容器。新的鏡像就至關於一個支持HTTP的鏡像
  docker inspect vm1    ##查看新建立的容器vm1的信息,如:IP(172.17.0.2)
  cd /tmp/docker/
  ls
    Dockerfile dvd.repo
  vim index.html
    www.westos.org
  docker container cp index.html vm1:/var/www/html
  curl 172.17.0.2      ##能夠正常訪問使用
    www.westos.org

 

5.添加數據卷掛載位置(VOLUME [「var/www/html」])  (設定docker的某一個目錄路徑掛載在數據卷)

  vim Dockerfile
    FROM rhel7
    COPY dvd.repo /etc/yum.repos.d/dvd.repo
    RUN rpmdb --rebuilddb && yum clean all && yum install -y httpd 

    EXPOSE 80
    VOLUME ["var/www/html"]  ##將主機上的某一個目錄或者路徑下的文件掛載到docker容器上
    CMD ["/usr/sbin/httpd","-D","FOREGROUND"]

  docker build -t rhel7:v2 .  ##封裝新的有掛載的鏡像 rhel7:v2

 

  此時能夠看到 rhel7:v2比 rhel7:v1多了一層: 利用 docker history 容器名  查看容器的每一層的實際的內容

      

  測試: (測試頁面是否能夠正常訪問)

  mkdir webdata
  mv index.html webdata/
  ls
    Dockerfile webdata yum.repo

  docker rm -f vm1
  docker run -d --name vm1 -v /tmp/docker/webdata:/var/www/html rhel7:v2  ##若是指定掛載的路徑,source就是指定的。若是沒有指定的話,就像下面的操做同樣,直接利用鏡像建立了容器,掛載的目錄路徑是默認的。能夠用 inspect 查看
    a8203a3b35eeeec170b7283fbbda136cffebc180a41c49891d3dd80c99f0e8ed
  curl 172.17.0.2     ##能夠正常的訪問
    www.westos.org

 

6.掛載修改掛載文件,發佈目錄內容相應改變  (在沒有特定的設置掛載目錄的狀況下)

  直接在數據卷中修改:docker rm -f vm1    docker run -d --name vm1 rhel7:v2

  查看vm1的數據卷的位置:

      

  進入數據卷位置並編寫發佈文件並測試:

  cd /var/lib/docker/volumes/f03725ca02f048a96dc78ed02f3c9b1c29b198458d99a0765e7d51d56d22def9/_data
  cp /tmp/docker/webdata/index.html   .
  ls
    index.html
  curl 172.17.0.2
    www.westos.org

  echo hello,world >> index.html
  cat index.html
    www.westos.org
    hello,world
  curl 172.17.0.2
    www.westos.org
    hello,world

 

7.設定的只讀掛載

   docker rm -f vm1  ##由於設置的不一樣,須要將以前的容器刪除。

  docker run -d --name vm1 -v /tmp/docker/webdata:/data:ro rhel7:v2  ##指定了docker掛載的目錄路徑,並且設置了只讀掛載 ro

  docker inspect vm1    ##找到source對應的數據卷位置
    /var/lib/docker/volumes/267d70d0fec9229a426c4e14f83b614ae4bc5acdbc2723862e0af006a78a2ec9/_data


  docker volume rm f03725ca02f048a96dc78ed02f3c9b1c29b198458d99a0765e7d51d56d22def9  ##刪除以前的數據卷

  docker exec -it vm1 bash
    bash-4.2# cd data/
    bash-4.2# ls
      index.html
    bash-4.2# rm -fr index.html
      rm: cannot remove 'index.html': Read-only file system      ##刪除文件被拒,顯示只讀Read-only
    bash-4.2# cat index.html      ##能夠正常讀取
      www.westos.org

 

 

四.Dockerfile優化以前的配置工做

        這裏以搭建nginx的鏡像爲例

1.安裝相應的安裝包

  ls
    Dockerfile nginx-1.15.8.tar.gz test webdata yum.repo

2.編寫相應的Dockerfile

  vim Dockerfile     ## 沒有優化以前的Dockerfile文件
    FROM rhel7
    COPY dvd.repo /etc/yum.repos.d/dvd.repo
    RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make   ##解決依賴性,相關的應用的下載 
    ADD  nginx-1.15.8.tar.gz  /mnt    ##ADD比COPY更強大,若是文件是可識別的壓縮文件,會幫忙解壓
    WORKDIR /mnt/nginx-1.15.8  ##至關於 cd 到剛纔解壓之後的目錄裏面,而後執行下面的操做
    RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc    ##關閉debug日誌
    RUN ./configure --prefix=/usr/local/nginx    ##源碼編譯生產相應的makefile
    RUN make
    RUN make install
    EXPOSE 80
    VOLUME ["/usr/local/nginx/html"]
    CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]

 

3.利用 docker build封裝nginx的鏡像,並建立nginx容器

  docker build -t rhel7:v3 .

  docker rm -f vm1
  docker run -d --name vm1 rhel7:v3

 

4.測試nginx是否能夠正常訪問

  docker inspect vm1    ##查看 source 的絕對路徑,和容器vm1的分配到的IP
    /var/lib/docker/volumes/2e9222ac2ddb8c79ea4391a9c4e6105dad6065cfcf2aa0f25c29273b9036769c/_data

  cd /var/lib/docker/volumes/2e9222ac2ddb8c79ea4391a9c4e6105dad6065cfcf2aa0f25c29273b9036769c/_data
  ls
    50x.html index.html
  echo "wangfang" > index.html
  cat index.html
    wangfang
  curl 172.17.0.2      #能夠正常訪問
    wangfang

 

5.優化前,鏡像大小爲276MB

      

 

 

五.Dockerfile的相關優化

1.第一次進行優化

(1)將不想看到的輸出都導入到垃圾箱,能夠減少文件的大小  

  vim Dockerfile
    FROM rhel7
    COPY yum.repo /etc/yum.repos.d/yum.repo
    RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make &> /dev/null && yum clean all
    ADD nginx-1.15.8.tar.gz /mnt
    WORKDIR /mnt/nginx-1.15.8
    RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
    RUN ./configure --prefix=/usr/local/nginx &> /dev/null
    RUN make &> /dev/null
    RUN make install &> /dev/null
    RUN rm -fr /mnt/nginx-1.15.8
    EXPOSE 80
    VOLUME ["/usr/local/nginx/html"]
    CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]

 

(2).從新封裝一個鏡像

  docker build -t rhel7:v4 .
    Successfully built fba2f9ad94cd
    Successfully tagged rhel7:v4

 

(3)查看新封裝的鏡像的大小,和以前的相互比較  (鏡像的大小減少了,變爲252MB)

      

 

2.第二次進行優化

(1)將RUN都放在一行,減小層數

  vim Dockerfile
    FROM rhel7
    COPY yum.repo /etc/yum.repos.d/yum.repo
    ADD nginx-1.15.8.tar.gz /mnt
    WORKDIR /mnt/nginx-1.15.8
    RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make &> /dev/null && yum clean all && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --prefix=/usr/local/nginx &> /dev/null && make &> /dev/null && make install &> /dev/null && rm -fr /mnt/nginx-1.15.8
    EXPOSE 80
    VOLUME ["/usr/local/nginx/html"]
    CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]

 

(2)從新封裝一個nginx的鏡像

  docker build -t rhel7:v5 .

(3)查看新封裝的鏡像的大小

  docker images rhel7    ##鏡像有減少,可是減少的不太明顯

      

 

3.第三次進行優化  分階段構建

(1)先將上面的 FROM rhel7 as build 利用 docker build 封裝成標籤爲 latest 的鏡像,而後下面的 FROM 會使用上面的標籤的鏡像進行接着封裝鏡像。就至關於新封裝的鏡像,只有141-140=1MB的大小。對於nginx之中,安裝過程當中會有一些輸出的過程、二進制文件、安裝相應的gcc等的依賴。到最後都用不到,可以使用到的就是那個1MB的安裝好的,只須要將這1MB獨自安裝成鏡像

  vim Dockerfile
    FROM rhel7 as build
    COPY yum.repo /etc/yum.repos.d/yum.repo
    ADD nginx-1.15.8.tar.gz /mnt
    WORKDIR /mnt/nginx-1.15.8
    RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make &> /dev/null && yum clean all && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --prefix=/usr/local/nginx &> /dev/null && make &> /dev/null && make install &> /dev/null && rm -fr /mnt/nginx-1.15.8

    FROM rhel7
    COPY --from=build /usr/local/nginx /usr/local/nginx
    EXPOSE 80
    CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]

 

(2)建立新的鏡像

  docker build -t rhel7:v6 .

(3)利用新的鏡像建立docker容器

       

 

4.最終極的鏡像優化  (從底層開始的優化)

(1)由於只須要安裝nginx只須要可以提供相應的底層的動態庫就能夠了,所以底層的鏡像能夠進一步的進行縮減。利用較小的大小的底層鏡像。

  docker load -i distroless.tar
  docker load -i nginx.tar

  vim Dockerfile 

    FROM nginx as base    ##https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

    ARG Asia/Shanghai

    RUN mkdir -p /opt/var/cache/nginx && \
      cp -a --parents /usr/lib/nginx /opt && \
      cp -a --parents /usr/share/nginx /opt && \
      cp -a --parents /var/log/nginx /opt && \
      cp -aL --parents /var/run /opt && \
      cp -a --parents /etc/nginx /opt && \
      cp -a --parents /etc/passwd /opt && \
      cp -a --parents /etc/group /opt && \
      cp -a --parents /usr/sbin/nginx /opt && \
      cp -a --parents /lib/x86_64-linux-gnu/libpcre.so.* /opt && \
      cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \
      cp -a --parents /lib/x86_64-linux-gnu/libc.so.* /opt && \
      cp -a --parents /lib/x86_64-linux-gnu/libdl.so.* /opt && \
      cp -a --parents /lib/x86_64-linux-gnu/libpthread.so.* /opt && \
      cp -a --parents /lib/x86_64-linux-gnu/libcrypt.so.* /opt && \
      cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \
      cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \
      cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime

      FROM gcr.io/distroless/base

    COPY --from=base /opt /

    EXPOSE 80

    ENTRYPOINT ["nginx", "-g", "daemon off;"]

 

(2)建立新的鏡像

  docker build -t rhel7:v7 .

(3)利用鏡像建立新的docker容器

  docker run -d --name vm1 rhel7:v7

(4)測試:查看該鏡像是否可以使用

  docker run -d --name vm1 rhel7:v7

  在網頁的頁面上輸入:172.17.0.2

      

相關文章
相關標籤/搜索