使用 Packer、Ansible 和 Terraform 构建不可变的基础设施Devops工具链
在容器編排領(lǐng)域,Kubernetes 已成為事實(shí)上的標(biāo)準(zhǔn),而容器鏡像 (Docker Image) 作為容器技術(shù)棧中最關(guān)鍵的創(chuàng)新之一,極大的推動了企業(yè)內(nèi)部 Devops 運(yùn)動的進(jìn)程。
容器鏡像所具有的輕量性、便攜性、分層機(jī)制和內(nèi)核共享機(jī)制真正意義上實(shí)現(xiàn)了 “Build once, run anywhere”。這種不可變的基礎(chǔ)設(shè)施 (Immutable Infrastruture) 高度保持了開發(fā)、測試和生產(chǎn)環(huán)境的一致性。因?yàn)殓R像的易移植、易復(fù)制的特性,也給運(yùn)維帶來了很大的彈性和靈活性。
對于還無法容器化,只能部署在虛擬機(jī)里的傳統(tǒng)應(yīng)用,是否也能構(gòu)建像容器鏡像這樣不可變的的基礎(chǔ)設(shè)施?
可變的服務(wù)器部署 vs. 不可變的服務(wù)器部署
可變的服務(wù)器部署
在可變的服務(wù)器部署模式中,首先我們通過?Terraform?創(chuàng)建出所需的虛擬機(jī)以及其它基礎(chǔ)設(shè)施資源,然后通過配置管理工具?Ansible?對已經(jīng)存在的服務(wù)器資源進(jìn)行應(yīng)用相關(guān)的配置和部署。
上述的部署過程看似非常快速、簡便,但是隨著業(yè)務(wù)的需求增加,需要對服務(wù)器操作系統(tǒng)進(jìn)行更新,或?qū)Σ渴鸬膽?yīng)用進(jìn)行頻繁的升級。這種情況下,可變的服務(wù)器部署模式所帶來的挑戰(zhàn)和風(fēng)險是我們無法預(yù)估的。
在真實(shí)的用戶場景里,運(yùn)行的應(yīng)用程序與操作系統(tǒng)、或第三方軟件資源存在各種各樣復(fù)雜的依賴。當(dāng)給操作系統(tǒng)打補(bǔ)丁,亦或升級應(yīng)用程序所依賴的軟件包時,可能會出現(xiàn)應(yīng)用程序無法正常啟動、DNS 解析異常、網(wǎng)絡(luò)不可達(dá)、性能下降等現(xiàn)象,這些異常可能是無法預(yù)測的,甚至是我們無法控制的。
即使應(yīng)用程序更新成功,一旦線上環(huán)境產(chǎn)生不可預(yù)知的嚴(yán)重 Bug ,需要將應(yīng)用程序回滾時,由于可變的服務(wù)器部署的不確定性,回滾的過程對于運(yùn)維人員仍然是一項(xiàng)挑戰(zhàn)。
當(dāng)線上環(huán)境負(fù)載過高時,在可變的服務(wù)器部署模式下,響應(yīng)也會顯得不夠高效。按照上述流程,需要創(chuàng)建新的虛擬機(jī)資源,再運(yùn)行配置管理工具去部署該版本的應(yīng)用。整個過程比較耗時,也較容易出錯。
不可變的服務(wù)器部署
相對于可變的服務(wù)器部署模式,不可變的服務(wù)器部署模式要求服務(wù)器在部署完成之后,后續(xù)每次做部署變更時,不再對現(xiàn)存的服務(wù)器做任何更新或升級。
不可變的服務(wù)器部署模式下,我們將會基于基礎(chǔ)的虛擬機(jī)鏡像,創(chuàng)建新的虛擬機(jī),為該虛擬機(jī)安裝所需軟件包,部署應(yīng)用程序所需要的新的代碼和配置。最后將該虛擬機(jī)打包成一個新的虛擬機(jī)應(yīng)用鏡像。
每次部署應(yīng)用時,基于以上過程創(chuàng)建出來的應(yīng)用鏡像,創(chuàng)建新的服務(wù)器,在這個過程中,我們不會去改動當(dāng)前環(huán)境中運(yùn)行的基礎(chǔ)設(shè)施資源。
同時在整個過程中,出現(xiàn)任何錯誤,我們將直接退出。待問題解決之后,基于以上過程重新打包鏡像。如果一切順利,待虛擬機(jī)啟動成功,再將線上環(huán)境流量切換到該新虛擬機(jī)上,隨后銷毀掉的虛擬機(jī)。這樣就完成了一次部署變更。
如果線上流量較高,需要橫向擴(kuò)展虛擬機(jī)數(shù)量時,只需將上述已經(jīng)打包好的應(yīng)用鏡像部署成新的虛擬機(jī),作為額外資源加入到線上集群即可。整個響應(yīng)過程十分迅速且可靠。
基礎(chǔ)設(shè)施即代碼 (IAC)
基于 Packer、Ansible 和 Terraform 等開源工具,構(gòu)建不可變服務(wù)器部署模式的持續(xù)集成和持續(xù)部署的 Jenkins Pipeline:
應(yīng)用代碼打包
為了使部署更加靈活,基礎(chǔ)設(shè)施及配置代碼將獨(dú)立于應(yīng)用本身代碼,單獨(dú)進(jìn)行存放。
基于該代碼倉庫,創(chuàng)建自服務(wù)的?Jenkins Multibranch, 基于分支的變動自動觸發(fā) Build,并將軟件包上傳至中央制品倉庫。
虛擬機(jī)鏡像打包 Packer
Packer?是一個優(yōu)秀的開源鏡像打包工具。Packer 的 builder 支持主流的公有云、私有云平臺以及常見的虛擬化類型。同時它支持的 provisioner 能覆蓋主流的配置管理工具: Ansible, Puppet, Chef, Windows Shell, Linux Shell 等。
在不可變的服務(wù)器的應(yīng)用場景中,通過 Packer 自動創(chuàng)建虛擬機(jī),然后調(diào)用 Ansible provisioner 從中央制品倉庫拉取軟件包、部署所需額外依賴包以及相關(guān)配置,最后自動打包成虛擬機(jī)鏡像并回收該虛擬機(jī)資源,上傳至云平臺中。
配置管理及安全加密 Ansible
Ansible?是一款簡單的,易上手的開源配置管理工具。它能簡化軟件的安裝部署,作為配置管理能提供靈活的模版渲染引擎以及針對敏感信息的加密。
基礎(chǔ)設(shè)施的創(chuàng)建和編排 Terraform
Terraform?作為開源的基礎(chǔ)設(shè)施資源編排工具,能覆蓋主流的云平臺,非常適用于多云的環(huán)境。能提供靈活的部署選擇,并能根據(jù)用戶需求開發(fā)可插拔式的、自定義的 provider。
鏡像部署過程中所面臨的挑戰(zhàn)
業(yè)務(wù)場景的不同,會帶來部署方式的多樣化要求,比如滾動部署、藍(lán)綠部署等。
針對不可變的服務(wù)器部署模式,下面將介紹兩種較典型的應(yīng)用類型:
- 負(fù)載均衡器 (LB) + 應(yīng)用服務(wù)器 (Web Server)
- 有狀態(tài)的后端應(yīng)用
Note: 主流的云廠商提供了類似動態(tài)虛擬機(jī)組的功能,來滿足以上兩種需求。本文主要介紹使用 Terraform 構(gòu)建通用的解決方案。
負(fù)載均衡器配置的平滑更新
在 LB + Web Server 這種業(yè)務(wù)場景下,為了盡量減少服務(wù)不可用的時間,制定了藍(lán)綠部署的解決方案。
在資源池中,會存在藍(lán)和綠兩種虛擬機(jī)組。每次版本更新時,會選擇非線上版本的一組虛擬機(jī)組做更新。
當(dāng)非線上的版本更新完畢之后,會獲取新創(chuàng)建的虛擬機(jī) (VM) 的 IP 列表,將其動態(tài)更新至 LB 的后端。
在對 LB 進(jìn)行更新時,定義該資源的?lifecycle?為?create_before_destroy = true。 這樣每次更新時會先把新的后端虛擬機(jī) IP 添加至 LB,待所有新虛擬機(jī)組的后端 IP 加入完畢之后,terraform 再去移除舊的虛擬機(jī) IP 組.
Default
?
| 1 2 3 4 5 6 7 8 9 | resource "xx_cloud_load_balance" "lb" { ??# ... ? ??lifecycle { ????create_before_destroy = true ??} ? } ? |
有狀態(tài)應(yīng)用的平滑升級
同樣為了有狀態(tài)的應(yīng)用更平滑的更新,在舊版本虛擬機(jī)銷毀之前,需要發(fā)送一些個性化的指令,讓應(yīng)用程序能夠優(yōu)雅地退出。
除了對該虛擬機(jī)組資源的 lifecycle 指定?create_before_destroy = true, 還指定了一個?local-exec?的 provisioner 去優(yōu)雅的停掉舊虛擬機(jī)組里的應(yīng)用。
Note: 在本例子中,腳本?drain_nodes.sh?相對復(fù)雜,因?yàn)闀⑿袆?chuàng)建多臺虛擬機(jī),所以需要加入類似鎖的機(jī)制來避免競爭的情況發(fā)生。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | resource "xx_cloud_vm_instance" "instances" { ??count = "${var.instance_count}" ? ??# ... ??lifecycle { ????create_before_destroy = true ??} ? ??provisioner "local-exec" { ????command = "bash ${path.module}/scripts/drain_nodes.sh ${var.old_instance_list} ??} ? } ? |
Note: 由于?terraform issue, 當(dāng)指定了?create_before_destroy = true?時, 不能再使用?Destroy-Time Provisioners。
部署的可靠性和穩(wěn)定性
為了提高部署的可靠性,在銷毀舊的虛擬機(jī)組或者更新 LB 配置之前,需要確保新創(chuàng)建的虛擬機(jī)是健康可用的。為此從兩個角度去優(yōu)化:
- 為了盡早發(fā)現(xiàn)潛在的問題,在使用 Packer 打包鏡像的時候,加入簡單的健康檢查機(jī)制,確保應(yīng)用代碼和配置是匹配的。
- 在新的虛擬機(jī)啟動之后,加入自我健康檢查的腳本:
| 1 2 3 4 5 6 7 8 9 10 | resource "xx_cloud_vm_instance" "instances" { ? ??# ... ? ??provisioner "local-exec" { ????command = "bash ${path.module}/scripts/health_check.sh ${self.ipv4_address} ??} ? } ? |
鏡像構(gòu)建一次,多處部署
不同的環(huán)境 (Dev, QA, Prod), 會對應(yīng)不同的配置文件。云環(huán)境中,支持給虛擬機(jī)傳入?user_metadata?去區(qū)分不同的環(huán)境,由于鏡像中包含所有環(huán)境的配置文件,可以通過傳入的?user_metadata?去選擇相應(yīng)的配置文件啟動應(yīng)用程序。
| 1 2 3 4 5 6 7 | resource "xx_cloud_vm_instance" "instances" { ? ??# ... ??user_metadata = "dev" ? } ? |
快速伸縮和回滾
從運(yùn)維角度來看,可伸縮、可回滾性是平臺維護(hù)中不可或缺的特性。
在 Terraform 中,我們可以通過簡單的指定?count?數(shù)量來伸縮虛擬機(jī)數(shù)量:
| 1 2 3 4 5 6 7 | resource "xx_cloud_vm_instance" "instances" { ? ??count = "${var.instance_count} ??# ... ? } ? |
由于鏡像包含應(yīng)用程序所需要的所有配置和代碼,虛擬機(jī)鏡像的版本也就代表了應(yīng)用程序的版本。回滾應(yīng)用程序相當(dāng)于指定虛擬機(jī)鏡像的版本重新部署:
| 1 2 3 4 5 6 7 | resource "xx_cloud_vm_instance" "instances" { ??# ... ??image_version = "${var.image_version}" } |
鏡像的打包效率
相對于可變服務(wù)器部署模式,由于打包虛擬機(jī)鏡像過程較為耗時,在一定程度上會加長整個部署的時間。
因?yàn)殓R像里包含了應(yīng)用程序所需要的代碼和配置,每一次配置更新或者代碼更新需要重新打包鏡像時,可以考慮把配置和代碼從鏡像中分離出來提高打包效率:
- 將鏡像分層管理,分為基礎(chǔ)操作系統(tǒng)鏡像和應(yīng)用鏡像。基礎(chǔ)操作系統(tǒng)鏡像中包含軟件運(yùn)行所需要的基本環(huán)境以及相關(guān)依賴(Python、C#、 第三方工具或者相關(guān)依賴包等)。這樣在構(gòu)建應(yīng)用鏡像時只安裝與應(yīng)用相關(guān)的代碼和配置,不必再重新安裝基礎(chǔ)鏡像中存在的基礎(chǔ)軟件包、配置,縮短了應(yīng)用鏡像的打包時間。
- 將配置遷移至配置管理服務(wù),應(yīng)用程序啟動時從該配置服務(wù)中動態(tài)獲取配置信息,避免每次因?yàn)榕渲梦募滦枰匦麓虬R像。
- 將配置和代碼遷移至網(wǎng)絡(luò)文件存儲(NFS),虛擬機(jī)每次啟動時掛載該網(wǎng)絡(luò)文件存儲去讀取配置和代碼。每次代碼或者配置文件更新只需更新掛載的文件系統(tǒng)中的內(nèi)容。可以極大的降低鏡像打包頻率。
總結(jié)
相對于?AWS auto scaling group、Google Autoscaling groups?和?Azure VMSS?等公有云平臺提供的原生服務(wù),以上解決方案還未能集成云平臺中的監(jiān)控系統(tǒng)做到資源的自動伸縮。但在多云的環(huán)境,或云平臺提供的虛擬機(jī)組功能欠缺時,這種基于 Terraform 本身構(gòu)造的通用解決方案仍有用武之地。在實(shí)際場景中用戶可以靈活選擇。
相對于傳統(tǒng)的應(yīng)用部署方式,不可變服務(wù)器部署模式延長了從代碼提交到收到部署反饋的時間,在初期也會給開發(fā)者帶來相應(yīng)的學(xué)習(xí)成本,在一定程度上犧牲了開發(fā)體驗(yàn)。但不可否認(rèn),它所帶來的環(huán)境的一致性、運(yùn)維的彈性,仍然是 Devops 運(yùn)動中首推的解決方案。
? ?更多多資訊或疑問內(nèi)容請關(guān)注?微信公眾號 “讓夢飛起來”?或添加小編微信,?后臺回復(fù) “Python” ,領(lǐng)取更多資料哦
? ?? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ??
總結(jié)
以上是生活随笔為你收集整理的使用 Packer、Ansible 和 Terraform 构建不可变的基础设施Devops工具链的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 交叉编译脚本命令
- 下一篇: 玩玩机器学习4——TensorFlow基