[DevOps系列] 爲何咱們使用Terraform 而不使用Puppet, Ansible

「DevOps系列]为什么我们使用Terraform 而不使用Puppet, Ansible

本篇文章是深刻理解Terraform系列的第一部分。在介紹文章中,咱們討論了爲何每家互聯網軟件公司都應該使用基礎設施即代碼(IAC)。那麼本篇,咱們打算討論下爲何咱們選擇Terraform 做爲咱們的IAC 工具。程序員

若是你在網上搜索「instrastructure-as-code」,很容易看到不少受歡迎的工具:數據庫

  • Chef
  • Puppt
  • Ansible
  • SaltStack
  • CloudFormation
  • Terraform

篩選出它們中你應該使用哪一個不是很容易。全部這些上述工具均可以用於基礎設施即代碼。它們都是開源的,背靠龐大的貢獻者社區,能夠很好配合各類不一樣的雲服務商。它們都提供商業支持,提供良好的文檔——在官方文檔和社區資源方面(好比博客文章和StackOverflow問答)。編程

更難以理解的是,您在這些工具之間在線找到的大多數比較只是列出每一個工具的通常屬性,並使其聽起來像您能夠一樣成功地使用它們中的任何一個。雖然這在技術上是正確的,但它沒有幫助。就像告訴一個程序員新手你能夠用PHP,C,彙編均可以建立網站。這個聲明在技術上是正確的,但卻省略了大量的信息,這些信息對於作出正確的決定很是有用。安全

本篇文章,咱們會分紅幾個特定緣由來解釋爲何咱們會選擇Terraform做爲IAC工具。與全部技術決策同樣,這是一個權衡和優先級的問題,雖然您的特定優先級可能與咱們的不一樣,但咱們但願分享咱們的思惟過程將幫助您作出本身的決定。如下是咱們考慮的主要權衡因素:bash

  • 配置文件管理 vs 配置
  • 可變基礎設施 vs 不可變基礎設施
  • 過程式的 vs 聲明式的
  • Master vs Masterless
  • Agent vs Agentless
  • 成熟 vs 前沿
  • 綜合使用多種工具

配置管理 vs 配置

Chef, Puppet, Ansible, and SaltStack 都是配置管理工具,這意味着它們設計初衷都是在現有的服務器上安裝和管理軟件。CloudFormation 和 Terraform 是配置(provisioning)工具,這意味這它們的設計初衷是配置服務器自己的(以及基礎設施的其餘部分,好比負載均衡器,數據庫,網絡配置等),將配置這些服務器的工做留給其餘工具。這兩類工具互相不排斥的。由於大多數配置管理工具能夠在某種程度上多一些配置工做而大多數配置工具也能夠在某種程度上作配置管理的工做。可是聚焦於配置管理或者配置意味着,這些工具對於特定類型的任務會更加合適。服務器

特別指出,咱們發現若是你使用Docker 或者 Packer,更加須要關注配置管理。使用Docker 或者Packer,你能夠建立所需軟件已經安裝或者配置了的鏡像。一旦你有這樣一個鏡像,你所須要作的就是運行它。若是你所須要作的是配置一組服務器,那麼想Terraform這樣的配置工具會比配置管理工具更加適合。微信

可變基礎設施 vs 不可變基礎設施

想Chef,Puppt,Ansible 這樣的配置管理工具默認針對一種可變的基礎設施範例。好比,若是你告訴Chef 安裝一個新版本的OpenSSL,它就會在你現有的服務器上運行軟件更新而且就地生效。隨着時間推移,你會更新的更多,每臺服務器都會構建一個惟一的修改歷史。這一般會致使稱爲配置漂移或者偏差的現象,其中每一個服務器與全部其餘服務器略有不一樣,致使難以診斷且幾乎不可能再現的細微配置錯誤。網絡

若是你正在使用像Terraform這樣的配置工具來部署由Docker 或者 Packer建立的鏡像,那麼每次"修改"事實上都是一次新服務器的部署(就像是函數式編程中每次變量的修改事實上會返回新的變量)。好比,當咱們部署一個新版本的OpenSSL,你會用裝有新版本OpenSSL的Packer或者Docker來建立鏡像,而後在整組新服務器中部署那個鏡像,同時卸載老的鏡像。這種方法減小了配置誤差問題的可能性,使得了解服務器上運行了哪些軟件變得更加容易,同時可讓你任什麼時候候均可以輕鬆部署任何版本的軟件。固然,也能夠強制配置管理工具來作不可變部署。可是對這些工具來講,這不是慣用的方式。無論怎樣,使用配置工具都是一種更加天然的方式。架構

程式化 vs 可聲明化

Chef和Ansible鼓勵一種程序風格,您能夠編寫代碼,逐步指定如何實現預期狀態。 Terraform,CloudFormation,SaltStack和Puppet都鼓勵更具說明性的風格,您能夠編寫指定所需最終狀態的代碼,IAC工具自己負責肯定如何實現該狀態。負載均衡

例如,假設您要部署10臺服務器(AWS術語中的「EC2 Instances」)來運行應用程序的v1版本。如下是使用過程方法執行此操做的Ansible模板的簡化示例:

- ec2: count: 10 
  image: ami-v1  
  instance_type: t2.micro複製代碼

如下是使用聲明方法執行相同操做的Terraform模板的簡化示例:

resource "aws_instance" "example" { 
  count = 10 
  ami = "ami-v1" 
  instance_type = "t2.micro"
}複製代碼

表面上看,這兩種方法可能看起來類似,當您最初使用Ansible或Terraform執行它們時,它們將產生相似的結果。有趣的是,當您想要進行更改時會發生什麼。

例如,假設流量增長,而且您但願將服務器數量增長到15。使用Ansible,您以前編寫的過程代碼就無法使用了;若是您剛剛將服務器數量更新爲15並從新啓動該代碼,那麼它將部署15臺新服務器,總共25臺服務器!所以,您必須瞭解已部署的內容並編寫一個全新的過程腳原本添加5臺新服務器:

- ec2: count: 5 
  image: ami-v1  
  instance_type: t2.micro複製代碼

使用聲明性代碼,由於你所作的就是聲明你想要的最終狀態,而Terraform計算出如何到達那個最終狀態,Terraform也會知道它過去建立的任何狀態。所以,要部署另外5臺服務器,你所做的只須要返回到以前相同的Terraform模板並將計數從10更新爲15:

resource "aws_instance" "example" {
   count = 15 
   ami = "ami-v1" 
   instance_type = "t2.micro"
}複製代碼

若是你執行了這個模板,Terraform會意識到它已經建立了10個服務器,所以它須要作的只是建立5個新服務器。實際上,在運行此模板以前,您可使用Terraform的plan命令來預覽它將進行的更改:

$ terraform plan
+ aws_instance.example.11 
  ami: "ami-v1" 
  instance_type: "t2.micro"
+ aws_instance.example.12 
  ami: "ami-v1" 
  instance_type: "t2.micro"
+ aws_instance.example.13 
  ami: "ami-v1" 
  instance_type: "t2.micro"
+ aws_instance.example.14 
  ami: "ami-v1" 
  instance_type: "t2.micro"
+ aws_instance.example.15 
  ami: "ami-v1" 
  instance_type: "t2.micro"
Plan: 5 to add, 0 to change, 0 to destroy.複製代碼

如今,當您想要部署v2服務時會發生什麼? 使用過程方法,您以前的兩個Ansible模板都沒有用,因此您必須編寫另外一個模板來跟蹤以前部署的10個服務器(或者如今是15個?)並仔細更新每一個模板到新版本。 使用Terraform的聲明式方法,您能夠再次返回徹底相同的模板,只需將ami版本號更改成v2:

resource "aws_instance" "example" {
   count = 15 
   ami = "ami-v2" 
   instance_type = "t2.micro"
}複製代碼

