隨著Docker的普及,許多公司的產(chǎn)品會將組件構(gòu)建為Docker鏡像。但隨著時(shí)間的推移,一些鏡像變得越來越大,對應(yīng)的CI構(gòu)建也變得越來越慢。
如果能在喝完一杯咖啡的時(shí)間(不超過5分鐘)內(nèi)完成構(gòu)建,將是一個(gè)理想狀態(tài)。否則,則會減慢開發(fā)人員的生產(chǎn)力。
本篇文章帶大家通過兩個(gè)小的改變,來提升Docker的構(gòu)建時(shí)間。
Docker最佳實(shí)踐
在講解改變之前,首先要確保遵循了編寫Dockerfile的最佳實(shí)踐:
容器應(yīng)該是短暫的; 鏡像層數(shù)盡可能少; 使用多階段構(gòu)建; 使用最小的基礎(chǔ)鏡像; 避免安裝不必要的包; 一個(gè)容器只運(yùn)行一個(gè)進(jìn)程; 將多行參數(shù)排序; 構(gòu)建緩存; ...
Buildkit
Buildkit是改進(jìn)后的后端,用于替代傳統(tǒng)的Docker構(gòu)建器。自2018年起,它已經(jīng)與Docker捆綁在一起,并成為Docker引擎23.0版本的默認(rèn)構(gòu)建器。
它提供了一些特殊的功能:
改進(jìn)的緩存能力; 并行構(gòu)建不同的層; 延遲拉取基礎(chǔ)鏡像(≥Buildkit 0.9);
使用Buildkit時(shí),會發(fā)現(xiàn)docker build命令的輸出看起來更清晰、更結(jié)構(gòu)化。
在Docker版本低于23.0時(shí),使用Buildkit的一種典型方法是設(shè)置Buildkit參數(shù)如下:
DOCKER_BUILDKIT=1dockerbuild--platformlinux/amd64.-tsomeImage:someVersion
DOCKER_BUILDKIT=1dockerpushsomeImage:someVersion
Buildx
Buildx是Docker的一個(gè)插件,能夠充分利用Docker中的Buildkit的潛力。它的創(chuàng)建是因?yàn)锽uildkit支持許多新的配置選項(xiàng),不能全部以向后兼容的方式集成到docker build命令中。
除了構(gòu)建鏡像之外,Buildx還支持管理多個(gè)構(gòu)建器。這在CI中非常有用,可以定義具有不同配置的作用域環(huán)境,因?yàn)樗鼈儾粫薷墓蚕淼腄ocker守護(hù)程序。
可以按照以下方式開始使用Buildx:
dockerbuildxcreate--bootstrap--namebuilder
dockerbuildxusebuilder
遠(yuǎn)程緩存
加快構(gòu)建速度的第一種方法是將鏡像緩存在遠(yuǎn)程注冊表中。這樣,即使構(gòu)建在不同的機(jī)器上執(zhí)行(通常在CI中會這樣),也可以從構(gòu)建緩存中受益。
作為一種解決方法,許多人在構(gòu)建新的鏡像版本之前拉取了最新版本的鏡像。好處是可以以拉取完整鏡像的代價(jià)來緩存未更改的層。拉取完整鏡像可能需要一些時(shí)間,但也不能保證層可以被重用。
為了說明這一點(diǎn),可以使用以下命令:
dockerpullsomeImage:latest||true
dockerbuild--platformlinux/amd64.\
-tsomeImage:someVersion\
-fDockerfile\
--cache-fromsomeImage:latest
使用Buildx,可以將緩存信息存儲在遠(yuǎn)程位置(例如容器注冊表、Blob存儲等)中。構(gòu)建器將檢查給定的層是否已經(jīng)存在,如果存在,則會重新使用它,而不是再次創(chuàng)建它。
甚至可以在不將層拉取到本地的情況下完成此操作。為了能夠從此機(jī)制中受益,我們對先前的命令進(jìn)行了改進(jìn):
dockerbuildxbuild--platformlinux/amd64.\
-tsomeImage:someVersion--push\
--cache-totype=registry,ref=someCachedImage:someVersion,mode=max
--cache-fromtype=registry,ref=someCachedImage:someVersion
模式“max”表示為每個(gè)層存儲構(gòu)建信息,甚至包括在生成的鏡像中未使用的層(例如在使用多階段構(gòu)建時(shí))。默認(rèn)情況下使用“min”模式,它僅存儲關(guān)于最終鏡像中存在的層的構(gòu)建信息。
緩存的一個(gè)特殊情況是將緩存數(shù)據(jù)“內(nèi)聯(lián)”存儲,這意味著它將與鏡像一起被緩存。即使在不使用Buildx的情況下使用Buildkit時(shí),該選項(xiàng)也是支持的。它是最容易使用的方法,但在使用多階段構(gòu)建時(shí)更加棘手,并且它不能清晰地區(qū)分輸出的工件和緩存。
將緩存數(shù)據(jù)“內(nèi)聯(lián)”存儲的命令如下所示:
dockerbuildxbuild--platformlinux/amd64.\
-tsomeImage:someVersion--push\
--cache-totype=inline,mode=max\
--cache-fromsomeImage:somePreviousVersion
添加文件到Docker鏡像的新方法
Docker引入了一種新版本的語法來編寫Dockerfile,即:#syntax=docker/dockerfile:1.4。它為COPY和ADD命令提供了額外的鏈接選項(xiàng)。
以前,當(dāng)使用COPY或ADD命令時(shí),構(gòu)建器會創(chuàng)建一個(gè)新的快照,將新文件與已存在的文件系統(tǒng)合并。結(jié)果是,在執(zhí)行此操作之前,所有父層都需要存在,否則目標(biāo)目錄可能尚不存在。
最終,鏡像(構(gòu)建命令的結(jié)果)將由每個(gè)層的tarball組成,其中包含各個(gè)快照之間的差異。
FROMbaseImage:version
COPYbinary/opt/
使用鏈接選項(xiàng)時(shí),新文件將放置在它們自己的快照中,而不依賴于先前的層。鏈接的文件存儲在它們自己的tarball中,并且不依賴于現(xiàn)有的文件系統(tǒng),如下圖所示。

#syntax=docker/dockerfile:1.4
FROMbaseImage:version
COPY[--chown=<user>:<group>][--chmod=<perms>]--linkbinary/opt/
主要優(yōu)勢是文件不再依賴于先前的層。只要文件沒有更改,層就可以被重復(fù)使用,即使父層發(fā)生了變化。
此外,這也可以提高構(gòu)建速度,因?yàn)楝F(xiàn)在可以并行執(zhí)行多個(gè)層的數(shù)據(jù)復(fù)制。
小論
本文介紹了兩種小的改變,可以讓整個(gè)Docker構(gòu)建時(shí)間大幅縮減的方法,希望在實(shí)踐的過程中對大家有所幫助。這兩個(gè)小改變分別是:
將構(gòu)建緩存信息存儲在遠(yuǎn)程位置; 在將文件添加、復(fù)制到docker鏡像時(shí)使用鏈接選項(xiàng);
當(dāng)然,在使用Docker時(shí),關(guān)于Dockerfile編寫的最佳實(shí)踐,大家也要留意一下。