Puppet、Chef、Ansible

本文試圖找到類似Puppet、Chef、Ansible這樣自動化配置管理工具的共性,以不至于迷失在雜亂的塵世中??倳懈鞣N人為各種目的造概念,來讓世界更復雜。

本文同樣適用于沒有運維經(jīng)驗的人。因為我就是一個沒有運維經(jīng)驗的人。歡迎斧正。

與這仨之間的歷史

本人接觸自動化運維的時間比較晚,也就一年前才知道Puppet及自動化運維(只限于知道),而Chef、Ansible就更晚了。然而在學習它們之前,我對運維要做哪些事情并沒有概念。這就對我學習Puppet,Chef和Ansible造成的障礙。因為不知道這三個工具在運維領(lǐng)域的位置,解決運維過程中的哪些問題。我對這三個工具的最初印象就是有了它們,不用我手工的SSH上服務(wù)器,然后一條條命令去執(zhí)行安裝軟件,不用SCP war包上服務(wù)器等,對服務(wù)器的操作都可以自動化了。

這個最初印象也就是我跟它們的歷史。為什么要說這些呢?就是因為這個最初印象,讓我覺得它們是有共性。所謂共性就是存在一些共通的概念或原理之類的東西,掌握這些“東西”,我就可以站在一個更高的高度去思考。Puppet, Chef, Ansible都是工具,對于工具來說,共性指的是它們共同要解決的問題。

但是當我翻了不少文章后依然沒有結(jié)果。所以,我決定自己去找它們的共性,并記錄下來。

自制一個自動化運維工具

但是要從多個相似的東西中找到共性,似乎需要同時很熟悉它們。但是我沒有那么多時間。所以,我選擇了另一種方法:在大腦中預想自己去實現(xiàn)一套自動化運維工具。但問題是,我都不知道“自動化運維工具要實現(xiàn)哪些功能。

那我就先把目標降低一些,把問題簡化一下:將我最初的“印象”實現(xiàn)自動化了。我能想到的就是寫一個bash腳本:

ssh ....

apt-get install -y java

apt-get -y nginx

scp ..

好,現(xiàn)在將問題難度加大:對多臺服務(wù)器進行同樣的操作。我能到想就是將所有的服務(wù)器的IP放在一個數(shù)組里,然后用for循環(huán)執(zhí)行。問題來了,如果我對服務(wù)器已經(jīng)執(zhí)行了一次命令時可能會失敗,我再想執(zhí)行第二次怎么辦呢?這時,我們可以在bash腳本里加上if語句,如果安裝了java就不安裝第二次了。

顯然現(xiàn)實中還會有很多問題,如:

反向配置問題,這時,我們應該另寫一個bash腳本來解決這個反向的問題?如果采用這種方案,我們bash腳本數(shù)量上升到一定程度,如何管理這些腳本及它們之間的關(guān)系,這個方案帶來的新的復雜性將會成為我們的新問題;

服務(wù)器上操作系統(tǒng)的兼容性問題:不同的操作系統(tǒng),我們的bash命令會不同;

軟件的版本升級或降級問題等等。

面對這些問題,我們是可以每次都用bash解決,但是這樣始終不是個辦法。因為,bash對于建立一個自動化運維工具過于底層。說到這句話,你應該很容易就想到設(shè)計一種DSL。到這個點,我覺得我們的方向已經(jīng)明朗。Puppet, Chef和Ansible都分別采用不同的DSL。而這種DSL是需要編譯成服務(wù)器可執(zhí)行的東西的。什么東西是可執(zhí)行的?目前,我們假設(shè)這個東西是bash腳本。

但是這個DSL是放在哪里編譯呢?放在受控機器端,還是主控機器上?所以,我認為所有的自動化運維工具都會遇到這個問題。什么是受控機器與主控機器?你就理解成一臺機器只發(fā)命令,另一臺機器只執(zhí)行命令。

我們剛剛談的是設(shè)計DSL。但是,要設(shè)計一個完整的自動化運維工具,我們最先應該考慮是主控機器如何與受控機器通信。這個問題讓我很長時間感到很無力,因為無從下手。后來醒悟,原來你不能單獨考慮這個問題,通信方式還與你設(shè)計的DSL及編譯DSL的方式有關(guān)。同時,受控機器的執(zhí)行結(jié)果這個問題,也影響著我們設(shè)計DSL。

上面我們似乎忘記了一個事實:自動化運維工具應對的往往不是一臺機器,而是很多臺機器。當面對多臺機器時,就會產(chǎn)生一個新的問題:如何組織它們?因為不同的機器的職責不同,所對應的配置也就不同。

我想我們已經(jīng)知道自動化運維工具都要解決的問題了:

1. 如何與受控機器通信

2. 如何組織成百上千臺機器

3. DSL的設(shè)計與編譯

4. 如何得到執(zhí)行結(jié)果

我不確定我們的思路與Puppet,Chef和Ansible的作者一樣。也不一定完全正確。但是至少,我們大概知道自動化運維工具要解決哪些問題了。而它們是不是自動化運維工具的共性?我不確定。

但是沒有關(guān)系,我們假設(shè)它們就是所有配置管理工具都要解決的問題,它們的共性,接下來我們來看看它們分別是怎么解決這些問題的。

這仨的背景

在進入學習之前,我們先看看它們的背景:

Puppet


如何與受控機器通信

Puppet的主控機器(Server)稱為Puppet?master,受控機器(client)稱為Puppet?agent。它們使用HTTPS進行通信。在安裝puppet之前,需要分別在主控機器和受控上設(shè)置的hostname。

因為是C/S架構(gòu)的,意味著你需要在主控機器上安裝Puppet?master,(安裝的過程或翻閱其它教程的時候,請注意教程所使用的Puppet的版本,Puppet版本之間是有差異的)我們以Ubuntu為例:

sudo?apt-getinstall-y?puppetmaster

在受控機器上安裝Puppet?agent:

sudo?apt-getinstall-y?puppet

安裝完成后,受控機器需要設(shè)置在/etc/puppet/puppet.conf文件的[main]節(jié)點下加入server=,同時運行sudo?puppet?agent?—test向主控機器申請認證。主控機器執(zhí)行sudo?puppet?cert?sign?<agent’s?hostname>認證。

在生產(chǎn)環(huán)境并不一定采取這樣的手工認證。

DSL的設(shè)計與編譯1

Puppet的DSL稱為Manifest?,以.pp為文件擴展名。像其它編程語言一樣,它也有一個程序的入口,它默認放在:/etc/puppet/manifests/site.pp?:


? #vim?/etc/puppet/manifests/site.pp? ?

node'mysqlserver.example.com'{? ? ? ? $mysql_password?=123456package?{? ? ? ? ? ? ? ?['mysql-common','mysql-client','mysql-server']:? ? ? ? ? ? ? ? ?ensure?=>?present? ? ? ? ?}? ? ? ? ?service?{'mysql':? ? ? ? ? ? ?enable?=>true,? ? ? ? ? ? ?ensure?=>?running,require=>?Package['mysql-server']? ? ? ? ?}? ? ? ? ?exec?{'set-root-password':? ? ? ? ? ? ?path? ?=>"/usr/bin:/usr/sbin:/bin",? ? ? ? ? ? ?subscribe?=>?[Package['mysql-common'],?Package['mysql-client'],?Package['mysql-server']],? ? ? ? ? ? ?refreshonly?=>true,? ? ? ? ? ? ?unless?=>"mysqladmin?-uroot?-p$mysql_password?status",? ? ? ? ? ? ?command?=>"mysqladmin?-uroot?password?$mysql_password",? ? ? ? ?}? ? ? ? ?file?{'/etc/mysql/my.cnf':? ? ? ? ? ? ?content?=>?template('my.cnf.erb'),require=>?Package['mysql-server'],? ? ? ? ? ? ?notify?=>?Service['mysql']? ? ? ? ?}? ? }


Manifest的基本格式:

node?NODENAME{? ? ? ? RESOURCE?{NAME:ATTRIBUTE=>VALUE,? ? ? ? ? ? ...? ? ? ? }? ? ?? ? }