顯然,上述例子是簡化的。 Ansible容許您在部署新的EC2實例以前使用標籤來搜索現有的EC2實例(例如,使用instance_tags和count_tag參數),可是必須根據每一個資源的狀況爲Ansible管理的每一個資源手動找出這種邏輯。 過去的歷史,可能會使人驚訝地複雜化(例如,不只經過標籤,還能夠經過圖像版本,可用區域等查找現有實例)。 這突出了程序IAC工具的兩個主要問題:

  1. 處理過程代碼時,代碼中未徹底捕獲基礎設施的狀態。 閱讀咱們上面建立的三個Ansible模板並不足以瞭解已部署的內容。 您還必須知道咱們應用這些模板的順序。 若是咱們以不一樣的順序應用它們,咱們最終可能會看到不一樣的的基礎設施架構,而這不是您在代碼庫自己中能夠看到的。 換句話說,要推理Ansible或Chef代碼庫,您必須知道所發生的每一個更改的完整歷史記錄。
  2. 過程代碼的可重用性本質上是有限的,由於您必須手動考慮代碼庫的當前狀態。 因爲該狀態不斷變化,所以一週前使用的代碼可能再也不可用,由於它旨在修改再也不存在的基礎設施狀態。 結果,程序代碼庫隨着時間的推移趨於變大和變得複雜。

另外一方面,在Terraform中使用的這種聲明式方法,代碼始終表明基礎架構的最新狀態。 一目瞭然,您能夠分辨當前部署的內容及其配置方式,而無需擔憂歷史記錄或時間安排。 這也使得建立可重用代碼變得容易,由於您沒必要手動考慮當前的世界狀態。 相反,您只需專一於描述您想要的狀態,Terraform會自動肯定如何從一個狀態到另外一個狀態。 所以,Terraform代碼庫每每保持小巧且易於理解。

固然,聲明性語言也有缺點。 若是沒法使用完整的編程語言,您的表達能力就會受到限制。 例如,某些類型的基礎設施更改(例如回滾,零停機時間部署)很難用純粹的聲明性術語表達。 一樣,若是沒有「邏輯」(例如if語句,循環)的能力,建立通用的,可重用的代碼可能會很棘手(特別是在CloudFormation中)。 幸運的是,Terraform提供了許多強大的原語,例如輸入變量,輸出變量,模塊,create_before_destroycount,這使得即便在聲明性語言中也能夠建立乾淨,可配置的模塊化代碼。 咱們將在第4部分,如何使用Terraform模塊建立可重用的基礎架構和第5部分,Terraform提示和技巧:循環,if語句和陷阱中更多地討論這些工具。

Master vs Masterless

默認狀況下,Chef,Puppet和SaltStack都要求您運行主服務器以存儲基礎設施的狀態並分發更新。 每次要更新基礎設施中的某些內容時,都使用客戶端(例如,命令行工具)向主服務器發出新命令,主服務器將更新推送到全部其餘服務器或那些服務器按期從主服務器中提取最新的更新。

主服務器提供了一些優勢。 首先,它是一個單一的中心位置,您能夠在其中查看和管理基礎設施的狀態。 許多配置管理工具甚至爲主服務器提供Web界面(例如,Chef Console,Puppet Enterprise Console),以便更容易查看正在發生的事情。 其次,一些主服務器能夠在後臺連續運行,並強制執行您的配置。 這樣,若是有人在服務器上進行手動更改,主服務器能夠還原該更改以防止配置偏移。

可是,必須運行主服務器有一些嚴重的缺點:

  • 額外的基礎設施:您必須部署額外的服務器,甚至是一組額外的服務器(用於高可用性和可伸縮性),以運行主服務器。
  • 維護:您必須維護,升級,備份,監控和擴展主服務器。
  • 安全性:您必須爲客戶端提供與主服務器通訊的方式以及主服務器與全部其餘服務器通訊的方式,這一般意味着打開額外的端口並配置額外的身份驗證系統, 全部這些都會增長被攻擊的範圍。

Chef,Puppet和SaltStack對無主模式有不一樣程度的支持,您只需在每一個服務器上運行代理軟件,一般在必定週期內(例如,每5分鐘運行一次的cron做業),並使用它從版本控制(而不是從主服務器)下拉最新更新。 這顯着減小了變更的次數,可是,以下一節所述,這仍然留下了許多未答覆的問題,尤爲是關於如何配置服務器以及首先在其上安裝代理軟件的問題。

