在Docker中構(gòu)建長時間運(yùn)行的腳本的一些方法
我想我已經(jīng)找到了一個非常不錯的Docker使用案例。你是不是會覺得這是一篇寫Docker有多好多好的文章,開始之前我想和你確認(rèn),這篇文章會介紹如何把文件系統(tǒng)作為持久性的數(shù)據(jù)結(jié)構(gòu)。
因此,這篇文章的見解同樣適用于其他的 copy-on-write文件系統(tǒng),如BTRFS和ZFS。
問題
讓我們從這個我試圖解決的問題開始。我開發(fā)了一個會運(yùn)行很長時間的構(gòu)建腳本,這個腳本中包含了很多的步驟。
這個腳本會運(yùn)行1-2個小時。
它會從網(wǎng)絡(luò)下載比較大的文件(超過300M)。
后面的構(gòu)建步驟依賴前期構(gòu)建的庫。
但最最煩人的是,運(yùn)行這個腳本真的需要花很長的時間。
文件系統(tǒng)是固有狀態(tài)
我們一般是通過一種有狀態(tài)的方式與文件系統(tǒng)進(jìn)行交互的。我們可以添加、刪除或移動文件。我們可以修改文件的 權(quán)限或者它的訪問時間。大部分獨(dú)立的操作都可以撤銷,例如將文件移動到其它地方后,你可以將文件恢復(fù)到原來的位置。但我們不會通過快照的方式來將它恢復(fù)到 原始狀態(tài)。這篇文章我將會介紹如何在耗時較長的腳本中充分利用快照這一特性。
使用聯(lián)合文件系統(tǒng)的快照
Docker使用的是聯(lián)合文件系統(tǒng)叫做AUFS(譯者注:簡單來說就是支持將不同目錄掛載到同一個虛擬文件系統(tǒng)下的文件系統(tǒng))。聯(lián)合文件系統(tǒng)實(shí)現(xiàn)了Union mount。顧名思義,也就是說不同的文件系統(tǒng)的文件和目錄可以分層疊加在單個連貫文件系統(tǒng)之上。這是通過分層的方式完成的。如果一個文件出現(xiàn)在兩個文件系統(tǒng),那最高層級的文件才會顯示(該文件其它版本也是存在于層級中的,不會改變,只是看不到的)。
在Docker中,每一個在Union mount轉(zhuǎn)哦給你的文件系統(tǒng)都被稱為layers(層)。使用這種技術(shù)可以輕松實(shí)現(xiàn)快照,每個快照都是所有層的一個Union mount。
生成腳本的快照
使用快照可以幫助構(gòu)建一個長時運(yùn)行的腳本??偟南敕ㄊ牵瑢⒁粋€大的腳本分解為許多小的腳本(我喜歡稱之為 scriptlets),并單獨(dú)運(yùn)行這些小的腳本,腳本運(yùn)行后為其文件系統(tǒng)打一個快照 (Docker會自動執(zhí)行此操作)。如果你發(fā)現(xiàn)一個scriptlet運(yùn)行失敗,你可以快速回退到上次的快照,然后再試一次。一旦你完成腳本的構(gòu)建,并且 可以保證腳本能正常工作,那你就可以將它分配給其它主機(jī)。
回過頭來再對比下,如果你沒有使用快照功能了?當(dāng)你辛辛苦苦等待了一個半小時后,腳本卻構(gòu)建失敗了,我想除了少部分有耐心的人外,很多人是不想再來一次了,當(dāng)然,你也會盡最大努力把系統(tǒng)恢復(fù)到失敗前的狀態(tài),比如可以刪除一個目錄或運(yùn)行make clean。
但是,我們可能沒有真正地理解我們正在構(gòu)建的組件。它可能有復(fù)雜的Makefile,它會把把文件放到文件系統(tǒng)中我們不知道的地方,唯一真正確定的途徑是恢復(fù)到快照。
使用快照構(gòu)建腳本的Docker
在本節(jié)中,我將介紹我是如何使用Docker實(shí)現(xiàn)GHC7.8.3 ARM交叉編譯器的構(gòu)建腳本。Docker非常適合做這件事,但并非完美。我做了很多看起來沒用的或者不雅的事情,但都是必要的,這都是為了保證將開發(fā)腳本的總時間降到最低限度。構(gòu)建腳本可以在這里找到。
用Dockerfile構(gòu)建
Docker通過讀取Dockerfile來構(gòu)建鏡像。Dockerfile會通過一些命令來具體指定應(yīng)該執(zhí)行哪些動作。在我的腳本中主要用到WORKDIR、ADD和RUN。ADD命令非常有用因?yàn)樗梢宰屇阍谶\(yùn)行之前將外部文件添加到當(dāng)前Docker鏡像中然后轉(zhuǎn)換成鏡像的文件系統(tǒng)。你可以在這里看到很多scriptlets構(gòu)成的構(gòu)建腳本。
設(shè)計
1. 在RUN之前ADD scriptlets
如果你很早就將所有的scriptletsADD在Dockerfile,您可能會遇到以下問題:如果你的腳本構(gòu)建失敗,你回去修改scriptlet并再次運(yùn)行docker build。但是你發(fā)現(xiàn),Docker開始在首次加入scriptlets的地方構(gòu)建!這樣做會浪費(fèi)了大量的時間并且違背了使用快照的目的。
出現(xiàn)這種情況的原因是由于Docker處理它的中間鏡像(快照)的方式。當(dāng)Docker通過Dockerfile構(gòu)建鏡像時,它會與中間鏡像比較當(dāng)前命令是否一致。然而,在ADD命令的情況下被裝進(jìn)鏡像的文件里的內(nèi)容也會被檢查。如果相對于現(xiàn)有的中間鏡像,文件已經(jīng)改變,那么Docker也別無選擇,只能從這點(diǎn)開始建立一個新的鏡像。因?yàn)镈ocker不知道這些變化會不會影響到構(gòu)建。
此外,使用RUN命令要注意,每次運(yùn)行時它都會導(dǎo)致文件系統(tǒng)有不同的更改。在這種情況下,Docker會發(fā)現(xiàn)中間鏡像并使用它,但是這將是錯誤的。RUN命令每次運(yùn)行時會造成文件系統(tǒng)相同的改變。舉個例子,我確保在我的scriptlets我總是下載了一個已知版本的文件與一個特定MD5校驗(yàn)。
對Docker 構(gòu)建緩存更詳細(xì)的解釋可以在這里找到。
2.不要使用ENV命令來設(shè)置環(huán)境變量,請使用scriptlet。
它似乎看起來很有誘惑力:使用ENV命令來設(shè)置所有構(gòu)建腳本需要的環(huán)境變量。但是,它不支持變量替換的方式,例如 ENV BASE=$HOME/base 將設(shè)置BASE的值為$HOME/base著很可能不是你想要的。
相反,我用ADD命令添加一個名為set-env.sh文件。此文件會包含在后續(xù)的scriptlet中:
source $THIS_DIR/set-env-1.sh
如果你沒有在第一時間獲取set-env.sh會怎么樣呢?它很早就被加入Dockerfile并不意味著修改它將會使隨后的快照無效?
是的,這會有問題。在開發(fā)腳本時,我發(fā)現(xiàn),我已經(jīng)錯過了在set-env.sh添加一個有用的環(huán)境變量。解決方案是創(chuàng)建一個新的文件set-env-1.sh包含:
source $THIS_DIR/set-env.sh
if ! [ -e "$CONFIG_SUB_SRC/config.sub" ] ; then
CONFIG_SUB_SRC=${CONFIG_SUB_SRC:-$NCURSES_SRC}
fi
然后,在所有后續(xù)的scriptlets文件中包含了此文件?,F(xiàn)在,我已經(jīng)完成了構(gòu)建腳本,我可以回去解決這個問題了,但是,在某種意義上,它會破壞最初的目標(biāo)。我將不得不從頭開始運(yùn)行構(gòu)建腳本看看這種變化是否能成功。
缺點(diǎn)
一個主要缺點(diǎn)是這種方法是,所構(gòu)建的鏡像尺寸是大于它實(shí)際需求的尺寸。在我的情況下尤其如此,因?yàn)槲以谧詈髣h除了大量文件的。然而,這些文件都仍然存在于聯(lián)合掛載文件系統(tǒng)的底層文件系統(tǒng)內(nèi),所以整個鏡像是大于它實(shí)際需要的大小至少多余的是刪除文件的大小。
然而,有一個變通。我沒有公布此鏡像到Docker Hub Registry。相反,我:
使用docker export導(dǎo)出內(nèi)容為tar文件。
創(chuàng)建一個新的Dockerfile簡單地添加了這個tar文件的內(nèi)容。
產(chǎn)生尺寸盡可能小的鏡像。
結(jié)論
這種方法的優(yōu)點(diǎn)是雙重的:
它使開發(fā)時間降至最低,不再做那些已經(jīng)構(gòu)建成功的子組件。你可以專注于那些失敗的組件。
這非常便于維護(hù)構(gòu)建腳本。構(gòu)建可能會失敗,但只要你搞定Dockerfiel,至少你不必再從頭開始。
相關(guān)文章

