作者 |?Shalabh Chaturvedi
譯者?| 鄧曉娟? ??責(zé)編 |?王子彧
(資料圖片)
出品 | CSDN(ID:CSDNnews)
無(wú)服務(wù)器開(kāi)發(fā)和反饋循環(huán)
Dagster 是一個(gè)數(shù)據(jù)編排器。在無(wú)服務(wù)器 Dagster 云上,不需要建立本地開(kāi)發(fā)環(huán)境或云基礎(chǔ)設(shè)施,就可以開(kāi)發(fā)和部署 Dagster 代碼。當(dāng)你向 GitHub 提交修改時(shí),GitHub Action?會(huì)直接構(gòu)建和部署你的代碼到 Dagster 云。你可以在用戶(hù)界面中查看和互動(dòng)你的 Dagster 對(duì)象。借助 Dagster 云,遠(yuǎn)程環(huán)境通常用于讓使用自動(dòng)創(chuàng)建的暫存環(huán)境與合作者共享部署。個(gè)人本地開(kāi)發(fā)和共享遠(yuǎn)程環(huán)境相結(jié)合,形成了一個(gè)強(qiáng)大的開(kāi)發(fā)周期。
最初,我們?cè)谶@上面使用了基于 Docker 的標(biāo)準(zhǔn)構(gòu)建流程。然而我們很快發(fā)現(xiàn),這讓編輯-部署-運(yùn)行的周期變得非常繁瑣緩慢。為了加快速度,我們構(gòu)建了一個(gè)系統(tǒng),實(shí)現(xiàn)在 Docker 鏡像之外運(yùn)送代碼。這篇文章描述了我們分析的問(wèn)題、確定的解決方案,以及在這個(gè)過(guò)程中做出的各種權(quán)衡。
Shalabh Chaturvedi 分享了 Dagster Cloud 新的快速部署能力的高水平概述,詳情請(qǐng)觀看視頻:https://youtu.be/mPT3FBFSw6g
Docker鏡像的問(wèn)題
當(dāng)我們?cè)?GitHub 上構(gòu)建 Docker 鏡像并將其部署到 Dagster 云時(shí),每次提交都需要3到5分鐘才能在 Dagster 用戶(hù)界面上顯示出來(lái)。無(wú)服務(wù)器開(kāi)發(fā)人員通常會(huì)在每次迭代中對(duì)代碼進(jìn)行小的改動(dòng),但卻每次都要等待3分鐘以上才能看到改動(dòng)的效果,這種無(wú)意義的等待很容易讓人厭煩。我們分析了一個(gè)問(wèn)題:“當(dāng)你修改一行代碼并提交后,會(huì)發(fā)生什么?”發(fā)現(xiàn)了以下的情況。
我們分析了 "當(dāng)你改變一行代碼并提交時(shí)會(huì)發(fā)生什么",發(fā)現(xiàn)了以下情況。
20s >提供 GitHub 運(yùn)行器并下載動(dòng)作?
10s >下載基于 Docker 的行動(dòng)?
60s >建立并上傳用戶(hù)的 Docker 鏡像*。
90s >在 AWS 中運(yùn)行用戶(hù)的 Docker 鏡像?
180s的運(yùn)行時(shí)間?
* 在啟用緩存的情況下需要60秒(如果沒(méi)有改變依賴(lài)關(guān)系的話);如果依賴(lài)關(guān)系有變化,則需要90秒以上。
如你所見(jiàn),花費(fèi)時(shí)間最長(zhǎng)的兩件事是:
構(gòu)建一個(gè) Docker 鏡像(60-90多秒)
部署 Docker 容器(90秒)
那就讓我們來(lái)看看這兩件事都做了些什么。
構(gòu)建 Docker 鏡像
關(guān)于構(gòu)建 Docker 鏡像需要注意的一些事情。
Docker 鏡像是由堆棧中的多個(gè)層堆疊而成的,其中每一層都是由 Docker 文件中的一個(gè)命令子集構(gòu)建的;
每一層都由一個(gè)哈希值來(lái)識(shí)別;
當(dāng)上傳鏡像到注冊(cè)表時(shí),只有不存在于注冊(cè)表中的層(由哈希值識(shí)別)被上傳;
使用 GitHub Actions 緩存在 GitHub 構(gòu)建機(jī)上重建鏡像時(shí),會(huì)將所有未受影響的層從緩存中拉到構(gòu)建機(jī)上。請(qǐng)注意,如果你的項(xiàng)目中有大量的依賴(lài)關(guān)系沒(méi)有改變,它們會(huì)在構(gòu)建過(guò)程中從緩存中一起被復(fù)制到構(gòu)建機(jī)器上;
Docker 的構(gòu)建不是確定性的。如果你用完全相同的內(nèi)容構(gòu)建一個(gè)鏡像兩次,每次都可能產(chǎn)生不同的哈希值。(雖然不直接相關(guān),但我們想記錄一下這個(gè)意外的觀察結(jié)果。作為一個(gè)極端案例,考慮到一個(gè)新構(gòu)建的大層與已經(jīng)在注冊(cè)表中的層相同,仍然可能作為一個(gè)新的層被上傳)。
啟動(dòng) Docker 容器
關(guān)于啟動(dòng) Docker 容器需要注意的是,我們使用?AWS Fargate,它需要45到90秒的時(shí)間來(lái)配置和啟動(dòng)一個(gè)鏡像。且不提供任何圖像緩存。啟動(dòng)一個(gè)新的容器會(huì)從注冊(cè)表中下載所有的層到配置的容器上。
其他限制
在 Docker 鏡像建立和啟動(dòng)后,我們運(yùn)行用戶(hù)的代碼來(lái)提取元數(shù)據(jù),顯示在用戶(hù)界面上。這一步無(wú)法避免,可能需要幾秒鐘到30秒,甚至更久,這取決于元數(shù)據(jù)的計(jì)算方式(比如它可以連接到數(shù)據(jù)庫(kù)來(lái)讀取模式)。這個(gè)代碼服務(wù)器保持活動(dòng)狀態(tài),為元數(shù)據(jù)請(qǐng)求提供服務(wù),直到推送新版本的代碼,然后啟動(dòng)一個(gè)新的容器。
我們的一個(gè)關(guān)鍵要求是可重復(fù)性:我們需要能夠多次重新部署完全相同的代碼和環(huán)境。使用 Docker 鏡像的哈希值作為代碼和環(huán)境的標(biāo)識(shí)符,可以很好地滿(mǎn)足這一要求。
備選方案綜述
除了上述的方案以外,我們還探索和討論了一些替代方案。
從 Fargate 切換到 EC2,以加快容器的啟動(dòng)。這將增加我們的運(yùn)營(yíng)負(fù)擔(dān),要求我們預(yù)先提供、監(jiān)控和擴(kuò)展我們的集群。我們?nèi)匀粫?huì)遇到 Docker 構(gòu)建緩慢的問(wèn)題;
換成不同的 Docker 構(gòu)建系統(tǒng),如 AWS CodeBuild。這將需要更多的部署工作,并與 GitHub 進(jìn)行更深入的整合。目前還不清楚這樣做的回報(bào)是否值得;
切換到 AWS Lambda,啟動(dòng)時(shí)間快得多。Lambda 環(huán)境有自己的基礎(chǔ)鏡像,對(duì)于自定義需求來(lái)說(shuō)不太友好。而且它的執(zhí)行時(shí)間還有15分鐘的限制,這對(duì)運(yùn)行時(shí)間較長(zhǎng)的服務(wù)器來(lái)說(shuō),需要復(fù)雜的變通方法;
通過(guò)構(gòu)建并只上傳修改后的代碼到同一服務(wù)器,重新使用長(zhǎng)期運(yùn)行的代碼服務(wù)器。這里的挑戰(zhàn)是實(shí)現(xiàn)打包和運(yùn)行機(jī)制,以確保一個(gè)可靠和可重復(fù)的執(zhí)行環(huán)境。我們研究了各種打包和分發(fā) Python 環(huán)境的方法,包括 rsync、poetry、nix、shiv 和 pex。還考慮了使用 EFS卷來(lái)掛載Python環(huán)境,與這些工具相結(jié)合。
我們作出最終決定背后的有一個(gè)關(guān)鍵因素,是意識(shí)到雖然 Docker 鏡像是行業(yè)標(biāo)準(zhǔn),但如果我們只需要同步一個(gè)小的變化時(shí),就去移動(dòng)100多兆的鏡像,是很不必要的繁重操作??紤]到 Git 只提供差異,但卻能產(chǎn)生完整而一致的存儲(chǔ)庫(kù)。因此我們傾向于方案4,只需要能找到一個(gè)合適的工具來(lái)做大部分的工作。經(jīng)過(guò)一些實(shí)驗(yàn),我們發(fā)現(xiàn) pex 的許多功能對(duì)我們的用例非常有效。
什么是 PEX?
pex?是 Python Executable 的縮寫(xiě),它是一種將 Python 包捆綁到稱(chēng)為 pex 文件的工具。這些是可執(zhí)行文件,其中包含 Python 包和一些引導(dǎo)代碼。例如,我們可以把 dagster 包和它的依賴(lài)項(xiàng)捆綁成一個(gè)文件,然后運(yùn)行它。
將整個(gè)環(huán)境放在一個(gè)文件中,便于運(yùn)輸和存儲(chǔ)在 S3 中。pex 提供的不僅僅是一個(gè) "文件中的虛擬環(huán)境",以下是我們使用的其他功能。
隔離
在運(yùn)行時(shí),pex 環(huán)境與其他網(wǎng)站范圍內(nèi)的包完全隔離。環(huán)境中唯一存在的包是那些捆綁在 pex 文件中的包。我們將多個(gè) pex 文件運(yùn)送到同一臺(tái)機(jī)器上,而不必?fù)?dān)心環(huán)境隔離問(wèn)題。
確定性
使用相同的輸入包會(huì)產(chǎn)生位對(duì)位的相同的 pex 文件。
這讓我們有信心用內(nèi)容尋址來(lái)識(shí)別這些 pex 文件。為了實(shí)現(xiàn)可重復(fù)性,除了Docker 鏡像的哈希值,還使用 pex 文件哈希值。?
組成
多個(gè) pex 文件可以在運(yùn)行時(shí)合并,有效地將環(huán)境合并成一個(gè)。
我們用它把代碼分成兩部分,在運(yùn)行時(shí)合并:一個(gè)包含所有依賴(lài)關(guān)系的 deps.pex 文件和一個(gè)只包含用戶(hù)代碼的 source.pex 文件。
跨平臺(tái)的構(gòu)建
我們?cè)跓o(wú)服務(wù)器云中使用 Linux python :*-slim 衍生的基礎(chǔ)鏡像。只要軟件包的輪子可用, pex 工具可以在任何平臺(tái)上為 Linux 構(gòu)建 pex 文件。
快速部署
我們使用 pex 與 S3 相結(jié)合來(lái)存儲(chǔ) pex 文件,建立了一個(gè)系統(tǒng),其中快速路徑避免了構(gòu)建和啟動(dòng) Docker 鏡像的開(kāi)銷(xiāo)。
我們的系統(tǒng)是這樣工作的:當(dāng)你向 GitHub 提交代碼時(shí),GitHub Action 要么進(jìn)行完全構(gòu)建,要么進(jìn)行快速構(gòu)建,這取決于你的依賴(lài)關(guān)系自上次部署后是否有變化。我們跟蹤 setup.py 和 requirements.txt 中指定的依賴(lài)項(xiàng)。
對(duì)于一個(gè)完整的構(gòu)建,將項(xiàng)目依賴(lài)性構(gòu)建到 deps.pex 文件,將代碼構(gòu)建到 source.pex 文件。兩者都被上傳到 Dagster 云端。對(duì)于快速構(gòu)建,只構(gòu)建和上傳 source.pex 文件。
在 Dagster 云中,可以重新使用一個(gè)現(xiàn)有的容器或提供一個(gè)新的容器作為代碼服務(wù)器。將 deps.pex 和 source.pex 文件下載到這個(gè)代碼服務(wù)器上,并使用它們?cè)谝粋€(gè)隔離的環(huán)境中運(yùn)行代碼。我們從不在用戶(hù)之間共享一個(gè)容器,一個(gè)容器上的所有環(huán)境都屬于同一個(gè)用戶(hù)??焖俨渴鸬淖罴亚闆r和最壞情況的時(shí)間線如下。
其結(jié)果是,在快速構(gòu)建(Fast Build)的路徑中,當(dāng)我們進(jìn)行快速構(gòu)建并重用現(xiàn)有容器時(shí),整個(gè)過(guò)程只需40秒,而不像以前一樣需要3分鐘以上。
我們將這一功能稱(chēng)為【快速部署】,現(xiàn)在所有新注冊(cè)的無(wú)服務(wù)器用戶(hù)都默認(rèn)開(kāi)啟這一功能。
權(quán)衡與問(wèn)題
快速部署極大地提高了部署速度(4-5倍),但它伴隨著一些需要權(quán)衡的問(wèn)題和其他因素,我們已經(jīng)進(jìn)行了調(diào)整:
雖然我們現(xiàn)在可以在一個(gè)代碼服務(wù)器上運(yùn)行多個(gè)環(huán)境,并且它們?cè)诖a上是隔離的,但它們?nèi)匀还蚕硐嗤膬?nèi)存和 CPU。如果我們?cè)谝粋€(gè)容器上放了太多的環(huán)境,而且一個(gè)環(huán)境占用了太多的內(nèi)存,就會(huì)對(duì)同一容器中的其他運(yùn)行環(huán)境產(chǎn)生不利的影響;
Docker 可以在任何操作系統(tǒng)上為 Linux 構(gòu)建 Python 包,因?yàn)槟繕?biāo) Linux 操作系統(tǒng)和 Python 解釋器在構(gòu)建過(guò)程中是可用的。pex 只能為 Linux 構(gòu)建提供輪子的包的 pex 文件。作為退路,我們?cè)跇?gòu)建過(guò)程中使用?Docker 容器來(lái)處理源碼分發(fā)。這個(gè)步驟可以在未來(lái)被移到一個(gè)單獨(dú)的共享服務(wù)中;
在構(gòu)建 Docker 鏡像時(shí),可以進(jìn)行深度定制,例如,你可以指定一個(gè)自定義的基礎(chǔ)鏡像,而不是默認(rèn)的 python :*-slim 鏡像之一。為了實(shí)現(xiàn)功能上的平等,我們必須實(shí)施一種方法,讓用戶(hù)指定他們自己的基礎(chǔ) Docker 鏡像,我們?cè)诳焖俨渴饡r(shí)使用這種鏡像。
GitHub 工作流程和 pex
很多人可能已經(jīng)注意到,原圖中,過(guò)去基于 Docker 的下載操作需要10秒左右。那么我們是如何完全消除這個(gè)步驟的呢?
以前我們把 GitHub Action 代碼打包成 Docker 鏡像,然后使用?Docker 容器操作。而現(xiàn)在,我們把動(dòng)作代碼打包成一個(gè) pex 文件,將其檢入動(dòng)作倉(cāng)庫(kù),直接在 GitHub 運(yùn)行器上運(yùn)行。這就省去了下載和啟動(dòng) Docker 動(dòng)作鏡像的時(shí)間,同時(shí)仍然允許我們打包所有的依賴(lài)項(xiàng)。
我們做的另一個(gè)小優(yōu)化是,只使用一個(gè) GitHub 工作流作業(yè)。在 GitHub 中的每一個(gè)工作啟動(dòng)都需要10秒鐘來(lái)配置一個(gè)新的運(yùn)行器。
結(jié)論
將部署時(shí)間從 3 分鐘以上減少到 40 秒,是一個(gè)顯著的加速,我們對(duì)這個(gè)結(jié)果非常滿(mǎn)意,特別是當(dāng)測(cè)試自己的服務(wù)時(shí)。使用 pex 使我們能夠在 Docker 之上建立一個(gè)可重復(fù)的、一致的環(huán)境,我們很高興使用這個(gè) pex-on-docker 組合來(lái)探索其他的可能性。
關(guān)鍵詞: