Docker鏡像瘦身與優化

2021-12-29 分布式實驗室

為什麼在存儲如此便宜的今天我們仍然需要對Docker鏡像進行瘦身?加速構建/部署。雖然存儲資源較為廉價,但是網絡IO是有限的,在帶寬有限的情況下,部署一個1G的鏡像和10M的鏡像帶來的時間差距可能就是分鐘級和秒級的差距。特別是在出現故障,服務被調度到其他節點時,這個時間尤為寶貴。提高安全性,減少攻擊面積。越小的鏡像表示無用的程序越少,可以大大的減少被攻擊的目標。在實際製作鏡像的過程中,一味的合併層不可取,需要學會充分的利用Docker的緩存機制,提取公共層,加速構建。

依賴文件和實際的代碼文件單獨分層

團隊/公司採用公共的基礎鏡像等

往往我們在構建階段和實際運行階段需要的依賴環境是不同的,例如Golang編寫的程序實際運行的時候僅僅需要一個二進位文件即可,對於Node來說,可能最後運行的只是一些打包之後的js文件而不需要包含node_modules裡成千上萬的依賴。Distroless,https://github.com/GoogleCloudPlatform/distroless「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,https://hub.docker.com/_/alpineAlpine Linux is a security-oriented, lightweight Linux distribution based on musl libc and busybox.Alpine 是一個基於MUSL,Busybox的安全的Linux發行版。麻雀雖小五臟俱全,雖然不到10M, 但是包含了一個包管理器和shell環境,這在我們實際的使用調試當中將非常有用。但是請注意,由於Alpine使用了更小的muslc替代glibc,會導致某些應用無法使用,需要重新編譯。Scratch,https://hub.docker.com/_/scratchScratch是空白鏡像,一般用於基礎鏡像構建,例如Alpine鏡像的Dockerfile便是從Scratch開始的:

FROM scratch

ADD alpine-minirootfs-20190228-x86_64.tar.gz /

CMD ["/bin/sh"]

Busybox,https://hub.docker.com/_/busybox一般而言,Distroless相對會更加的安全,但是在實際使用的過程中可能會遇到添加依賴以及調試方面的問題,alpine更小,自帶包管理器,更加貼合使用習慣,但是muslc可能會帶來兼容性的問題,一般而言我會選擇alpine作為基礎鏡像使用。除此之外,在Docker Hub當中我們可以發現常用的Debian的鏡像也會提供的只包含基礎功能的小鏡像。此處直接拉取基礎鏡像,查看鏡像大小, 通過觀察我們可以發現,alpine只有5M左右為debian的20分之一:

alpine latest 5cb3aa00f899 3 weeks ago 5.53MB

debian latest 0af60a5c6dd0 3 weeks ago 101MB

ubuntu 18.04 47b19964fb50 7 weeks ago 88.1MB

ubuntu latest 47b19964fb50 7 weeks ago 88.1MB

alpine 3.8 3f53bb00af94 3 months ago 4.41MB

似乎從上面看,感覺差距不大,實踐中,不同語言的基礎鏡像都會提供一些採用不同基礎鏡像製作的tag,下面我們以Ruby的鏡像為例,查看不同基礎鏡像的差異。可以看到默認的latest鏡像881MB而alpine僅僅只有不到50MB這個差距就十分的可觀了:

ruby latest a5d26127d8d0 4 weeks ago 881MB

ruby alpine 8d8f7d19d1fa 4 weeks ago 47.8MB

ruby slim 58dd4d3c99da 4 weeks ago 125MB

# dockerfile 1

FROM alpine

RUN wget https://github.com/mohuishou/scuplus-wechat/archive/1.0.0.zip && rm 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

test 3 351a80e99c22 5 seconds ago 5.53MB

test 2 ad27e625b8e5 49 seconds ago 6.1MB

test 1 165e2e0df1d3 About a minute ago 6.1MB

可以發現1,2兩個大小一樣,但是3小了0.5MB,這是因為Docker幾乎每一行命令都會生成一個層,刪除文件的時候:因為底下各層都是只讀的,當需要刪除這些層中的文件時,AUFS 使用whiteout機制,它的實現是通過在上層的可寫的目錄下建立對應的whiteout隱藏文件來實現的,所以在當前層去刪除上一層的文件,只是會把這個文件隱藏掉罷了。除了刪除語句需要放在一行以外,由於層的機制,我們安裝依賴的一些公共的語句最好也使用條RUN命令生成,減少最終的層數。這是一個最佳實踐,在實際的開發過程中,我們的依賴包往往是變動不大的,但是我們正在開發的源碼的變動是較為頻繁,如果我們實際的代碼只有10M,但是依賴項有1G,如果在COPY的時候直接 COPY..,會導致每次修改代碼都會使這一層的緩存失效,導致浪費複製以及推送到鏡像倉庫的時間,將COPY語句分開,每次push就可以只變更我們頻繁修改的代碼層,而不是連著依賴一起。在使用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"]