推出跨端全天候智能助手Uclaw! 統(tǒng)信UOS桌面操作系統(tǒng)V25發(fā)布
4月15日,統(tǒng)信軟件正式發(fā)布統(tǒng)信桌面操作系統(tǒng)V25,作為國產(chǎn)操作系統(tǒng)里程碑式版本,該版本在智能化、高效性、可靠性三大維度實(shí)現(xiàn)系統(tǒng)性突破,標(biāo)志著國產(chǎn)操作系統(tǒng)從“可用”全2026-04-16
哪個系統(tǒng)誰最懂你? 2026全球十大操作系統(tǒng)排行榜
根據(jù)最新Statcounter、Statista等權(quán)威數(shù)據(jù)統(tǒng)計,全球操作系統(tǒng)市場份額發(fā)生了微妙變化,下面我們就來看看2026全球十大操作系統(tǒng)排行榜2026-04-08
輕松玩轉(zhuǎn)虛擬機(jī)套娃! 在Hyper-V中開啟嵌套虛擬化的技巧
有辦法在虛擬機(jī)中創(chuàng)建虛擬機(jī)嗎?答案是肯定的,在虛擬機(jī)中運(yùn)行虛擬機(jī)是可能的,我們將其稱為嵌套虛擬化,下面我們就來看看詳細(xì)操作方法2025-02-19
龍蜥操作系統(tǒng)Anolis OS-23.x安裝配置圖解教程(保姆級)
本文主要介紹了安裝和配置AnolisOS 23.2系統(tǒng),包括分區(qū)、軟件選擇、設(shè)置root密碼、網(wǎng)絡(luò)配置、主機(jī)名設(shè)置和禁用SELinux的步驟,具有一定的參考價值,感興趣的可以了解一下2025-01-20
VMware虛擬機(jī)怎么復(fù)制/克隆/導(dǎo)出VMDK文件?
在處理文件拷貝復(fù)制到VMware虛擬機(jī)中的任務(wù)時,有多種方法可以實(shí)現(xiàn)文件的高效傳輸,使用這些方法,可以在不同的操作系統(tǒng)和環(huán)境下,實(shí)現(xiàn)文件的快速、安全遷移,詳細(xì)請看下文2024-09-24
統(tǒng)信 UOS V20 桌面專業(yè)版更新發(fā)布:附更新內(nèi)容匯總
統(tǒng)信 UOS V20 桌面專業(yè)版(1070u1)正式發(fā)布,支持微信掃碼登錄、應(yīng)用窗口縮略圖,海量功能修復(fù),詳細(xì)請看下文介紹2024-08-08
怎么看自己IP地址? 不同的操作系統(tǒng)中查看路由器的IP地址的方法
在互聯(lián)網(wǎng)上進(jìn)行網(wǎng)絡(luò)連接,每個設(shè)備都有一個唯一的標(biāo)識,即IP地址,本文介紹了如何在不同平臺上查找路由器的IP地址,包括Windows、Mac、iPhone、iPad、Android、Chrome OS和2024-07-16
mware虛擬機(jī)運(yùn)行速度卡慢原因分析及解決辦法
很多人在使用虛擬機(jī)系統(tǒng)的時候,經(jīng)常對虛擬機(jī)的運(yùn)行速度不甚滿意,甚至經(jīng)常很惱火,虛擬機(jī)速度慢有很多原因,每個人需要根據(jù)自己的情況具體分析,本文根據(jù)筆者的使用經(jīng)驗(yàn)將2024-02-21
Vmware虛擬機(jī)如何與主機(jī)之間直接復(fù)制粘貼文件?
使用虛擬機(jī)的時候,遇到了錯誤,想要把錯誤的代碼或是運(yùn)行情況復(fù)制粘貼到Windows的網(wǎng)上論壇區(qū)求助大神,該怎么操作呢?詳細(xì)請看下文介紹2024-02-21
統(tǒng)信UOS V20桌面專業(yè)版(1060)11月更新發(fā)布(附更新內(nèi)容匯總)
國產(chǎn)桌面操作系統(tǒng)統(tǒng)信 UOS V20 專業(yè)版(1060)11月更新發(fā)布,這是統(tǒng)信 UOS V20 專業(yè)版(1060)2023 年度最后一次更新,下面我們就來看看詳細(xì)的更新內(nèi)容2023-12-01










