欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

優(yōu)化docker鏡像體積的方法詳解

 更新時間:2025年03月27日 10:09:17   作者:企鵝俠客  
鏡像的本質(zhì)是鏡像層和運(yùn)行配置文件組成的壓縮包,構(gòu)建鏡像是通過運(yùn)行 Dockerfile 中的 RUN 、COPY 和 ADD 等指令生成鏡像層和配置文件的過程,本文給大家介紹了如何優(yōu)化 docker 鏡像體積,需要的朋友可以參考下

關(guān)注鏡像體積有關(guān)的關(guān)鍵點(diǎn)如下:

  • RUNCOPY 和 ADD 指令會在已有鏡像層的基礎(chǔ)上創(chuàng)建一個新的鏡像層,執(zhí)行指令產(chǎn)生的所有文件系統(tǒng)變更會在指令結(jié)束后作為一個鏡像層整體提交。
  • 鏡像層具有 copy-on-write 的特性,如果去更新其他鏡像層中已存在的文件,會先將其復(fù)制到新的鏡像層中再修改,造成雙倍的文件空間占用。
  • 如果去刪除其他鏡像層的一個文件,只會在當(dāng)前鏡像層生成一個該文件的刪除標(biāo)記,并不會減少整個鏡像的實(shí)際體積。

上述理論可以通過如下 Dockerfile 來驗(yàn)證:

FROM alpine:latest
COPY resource.tar /
RUN touch /resource.tar
RUN rm -f /resource.tar
ENTRYPOINT ["/bin/ash"]

我們在 Dockerfile 中簡單地添加、修改和刪除某個資源文件,然后構(gòu)建鏡像查看其鏡像層信息:

$ docker build -t test-image -f Dockerfile .
$ docker history test-image:latest
IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
95f1695b2904   About a minute ago   /bin/sh -c #(nop)  ENTRYPOINT ["/bin/ash"]      0B
1780448c656f   About a minute ago   /bin/sh -c rm -f /resource.tar                  0B
a85d29bf7738   About a minute ago   /bin/sh -c touch /resource.tar                  135MB
6dac335fa653   4 minutes ago        /bin/sh -c #(nop) COPY file:66065d6e23e0bc52…   135MB
e66264b98777   7 weeks ago          /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      7 weeks ago          /bin/sh -c #(nop) ADD file:8e81116368669ed3d…   5.53MB

在 docker history 的輸出結(jié)果中可以看到:

  • RUN touch /resource.tar 指令只是修改了文件的元信息,但依然將整個文件拷貝到了新的鏡像層中。
  • RUN rm -f /resource.tar 指令雖然刪除了文件,并且該文件在運(yùn)行容器時不可見,但依然在前兩個鏡像層中以及最終的鏡像中存在。

分析工具

給代碼做性能調(diào)優(yōu)時,首先要借助 Profiling 工具找到代碼的性能瓶頸,對于優(yōu)化鏡像體積也是如此。下面介紹兩個可以分析鏡像體積的工具:

docker history

docker 自帶的 docker history 命令,該命令可以展示所有鏡像層的創(chuàng)建時間、指令以及體積等較為基礎(chǔ)的信息,但對于復(fù)雜的鏡像則有些乏力。使用方式見上方的示例。

dive

第三方的 dive 工具,該工具可以分析鏡像層組成,并列出每個鏡像層所包含的文件列表,可以很方便地定位到影響鏡像體積的構(gòu)建指令以及具體文件。

以 golang:1.16 鏡像為例,首先安裝 dive,然后執(zhí)行 dive golang:1.16,輸出如下:

如上圖所示,在左側(cè)選中鏡像層后,在右側(cè)的文件樹視圖中可以清晰地看到該層的具體文件,并能夠篩選相比上一層新增、更新或刪除的文件。在選中的鏡像層中,由于執(zhí)行了 apt-get 安裝編譯依賴,因此在 /usr/lib 目錄下新增了 150MB 依賴庫文件。

優(yōu)化技巧

下面介紹一些優(yōu)化效果比較顯著的優(yōu)化技巧。

分階段構(gòu)建與從零構(gòu)建

