天天看點

[翻譯]用 Puppet 搭建易管理的伺服器基礎架構(3)

我通過伯樂線上翻譯了一個Puppet簡明教程,一共分為四部分,這是第三部分。

本文由 伯樂線上 - Wing 翻譯,黃利民 校稿。未經許可,禁止轉載!

英文出處:Manuel Kiessling。歡迎加入翻譯組。

  • 《用 Puppet 搭建易管理的伺服器基礎架構(1)》
  • 《用 Puppet 搭建易管理的伺服器基礎架構(2)》

關于

在《用 Puppet 搭建易管理的伺服器基礎架構(2)》中,我們在 Puppet master上編寫了第一個非常簡單的清單,來對puppetclient虛拟機進行配置。現在我們要看一些更加複雜的配置,并且學習如何将清單組織成一個有用的結構。

子產品

如果我們把伺服器基礎結構的那些可管理的配置一直存放在一個檔案中,一旦當基礎設施中的系統數目增加到一定程度時,就會不能很好擴充。對于一個基礎結構來說,它的幾個伺服器要滿足不同的角色,這樣基礎結構就會變得複雜。我們希望Puppet可以控制這種複雜局面。為了實作這些,我們需要保持清單中的資訊整潔有序。

一種方式是将清單分解到不同的子產品裡。一個子產品是一個或者多個清單的集合,這些清單放在一起,是因為它們有共同的用途。一個典型的子產品示例是一個清單的集合,這些清單用來在目标機器上通過Apache HTTP伺服器來管理web頁面。這個子產品會組裝一些清單,這些清單關注不同的配置點:軟體包安裝、配置檔案管理、服務管理。通過管理這些配置點,我們可以提供一個具有全部功能的web伺服器。

我們現在要建立這樣一個子產品。一旦完成後,它會負責在puppetclient虛拟機上提供一個完整的、可操作的web伺服器。

子產品檔案夾結構

從外部的角度來看,Puppet子產品隻是一些包含了特定檔案的檔案夾結構。如果你把正确的檔案放到正确的檔案夾結構中,那麼這個結構中的清單可以被内部其他清單所引用。

我們來示範這一點,将在第二部分中建立的第一個簡單清單中的相關部分移到子產品中,然後我們會從主清單site.pp内部引用子產品化的清單,而不是在site.pp内部直接建立清單聲明。

當然,我們第一個清單隻是一個”Hello World”示例,是以,我們将子產品命名為helloworld。

為了做到這一點,我們隻需要在puppetserver虛拟機上建立一個檔案夾 /etc/puppet/modules/helloworld/manifests:

On the puppetserver VM

1

~

# sudo mkdir -p /etc/puppet/modules/helloworld/manifests

除了清單本身,子產品也可以在一個名為files的子目錄下擁有檔案,我們來建立這個目錄,然後将helloworld.txt檔案移動到這個目錄下:

2

~

# sudo mkdir -p /etc/puppet/modules/helloworld/files

~

# sudo mv /etc/puppet/files/helloworld.txt /etc/puppet/modules/helloworld/files/

現在我們需要為新子產品建立主清單檔案,在/etc/puppet/modules/helloworld/manifests/init.pp中輸入以下内容:

/etc/puppet/modules/helloworld/manifests/init.pp on puppetserver

3

4

5

6

7

8

9

10

11

class helloworld {

file

{

"/home/ubuntu/helloworld.txt"

:

ensure =>

file

,

owner  =>

"ubuntu"

,

group  =>

"ubuntu"

,

mode   => 0644,

source

=>

"puppet://puppetserver/modules/helloworld/helloworld.txt"

}

}

如你所見,這基本上是我們最初的file配置塊,但是它現在被包在一個class塊内,而且helloworld.txt的路徑改變了。在Puppet清單中,class塊可以用來定義配置塊的地方,這樣後期可以在其他地方使用這些配置塊。file 塊并不是 node 定義的一部分,雖然我們把它定義成 node 的一部分,但它還是在那裡估算的。類本身并不是做任何事情,隻有它在其它地方被聲明時,才會有意義。我們可以通過including語句來在node塊中聲明我們新定義的類:

/etc/puppet/manifests/site.pp on puppetserver

node

"puppetclient"

{

include helloworld

}