由於本文篇幅較長,這裡不對多階段構建展開講解,詳情可以參考多階段構建[1]。使用dive[2]查看Docker鏡像的層,可以幫助你分析減少鏡像體積。使用docker-slim[3]可以自動幫助你減少鏡像體積,對於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

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

rm -rf node_modules tmp/cache app/assets vendor/assets spec

Golang在使用多階段構建之後,只剩下了一個二進位文件,這時候再要優化,就只有使用upx之類的工具壓縮二進位文件的體積了。

https://yeasy.gitbooks.io/docker_practice/image/multistage-builds/#%E5%A4%9A%E9%98%B6%E6%AE%B5%E6%9E%84%E5%BB%BA

https://github.com/wagoodman/dive

https://github.com/docker-slim/docker-slim

https://github.com/jwilder/docker-squash

原文連結:https://lailin.xyz/post/notes/docker%E9%95%9C%E5%83%8F%E7%98%A6%E8%BA%AB/基於Kubernetes的DevOps實踐培訓將於2019年5月10日在上海開課,3天時間帶你系統掌握Kubernetes,學習效果不好可以繼續學習。本次培訓包括:容器特性、鏡像、網絡;Kubernetes架構、核心組件、基本功能;Kubernetes設計理念、架構設計、基本功能、常用對象、設計原則;Kubernetes的資料庫、運行時、網絡、插件已經落地經驗;微服務架構、組件、監控方案等,點擊下方圖片查看詳情。