分階段構(gòu)建(multi-stage builds)和從零構(gòu)建(build from scratch)是優(yōu)化鏡像體積的基本手段和必備技巧。該技巧將鏡像構(gòu)建過程區(qū)分為構(gòu)建和運(yùn)行環(huán)境,在構(gòu)建環(huán)境安裝編譯器等依賴并編譯所需的二進(jìn)制包,然后將其復(fù)制到僅包含必要運(yùn)行依賴的運(yùn)行環(huán)境中。

對 golang 這類能夠編譯靜態(tài)二進(jìn)制文件的語言來說分階段構(gòu)建的效果尤為明顯,我們可以將編譯產(chǎn)生的二進(jìn)制文件放到 scratch 鏡像中運(yùn)行(scratch 是一個特殊的空鏡像):

FROM golang
COPY hell0.go .
ENV CGO_ENABLED=0
RUN go build hello.go

FROM scratch
COPY --from=0 /go/hello .
CMD ["./hello"]

如果直接使用 golang 鏡像作為運(yùn)行環(huán)境,其鏡像體積通常接近 1 個 G,其中大部分文件都不是在運(yùn)行容器時所必要的。 將編譯結(jié)果拷貝到運(yùn)行環(huán)境后,體積只有幾十 kb~mb 不等,如果需要在運(yùn)行容器中保留基本的系統(tǒng)工具,可以考慮使用 alpine 鏡像作為運(yùn)行環(huán)境。

關(guān)于分階段構(gòu)建和從零構(gòu)建的更多細(xì)節(jié)可參考 Docker 官方文檔中的 Use multi-stage buildsCreate a simple parent image using scratch。

避免產(chǎn)生無用的文檔或緩存

docker 鏡像不應(yīng)該包含文檔、緩存等對運(yùn)行容器沒有作用的內(nèi)容。

docker 鏡像不應(yīng)該包含文檔、緩存等對運(yùn)行容器沒有作用的內(nèi)容。

  1. 避免在本地保留安裝緩存。

    大部分包管理器會在安裝時緩存下載的資源以備之后使用,以 pip 為例,會將下載的響應(yīng)和構(gòu)建的中間文件保存在 ~/.cache/pip 目錄,應(yīng)使用 --no-cache-dir 選項禁用默認(rèn)的緩存行為。

  2. 避免安裝文檔。

    部分包管理器提供了選項可以不安裝附帶的文檔,如 dnf 可使用 --nodocs 選項。

  3. 避免緩存包索引。

    部分包管理器在執(zhí)行安裝之前,會嘗試查詢所有已啟用倉庫的包列表、版本等元信息緩存在本地作為索引。個別倉庫的索引緩存可達(dá)到 150 M 以上。 我們應(yīng)該僅在安裝包時查詢索引,并在安裝完成后清理,不應(yīng)該在單獨(dú)的指令中執(zhí)行 yum makecache 這類緩存索引的命令。

及時清理不需要的文件

運(yùn)行容器時不需要的文件,一定要在創(chuàng)建的同一層清理,否則依然會保留在最終的鏡像中。

通過包管理安裝包,通常會產(chǎn)生大量的緩存文件,一定要在同一 RUN 指令的結(jié)尾處立刻清理。在安裝依賴數(shù)量較多時,可以節(jié)省大量的緩存空間。

以 dnf 為例:

RUN dnf install -y --nodocs <PACKAGES> \
  && dnf clean all \
  && rm -rf /var/cache/dnf

以 apt 為例:

RUN apt-get update \
  && apt-get install -y <PACKAGES> \
  && rm -rf /var/lib/apt/lists/*
# 官方的 ubuntu/debian 鏡像 apt-get 會在安裝后自動執(zhí)行 clean 命令

合并多個鏡像層

上文解釋過,應(yīng)該避免在不同鏡像層中更新文件而造成額外的體積占用。當(dāng)構(gòu)建的層數(shù)很多且執(zhí)行指令較復(fù)雜時,很難避免在不同的鏡像層中更新文件,可通過以下手段精簡這部分額外體積:

在最終生成鏡像時將所有鏡像層合并成一層,在 docker build 命令中使用 —squash 即可實(shí)現(xiàn)(需要開啟 docker daemon 的實(shí)驗(yàn)性功能)。以本文開頭的 Dockerfile 為例:

$ docker build -t squash-image --squash -f Dockerfile . 
$ docker history squash-image
IMAGE          CREATED        CREATED BY                                      SIZE      COMMENT
55ded8881d63   9 hours ago                                                    0B        merge sha256:95f1695b29044522250de1b0c1904aaf8670b991ec1064d086c0c15865051d5d to sha256:e66264b98777e12192600bf9b4d663655c98a090072e1bab49e233d7531d1294
<missing>      11 hours ago   /bin/sh -c #(nop)  ENTRYPOINT ["/bin/ash"]      0B
<missing>      11 hours ago   /bin/sh -c rm -f /resource.tar                  0B
<missing>      11 hours ago   /bin/sh -c touch /resource.tar                  0B
<missing>      11 hours ago   /bin/sh -c #(nop) COPY file:66065d6e23e0bc52…   0B
<missing>      7 weeks ago    /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      7 weeks ago    /bin/sh -c #(nop) ADD file:8e81116368669ed3d…   5.53MB
  1. 最終生成的鏡像只有一個鏡像層,包含最后實(shí)際存在的文件系統(tǒng),在合并所有鏡像層的過程中,相當(dāng)于禁用了 copy-on-write 特性。

    這種做法的壞處在于,鏡像在保存和分發(fā)時是可以復(fù)用鏡像層的,推送鏡像時會跳過鏡像倉庫已存在的鏡像層,拉取鏡像時會跳過本地已拉取過的鏡像層,而合并成一層后則失去了這種優(yōu)勢。

    對于可能和其他共用鏡像層的場景,可以采取下面一種方式。

  2. 分階段構(gòu)建,將部分中間鏡像層壓縮成一層作為基礎(chǔ)鏡像。 在開發(fā)團(tuán)隊內(nèi)部,我們往往會在官方鏡像的基礎(chǔ)上添加或更新部分依賴,然后作為團(tuán)隊內(nèi)部統(tǒng)一使用的基礎(chǔ)鏡像,這種復(fù)用方式可以大大減少實(shí)際占用的鏡像體積。 更進(jìn)一步,我們可以將這類基礎(chǔ)鏡像壓縮成一層。下面以 golang 官方鏡像為例:

FROM golang:1.16 as base

FROM scratch
COPY --from=base / /
ENTRYPOINT ["/bin/bash"]
  1. 壓縮成一層后,golang:1.16 的鏡像體積從 919MB 變成 913MB,官方鏡像已經(jīng)做了很多優(yōu)化所以節(jié)省空間十分有限,但對于開發(fā)團(tuán)隊內(nèi)部制作的基礎(chǔ)鏡像,這種優(yōu)化往往會帶來意外驚喜。

復(fù)制文件的同時修改元信息

先將文件添加到鏡像內(nèi),然后再修改文件的執(zhí)行權(quán)限和所屬用戶,這類 COPY-RUN 指令在 Dockerfile 中十分常見:

COPY output/hello /usr/bin/hello
RUN chmod +x /usr/bin/hello && chown normal:normal /usr/bin/hello

但修改文件元信息也會將文件復(fù)制到新的鏡像層,以上指令會產(chǎn)生兩份相同的文件。在文件體積較大時,會顯著增加整個鏡像的體積。 事實(shí)上,我們可以在復(fù)制文件的同時完成對文件元信息的修改,COPY 和 ADD 指令都提供了修改元信息的 --chmod 和 --chown 選項:

COPY --chmod=755 --chown=normal:normal output/hello /usr/bin/hello

--chmod 特性目前還未添加到官方文檔,使用前需要開啟 docker 的 buildkit 特性(在 docker build 命令前添加 DOCKER_BUILDKIT=1 即可),目前只支持 --chmod=755 和 --chmod=0755 這種設(shè)置方法,不支持 --chmod=+x

注:經(jīng)測試,當(dāng)使用 ADD 指令且源文件為下載鏈接時 --chmod 選項不起作用,不清楚這是 docker 的 bug 還是 feature。解決方案是直接使用 RUN 指令 wget + chmod 來替代 ADD。

以上就是優(yōu)化docker鏡像體積的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于docker鏡像體積優(yōu)化的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論