2019-09-30
任何曾經管理(lǐ)過幾十上百台物(wù)理(lǐ)服務器的(de)人(rén)都知道:确保所有服務器始終安裝最新安全更新,或者保證所有服務器的(de)配置和(hé)狀态相一緻,這(zhè)始終是一件很難完成的(de)任務。爲了(le)解決這(zhè)個(gè)問題,系統管理(lǐ)員(yuán)通(tōng)常會使用(yòng) Puppet 、 Salt 等工具,或将應用(yòng)程序部署到容器中。如果整個(gè)環境都能由你控制,這(zhè)些當然都是很棒的(de)方法,但如果你使用(yòng)了(le)類似 BCDR 一體機之類的(de)設備(或者任何未部署在自己基礎架構内的(de)一體機 / 服務器設施),這(zhè)些方法往往就不怎麽實用(yòng)了(le)。除此之外,替換系統内核、安裝大(dà)型系統升級,或安裝其他(tā)需要重啓的(de)大(dà)型補丁,此時(shí)也(yě)無法适用(yòng)這(zhè)些方法。
當我們使用(yòng)的(de) BCDR 設備面臨這(zhè)些問題後,我們開始尋找其他(tā)更可(kě)行的(de)方法,并且真的(de)有所收獲。近兩年來(lái),我們爲超過 80,000 台設備使用(yòng)了(le)這(zhè)種方法,效果一直很穩定。本文我将談談我們是如何通(tōng)過鏡像、回環設備(Loop device)以及大(dà)量和(hé) Grub 有關的(de)“魔法”解決這(zhè)個(gè)問題的(de)。如果對(duì)此話(huà)題感興趣,歡迎繼續閱讀下(xià)去。
1
從頭到尾使用(yòng) Debian 軟件包?
我們的(de) BCDR 一體機始終運行了(le) Ubuntu,因此在更新軟件時(shí),最自然的(de)方法就是使用(yòng) Debian 軟件包。過去很長(cháng)時(shí)間以來(lái)都是這(zhè)樣做(zuò)的(de):每兩周,我們會爲 Ubuntu 10.04/12.04(沒錯,我知道你有疑問,請繼續讀下(xià)去!)構建所需的(de)發布,經過全面測試後将其正式部署出去。
很長(cháng)時(shí)間以來(lái)這(zhè)樣做(zuò)完全沒問題,但這(zhè)種做(zuò)法有一些很明(míng)顯的(de)不足之處:
第三方依賴項的(de)更新:使用(yòng)少量 Debian 軟件包,這(zhè)容易讓人(rén)覺得(de)隻需要爲自己的(de)軟件負責,而不需要爲一體機中運行的(de)其他(tā)軟件負責。如果你隻使用(yòng)了(le)自己的(de) datto.deb,但此時(shí) Apache、Samba、libc 甚至 PHP 的(de)更新管理(lǐ)工作其實同樣重要。鑒于我們作爲 Datto,本身所銷售的(de)就是完整的(de)一體機,當然也(yě)就需要負責管理(lǐ)整個(gè)棧,尤其是第三方軟件的(de)安全補丁等内容。
服務重啓動 / 重引導:對(duì)于一些需要重啓動服務甚至重引導計算(suàn)機的(de)大(dà)型更新,依賴項問題也(yě)會變得(de)異常棘手。當然,Debian 軟件包自己就應該能處理(lǐ)服務重啓動問題,但實際上并非所有軟件包都能妥善搞定。并且一旦需要重引導(例如需要升級系統内核),還(hái)需要确保不會打斷重要的(de)設備任務(例如備份、虛拟化(huà)……),此外還(hái)要保證設備最終能引導成功(這(zhè)事情并不像你想的(de)那麽容易,下(xià)文将會詳細介紹!)。
發行版升級:如果整個(gè)操作系統的(de)版本需要升級,這(zhè)才是最麻煩的(de)地方。舉例來(lái)說,如果隻使用(yòng) apt-get dist-upgrade 命令以及 reboot 命令将 Ubuntu 10.04 升級到 16.04,整個(gè)過程将變得(de)漫長(cháng)無比,并且很多(duō)時(shí)候可(kě)能會升級失敗(隻要你用(yòng)過 usedapt-get dist-upgrade,那麽肯定會明(míng)白)。
數千個(gè)版本和(hé)狀态:在 Debian 的(de)升級模型中,“設備”的(de)實際行爲其實和(hé)普通(tōng)計算(suàn)機無異:剛剛創建好鏡像并部署後,一切都是嶄新的(de),一切都可(kě)以正常運轉。但随著(zhe)鏡像越來(lái)越老,操作系統退化(huà)的(de)問題就變得(de)越嚴峻,導緻不同設備的(de)狀态産生巨大(dà)差異。能嚴重到什(shén)麽程度?我們的(de)設備(在切換到 KVM 前)曾經使用(yòng)了(le) 40 個(gè)不同版本的(de) VirtualBox、25 個(gè)不同的(de) ZFS 版本,以及超過 80 個(gè)不同的(de) Linux 内核!
其實,實際遇到的(de)問題遠(yuǎn)比上面列出的(de)更多(duō),不過這(zhè)裏就不拿更多(duō)問題來(lái)給大(dà)家添堵了(le)。快(kuài)速開始介紹最有趣的(de)内容:如何解決!
2
使用(yòng)鏡像,而非軟件包!
鑒于會遇到這(zhè)麽多(duō)問題,很明(míng)顯,我們需要用(yòng)更好的(de)解決方案來(lái)管理(lǐ)設備狀态和(hé)配置。産品中不同的(de)設備配置 / 軟件包 / 版本數量不僅要降至最低,并且在每次升級時(shí)必需能保證能夠升級整個(gè)棧:不僅要能升級我們自己的(de)軟件,還(hái)要能升級第三方軟件,甚至諸如 Libc 或系統内核等系統庫。
3
前提要求
随後我們開始确定這(zhè)個(gè)解決方案的(de)前提要求,其實這(zhè)些要求并不多(duō):
所有設備沿用(yòng)相同的(de)升級路徑,并且隻存在一個(gè)升級路徑。
所有設備均可(kě)通(tōng)過這(zhè)種方式升級(哪怕操作系統盤較小的(de)老設備)。
從一個(gè)版本切換到另一個(gè)版本的(de)過程必需滿足原子性要求(或盡可(kě)能滿足這(zhè)種要求)。(如果升級失敗)能夠回滾到上一個(gè)版本。而這(zhè)些要求還(hái)暗含了(le)一個(gè)最重要的(de)前提條件:不能繼續使用(yòng)基于軟件包的(de)升級方法了(le),并且(從字裏行間也(yě)能體會到)在升級過程中重引導一體機,這(zhè)是可(kě)以接受的(de)。
這(zhè)些都是很大(dà)膽的(de)念頭。我們确實做(zuò)出了(le)一個(gè)重大(dà)決定!
4
那麽鏡像到底是什(shén)麽?
爲了(le)減少配置的(de)數量,我們決定不再将我們的(de)軟件及其所有依賴項看作不同個(gè)體,而是将所有這(zhè)一切組合成一個(gè)統一的(de)可(kě)交付物(wù):鏡像。
那麽鏡像到底是什(shén)麽?鏡像(在我們的(de)環境中)是指一種 EXT4 文件系統,其中包含了(le)引導和(hé)運行 BCDR 一體機所需的(de)一切,例如:
Ubuntu 基礎操作系統(内核、系統庫……)
必需的(de)第三方工具和(hé)庫(Apache、KVM、ZFS……)
Datto 設備軟件(我們的(de)營銷團隊将其稱之爲 IRIS)
下(xià)圖就顯示了(le)一個(gè)這(zhè)種鏡像所包含的(de)内容:
我們對(duì)這(zhè)種想法非常激動,因爲通(tōng)過使用(yòng)鏡像,隻需要一個(gè)數字,也(yě)就是鏡像的(de)版本号(例如上圖中的(de)“415”)就可(kě)以定義所安裝的(de)每個(gè)軟件的(de)具體版本。再也(yě)不用(yòng)針對(duì)多(duō)種 ZFS 版本測試我們的(de)軟件,更不用(yòng)暗自祈禱我們的(de)軟件能兼容所有 KVM 版本。太棒了(le)!
5
基于鏡像的(de)升級
做(zuò)出所有這(zhè)些重要決定後,我們依然需要通(tōng)過某種方法來(lái)構建、分(fēn)發,并在設備上引導這(zhè)些鏡像。具體怎麽做(zuò)呢(ne)?
構建鏡像
通(tōng)常來(lái)說,每次标記了(le)一個(gè)新的(de)發布(或發布候選)後,我們會自動構建鏡像:每次在 Git 中推送标簽後,一個(gè) CI 工作進程會開始構建鏡像。構建過程本身也(yě)挺有趣,不過已經超出了(le)本文的(de)範圍,但爲了(le)不吊大(dà)家胃口,下(xià)文将簡單介紹這(zhè)個(gè)過程:
我們首先會爲自己的(de)軟件構建 Debian 軟件包,并将其發布至一個(gè) Debian 倉庫。随後使用(yòng) aptly (參閱“Datto packages”一圖)爲這(zhè)個(gè) Debian 倉庫創建快(kuài)照(zhào),同時(shí)還(hái)會定期對(duì)一個(gè)上遊 Ubuntu 倉庫(“Upstream packages”)執行類似操作。随後使用(yòng) debootstrap 創建一個(gè) Ubuntu 基準系統,并将我們的(de)所有軟件及其依賴項安裝到一個(gè) Chroot 中。一旦完成這(zhè)些操作,會對(duì)其創建 Tar 歸檔并 Rsync 到我們自己的(de)鏡像服務器。在鏡像服務器上,我們會提取出 Tarball 并 Rsync 給最新鏡像,這(zhè)個(gè)最新鏡像位于一個(gè)格式化(huà)爲 EXT4 文件系統的(de) ZFS 卷(zvol)中。在将所有未使用(yòng)的(de) EXT4 塊歸零後,會對(duì)包含該文件系統的(de) zvol 創建最終快(kuài)照(zhào)。
因此在鏡像服務器上可(kě)以看到類似下(xià)圖所示的(de)内容:
上述 zvol 包含了(le)我們 BCDR 一體機的(de) EXT4 文件系統。這(zhè)就是一個(gè)鏡像,也(yě)是我們唯一需要交付的(de)東西。它可(kě)以作爲一個(gè)整體進行測試,一旦通(tōng)過了(le) QA 流程,就可(kě)以分(fēn)發到客戶的(de) BCDR 設備中了(le)。
分(fēn)發鏡像
在成功構建鏡像後,又該如何将其從我們的(de)數據中心發送給超過 8 萬台設備?很簡單,我們使用(yòng)了(le) ZFS send/recv !
我們的(de)所有設備都具備 ZFS 池,其中存儲了(le)設備的(de)鏡像備份,并且之前我們就在大(dà)量使用(yòng) ZFS send/recv 爲這(zhè)些備份提供離場(chǎng)保存能力。而此時(shí)隻不過是換種方向使用(yòng)這(zhè)種技術。
我們是這(zhè)樣做(zuò)的(de):需要升級時(shí),會讓一部設備通(tōng)過 HTTPS 下(xià)載 ZFS sendfile diff(之前曾經嘗試過直接通(tōng)過 SSH 使用(yòng) ZFS send/recv,但這(zhè)種方式無法進行緩存):
從上圖中可(kě)以看到,通(tōng)常并不需要下(xià)載完整鏡像,因爲設備以前就升級過,已經在本地池中保存了(le)鏡像的(de)一個(gè)版本。這(zhè)就很棒了(le):通(tōng)過這(zhè)種技術,我們可(kě)以進行差異化(huà)的(de)操作系統升級,也(yě)就是說,設備隻需要下(xià)載鏡像中有變化(huà)的(de)塊。
這(zhè)是一種雙赢的(de)結果,因爲不會過多(duō)占用(yòng)客戶網絡帶寬,而我們自己的(de)數據中心也(yě)可(kě)以節約一筆帶寬費用(yòng)。
下(xià)載好的(de)鏡像會被導入本地 ZFS 池。這(zhè)對(duì)于下(xià)一次升級很必要(可(kě)以确保隻需要下(xià)載有變化(huà)的(de)内容):
引導鏡像
拿到鏡像後,如何引導至這(zhè)個(gè)新的(de)文件系統?如果我們構建的(de)每個(gè)鏡像版本都是全新操作系統,又該如何從一個(gè)版本引導至下(xià)一個(gè)版本?
6
ZFS-on-root、A/B 分(fēn)區(qū)和(hé) A/B 文件夾
毫無疑問,這(zhè)些問題的(de)答(dá)案并不隻有一種。我們可(kě)以通(tōng)過多(duō)種方法使用(yòng)鏡像生成可(kě)引導的(de)系統,因此需要多(duō)次實驗找出一種最佳方法。
這(zhè)個(gè)過程也(yě)很有趣,因此我準備簡要介紹每種方法,以及最終未選擇這(zhè)些方法的(de)原因:
ZFS-on-root 和(hé) A/B 數據集:我們的(de)鏡像備份操作中大(dà)量使用(yòng)了(le) ZFS,因此一開始很自然就覺得(de)也(yě)可(kě)以将 ZFS 用(yòng)作一體機的(de)根文件系統。爲此可(kě)以将 BCDR 一體機的(de)鏡像作爲一個(gè) ZFS 數據集(而非上文提到的(de) zvol)來(lái)進行分(fēn)發,對(duì)其進行克隆并直接引導至 ZFS 的(de)克隆副本。由于 Grub 的(de)新版本已經可(kě)以支持讀取 ZFS,此外還(hái)提供了(le) ZFS initramfs 模塊,ZFS-on-root 絕對(duì)是可(kě)行的(de)。如果要從一個(gè)鏡像升級到下(xià)一個(gè)(例如從一個(gè) ZFS 數據集升級到下(xià)一個(gè)),隻需要更新 Grub 的(de)配置并重引導就行。這(zhè)種方式可(kě)以正常起效,但因爲引導至 ZFS,這(zhè)是一種比較新的(de)做(zuò)法,我們認爲其成熟度還(hái)不足以滿足我們産品的(de)需求。不予考慮。
簡單的(de) A/B 分(fēn)區(qū):有些一體機和(hé)手機會使用(yòng)兩個(gè)分(fēn)區(qū),其中一個(gè)包含當前系統,另一個(gè)包含下(xià)一個(gè)系統。這(zhè)種思路也(yě)很簡單:下(xià)載新鏡像,将其 Rsync 到不活躍分(fēn)區(qū),更新 Grub,然後重引導。然而這(zhè)種做(zuò)法的(de)問題在于,我們的(de)有些設備不具備額外創建一個(gè)分(fēn)區(qū)所需的(de)存儲空間(或者至少需要重建分(fēn)區(qū))。我們在實驗中嘗試過在首次重引導過程中,從 initramfs 内部将活躍根分(fēn)區(qū)拆分(fēn)爲兩個(gè)并且成功了(le)(挺酷的(de)對(duì)吧),但考慮到這(zhè)将要用(yòng)于我們的(de)主要産品,該方法風險太大(dà)。不予考慮。
引導至 A/B 目錄:由于一些設備缺乏備用(yòng)分(fēn)區(qū),我們還(hái)實驗過将鏡像的(de)兩個(gè)副本保存到根分(fēn)區(qū)中的(de)兩個(gè)文件夾中(例如一個(gè) /images/412 和(hé)一個(gè) /images/415),随後修改 initramfs 引導至 /images/415,而非引導至 /。不管你信不信,雖然聽(tīng)起來(lái)挺瘋狂,但這(zhè)樣做(zuò)竟然也(yě)成功了(le),并且整個(gè)方法也(yě)超級簡單,隻要對(duì) initramfs 進行少量修改:mount --bind /images/415 /root 改成這(zhè)樣就行。一切都可(kě)以正常運轉,不過很多(duō) Linux 工具(df、mount……)會因爲根目錄不是 / 而遇到一些問題,所以這(zhè)個(gè)方法也(yě)不予考慮。
7
循環往複,這(zhè)就夠了(le)!
在嘗試過用(yòng)多(duō)種方法引導鏡像後,我們最終采取的(de)做(zuò)法似乎感覺有些無趣。不過無趣也(yě)是好事對(duì)吧!
我們發現,如果要引導一個(gè)鏡像,最簡單可(kě)靠的(de)方法是利用(yòng) Grub 的(de)回環引導(Loopback booting)機制,并配合 initramfs 對(duì) Loop 的(de)支持(請參閱 loop=…參數):
衆所周知,Grub 是種引導加載器(Boot loader)。它的(de)責任是加載初始的(de) RAM 磁盤和(hé)内核。爲此,Grub 内置了(le)對(duì)很多(duō)文件系統的(de)讀取能力,并能通(tōng)過 loopback 命令支持稍後将要提到的(de)“文件系統中的(de)文件系統”。loopback 命令可(kě)在根分(fēn)區(qū)找到鏡像文件并對(duì)其進行環回(Loop),這(zhè)樣就可(kě)以照(zhào)常使用(yòng) linux 和(hé) initrd 命令找到内核和(hé) RAM 磁盤。例如我們在設備 grub.cfg 文件中(通(tōng)過 /etc/grub.d 中的(de)鈎子)生成的(de)菜單項範例如下(xià)所示:
在這(zhè)個(gè)例子中,Grub 首先會通(tōng)過 search 以及 UUID 尋找根分(fēn)區(qū)(就像對(duì)常規安裝的(de) Ubuntu 做(zuò)的(de)那樣)。随後會發現根分(fēn)區(qū)中的(de)鏡像文件 /images/415.0.img,最後找到鏡像中的(de)内核((loop)/vmlinuz)和(hé) RAM 磁盤((loop)/initrd.img)。
整個(gè)過程異常簡單,但同時(shí)卻非常酷:引導加載器竟然能這(zhè)樣做(zuò),這(zhè)一點讓我大(dà)爲驚奇。
當 Grub 找到内核和(hé)初始 RAM 磁盤後,會将 RAM 磁盤載入内存(震驚!),随後挂載根文件系統,最後将控制權轉交給 init 進程。
在 Ubuntu 中,initramfs-tools 軟件包提供了(le)創建和(hé)修改初始 RAM 磁盤的(de)工具。幸虧該軟件包已經可(kě)以支持回環引導機制,因此一般來(lái)說除了(le)需要在内核行傳遞 loop= 參數,其他(tā)什(shén)麽都不用(yòng)做(zuò)。如果設置了(le)該參數,initramfs 會用(yòng)回環的(de)方式,使用(yòng) mount -o loop(參閱源代碼)将根文件系統加載至鏡像。考慮到代碼中有一條相當吓人(rén)的(de) FIXME 消息(# FIXME This has no error checking),我們認爲最好能提高(gāo)它的(de)彈性,爲其增加錯誤處理(lǐ)和(hé) fsck 能力。不過大(dà)部分(fēn)情況下(xià),使用(yòng) initramfs 都可(kě)以順利引導并且不顯示任何信息。
就是這(zhè)樣,一個(gè)簡單的(de)解決方案,洋洋灑灑寫了(le)這(zhè)麽多(duō)。
這(zhè)種方法在實踐中用(yòng)起來(lái)是這(zhè)樣的(de)。如圖所示,該設備的(de)根文件系統位于 /dev/loop0,該回環設備在 initramfs 中設置而來(lái),指向了(le)一個(gè)鏡像文件:
本例中,鏡像是位于根分(fēn)區(qū)(如 /dev/sda1)下(xià)的(de) /images/412.0.img。請注意,如果鏡像中存在空的(de) /host 文件夾,initramfs 會将根分(fēn)區(qū)挂載在這(zhè)裏:
8
鏡像間的(de)升級
我們已經可(kě)以構建、分(fēn)發并引導鏡像。如果将這(zhè)一切結合在一起就會發現,從一個(gè)鏡像到下(xià)一個(gè)鏡像的(de)升級其實一點也(yě)不難:
清理(lǐ)老鏡像,下(xià)載新鏡像,導入到池,導出到鏡像文件。
将配置從當前鏡像遷移到下(xià)一個(gè)鏡像。
更新 Grub 以指向新鏡像。
重引導。
我們所做(zuò)的(de)就是這(zhè)樣。爲此還(hái)開發了(le)一個(gè)名爲 upgradectl 的(de)工具:
upgradectl 通(tōng)常可(kě)由我們的(de)簽入進程遠(yuǎn)程觸發:在設備正常運轉的(de)過程中,它可(kě)以下(xià)載并導出鏡像(第 1 步),借此在後台爲升級過程做(zuò)準備。需要進行升級時(shí)(通(tōng)常是夜間的(de)設備閑置時(shí)段),實際的(de)升級過程将非常快(kuài)速地完成,因爲隻需要遷移配置,更新 Grub 并重引導(第 2-4 步)即可(kě)。一般來(lái)說,升級過程中的(de)設備停機時(shí)間約爲 5-10 分(fēn)鐘(zhōng),并且這(zhè)主要取決于重引導所需的(de)時(shí)間(大(dà)型設備可(kě)能需要更久,因爲需要 IPMI/BMC 初始化(huà))。
當然,這(zhè)一過程中也(yě)有數不勝數的(de)問題和(hé)邊緣案例需要考慮:聽(tīng)起來(lái)确實簡單,但想要做(zuò)對(duì)其實并不容易,尤其是考慮到我們現有的(de) 8 萬台一體機中,有些在生産環境中連續運轉已經有超過 7 年時(shí)間了(le)。
但這(zhè)也(yě)造就了(le)一些有趣的(de)挑戰:我們已經将數千台設備從 Ubuntu 12.04(甚至 10.04)直接升級至 Ubuntu 16.04。如果升級過程因爲某些原因失敗,會通(tōng)過一些邏輯來(lái)處理(lǐ)老鏡像的(de)回滾。我們處理(lǐ)了(le)完整的(de)操作系統盤、有故障的(de)硬件(磁盤、IPMI、RAM……)、配置爲 RAID 的(de)操作系統盤以及 Grub 無法向其中寫入的(de)問題,當然還(hái)有 ZFS 池出錯、Linux 進程挂起(D 狀态)、重引導挂起等各種問題。
但是你猜怎樣:這(zhè)一切都是值得(de)的(de)。這(zhè)就好像結束了(le)一場(chǎng)爲期 7 年的(de)寒冬之後進行的(de)春季大(dà)掃除。我們讓這(zhè)些設備重新煥發了(le)生機,并且這(zhè)樣的(de)工作還(hái)将繼續,每兩周進行一次!
9
總結
本文介紹了(le)如何将 BCDR 一體機的(de)部署流程由基于 Debian 軟件包的(de)方法改爲基于鏡像的(de)方法。此外還(hái)介紹了(le)構建、分(fēn)發鏡像的(de)方法,以及如何使用(yòng) Grub 的(de) loopback 機制引導鏡像的(de)做(zuò)法。
雖然這(zhè)種基于鏡像的(de)升級方法的(de)誕生有我的(de)全程參與,但這(zhè)其中最讓人(rén)激動的(de)一點在于:借助這(zhè)種機制,我們甚至可(kě)以在不同内核,以及不同的(de)操作系統大(dà)版本之間切換。每次發布升級後,我們都可(kě)以有效地引導至一個(gè)全新操作系統,這(zhè)意味著(zhe)系統不會随著(zhe)時(shí)間的(de)延長(cháng)而退化(huà),所有手工改動都會被消除,甚至從技術上來(lái)看,還(hái)可(kě)以在願意的(de)情況下(xià)切換使用(yòng)不同的(de) Linux 發行版。
并且這(zhè)一切都是在後台進行的(de),完全無需用(yòng)戶介入,對(duì)用(yòng)戶來(lái)說完全透明(míng):每兩周對(duì) 8 萬個(gè)操作系統進行升級,這(zhè)該有多(duō)酷啊!
責任編輯:中山網站建設
【網訊網絡】國家高(gāo)新技術企業》十年專注軟件開發,網站建設,網頁設計,APP開發,小程序,微信公衆号開發,定制各類企業管理(lǐ)軟件(OA、CRM、ERP、訂單管理(lǐ)系統、進銷存管理(lǐ)軟件等)!服務熱(rè)線:0760-88610046、13924923903,http://www.wansion.net
下(xià)一篇:NET Core 3.0 正式公布:新特性詳細解讀
*請認真填寫需求,我們會在24小時(shí)内與您取得(de)聯系。