Ansible,CloudFormation,Heat和Terraform默認都是無主的。 或者,更準確一些,它們中的一些可能依賴於主服務器,但它已是您正在使用的基礎設施的一部分,而不是您必須管理的額外部分。 例如,Terraform使用雲提供商的API與雲提供商進行通訊,所以在某種意義上,API服務器是主服務器,除了它們不須要任何額外的基礎設施或任何額外的認證機制(即,只使用您的API密鑰)。 Ansible的工做方式是經過SSH直接鏈接到每一個服務器,所以,您沒必要再運行任何額外的基礎結構或管理額外的身份驗證機制(即只使用SSH密鑰)。

Agent vs Agentless

Chef,Puppet和SaltStack都要求您在要配置的每臺服務器上安裝代理軟件(例如,Chef Client,Puppet Agent,Salt Minion)。 代理一般在每一個服務器的後臺運行並負責

安裝最新的配置管理更新。

這有一些缺點:

  • 引導:如何配置服務器並首先在其上安裝代理軟件?一些配置管理工具能夠解決這個問題,假設一些外部流程會爲他們處理這個問題(例如,您首先使用Terraform部署一堆服務器,其中包含已安裝代理的VM映像);其餘配置管理工具具備特殊的引導過程,您可使用雲服務商API來運行一次性命令來配置服務器,並經過SSH在這些服務器上安裝代理軟件。
  • 維護:您必須按期仔細更新代理軟件,若是有主服務器,請務必與主服務器保持同步。您還必須監控代理軟件並在崩潰時從新啓動它。
  • 安全性:若是代理軟件從主服務器(或某些其餘服務器,若是您不使用主服務器)下拉配置,則必須在每臺服務器上打開出站端口。若是主服務器將配置推送到代理,則必須在每一個服務器上打開入站端口。在任何一種狀況下,您都必須弄清楚如何向正在與之交談的服務器驗證代理。全部這些都會增長攻擊者的攻擊範圍。

再強調一次,Chef,Puppet和SaltStack都對無代理模式(例如,salt-ssh)有不一樣程度的支持,可是這些一般感受它們是做爲過後的想法加入的,並不老是支持完整的配置管理工具的功能集。這就是爲何Chef,Puppet和SaltStack的默認或慣用配置幾乎老是包含一個代理,一般也包含一個master。

全部這些額外的動態部分都會在您的基礎架構中引入大量新的故障模式。 每次凌晨3點收到錯誤報告時,您都必須弄清楚它是不是應用程序代碼,IAC代碼,配置管理客戶端,主服務器或者服務器中的錯誤。 客戶端與主服務器通訊,或者其餘服務器與主服務器通訊的方式,或者......

Ansible,CloudFormation,Heat和Terraform不要求您安裝任何額外的代理。 或者,更準確一些,它們中的一些須要代理,但這些代理一般已做爲您正在使用的基礎結構的一部分安裝。 例如,AWS,Azure,Google Cloud和全部其餘雲提供商負責在每臺物理服務器上安裝,管理和驗證代理軟件。 做爲Terraform的用戶,您沒必要擔憂任何問題:您只需發出命令而後雲服務商會在全部你的服務器上爲你執行它們。 使用Ansible,您的服務器須要運行SSH守護程序,無論怎麼樣,這都會廣泛運行在大多數服務器上的。

成熟與前沿

選擇任何技術時要考慮的另外一個關鍵因素是成熟度。 下表顯示了每一個IAC工具的初始發佈日期和當前版本號(截至2019年5月)。

「DevOps系列]爲何咱們使用Terraform 而不使用Puppet, Ansible

一樣,這不是一個同類的比較,由於不一樣的工具備不一樣的版本控制方案,但一些趨勢是明確的。 到目前爲止,Terraform是此比較中最年輕的IAC工具。 它仍然是處於1.0.0版本以前,所以沒法保證穩定或向後兼容的API,而且錯誤相對常見(儘管大多數都是次要的)。 這是Terraform最大的弱點:雖然它在短期內變得很是受歡迎,但使用這種新的尖端工具所付出的代價是它不像其餘一些IAC選項那樣成熟。

