構建 Docker 映象的 N 個小技巧

Linksla發表於2022-12-27

構建映象的幾個小技巧

構建上下文

執行  docker build  命令時,當前的工作目錄被稱為構建上下文。預設情況下,Dockerfile 就位於該路徑下。也可以透過  -f  引數來指定 dockerfile ,但 docker 客戶端會將當前工作目錄下的所有檔案傳送到 docker 守護程式進行構建。

所以來說,當執行 docker build 進行構建映象時,當前目錄一定要  乾淨  ,切記不要在家裡錄下建立一個 Dockerfile 緊接著  docker build  一把梭 ?。

正確做法是為專案建立一個資料夾,把構建映象時所需要的資源放在這個資料夾下。比如這樣:

mkdir projectcd !$vi Dockerfile# 編寫 Dockerfile

也可以透過  .dockerignore 檔案來忽略不需要的檔案傳送到 docker 守護程式

基礎映象

使用體積較小的基礎映象,比如  alpine  或者  debian:buster-slim ,像 openjdk 可以選用  openjdk:xxx-slim ,由於 openjdk 是基於 debian 的基礎映象構建的,所以向 debian 基礎映象一樣,後面帶個  slim  就是基於  debian:xxx-slim  映象構建的。

REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZEdebian                      buster-slim         e1af56d072b8        4 days ago          69.2MBalpine                      latest              cc0abc535e36        8 days ago          5.59MB

不過需要注意的是,alpine 的 c 庫是  musl libc  ,而不是正統的  glibc ,另外對於一些依賴  glibc  的大型專案,像 openjdk 、tomcat、rabbitmq 等都不建議使用 alpine 基礎映象,因為 musl libc 可能會導致 JVM 一些奇怪的問題。這也是為什麼 tomcat 官方沒有給出基礎映象是 alpine 的 Dockerfile 的原因。

國內軟體源

使用預設的軟體源安裝構建時所需的依賴,對於絕大多數基礎映象來說,可以透過修改軟體源的方式更換為國內的軟體源映象站。目前國內穩定可靠的映象站主要有,華為雲、阿里雲、騰訊雲、163 等。

  • 對於 alpine 基礎映象修改軟體源

echo "http://mirrors.huaweicloud.com/alpine/latest-stable/main/" > /etc/apk/repositories ;\echo "http://mirrors.huaweicloud.com/alpine/latest-stable/community/" >> /etc/apk/repositories ;\apk update ;
  • debian 基礎映象修改預設原件原始碼

sed -i 's/deb.debian.org/mirrors.huaweicloud.com/g' /etc/apt/sources.list ;\sed -i 's|security.debian.org/debian-security|mirrors.huaweicloud.com/debian-security|g' /etc/apt/sources.list ;\apt update ;\
  • Ubuntu 基礎映象修改預設原件原始碼

sed -i 's/archive.ubuntu.com/mirrors.huaweicloud.com/g' /etc/apt/sources.listapt update ;\
  • 對於 CentOS ???

你確定要用 220MB 大小的基礎映象?

REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZEcentos                      latest              0f3e07c0138f        3 months ago        220MB

建議這些命令就放在 RUN 指令的第一條,update 以下軟體源,之後再 install 相應的依賴。

時區設定

由於絕大多數基礎映象都是預設採用 UTC 的時區,與北京時間相差 8 個小時,這將會導致容器內的時間與北京時間不一致,因而會對一些應用造成一些影響,還會影響容器內日誌和監控的資料。

因此對於東八區的使用者,最好在構建映象的時候設定一下容器內的時區,以免以後因為時區遇到一些 bug?。
可以透過環境變數設定容器內的時區。在啟動的時候可以透過設定環境變數  -e TZ=Asia/Shanghai  來設定容器內的時區。

alpine
  • 但對於 alpine 基礎映象無法透過 TZ 環境變數的方式設定時區,需要安裝 tzdata 來配置時區。