Nodename實際上就是節(jié)點的hostname。這里有個提問:為什么不直接使用IP呢?Resource代表的是資源,也就是Puppet將節(jié)點內(nèi)所有的東西都當作資源。具體細節(jié),我們稍后會講到。

如何組織成百上千臺機器

如果我要控制1000臺機器是不是意味著我要在site.pp中寫1000個 節(jié)點 node NODENAME{…}呢?答案是肯定的。

DSL的設(shè)計與編譯2

我們注意到`node`節(jié)點中所包含的所有內(nèi)容,本質(zhì)上都是在描述這個node的狀態(tài),如:

service {'mysql':enable=>true,? ?? ensure => running? ? ? ?}

指的是`service mysql`這個Resource的狀態(tài)是可用的且正在運行的。Puppet中內(nèi)建了不少Resource供給我們使用,如:file,exec, package等。但是當Puppet內(nèi)建的Resource不夠用了呢?所以,它應該支持resource的擴展。以此類推,所有的自動化運維工具都應該支持此類擴展。

同時,Puppet提供了模板機制。Puppet的模板模式使用的是Ruby的ERB。你將`.erb`的文件放在`/etc/puppet/templates`后,就可以在puppet的代碼中使用`template('my.cnf.erb’)`:

?#vim?/etc/puppet/templates/my.cnf.erb[mysql]????...????...

既然有模板,怎么少得了變量?變量的定義:$VAR_NAME = VALUE。有變量的地方,一定會引用作用域概念,不論它是靜態(tài)作用域還是全局作用域??梢愿嬖V你Puppet是靜態(tài)作用域。猜猜Puppet的變量的作用域分幾級?實際上,Puppet,Chef,Ansible的變量都是靜態(tài)作用域概念,它們的變量作用域分又都分成幾級?多問一句:這也是它們的共性嗎?

現(xiàn)在我們了解了Manifest及其基本格式、node、resource概念、模板和變量。同時,我們并不應該把所有的內(nèi)容都寫到一個site.pp文件中,這就像我們不應該把所有的邏輯都寫在C語言中的main函數(shù)中一樣。那Puppet的DSL是如何解決這個問題的:如何讓開發(fā)者更方便的組織代碼?

現(xiàn)在我們來看相對模塊化一些的Puppet代碼:

#vim?/etc/puppet/manifests/site.pp

classmysql($mysql_password="123456")?{? ? package?{? ? ? ? ? ?['mysql-common','mysql-client','mysql-server']:ensure=>present? ? ?}? ? ?service?{'mysql':enable=>true,? ? ? ? ?ensure?=>?running,require=>?Package['mysql-server']? ? ?}? ? ?exec?{'set-root-password':path=>"/usr/bin:/usr/sbin:/bin",? ? ? ? ?subscribe?=>?[Package['mysql-common'],?Package['mysql-client'],?Package['mysql-server']],? ? ? ? ?refreshonly?=>true,? ? ? ? ?unless?=>"mysqladmin?-uroot?-p$mysql_password?status",? ? ? ? ?command?=>"mysqladmin?-uroot?password?$mysql_password",? ? ?}? ? ?file?{'/etc/mysql/my.cnf':content=>template('my.cnf.erb'),require=>?Package['mysql-server'],? ? ? ? ?notify?=>?Service['mysql']? ? ?}}node'agent'{? ? include?mysqlclass{'mysql':mysql_password=>'456789'}}

這個版本的site.pp我們用到了class概念。你可以定義了一個class,然后將職責相同的邏輯放在其中。最后在其它地方引用。但是引用的時候要分情況,這個class有沒有帶參數(shù), 將影響使用這個class的方式。

但是就算這樣,我們的site.pp的代碼可維護性一樣不高。所以,Puppet還提供一種module概念。實際上,用了你就知道,puppet就是將class從site.pp移出去了的另一種說法。我們來看使用了module的第三版:

我們將mysql class抽到mysql module中:

#vim?/etc/puppet/modules/mysql/manifests/init.ppclass?mysql($mysql_password="123456")?{????package?{???????['mysql-common','mysql-client','mysql-server']:?????????????????ensure?=>?present?????}?????service?{'mysql':enable=>true,?????????ensure?=>?running,?????????require?=>?Package['mysql-server']?????}exec{'set-root-password':?????????path???=>"/usr/bin:/usr/sbin:/bin",?????????subscribe?=>?[Package['mysql-common'],?Package['mysql-client'],?Package['mysql-server']],?????????refreshonly?=>true,?????????unless?=>"mysqladmin?-uroot?-p$mysql_passwordstatus",command=>"mysqladmin?-uroot?password$mysql_password",?????}?????file?{'/etc/mysql/my.cnf':?????????content?=>?template('mysql/my.cnf.erb'),?????????require?=>?Package['mysql-server'],?????????notify?=>?Service['mysql']?????}}

在使用module之前,我們的文件結(jié)構(gòu)是這樣的:

|-- auth.conf|-- environments|-- files|-- manifests|?? `-- site.pp|-- puppet.conf|-- templates| ?`—my.cnf.erb

使用module后:

|-- auth.conf|-- environments|-- files|-- manifests|?? `-- site.pp|-- modules|?? `-- mysql|?????? |-- manifests|?????? |?? `-- init.pp|?????? `-- templates|?????????? `-- my.cnf.erb|-- puppet.conf`-- templates

定義了module,那怎么用呢?

#vim?/etc/puppet/manifests/site.ppnode'agent'{includemysql}

到這里,我相信你知道大概怎么寫Puppet,但是我覺得是不夠的。我不理解它為什么這樣設(shè)計。我們?yōu)槭裁床皇牵?/p>

node'agent_hostname'{installpackage [‘mysql’,’mysqlserver’]? ? start?service ‘mysql'

? ? create template mysql.cnf

}

我的意思是為什么是以名詞導向的描述性語言,而不是以動詞為導向。然后,我就去找《配置管理最佳實踐》。醒悟了,原來配置管理不是一兩個工具就可以搞定。它是一個系統(tǒng),包括六個核心職能:

1. 源代碼管理

2. 構(gòu)建工程

3. 環(huán)境配置

4. 變更控制

5. 發(fā)布工程

6. 部署

所以,我以前一直不理解自動化運維和自動化配置管理之間的區(qū)別。同時我看到了一些文章里:配置管理實際上就是狀態(tài)管理,不論是服務(wù)器狀態(tài)還是軟件狀態(tài)。

這下終于感覺明白了,也難怪Puppet,Chef,Ansible的DSL都是描述性的語言。

但是,Puppet如何編譯,在哪里編譯我們寫好的manifest呢?在主控機器與受控機器認證成功后,受控機器會每隔一段時間就向主控機器發(fā)請求,這個請求將會把自己(受控機器)的信息告訴主控機器。主控機器拿到這些信息后與manifest鏈接編譯,最后生成一份受控機器(puppet客戶端)可執(zhí)行的catalog。受控機器在執(zhí)行的過程中,將執(zhí)行情況反饋給主控主機。這就是Puppet中主控機器如何得到受控機器的命令執(zhí)行結(jié)果的。

到此,我們看到了Puppet已經(jīng)回答了我們之前的四個問題。

小結(jié)

1. 如何與受控機器通信

?????采用C/S架構(gòu),使用HTTPS,agent向master申請證書。

2. 如何組織成百上千臺機器

?????在manifest中使用`node`關(guān)鍵字定義。

3. DSL的設(shè)計與編譯

?????* 組織代碼的方式

? ?? ?????Puppet在manifest文件中定義node(受控節(jié)點),將所有node中的構(gòu)件抽象為resource,我們可以給這個resource的attribute設(shè)置值。node下可以包含多個resource,這些resource共同構(gòu)成了這個node的狀態(tài)。但是不可能將所有的resource都寫在一個文件中,再說一個manifest文件通常不止一個node。所以,所以,Puppet提供一種module和class機制,讓你能將一些共同起到同一職責的resource打包到一起。class與module有什么不同呢?class可以直接寫到manifest文件中,而module必須另外新建一個目錄結(jié)構(gòu)。這就是Puppet組織代碼的方式。

