共計 6444 個字符,預計需要花費 17 分鐘才能閱讀完成。
這篇文章主要為大家展示了“精簡 Docker 鏡像的方法有哪些”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓丸趣 TV 小編帶領大家一起研究并學習一下“精簡 Docker 鏡像的方法有哪些”這篇文章吧。
精簡 Docker 鏡像大小的必要性
Docker 鏡像由很多鏡像層(Layers)組成(最多 127 層),鏡像層依賴于一系列的底層技術,比如文件系統 (filesystems)、寫時復制(copy-on-write)、聯合掛載(union mounts) 等技術,你可以查看 Docker 社區文檔以了解更多有關 Docker 存儲驅動的內容,這里就不再贅述技術細節。總的來說,Dockerfile 中的每條指令都會創建一個鏡像層,繼而會增加整體鏡像的尺寸。
下面是精簡 Docker 鏡像尺寸的好處:
1、減少構建時間
2、減少磁盤使用量
3、減少下載時間
4、因為包含文件少,攻擊面減小,提高了安全性
5、提高部署速度
五點建議減小 Docker 鏡像尺寸
優化基礎鏡像
優化基礎鏡像的方法就是選用合適的更小的基礎鏡像,常用的 Linux 系統鏡像一般有 Ubuntu、CentOs、Alpine,其中 Alpine 更推薦使用。大小對比如下:
lynzabo@ubuntu ~/s docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 74f8760a2a8b 8 days ago 82.4MB
alpine latest 11cd0b38bc3c 2 weeks ago 4.41MB
centos 7 49f7960eb7e4 7 weeks ago 200MB
debian latest 3bbb526d2608 8 days ago 101MB
lynzabo@ubuntu ~/s
Alpine 是一個高度精簡又包含了基本工具的輕量級 Linux 發行版,基礎鏡像只有 4.41M,各開發語言和框架都有基于 Alpine 制作的基礎鏡像,強烈推薦使用它。
查看上面的鏡像尺寸對比結果,你會發現最小的鏡像也有 4.41M,那么有辦法構建更小的鏡像嗎?答案是肯定的,例如 gcr.io/google_containers/pause-amd64:3.1 鏡像僅有 742KB。為什么這個鏡像能這么小?在為大家解密之前,再推薦兩個基礎鏡像:
scratch 鏡像
scratch 是一個空鏡像,只能用于構建其他鏡像,比如你要運行一個包含所有依賴的二進制文件,如 Golang 程序,可以直接使用 scratch 作為基礎鏡像。現在給大家展示一下上文提到的 Google pause 鏡像 Dockerfile:
FROM scratch
ARG ARCH
ADD bin/pause-${ARCH} /pause
ENTRYPOINT [/pause]
Google pause 鏡像使用了 scratch 作為基礎鏡像,這個鏡像本身是不占空間的,使用它構建的鏡像大小幾乎和二進制文件本身一樣大,所以鏡像非常小。當然在我們的 Golang 程序中也會使用。對于一些 Golang/ C 程序,可能會依賴一些動態庫,你可以使用自動提取動態庫工具,比如 ldd、linuxdeployqt 等提取所有動態庫,然后將二進制文件和依賴動態庫一起打包到鏡像中。
busybox 鏡像
scratch 是個空鏡像,如果希望鏡像里可以包含一些常用的 Linux 工具,busybox 鏡像是個不錯選擇,鏡像本身只有 1.16M,非常便于構建小鏡像。
串聯 Dockerfile 指令
大家在定義 Dockerfile 時,如果太多的使用 RUN 指令,經常會導致鏡像有特別多的層,鏡像很臃腫,而且甚至會碰到超出最大層數(127 層)限制的問題,遵循 Dockerfile 最佳實踐,我們應該把多個命令串聯合并為一個 RUN(通過運算符 和 / 來實現),每一個 RUN 要精心設計,確保安裝構建最后進行清理,這樣才可以降低鏡像體積,以及最大化的利用構建緩存。
下面是一個優化前 Dockerfile:
FROM ubuntu
ENV VER 3.0.0
ENV TARBALL http://download.redis.io/releases/redis-$VER.tar.gz
# == Install curl and helper tools...
RUN apt-get update
RUN apt-get install -y curl make gcc
# == Download, compile, and install...
RUN curl -L $TARBALL | tar zxv
WORKDIR redis-$VER
RUN make
RUN make install
#...
# == Clean up...
WORKDIR /
RUN apt-get remove -y --auto-remove curl make gcc
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* /redis-$VER
#...
CMD [redis-server]
構建鏡像,名稱叫 test/test:0.1。
我們對 Dockerfile 做優化,優化后 Dockerfile:
FROM ubuntu
ENV VER 3.0.0
ENV TARBALL http://download.redis.io/releases/redis-$VER.tar.gz
RUN echo == Install curl and helper tools... \
apt-get update \
apt-get install -y curl make gcc \
echo == Download, compile, and install... \
curl -L $TARBALL | tar zxv \
cd redis-$VER \
make \
make install \
echo == Clean up... \
apt-get remove -y --auto-remove curl make gcc \
apt-get clean \
rm -rf /var/lib/apt/lists/* /redis-$VER
#...
CMD [redis-server]
構建鏡像,名稱叫 test/test:0.2。
對比兩個鏡像大小:
root@k8s-master:/tmp/iops# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test/test 0.2 58468c0222ed 2 minutes ago 98.1MB
test/test 0.1 e496cf7243f2 6 minutes ago 307MB
root@k8s-master:/tmp/iops#
可以看到,將多條 RUN 命令串聯起來構建的鏡像大小是每條命令分別 RUN 的三分之一。
提示:為了應對鏡像中存在太多鏡像層,Docker 1.13 版本以后,提供了一個壓扁鏡像功能,即將 Dockerfile 中所有的操作壓縮為一層。這個特性還處于實驗階段,Docker 默認沒有開啟,如果要開啟,需要在啟動 Docker 時添加 -experimental 選項,并在 Docker build 構建鏡像時候添加 –squash。我們不推薦使用這個辦法,請在撰寫 Dockerfile 時遵循最佳實踐編寫,不要試圖用這種辦法去壓縮鏡像。
使用多階段構建
Dockerfile 中每條指令都會為鏡像增加一個鏡像層,并且你需要在移動到下一個鏡像層之前清理不需要的組件。實際上,有一個 Dockerfile 用于開發(其中包含構建應用程序所需的所有內容)以及一個用于生產的瘦客戶端,它只包含你的應用程序以及運行它所需的內容。這被稱為“建造者模式”。Docker 17.05.0-ce 版本以后支持多階段構建。使用多階段構建,你可以在 Dockerfile 中使用多個 FROM 語句,每條 FROM 指令可以使用不同的基礎鏡像,這樣您可以選擇性地將服務組件從一個階段 COPY 到另一個階段,在最終鏡像中只保留需要的內容。
下面是一個使用 COPY –from 和 FROM … AS … 的 Dockerfile:
# Compile
FROM golang:1.9.0 AS builder
WORKDIR /go/src/v9.git...com/.../k8s-monitor
COPY . .
WORKDIR /go/src/v9.git...com/.../k8s-monitor
RUN make build
RUN mv k8s-monitor /root
# Package
# Use scratch image
FROM scratch
WORKDIR /root/
COPY --from=builder /root .
EXPOSE 8080
CMD [/root/k8s-monitor]
構建鏡像,你會發現生成的鏡像只有上面 COPY 指令指定的內容,鏡像大小只有 2M。這樣在以前使用兩個 Dockerfile(一個 Dockerfile 用于開發和一個用于生產的瘦客戶端),現在使用多階段構建就可以搞定。
構建業務服務鏡像技巧
Docker 在 build 鏡像的時候,如果某個命令相關的內容沒有變化,會使用上一次緩存(cache)的文件層,在構建業務鏡像的時候可以注意下面兩點:
1、不變或者變化很少的體積較大的依賴庫和經常修改的自有代碼分開;
2、因為 cache 緩存在運行 Docker build 命令的本地機器上,建議固定使用某臺機器來進行 Docker build,以便利用 cache。
下面是構建 Spring Boot 應用鏡像的例子,用來說明如何分層。其他類型的應用,比如 Java WAR 包,Nodejs 的 npm 模塊等,可以采取類似的方式。
1、在 Dockerfile 所在目錄,解壓縮 maven 生成的 jar 包
$ unzip path-to-app-jar .jar -d app
2、Dockerfile 我們把應用的內容分成 4 個部分 COPY 到鏡像里面:其中前面 3 個基本不變,第 4 個是經常變化的自有代碼。最后一行是解壓縮后,啟動 spring boot 應用的方式。
FROM openjdk:8-jre-alpine
LABEL maintainer opl-xws@xiaomi.com
COPY app/BOOT-INF/lib/ /app/BOOT-INF/lib/
COPY app/org /app/org
COPY app/META-INF /app/META-INF
COPY app/BOOT-INF/classes /app/BOOT-INF/classes
EXPOSE 8080
CMD [/usr/bin/java , -cp , /app , org.springframework.boot.loader.JarLauncher]
這樣在構建鏡像時候可大大提高構建速度。
其他優化辦法
當然,除了以上 4 類辦法外,還有其他優化辦法可以精簡鏡像;
1. RUN 命令中執行 apt、apk 或者 yum 類工具技巧
如果在 RUN 命令中執行 apt、apk 或者 yum 類工具,可以借助這些工具提供的一些小技巧來減少鏡像層數量及鏡像大小。舉幾個例子:
(1)在執行 apt-get install -y 時增加選項—?no-install-recommends ,可以不用安裝建議性(非必須)的依賴,也可以在執行 apk add 時添加選項 –no-cache 達到同樣效果;
(2)執行 yum install -y 時候,可以同時安裝多個工具,比如 yum install -y gcc gcc-c++ make …。將所有 yum install 任務放在一條 RUN 命令上執行,從而減少鏡像層的數量;
(3)組件的安裝和清理要串聯在一條指令里面,如 apk –update add php7 rm -rf /var/cache/apk/* ,因為 Dockerfile 的每條指令都會產生一個文件層,如果將 apk add … 和 rm -rf … 命令分開,清理無法減小 apk 命令產生的文件層的大小。Ubuntu 或 Debian 可以使用 rm -rf /**var**/lib/apt/lists/* 清理鏡像中緩存文件;CentOS 等系統使用 yum clean all 命令清理。
2. 壓縮鏡像
Docker 自帶的一些命令還能協助壓縮鏡像,比如 export 和 import
$ docker run -d test/test:0.2
$ docker export 747dc0e72d13 | docker import - test/test:0.3
使用這種方式需要先將容器運行起來,而且這個過程中會丟失鏡像原有的一些信息,比如:導出端口,環境變量,默認指令。
查看這兩個鏡像 history 信息,如下,可以看到 test/test:0.3 丟失了所有的鏡像層信息:
root@k8s-master:/tmp/iops# docker history test/test:0.3
IMAGE CREATED CREATED BY SIZE COMMENT
6fb3f00b7a72 15 seconds ago 84.7MB Imported from -
root@k8s-master:/tmp/iops# docker history test/test:0.2
IMAGE CREATED CREATED BY SIZE COMMENT
58468c0222ed 2 hours ago /bin/sh -c #(nop) CMD [redis-server] 0B
1af7ffe3d163 2 hours ago /bin/sh -c echo == Install curl and helper… 15.7MB
8bac6e733d54 2 hours ago /bin/sh -c #(nop) ENV TARBALL=http://downlo… 0B
793282f3ef7a 2 hours ago /bin/sh -c #(nop) ENV VER=3.0.0 0B
74f8760a2a8b 8 days ago /bin/sh -c #(nop) CMD [/bin/bash] 0B
missing 8 days ago /bin/sh -c mkdir -p /run/systemd echo do… 7B
missing 8 days ago /bin/sh -c sed -i s/^#\s*\(deb.*universe\)$… 2.76kB
missing 8 days ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B
missing 8 days ago /bin/sh -c set -xe echo #!/bin/sh /… 745B
missing 8 days ago /bin/sh -c #(nop) ADD file:5fabb77ea8d61e02d… 82.4MB
root@k8s-master:/tmp/iops#
以上是“精簡 Docker 鏡像的方法有哪些”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注丸趣 TV 行業資訊頻道!