root@ubuntu:~/docke/alpine# docker run --rm -it -e TZ=Asia/Shanghai alpine dateThu Jan  2 03:37:44 UTC 2020
  • 對於 alpine 基礎映象,可以在 RUN 指令後面追加上以下命令
apk add --no-cache tzdata ;\cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ;\echo "Asia/Shanghai" > /etc/timezone ;\apk del tzdata ;\
  • 透過 tzdate 設定時區

root@ubuntu:~/docke/alpine# docker build -t alpine:tz2 .Sending build context to Docker daemon  2.048kBStep 1/2 : FROM alpine ---> cc0abc535e36Step 2/2 : RUN set -xue ;    echo "http://mirrors.huaweicloud.com/alpine/latest-stable/main/" > /etc/apk/repositories ;    echo "http://mirrors.huaweicloud.com/alpine/latest-stable/community/" >> /etc/apk/repositories ;    apk update ;    apk add --no-cache tzdata ;    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ;    echo "Asia/Shanghai" > /etc/timezone ;    apk del tzdata ---> Running in 982041a34dbf+ echo http://mirrors.huaweicloud.com/alpine/latest-stable/main/+ echo http://mirrors.huaweicloud.com/alpine/latest-stable/community/+ apk updatefetch http://mirrors.huaweicloud.com/alpine/latest-stable/main/x86_64/APKINDEX.tar.gzfetch http://mirrors.huaweicloud.com/alpine/latest-stable/community/x86_64/APKINDEX.tar.gzv3.11.2-11-gd5cdcefa20 [http://mirrors.huaweicloud.com/alpine/latest-stable/main/]v3.11.2-14-g973431591e [http://mirrors.huaweicloud.com/alpine/latest-stable/community/]OK: 11261 distinct packages available+ apk add --no-cache tzdatafetch http://mirrors.huaweicloud.com/alpine/latest-stable/main/x86_64/APKINDEX.tar.gzfetch http://mirrors.huaweicloud.com/alpine/latest-stable/community/x86_64/APKINDEX.tar.gz(1/1) Installing tzdata (2019c-r0)Executing busybox-1.31.1-r8.triggerOK: 9 MiB in 15 packages+ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime+ echo Asia/Shanghai+ apk del tzdata(1/1) Purging tzdata (2019c-r0)Executing busybox-1.31.1-r8.triggerOK: 6 MiB in 14 packagesRemoving intermediate container 982041a34dbf ---> 3ec89f3e824dSuccessfully built 3ec89f3e824dSuccessfully tagged alpine:tz2root@ubuntu:~/docke/alpine# docker run --rm -it alpine:tz2 dateThu Jan  2 11:12:23 CST 2020
debian
  • 透過啟動時設定環境變數指定時區