混合使用多種工具

雖然我一直在比較整個博客文章中的IAC工具,但事實是您可能須要使用多種工具來構建您的基礎設施。 您看到的每一個工具都有優勢和缺點,所以您須要爲正確的工做選擇合適的工具。

如下是我見過的三種常見組合在不少公司都很好用:

  • 配置 + 配置管理
  • 配置 + 服務器模板
  • 配置 + 服務器模板 + 編排

配置 + 配置管理

示例:Terraform和Ansible。 您可使用Terraform部署全部底層基礎設施,包括網絡拓撲(即VPC,子網,路由表),數據存儲(例如,MySQL,Redis),負載均衡器和服務器。 而後,您使用Ansible在這些服務器之上部署您的應用程序。

這是一個簡單的方法,由於沒有運行額外的基礎設施(Terraform和Ansible都是客戶端應用程序),而且有不少方法可使Ansible和Terraform一塊兒工做(例如,Terraform爲您的服務器添加特殊標籤而後Ansible使用這些標籤來查找服務器並對其進行配置。 主要缺點是使用Ansible一般意味着您編寫了大量程序式代碼,使用可變服務器,所以隨着代碼庫,基礎架構和團隊的增加,維護可能會變得更加困難。

配置 + 服務器模板

示例:Terraform和Packer。您使用Packer將應用程序打包爲虛擬機鏡像。而後使用Terraform部署(a)具備這些虛擬機鏡像的服務器和(b)基礎架構的其他部分,包括網絡拓撲(即VPC,子網,路由表),數據存儲(例如,MySQL,Redis),和負載均衡器。

這也是一種簡單的方法,由於沒有運行額外的基礎設施(Terraform和Packer都是僅客戶端應用程序)。此外,這是一種不可變的基礎架構方法,這將使維護更容易。可是,有兩個主要缺點。首先,虛擬機可能須要很長時間才能構建和部署,這會下降迭代速度。其次,您可使用Terraform實施的部署策略是有限的(例如,您沒法在Terraform中本地實施藍綠色部署),所以您要麼最終編寫大量複雜的部署腳本,要麼轉向編排工具,以下所述。

配置 + 服務器模板 + 編排

示例:Terraform,Packer,Docker和Kubernetes。 您使用Packer建立安裝了Docker和Kubernetes的虛擬機映像。 而後使用Terraform部署(a)服務器集羣,每一個服務器運行此虛擬機鏡像,以及(b)基礎架構的其他部分,包括網絡拓撲(即VPC,子網,路由表),數據存儲( 例如,MySQL,Redis)和負載均衡器。 最後,當服務器集羣啓動時,它造成一個Kubernetes集羣,用於運行和管理Dockerized應用程序。

這種方法的優勢是Docker鏡像構建至關快,您能夠在本地計算機上運行和測試它們,而且您能夠利用Kubernetes的全部內置功能,包括各類部署策略,自動修復,自動縮放, 等等。 缺點是增長了複雜性,不管是在運行額外的基礎設施方面(Kubernetes集羣都很難部署和運營,儘管大多數主要的雲提供商如今提供託管的Kubernetes服務,能夠減輕部分工做),仍是學習、管理和debug額外的抽象層(Kubernetes,Docker,Packer)方面。

總結

咱們想要的是一個開源的,與雲無關的配置工具,它支持不可變基礎架構,一種聲明性語言和僅客戶端架構。 從上表中能夠看出,Terraform是惟一符合咱們全部標準的工具。 它固然不是完美的,特別是在成熟度方面,但咱們發現Terraform的優點遠遠超過它的弱點,而且沒有其餘IAC工具可以知足咱們的標準。

「DevOps系列]为什么我们使用Terraform 而不使用Puppet, Ansible

天天三分鐘,知識更輕鬆。

歡迎關注同名微信公衆帳號和頭條號極客24h。

相關文章
相關標籤/搜索