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