? ?? ?????深入學習:如果處理resource之間的關(guān)系問題,它們很有可能有依賴關(guān)系。class及module也會有同樣的問題。if else及for呢?

?????* 變量定義:?$VAR_NAME = VALUE。?

? ??????? 深入學習:了解變量的作用域

?????* 模板:使用ruby的erb文件

4. 如何得到執(zhí)行結(jié)果

?????受控機器主動將執(zhí)行結(jié)果發(fā)送給主控機器。

它真的一定要有master才能用嗎?不是的。Puppet提供了單機版的使用方法。具體請google:?puppet apply。

Chef

Chef的中文意思是廚師。所以它將所有的受控機器看作“菜肴”。但是如果我們不給告訴它菜譜(Cookbook),它是不會給我們做菜的。菜譜上都寫著什么呢?是配方(Recipe)。所以,我們把recipe一個個的寫到Cookbook中,最后交給Chef。

Chef同樣是C/S架構(gòu),C與S也是使用HTTPS進行通信的。同樣的,正因為這樣,我們可能重用學習Puppet的pattern來學習Chef。但是因為Chef的C/S模式的投資回報率太低了,所以,我堅持一段時間后,就放棄了。和Puppet一樣,Chef也提供了單機版:Chef-solo。

Ansible

Ansible說是agentles(去客戶端)的。但是實際上,它要求受控機器上裝有SSH及Python,并且裝了python-simplejson包。實際上,我們現(xiàn)在的機器基本上默認都已經(jīng)安裝這些。所以,在使用Ansible時,你不需要特意準備一臺機器做為主控服務(wù)器。只要你想,任何機器隨時都可以變成主控機器。

關(guān)于Ansible的安裝看文檔就好了。與Chef和Puppet不同的是,Ansible組織受控機器的那部分邏輯抽來單獨放,叫Inventory。它是一個ini格式的文件,如hosts:

[web]192.168.33.10[db]192.168.33.11

文件名和路徑都任意,但是建議使用表意的名字及合適的路徑。

Puppet和Chef都自己做了一套DSL,然后再自己寫編譯器,但是Ansible使用的是yaml格式。我覺得這是非常聰明的設(shè)計:一是大家都熟悉yaml格式比熟悉自定義的DSL來得簡單,二是不需要自己設(shè)計DSL了,三是不用自己寫編譯器了。所以,我個人學習過程中,發(fā)現(xiàn)它是相對Puppet,Chef簡單很多。

了解yaml文件格式后,接著就是理解Ansible的隱喻了。Ansible是導演,將所有的受控機器理解為演員。而我們開發(fā)者則是編劇。我們只要把劇本(playbook)寫好,Ansible拿劇本再與Invenstory一對上號,演員只會按照劇本上的如實發(fā)揮,不會有任何的個人發(fā)揮。

好,我們就來寫第一版本的playbook.yml(路徑和名字都可自定義):

----?hosts:?web??tasks:????-?name:installnginx??????apt:name=nginx?state=latest-hosts:?db??vars:????mysql_password:'123465'tasks:????-name:installmysql??????yum:name={{item}}??????with_items:????????-'mysql-common'-'mysql-client'-'mysql-server'-name:?configurate?mysql-servertemplate:?src=my.cnf.j2?dest=/etc/mysql/my.cnf????-name:startservice??????service:name=mysql?state=started

#vim?templates/my.cnf.j2[mysql]...passowrd={{mysql_password}}

我們的劇本包括兩個演員:web,db。它們都對應哪些受控機器呢?看Invenstory文件就知道了。那這些演員都要做哪些事情呢?看tasks,它下面跟的是一個列表。像`yum`,`apt`,`template`,`service`,被稱為module,類似于Puppet的resource和Chef的recipe。Ansible本身提供了不少module,但是想都不用想,一定不能滿足所有項目的需求,所以,你可以開發(fā)自己的module。

同樣的,Ansible提供變量和模板(Jinja2)機制。問題來了,Ansible的作用域分為幾級呢?