相關焦點

  • 優化Docker 鏡像小技巧
    在本文中,我將介紹一些經常被忽視的概念,這些概念將有助於優化Docker鏡像的開發和構建過程。讓我們從Docker構建過程開始。Docker構建是通過使用Docker CLI工具中的docker build命令觸發。docker build命令根據Dockerfile文件中指定的指令構建Docker鏡像。Dockerfile是一個文本文檔,其中包含用戶組裝鏡像所有的有序命令。
  • 玩轉Docker鏡像
    介紹鏡像層(Layers)製作步驟lab-1:初始化構建 Redis 鏡像lab-2:優化基礎鏡像lab-3:串聯 Dockerfile 指令lab-4:壓縮你的鏡像lab-5:使用最精簡的 base imagelab-6:提取動態連結的 .so 文件lab-7:為
  • Docker鏡像優化:從1.16GB到22.4MB
    容器是在獨立環境中運行的進程,它運行在自己的文件系統上,該文件系統是使用 docker 鏡像構建的。鏡像中包含運行應用程式所需的一切(編譯後的代碼、依賴項、庫等等)。鏡像使用 Dockerfile 文件定義。術語 dockerization 或 containerization 通常用於定義創建 Docker 容器的過程。
  • Docker鏡像
    鏡像可以用來創建Docker容器的。一個鏡像可以包含一個完整的作業系統環境和用戶需要的其它應用程式。在docker hub 裡面有大量現成的鏡像提供下載。docker的鏡像是只可讀的,一個鏡像可以創建多個容器。如同在電腦上安裝系統鏡像文件。
  • Docker鏡像常用命令
    docker info   顯示 Docker 系統信息,包括鏡像和容器數命令參數 OPTIONS意義-a列出本地所有的鏡像-q只顯示鏡像ID--digests顯示鏡像的摘要信息--no-trunc顯示完整的鏡像信息docker search 鏡像名稱docker search 鏡像名稱:TAG    從Docker Hub查找鏡像
  • 【Docker】系列教程01-使用Docker鏡像
    當同一個鏡像擁有多個標籤時,docker rmi命令只是刪除該鏡像的一個標籤副本,並不影響鏡像文件。2)使用鏡像ID刪除鏡像 docker rmi 命令後跟鏡像ID的前綴即可匹配刪除docker pull ubuntu:18.043、列出本地鏡像列表docker images4、查看鏡像詳細信息docker inspect ubuntu:18.045、查看鏡像歷史信息docker history ubuntu
  • Maven構建Docker鏡像
    SpringBoot應構建Docker鏡像 本文主要介紹使用Maven將SpringBoot應用打包成Docker鏡像,並上傳到私有鏡像倉庫Docker Registry.鏡像倉庫Docker Registry的搭建 1.Pull Registry鏡像docker pull registry:22.鏡像倉庫的容器實例docker run -d -p 5000:5000 --restart=always --name registry
  • Docker 教程——理解 Docker 鏡像和容器的存儲路徑
    許多公司投入越來越多的精力來優化本地和遠程 Docker 容器中的開發流程,由此也帶來了諸多好處。執行以下命令可以查看 Docker 的配置信息:$ docker info...Docker 鏡像和容器的存儲路徑Docker 容器由網絡文件、卷和鏡像組成。Docker 文件的存儲路逕取決於你的作業系統。
  • DIY自己的docker鏡像
    例如WORKDIR /aWORKDIR bWORKDIR cRUN pwd則最終路徑為 /a/b/c。編寫完成 Dockerfile 之後,可以通過 docker build 命令來創建鏡像。基本的格式為 docker build [選項] ] [Dockerfile路徑],該命令將讀取指定路徑下(包括子目錄)的 Dockerfile,並將該路徑下所有內容發送給 Docker 服務端,由服務端來創建鏡像。因此一般建議放置 Dockerfile 的目錄為空目錄。
  • 基於Dockerfile構建容器鏡像的最佳實踐
    目錄1、背景概述2、為什麼鏡像會這麼大2.1 基礎鏡像過大2.2 基礎鏡像過大,而且找不到了2.3 .git 目錄(非必要目錄)2.4 Dockerfile 本身有其他問題3、Dockerfile 如何優化4、除了這些優化還可以做什麼
  • 【Docker學習】2. 操作鏡像
    獲取鏡像Docker Hub 上有大量的高質量的鏡像可以用。從 Docker 鏡像倉庫獲取鏡像的命令是 docker pull。列出鏡像要想列出已經下載下來的鏡像,可以使用 docker image ls 或者docker images命令。
  • Docker鏡像運行&刪除(三)
    獲取docker的鏡像後,就可以運行對應的docker的鏡像信息了,運行的命令為run,具體指令總結如下:-it:
  • Docker 鏡像的備份恢復遷移
    tar 歸檔文件,需要使用時將 tar 包恢復為鏡像即可;登錄 DockerHub 註冊中心,將鏡像推送至 DockerHub 倉庫方便使用;搭建私有鏡像倉庫,將鏡像推送至私有鏡像倉庫方便使用。接下來我們通過 tar 歸檔文件的方式實現鏡像的備份恢復遷移。鏡像備份使用 docker save 將指定鏡像保存成 tar 歸檔文件。docker save [OPTIONS] IMAGE [IMAGE...]
  • Docker 開發鏡像搭建與DockerFile分享
    ,包含了 java、go 語言等基本的開發環境,結合 vscode remote 模式,可在本地直接進行開發,免去了安裝各種基礎環境和配置環境變量的麻煩但是對於使用者來說,依然有兩個並不方便的地方:易用性:對於使用 X86 系統來說,之前的開發鏡像確實可以直接用,因為我的鏡像就是基於 X86 系統的。
  • 如何通過docker pull以外的方式 Tag Docker鏡像
    ,意識到我們並不需要拖拽整個鏡像來標記鏡像,用Docker Registry API也可以完成。        從 Docker 鏡像倉庫 API 的角度來看,一個鏡像包含三種類型的數據:        在Docker鏡像倉庫的專門術語中,存儲庫存儲名稱相同但標記不同的所有鏡像的文件層和鏡像配置。
  • Docker運行centos鏡像
    註:如果對版本有要求可以按照以下操作進行
  • 理解Docker的多階段鏡像構建
    對於已經接納和使用Docker技術在日常開發工作中的開發者而言,構建Docker鏡像已經是家常便飯。但這是否意味著Docker的image構建機制已經相對完美了呢?不是的,Docker官方依舊在持續優化鏡像構建機制。這不,從今年發布的Docker 17.05版本起,Docker開始支持容器鏡像的多階段構建(multi-stage build)了。什麼是鏡像多階段構建呢?
  • Synopsys全套docker鏡像使用指南
    鏡像是由網友製作的,eetop連結為:http://bbs.eetop.cn/thread-883833-1-1.html僅供個人學習使用win10下安裝docker在官網找到win10的docker安裝包,進行安裝https://docs.docker.com
  • Dockerfile構建beego鏡像
    Beego是Go語言下用戶使用比較多的Web框架,國人開發,中文文檔完善,本文通過Docker搭建一個簡單的Beego鏡像,記錄一次學習的摸索流程。
  • Windows下構建Node.js的Docker Nano Server基礎鏡像
    但是這種兼容性是有代價的,這個OS鏡像的大小有9.3GB,因為它幾乎包含了整個伺服器。Nano Server鏡像是一個經過高度優化的鏡像。為了能在雲伺服器上部署更多這樣的容器,它幾乎把所有安裝包都移除了。它的大小只有817MB,使得在Windows 10上安裝Docker比用windowsservercore鏡像快很多。