我們的節點定義現在不直接執行配置塊,而隻需要引用我們在helloworld類中定義的配置塊,這樣我們的節點也是新的helloworld子產品中的一部分。将helloworld相關的配置都放到一個子產品中,給我們帶來了非常大的靈活性。設想一下我們有一個包含了成百上千個節點的叢集,這些節點都需要接收helloworld配置塊,我們可以通過複制-粘貼配置塊的方式來實作,但這樣在擴充方面不是特别好。然而,helloworld子產品是可重用的,任意多數目的節點都可以使用它。

現在我們已經重新組織了現有的配置設定結構,它沒有改變實際的配置目錄,但是,當我們在puppetclient虛拟機上再次運作代理的話,可以看到:

On the puppetclient VM

~

# sudo puppet agent --verbose --no-daemonize --onetime

info: Caching catalog

for

puppetclient

info: Applying configuration version

'1396287576'

notice: Finished catalog run

in

0.05 seconds

因為實際的狀況和期望的配置是一緻的,是以代理不會采取任何行動。

我們的新的清單在/etc/puppet目錄下,新的目錄結構如下所示。

files

manifests

site.pp

modules

helloworld

manifests

init.pp

files

helloworld.txt

第一個真實的子產品:apache2

關于那些枯燥的Hello World示例,我已經談了足夠多的内容了,現在讓我們在puppetclient用戶端建立一個真實的web伺服器吧。

我們首先來重命名helloworld子產品, 因為我們已經不再需要它了。我們将會使用apache2 Ubuntu包來安裝web伺服器,這樣我們也可以來調用新的apache2子產品:

~

# sudo mv /etc/puppet/modules/helloworld /etc/puppet/modules/apache2

一個包含了全部功能的apache2 Puppet子產品需要處理下面幾個方面:它必須保證某些軟體包被安裝了;它必須處理一些配置檔案;它必須保證http守護程序服務在運作(當伺服器的配置無論何時發生變化,這個程序都會被重新開機)。

一個好消息是Puppet子產品在将來會被拆分成多個類。這樣可以幫助我們很清晰的分離關注點。這種分離并不是Puppet本身所要求的,從長遠來看,這樣做可以讓我們更容易的管理子產品。

我們要談論上一個子產品中已有的init.pp清單檔案,我們在/etc/puppet/modules/apache2/manifest目錄下建立清單檔案,檔案名是install.pp。在這個檔案裡,我們會來處理一些軟體包的安裝工作。

/etc/puppet/modules/apache2/manifests/install.pp on puppetserver

class apache2::

install

{

package { [

"apache2-mpm-prefork"

,

"apache2-utils"

]:

ensure => present

}

}

我們這裡會引入一個新的塊類型:package。我們幾乎可以它被使用的方式來讀出它的目的:我們使用它來保證在目标系統中包括apache2-mpm-prefork包和apache-utils包。

另外,請注意對類進行命名的新文法,我們使用兩個冒号來表示類結構中的命名空間。這個子產品中的主類是apache2(我們會在init.pp清單檔案中使用這個類名),其他的類會使用apache2名字空間。我們可以随便來為這些類命名——對于用來處理安裝問題的清單來說,install可能是一個明智的選擇。

就像之前解釋的那樣,Puppet為我們做了所有的重活,并利用apt-get來實作我們的期望,安裝哪些有問題的軟體包。

實際上,如果你想要的是一個運作的web伺服器,那麼安裝這些軟體包就足夠了,但我們會再向前一步,我們會處理web伺服器上的配置資訊。

模闆和變量

為了實作這一點,我們來建立一個新的類:apache::config,這個類位于/etc/puppet/modules/apache2/manifest/config.pp檔案中:

/etc/puppet/modules/apache2/manifests/config.pp on puppetserver

class apache2::config {

file

{

"/etc/apache2/sites-available/${hostname}.conf"

:

mode    => 0644,

owner   =>

"root"

,

group   =>

"root"

,

content => template(

"apache2/etc/apache2/sites-available/vhost.conf.erb"

)

}

}

這看上去和我們之前遇到的file塊有點兒像,但還是有一些明顯的差別的。它包括了兩個新内容:變量和模闆(我們後面還會有模闆中的變量)。

變量是一個強大的工具,我們可以用它來編寫更加智能的清單。如你所見,檔案路徑通過hostname變量被參數化了,這個變量用來解析目标節點的完全限定域名字(full qualified domain name),對于我們的puppetclient虛拟機來說,主機名字當然就是puppetclient。

~

# hostname

puppetclient

這以為這當我們在目标節點上運作清單檔案時,它會建立一個名為/etc/apache2/sites-available/puppetclient.conf檔案。

Puppet自己可以決定很多變量的值,hostname隻是其中一個,稍後我們會在這個系列中看到如何定義我們自己的變量。

在file塊中另外一個差別是content語句,它和我們之前遇到的source語句的不同之處在于,它不會指向一個靜态檔案,而是一個内嵌的ruby模闆。對于靜态檔案,它隻是從puppet master傳輸到用戶端,而對于模闆檔案來說,它是動态的,可以包含變量和語句,這些變量會被替換成對應的值,而語句則會被執行。

我們可以在puppetserver虛拟機上檢視一下/etc/puppet/modules/apache2/templates/etc/apache2/sites-available/vhost.conf.erb檔案,以便了解模闆檔案的結構:

/etc/puppet/modules/apache2/templates/etc/apache2/sites-available/vhost.conf.erb on puppetserver

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

<VirtualHost *:80>

ServerName <%=

hostname

%>

ServerAdmin webmaster@<%=

hostname

%>

DocumentRoot

/var/www

<Directory />

Options FollowSymLinks

AllowOverride None

<

/Directory

>

<Directory

/var/www/

>

Options Indexes FollowSymLinks MultiViews

AllowOverride All

Order allow,deny

allow from all

<

/Directory

>

ScriptAlias

/cgi-bin/

/usr/lib/cgi-bin/

<Directory

"/usr/lib/cgi-bin"

>

AllowOverride None

Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch

Order allow,deny

Allow from all

<

/Directory

>

ErrorLog ${APACHE_LOG_DIR}/<%=

hostname

%>.error.log

LogLevel warn

CustomLog ${APACHE_LOG_DIR}/<%=

hostname

%>.access.log combined

<

/VirtualHost

>

如你所見,這個檔案基本上就是你在平時常見的Apache虛拟機檔案,但其中包含了一些占位符。在這裡,我們隻有一個占位符:<%=hostname%>。當我們将這個檔案放到目标系統的目标檔案位置後,檔案中出現這個占位符的每個地方都會被替換成目标系統的完全限定域名字。

我們可以先試着運作一下新的清單,但目前還有一些不完美的地方需要我們首先做一些調整。對于Puppet清單來說,一個問題是你不能夠真正預測清單塊中各個應用程式的順序。Puppet清單并不是批處理檔案——它們并不是簡單的按照從上到下的順序執行。每一個塊——就像檔案或者包——都有自己的順序。Puppet确實試着去決定一個有意義的執行順序,但這不是很完美的。對我們來說,如果執行順序很重要,那麼我們需要幫助Puppet來實作這一點。

對于我們這個簡單的apache2子產品來說,在執行順序方面,可能會有什麼問題呢?好吧,我們安裝了一個軟體包,這個軟體包在安裝過程中會建立一個檔案夾,我們要将一個檔案放到這個檔案夾中。如果Puppet在檔案夾被建立出來之前(檔案夾通過安裝apache2-mpm-prefork包産生),就試着将虛拟機檔案放到該檔案夾中,那麼就會發生一個錯誤。

我們可以在清單中向Puppet解釋清單塊之間的依賴關系。為了說明針對虛拟機檔案的file子產品依賴于apache2-mpm-prefork安裝結束,我們可以向file塊中添加require語句:

class apache2::config {

file

{

"/etc/apache2/sites-available/${hostname}.conf"

:

mode    => 0644,

owner   =>

"root"

,

group   =>

"root"

,

content => template(

"apache2/etc/apache2/sites-available/vhost.conf.erb"

),

require => Package[

"apache2-mpm-prefork"

]

}

}

請注意,當我們引用一個包時,Package語句中的P應該是大寫的。

這樣,Puppet就可以知道如何決定清單的執行順序,它可以保證這樣的規則:隻有在apache2-mpm-prefork被安裝到系統之後,才會執行file塊。

第一次測試運作

現在關于apache2子產品的install和config清單,我們有了第一個可以工作的版本。現在我們需要保證這個子產品是可以應用到puppetclient虛拟機上的。為此,我們首先需要重寫子產品中的主清單:

/etc/puppet/modules/apache2/manifests/init.pp on puppetserver

class apache2 {

include apache2::

install

include apache2::config

}

然後,我們需要将新子產品映射到puppetclient節點的/etc/puppet/manifests/site.pp中:

node

"puppetclient"

{

include apache2

}

~

# sudo puppet agent --verbose --no-daemonize --onetime

info: Caching catalog

for

puppetclient

info: Applying configuration version

'1396980729'

notice:

/Stage

[main]

/Apache2

::Install

/Package

[apache2-utils]

/ensure

: ensure changed

'purged'

to

'present'

notice:

/Stage

[main]

/Apache2

::Install

/Package

[apache2-mpm-prefork]

/ensure

: ensure changed

'purged'

to

'present'

notice:

/Stage

[main]

/Apache2

::Config

/File

[

/etc/apache2/sites-available/puppetclient

.conf]

/ensure

: defined content as

'{md5}c55c5bd945cea21c817bca1a465b7dd3'

notice: Finished catalog run

in

16.62 seconds

好吧,現在可以工作了,但我們現在還沒有實作目标。puppetclient虛拟機需要被激活。為了實作這一點,隻需要需要一個從 /etc/apache2/sites-enabled/puppetclient.conf 到 /etc/apache2/sites-available/puppetclient.conf的符号連結,我們可以建立另外一個file類型塊:

class apache2::config {

file

{

"/etc/apache2/sites-available/${hostname}.conf"

:

mode    => 0644,

owner   =>

"root"

,

group   =>

"root"

,

content => template(

"apache2/etc/apache2/sites-available/vhost.conf.erb"

),

require => Package[

"apache2-mpm-prefork"

]

}

file

{

"/etc/apache2/sites-enabled/${hostname}.conf"

:

ensure  => link,

target  =>

"/etc/apache2/sites-available/${hostname}.conf"

,

require => File[

"/etc/apache2/sites-available/${hostname}.conf"

]

}

}

我們應用新的清單:

~

# sudo puppet agent --verbose --no-daemonize --onetime

info: Caching catalog

for

puppetclient

info: Applying configuration version

'1396983687'

notice:

/Stage

[main]

/Apache2

::Config

/File

[

/etc/apache2/sites-enabled/puppetclient

.conf]

/ensure

: created

notice: Finished catalog run

in

0.07 seconds

這樣我們就做完了,對嗎?好吧,從某種程度上說,是的。我們安裝了Apache,配置了虛拟主機。但是,如果使用Puppet,我們可以做得更好。如果手動管理puppetclient,我們會如何做呢?在修改了Apache配置後,我們會測試改動是否有任何錯誤(使用apachectl configtest),如果沒有錯誤,我們會重新開機apache2服務。事實證明,Puppet也可以做到這一點。

改進模型

首先,我們需要讓Puppet學習apache2服務。為此,我們需要在puppetserver虛拟機上建立另外一個清單檔案,這個檔案位于/etc/puppet/modules/apache2/manifests/service.pp:

/etc/puppet/modules/apache2/manifests/service.pp on puppetserver

class apache2::service {

service {

"apache2"

:

ensure     => running,

hasstatus  =>

true

,

hasrestart =>

true

,

restart    =>

"/usr/sbin/apachectl configtest && /usr/sbin/service apache2 reload"

,

enable

=>

true

,

require    => Package[

"apache2-mpm-prefork"

]

}

}

如你所見,Puppet包括了service類型,可以用來處理守護(daemon)應用程式。在這個清單中,我們讓Puppet來控制apache2服務。無論何時Puppet代理在目标系統運作,它都會確定這個服務是在運作。同時,Puppet還可以重新開機服務。在這種情況下,我們甚至可以教會Puppet隻有在apachectl configtest運作沒有錯誤時才會重新開機apache2服務。這樣,我們就可以實作自動化的服務管理,而且沒有放棄手動管理過程中的安全性檢查。

當然,我們需要在主清單檔案中聲明一個新類:

class apache2 {

include apache2::

install

include apache2::config

include apache2::service

}

最後,因為無論何時修改虛拟主機的配置,我們都想觸發Apache重新開機動作,是以我們需要将config清單和service清單連在一起,為此實作這一點,我們可以添加一個notify語句:

class apache2::config {

file

{

"/etc/apache2/sites-available/${hostname}.conf"

:

mode    => 0644,

owner   =>

"root"

,

group   =>

"root"

,

content => template(

"apache2/etc/apache2/sites-available/vhost.conf.erb"

),

require => Package[

"apache2-mpm-prefork"

],

notify  => Service[

"apache2"

]

}

file

{

"/etc/apache2/sites-enabled/${hostname}.conf"

:

ensure  => link,

target  =>

"/etc/apache2/sites-available/${hostname}.conf"

,

require => File[

"/etc/apache2/sites-available/${hostname}.conf"

]

}

}

現在我們看一下是否可以按照我們期望的那樣工作。可以簡單的修改一下位于/etc/puppet/modules/apache2/templates/etc/apache2/sites-available/vhost.conf.erb的虛拟主機模闆檔案(添加一個空格或者空行就可以了),然後在puppetclient虛拟機上重新運作代理,我已經高亮顯示了其中值得注意的地方:

~

# sudo puppet agent --verbose --no-daemonize --onetime

info: Caching catalog

for

puppetclient

info: Applying configuration version

'1396998784'

info: FileBucket adding {md5}c55c5bd945cea21c817bca1a465b7dd3

info:

/Stage

[main]

/Apache2

::Config

/File

[

/etc/apache2/sites-available/puppetclient

.conf]: Filebucketed

/etc/apache2/sites-available/puppetclient

.conf to puppet with

sum

c55c5bd945cea21c817bca1a465b7dd3

notice:

/Stage

[main]

/Apache2

::Config

/File

[

/etc/apache2/sites-available/puppetclient

.conf]

/content

: content changed

'{md5}c55c5bd945cea21c817bca1a465b7dd3'

to

'{md5}afafea12b21e61c5e18879ce3fe475d2'

info:

/Stage

[main]

/Apache2

::Config

/File

[

/etc/apache2/sites-available/puppetclient

.conf]: Scheduling refresh of Service[apache2]

notice:

/Stage

[main]

/Apache2

::Service

/Service

[apache2]: Triggered

'refresh'

from 1 events

notice: Finished catalog run

in

0.32 seconds

我們也可以檢查試下安全機制是否正常工作。再一次編輯虛拟機模闆檔案,讓它變得不正确,例如,可以删掉一個标簽的結束符>。

代理會像我們期望的那樣處理:

~

# sudo puppet agent --verbose --no-daemonize --onetime

info: Caching catalog

for

puppetclient

info: Applying configuration version

'1396998943'

info: FileBucket adding {md5}d15f727106b64c29d1c188efc9e6c97d

info:

/Stage

[main]

/Apache2

::Config

/File

[

/etc/apache2/sites-available/puppetclient

.conf]: Filebucketed

/etc/apache2/sites-available/puppetclient

.conf to puppet with

sum

d15f727106b64c29d1c188efc9e6c97d

notice:

/Stage

[main]

/Apache2

::Config

/File

[

/etc/apache2/sites-available/puppetclient

.conf]

/content

: content changed

'{md5}d15f727106b64c29d1c188efc9e6c97d'

to

'{md5}7c86c7ba3cd4d7522d36b9d9fdcd46e2'

info:

/Stage

[main]

/Apache2

::Config

/File

[

/etc/apache2/sites-available/puppetclient

.conf]: Scheduling refresh of Service[apache2]

err:

/Stage

[main]

/Apache2

::Service

/Service

[apache2]: Failed to call refresh: Could not restart Service[apache2]: Execution of

'/usr/sbin/apachectl configtest && /usr/sbin/service apache2 reload'

returned 1:  at

/etc/puppet/modules/apache2/manifests/service

.pp:10

notice: Finished catalog run

in

0.19 seconds

不要忘記修改這個模闆檔案,這樣才能正常工作。

結論和展望

在使用了模闆、變量、依賴以及通知後,建構一個成熟的、健壯的清單是非常直接和簡單的。使用子產品以後,清單可以讓更廣闊的範圍内的客戶重用,在本系列的第4部分中,我們會讨論參數化如何讓我們的子產品更加靈活。

    作者:李潘

    出處:http://wing011203.cnblogs.com/

    本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

繼續閱讀