同樣的,我們可以不能容忍把所有的task都寫在一個文件里。Ansible是如何組織代碼的呢?Ansible提出role的概念。是的,扮演共同職責的task,我們把它們歸到同一個role中。所以,我們文件結(jié)構(gòu)也變了,由原來的只有兩個文本文件,到現(xiàn)在需要新建目錄了:

├──hosts├──playbook.yml└──roles└──mysql├──tasks│?? └──main.yml└──templcates└──my.cnf.j2

這時,playbook.yml:

----?hosts:?web??????tasks:????????-?name:installnginx??????????apt:name=nginx?state=latest????-hosts:?dbvar:????????mysql_password:'123456'roles:????????-?mysql

而main.yml:

-?name:installmysql??yum:name={{item}}??with_items:????-'mysql-common'-'mysql-client'-'mysql-server'-name:?configurate?mysql-servertemplate:?src=my.cnf.j2?dest=/etc/mysql/my.cnf-name:startservice??service:name=mysql?state=started

就是task和role之間的關(guān)系。那task之間的關(guān)系,role之間的關(guān)系呢?

Ansible的DSL就是這樣組織代碼的。最后一個問題如何得到執(zhí)行結(jié)果呢?這就要說到Ansible的原理:Ansible將本地的yml文件編譯成python代碼,然后傳到受控機器,受控機器執(zhí)行結(jié)果以Json格式返回。

Ansible的入門非常簡單。

小結(jié)

1. 如何與受控機器通信

?????只要主控機器與受控機器雙方將有SSH

2. 如何組織成百上千臺機器

?????使用Invenstory管理

3. DSL的設(shè)計與編譯

?????* 組織代碼的方式

? ??????? Ansible Language的入口就是playbook。你可以直接在playbook里加`tasks`。很自然,我們想到的這個tasks里是一批小task。事實的確是這樣,但是在Ansible中,它叫module。但是,我們不希望所有的task寫在同一個文件中,這時,Ansible的role機制就起作用了。你可以把完成同一職責的一批放在一個role中就好了。

? ?? ?????深入學習:module之間的依賴問題,if-else問題

?????* 變量定義:不同的級別有不同的定義方法?

? ?? ?????深入學習:了解變量的作用域

?????* 模板:使用Jinja2

4. 如何得到執(zhí)行結(jié)果

?????受控機器主動將執(zhí)行結(jié)果發(fā)送給主控機器。

總結(jié)

面對層出不窮的新編程語言、新框架、新概念,我們程序員總是學不完。誠然,我假設(shè)大家都愛學習,但是,我們更需要問:學到的這些東西,到底屬于解決域還是問題域。所以,來了新東西,我總是要問這東西解決了什么問題?為什么它能解決,依據(jù)是什么?我們需要的是問題的本質(zhì)和問題的解決模型,而不是別人葫蘆里的藥。

說遠了。實際上,除了上述的仨自動化配置管理工具,市面還有很多別的。總的來,我覺得都可以用以上思路去學習?;氐轿覀冏铋_始的問題:Puppet,Chef,Ansible的共性是什么? 我能不能說那四個問題就是它們的共性,答案是我也不知道。

最后,我申明:除了上述工具,我沒有將“思路”應用到其它工具。希望應用到的同學給我反饋。謝謝。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1)安裝2)常用模塊3)inventory4)playbook(role\tag\template)5) yaml...
    秦記閱讀 4,612評論 2 5
  • 在項目中有很多地方用到ansible。最初使用ansible只是為了方便代碼部署和模板配置,畢竟手動去30+臺機器...
    __七把刀__閱讀 10,930評論 6 79
  • Puppet理論定義: Puppet 是一個跨平臺的集中化配置管理系統(tǒng),它使用自有的描述語言,可管理配置文件、用戶...
    屬于你的世界閱讀 1,103評論 0 2
  • Getting Started Use the Current Stable Version (7.1) Buil...
    Leonzai閱讀 2,072評論 0 3
  • 查單車 ——鄉(xiāng)村懷舊系列散文之“查車” 火山 上午還沒有完全睡醒,習慣性地打開手機看看微信的信息,老家村圍子的信息...
    朱明云閱讀 338評論 0 1

友情鏈接更多精彩內(nèi)容