Docker最佳實踐:5個方法精簡映象
本文記錄了精簡Docker映象尺寸的必要性及好處
精簡Docker映象的好處很多,不僅可以節省儲存空間和頻寬,還能減少安全隱患。最佳化映象大小的手段多種多樣,因服務所使用的基礎開發語言不同而有差異。本文將介紹精簡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時,如果太多的使用RUN指令,經常會導致映象有特別多的層,映象很臃腫,而且甚至會碰到超出最大層數(127層)限制的問題,遵循 Dockerfile 最佳實踐,我們應該把多個命令串聯合併為一個 RUN(透過運算子&&
和/
來實現),每一個 RUN 要精心設計,確保安裝構建最後進行清理,這樣才可以降低映象體積,以及最大化的利用構建快取。
下面是一個最佳化前Dockerfile:
FROM ubuntu
ENV VER 3.0.0
ENV TARBALL
# ==> 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
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=… 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-squash ,用起來更簡單方便,並且不會丟失原有映象的自帶資訊,大家有興趣可以試試。
總結
Docker映象的精簡手段和精簡效果值得深入探討和實踐,希望本文能為大家帶來幫助。如果你有更好的方法和經驗,歡迎交流~
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559359/viewspace-2649590/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Docker 映象優化與最佳實踐Docker優化
- 最佳化您的部署:Docker 映象最佳實踐Docker
- 製作 Python Docker 映象的最佳實踐PythonDocker
- docker映象體積優化方法與實踐Docker優化
- docker容器 如何精簡映象減小體積Docker
- 5個async/await最佳實踐AI
- Docker進階與實踐之三:Docker映象Docker
- 實踐:Docker容器與映象管理Docker
- 製作容器映象的最佳實踐
- 精益生產流程最佳化的5個方法
- docker映象最佳化Docker
- Docker進階與實踐之四:Docker映象倉庫Docker
- 突破難關:Docker映象和容器的區別以及構建的最佳實踐Docker
- Docker實踐5:搭建redmineDocker
- docker映象瘦身方法Docker
- Nodejs Docker 映象體積優化實踐NodeJSDocker優化
- Docker容器日誌管理最佳實踐Docker
- 影片生產大映象最佳化實踐
- Docker 映象製作教程:針對不同語言的精簡策略Docker
- 利用docker部署深度學習模型的一個最佳實踐Docker深度學習模型
- 15 個變數和方法命名的最佳實踐變數
- 10 個最佳化技巧,減少 Docker 映象大小【轉】Docker
- 掌握Docker:簡化KES單機安裝與管理的最佳實踐Docker
- Python開發指南:最佳實踐精選Python
- 有效資料湖攝取的5個最佳實踐
- 企業需要知道的5個 IAM 最佳實踐
- WebSocket簡介與最佳實踐Web
- Docker 映象製作方法Docker
- Docker多階段構建最佳實踐Docker
- [Docker]寫 Dockerfile 的最佳實踐理論Docker
- 24個javascript最佳實踐JavaScript
- 7 個 jQuery 最佳實踐jQuery
- webService幾個最佳實踐Web
- Docker實踐(5)—資源隔離Docker
- Docker映象構建:技術深度解析與實踐指南Docker
- 簡訊驗證碼“最佳實踐”
- 使用nodejs構建Docker image最佳實踐NodeJSDocker
- 修改docker映象源的方法Docker