root@ubuntu:~/docke/alpine# docker run --rm -it -e TZ=Asia/Shanghai debian dateThu Jan  2 11:38:56 CST 2020
  • 也可以再構建映象的時候複製時區檔案設定容器內時區

    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ;\echo "Asia/shanghai" > /etc/timezone ;\

    ubuntu
    • 透過啟動時設定環境變數指定時區,發射失敗 ?,只能透過時區檔案來設定時區了。
      
      root@ubuntu:~/docke/alpine# docker run --rm -it -e TZ=Asia/Shanghai debian date
      
      Thu Jan  2 11:38:56 CST 2020
      
      
      
      root@ubuntu:~/docke/alpine# ^debian^ubuntu docker run --rm -it -e TZ=Asia/Shanghai ubuntu date Thu Jan  2 03:44:13 Asia 2020

      在這裡有個命令執行的小技巧,透過脫字元  ^  來替換上一條命令中的 debian 為 ubuntu 然後執行相同的命令。

      • 透過時區檔案來設定時區

        apt update ;\apt install tzdata -y ;\cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ;\echo "Asia/shanghai" > /etc/timezone ;\

        儘量使用 URL 新增原始碼

        如果不採用分階段構建,對於一些需要在容器內進行編譯的專案,最好透過 git 或者 wegt 的方式將原始碼打入到映象內,而非採用 ADD 或者 COPY ,因為原始碼編譯完成之後,原始碼就不需要可以刪掉了,而透過 ADD 或者 COPY 新增進去的原始碼已經用在下一層映象中了,是刪不掉滴啦。
        也就是說  git & wget source  然後  build ,最後  rm -rf source/  這三部放在一條 RUN 指令中,這樣就能避免原始碼新增到映象中而增大映象體積啦。
        下面以 FastDFS 的 Dockerfile 為例:

        • 專案官方的 Dockerfile
          
          # centos 7
          
          FROM centos:7
          
          # 新增配置檔案
          
          # add profiles
          
          ADD conf/client.conf /etc/fdfs/
          
          ADD conf/http.conf /etc/fdfs/
          
          ADD conf/mime.types /etc/fdfs/
          
          ADD conf/storage.conf /etc/fdfs/
          
          ADD conf/tracker.conf /etc/fdfs/
          
          ADD fastdfs.sh /home
          
          ADD conf/nginx.conf /etc/fdfs/
          
          ADD conf/mod_fastdfs.conf /etc/fdfs
          
          
          
          # 新增原始檔 # add source code ADD source/libfastcommon.tar.gz /usr/local/src/ ADD source/fastdfs.tar.gz /usr/local/src/ ADD source/fastdfs-nginx-module.tar.gz /usr/local/src/ ADD source/nginx-1.15.4.tar.gz /usr/local/src/
          # Run RUN yum install git gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl-devel wget vim -y \  &&  mkdir /home/dfs   \  &&  cd /usr/local/src/  \  &&  cd libfastcommon/   \  &&  ./make.sh && ./make.sh install  \  &&  cd ../  \  &&  cd fastdfs/   \  &&  ./make.sh && ./make.sh install  \  &&  cd ../  \  &&  cd nginx-1.15.4/  \  &&  ./configure --add-module=/usr/local/src/fastdfs-nginx-module/src/   \  &&  make && make install  \  &&  chmod +x /home/fastdfs.sh # export config VOLUME /etc/fdfs
          EXPOSE 22122 23000 8888 80 ENTRYPOINT ["/home/fastdfs.sh"]
          • 經過本人最佳化後的 Dockerfile

            
            FROM alpine:3.10
            
            
            
            RUN set -x \    && echo "http://mirrors.huaweicloud.com/alpine/latest-stable/main/" > /etc/apk/repositories \    && echo "http://mirrors.huaweicloud.com/alpine/latest-stable/community/" >> /etc/apk/repositories \    && apk update \    && apk add --no-cache --virtual .build-deps gcc libc-dev make perl-dev openssl-dev pcre-dev zlib-dev git \    && mkdir -p /usr/local/src \    && cd /usr/local/src \    && git clone --depth 1 \    && git clone --depth 1    \    && git clone --depth 1  \    && wget \    && tar -xf nginx-1.15.4.tar.gz \    && cd /usr/local/src/libfastcommon \    && ./make.sh \    && ./make.sh install \    && cd /usr/local/src/fastdfs/ \    && ./make.sh \    && ./make.sh install \    && cd /usr/local/src/nginx-1.15.4/ \    && ./configure --add-module=/usr/local/src/fastdfs-nginx-module/src/ \    && make && make install \    && apk del .build-deps \    && apk add --no-cache pcre-dev bash \    && mkdir -p /home/dfs  \    && mv /usr/local/src/fastdfs/docker/dockerfile_network/fastdfs.sh /home \    && mv /usr/local/src/fastdfs/docker/dockerfile_network/conf/* /etc/fdfs \    && chmod +x /home/fastdfs.sh \    && rm -rf /usr/local/src* VOLUME /home/dfs EXPOSE 22122 23000 8888 8080 CMD ["/home/fastdfs.sh"]
            • 構建之後的對比

            使用專案預設的 Dockerfile 進行構建的話,映象大小接近 500MB,而經過一些的最佳化,將所有的 RUN 指令合併為一條,最終構建出來的映象大小為 30MB。

            REPOSITORY          TAG                 IMAGE ID            CREATED             SIZEfastdfs             alpine              e855bd197dbe        10 seconds ago      29.3MBfastdfs             debian              e05ca1616604        20 minutes ago      103MBfastdfs             centos              c1488537c23c        30 minutes ago      483MB

            使用虛擬編譯環境

            對於只在編譯過程中使用到的依賴,我們可以將這些依賴安裝在虛擬環境中,編譯完成之後可以一併刪除這些依賴,比如 alpine 中可以使用  apk add --no-cache --virtual .build-deps ,後面加上需要安裝的相關依賴。

            apk add --no-cache --virtual .build-deps gcc libc-dev make perl-dev openssl-dev pcre-dev zlib-dev git

            構建完成之後可以使用 apk del .build-deps 命令,一併將這些編譯依賴全部刪除。

            需要注意的是,.build-deps 後面接的是編譯時以來的軟體包,並不是所有的編譯依賴都可以刪除,不要把執行時的依賴包接在後面,最好單獨 add 一下。

            最小化層數

            docker 在 1.10 以後,只有  RUN、COPY 和 ADD  指令會建立層,其他指令會建立臨時的中間映象,但是不會直接增加構建的映象大小了。

            前文提到了建議使用 git 或者 wget 的方式來將檔案打入到映象當中,但如果我們必須要使用 COPY 或者 ADD 指令呢?

            還是拿 FastDFS 為例:

              
              # centos 7
              
              FROM centos:7
              
              # 新增配置檔案
              
              # add profiles
              
              ADD conf/client.conf /etc/fdfs/
              
              ADD conf/http.conf /etc/fdfs/
              
              ADD conf/mime.types /etc/fdfs/
              
              ADD conf/storage.conf /etc/fdfs/
              
              ADD conf/tracker.conf /etc/fdfs/
              
              ADD fastdfs.sh /home
              
              ADD conf/nginx.conf /etc/fdfs/
              
              ADD conf/mod_fastdfs.conf /etc/fdfs
              
              
              
              # 新增原始檔 # add source code ADD source/libfastcommon.tar.gz /usr/local/src/ ADD source/fastdfs.tar.gz /usr/local/src/ ADD source/fastdfs-nginx-module.tar.gz /usr/local/src/ ADD source/nginx-1.15.4.tar.gz /usr/local/src/

              多個檔案需要新增到容器中不同的路徑,每個檔案使用一條 ADD 指令的話就會增加一層映象,這樣戲曲就多了 12 層映象 。
              其實大可不必,我們可以將這些檔案全部打包為一個檔案為  src.tar.gz  然後透過 ADD 的方式把檔案新增到當中去,然後在 RUN 指令後使用  mv  命令把檔案移動到指定的位置。這樣僅僅一條 ADD 和 RUN 指令取代掉了 12 個 ADD 指令。

              FROM alpine:3.10COPY src.tar.gz /usr/local/src.tar.gzRUN set -xe \    && apk add --no-cache --virtual .build-deps gcc libc-dev make perl-dev openssl-dev pcre-dev zlib-dev tzdata \    && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \    && tar -xvf /usr/local/src.tar.gz -C /usr/local \    && mv /usr/local/src/conf/fastdfs.sh /home/fastdfs/ \    && mv /usr/local/src/conf/* /etc/fdfs \    && chmod +x /home/fastdfs/fastdfs.sh \    && rm -rf /usr/local/src/* /var/cache/apk/* /tmp/* /var/tmp/* $HOME/.cacheVOLUME /var/fdfs

              其他最小化層數無非就是把構建專案的整個步驟弄成一條 RUN 指令,不過多條命令合併可以使用  &&  或者  ;  這兩者都可以,不過據我在 docker hub 上的所見所聞,使用  ;  的居多,尤其是官方的 Dockerfile。

              文章略有精 簡。

              文章轉載: https://blog.k8s.li/dockerfile-tips.html



              來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70013542/viewspace-2929720/,如需轉載,請註明出處,否則將追究法律責任。

              相關文章