docker映象瘦身&優化
-
"Distroless" images contain only your application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution.
distroless
是 Google 推出的一個僅僅包含執行時環境,不包含包管理器,shell
等其他程式。如果你的程式沒有其他依賴的話,這是一個不錯的選擇 -
Alpine Linux is a security-oriented, lightweight Linux distribution based on musl libc and busybox.
alpine 是一個基於
musl
,busybox
的安全的linux
發行版。麻雀雖小五臟俱全,雖然不到 10M, 但是包含了一個包管理器和shell
環境,這在我們實際的使用除錯當中將非常有用。但是請注意,由於
alpine
使用了更小的muslc
替代glibc
,會導致某些應用無法使用,需要重新編譯 -
scratch 是空白映象,一般用於基礎映象構建,例如
alpine
映象的dockerfile
便是從scratch
開始的FROM scratch ADD alpine-minirootfs-20190228-x86_64.tar.gz / CMD ["/bin/sh"] 複製程式碼
一般而言,distroless
相對會更加的安全,但是在實際使用的過程中可能會遇到新增依賴以及除錯方面的問題,alpine
更小,自帶包管理器,更加貼合使用習慣,但是muslc
可能會帶來相容性的問題,一般而言我會選擇alpine
作為基礎映象使用。
除此之外,在Docker Hub當中我們可以發現常用的Debian
的映象也會提供的只包含基礎功能的小映象
基礎映象對比
此處直接拉取基礎映象,檢視映象大小, 通過觀察我們可以發現,alpine
只有 5M 左右為debian
的 20 分之一
alpinelatest5cb3aa00f8993 weeks ago5.53MB debianlatest0af60a5c6dd03 weeks ago101MB ubuntu18.0447b19964fb507 weeks ago88.1MB ubuntulatest47b19964fb507 weeks ago88.1MB alpine3.83f53bb00af943 months ago4.41MB 複製程式碼
似乎從上面看,感覺差距不大,實踐中,不同語言的基礎映象都會提供一些採用不同基礎映象製作的 tag,下面我們以ruby
的映象為例,檢視不同基礎映象的差異。可以看到預設的 latest 映象881MB
而alpine
僅僅只有不到50MB
這個差距就十分的可觀了
rubylatesta5d26127d8d04 weeks ago881MB rubyalpine8d8f7d19d1fa4 weeks ago47.8MB rubyslim58dd4d3c99da4 weeks ago125MB 複製程式碼
減少層,去除非必要的檔案
- 刪除檔案不要跨行
# dockerfile 1 FROM alpine RUN wget https://github.com/mohuishou/scuplus-wechat/archive/1.0.0.zip # dockerfile 2 FROM alpine RUN wget https://github.com/mohuishou/scuplus-wechat/archive/1.0.0.zip RUN rm 1.0.0.zip # dockerfile 3 FROM alpine RUN wget https://github.com/mohuishou/scuplus-wechat/archive/1.0.0.zip &&rm 1.0.0.zip 複製程式碼
test3351a80e99c225 seconds ago5.53MB test2ad27e625b8e549 seconds ago6.1MB test1165e2e0df1d3About a minute ago6.1MB 複製程式碼
可以發現 1,2 兩個大小一樣,但是 3 小了 0.5MB,這是因為 docker 幾乎每一行命令都會生成一個層,刪除檔案的時候:因為底下各層都是隻讀的,當需要刪除這些層中的檔案時,AUFS 使用 whiteout 機制,它的實現是通過在上層的可寫的目錄下建立對應的 whiteout 隱藏檔案來實現的,所以在當前層去刪除上一層的檔案,只是會把這個檔案隱藏掉罷了
- 使用單行命令
除了刪除語句需要放在一行以外,由於層的機制,我們安裝依賴的一些公共的語句最好也使用條RUN
命令生成,減少最終的層數
-
分離依賴包,以及原始碼程式,充分利用層的快取
這是一個最佳實踐 ,在實際的開發過程中,我們的依賴包往往是變動不大的,但是我們正在開發的原始碼的變動是較為頻繁,如果我們實際的程式碼只有
10M
,但是依賴項有1G
, 如果在COPY
的時候直接COPY . .
會導致每次修改程式碼都會時這一層的快取失效,導致浪費複製以及推送到映象倉庫的時間,將 COPY 語句分開,每次 push 就可以只變更我們頻繁修改的程式碼層,而不是連著依賴一起 -
使用
.dockerignore
在使用
Git
時,我們可以通過.gitignore
忽略檔案,在 docker build 的時候也可以使用.dockerignore
在 Docker 上下文中忽略檔案,這樣不僅可以減少一些非必要檔案的匯入,也可以提高安全性,避免將一些配置檔案打包到映象中
多階段構建
多階段構建其實也是減少層的一種,通過多階段構建,最終映象可以僅包含最後生成的可執行檔案,和必須的執行時依賴,大大減少映象體積。
以GO
語言為例,實際執行的過程中只需要最後編譯生成的二進位制檔案即可,而GO
語言本省以及擴充套件包,程式碼檔案都是不必要的,但是我們在編譯的時候這些依賴又是必須的,這時候就可以使用多階段構建的方式,減少最終生成的映象體積
# 使用golang映象作為builder映象 FROM golang:1.12 as builder WORKDIR /go/src/github.com/go/helloworld/ COPY app.go . RUN go build -o app . # 編譯完成之後使用alpine映象作為最終的基礎映象 FROM alpine:latest as prod RUN apk --no-cache add ca-certificates WORKDIR /root/ # 從builder中複製編譯好的二進位制檔案 COPY --from=builder /go/src/github.com/go/helloworld/app . CMD ["./app"] 複製程式碼
由於本文篇幅較長,這裡不對多階段構建展開講解,詳情可以參考多階段構建
奇淫技巧
-
使用dive 檢視 docker 映象的層,可以幫助你分析減少映象體積
-
使用docker-slim 可以自動幫助你減少映象體積,對於 Web 應用較為有用
-
安裝軟體時去除依賴
# ubuntu apt-get install -y — no-install-recommends #alpine apk add --no-cache &&apk del build-dependencies # centos yum install -y ... && yum clean all 複製程式碼
-
使用
--flatten
引數,減少層(不推薦) -
使用docker-squash 壓縮層
不同語言的示例
新增中......
Ruby(Rails)
-
只安裝生產所需的依賴
-
刪除不需要的依賴檔案
bundle install --without development:test:assets -j4 --retry 3 --path=vendor/bundle \ # Remove unneeded files (cached *.gem, *.o, *.c) && rm -rf vendor/bundle/ruby/2.5.0/cache/*.gem \ && find vendor/bundle/ruby/2.5.0/gems/ -name "*.c" -delete \ && find vendor/bundle/ruby/2.5.0/gems/ -name "*.o" -delete 複製程式碼
-
刪除前端的
node_modules
以及快取檔案
rm -rf node_modules tmp/cache app/assets vendor/assets spec 複製程式碼
上述內容可以結合多階段構建 實現