使用Puppet 前面看过Puppet语言的句法了,并且说了如何使用句法去表达配置,这次我们开始看一下Puppet的能力和怎么样在你的结点中充分利用这门语言去接合实际的配置。我们开始通过演示如何使用Puppet在一个真实的环境中配置你的结点,我们将充分利用(三)中我们讲过的句法。 所有的这些例子表现了在你的环境中实现Puppet的一种方式.这些例子被设计用做给你一个如何实现生产Puppet能够工作的主意,一些例子表现出一些最好的实践准则,但是还有一些是关于使用Puppet去使你配置清晰的主意。 Manifest Organization 在我们开始配置我们的环境之前,我们需要去看一下Puppet是如何管理和存储它的配置的,像你记得的那样,我们的资源、classes、definitions 都存储在manifest文件中。一个特殊的manifest文件叫做site manifest,这个文件是我们配置的核心,当启动Puppet master守护进程的时候,site manifest文件默认存储在/etc/puppet/manifest/site.pp这个文件中,需要表现依照句法判定配置是否正确。 Importing Manifests 如果我们把配置文件都放在site.pp文件中。它将很快就变得很复杂并且很难去管理,因此,我们把配置文件存储在多个文件中,我们把site manifest文件看作一个金字塔的顶点。在这个文件中,其他的manifests和其他配置定义必须被引用,就像这样: import "templates.pp" import "nodes.pp" import "classes/*" import "groups/*" import "users/*" import "os/*" 我们使用了5个import语句,第一我们引入了templates.pp和node.pp,你不需要去指明.pp扩展,默认的Puppet将添加.pp扩展给任意的引入的那些没有扩展的文件。然后我们引入了3个子文件夹:groups,users和os,×代表所有的.pp文件. 我们也可以使用site.pp文件建立一个默认的文件备份或者其他默认类型的文件,像这样: filebucket { main: server => puppet } File { backup => main } 我们定义了一个默认的filebucket 并且告诉文件类型总是把文件备份到filebucket里面 我们也可以将一个我们引入到site.pp文件的结构应用到这个文件,下面我展示一个关于文件结构和目录的一个例子。 Structure Description /manifests/ The manifest root directory /manifests/site.pp The primary manifest file /manifests/templates.pp Contains template nodes /manifests/nodes.pp Contains node definitions /manifests/definitions/ Houses all definitions /manifests/groups/ Contains manifests configuring groups /manifests/os/ Contains classes designed to configure nodes with particularoperating systems /manifests/users/ Contains manifests configuring users /manifest/files/ Contains file server modules for Puppet distributable files /manifests/templates/classname ERB templates contained in subdirectories named after the class that uses the template manifestdir = $confdir/manifests 还有一些人把他们的mainfests放到/var/puppet文件目录下,你必须选一个适合你环境和标准的位置,我们也可以这样实现和改变site.pp的位置,使用manifest配置项 manifest = $manifestdir/site.pp 这个manifest项默认值为$manifestdir/site.pp Managing Manifests with Subversion 版本控制工具是管理大量的manifest最好的工具,我们可以使用Subversion,svn是一个开源的版本控制工具。首先创建一个版本库 $ svnadmin create /usr/local/svn/puppet 然后我们添加Puppet目录到版本库里面 $ svn import puppet file:///usr/local/svn/puppet -m "初始导入Puppet结构" Adding puppet/site.pp Adding puppet/template.pp Adding puppet/nodes.pp ... Committed revision 1. 取出一个工作副本 $ svn checkout file:///usr/local/svn/puppet /etc/puppet/manifests 然后我们把做得改变更新到版本库中 $ cd /etc/puppet/manifests $ svn commit -m "Added firewall node" Defining Nodes 我们首先来配置结点,我们开始结点定义,为了简单起见,我们将问你的结点按类型分类,建立一些模板, 我们将分别问不同的服务建立不同的模板,我们放置一个叫做templates.pp的文件,但是我们必须在site.pp中引入这个文件。 node basenode { case $operatingsystem { fedora: { include fedora } debian: { include debian } default: { include fedora} } include baseapps, sshd } node default inherits basenode {} node webserver inherits basenode { include apache } node dbserver inherits basenode { include mysql } node mailserver inherits basenode { include postfix } 我们已经创建了一个我们的模板结点,首先是basenode,配置将应用到我们所有结点,在我们的basenode我们利用了一个fact $operatingsystem为了包含一个基于操作系统的结点,比如,如果Facter 返回的值是fedora ,fedora class 将被包含进来。 我们已经定义了一个默认的node,它继承basebode,这个默认的node适用于任何没用特殊声明的结点,然后我们创建了3个模板,webserver,dbserver,mailserver他们都继承basenode并且添加他们自己的classes,在目前阶段,每个模板中仅仅包含一个少数的class 这有一个关于子类和父类之间继承的二者择一的方法就是使用include函数,这个方法适合那些继承有变量范围的,如: class baseclass { case $operatingsystem { fedora: { include fedora } debian: { include debian } default: { include fedora} } include baseapps, sshd } node default { include baseclass } class webserver { include baseclass include apache } class dbserver { include baseclass include mysql } class mailserver { include baseclass include postfix } 这个方法中,我们定义了一个class叫做baseclass而不是一个base结点,这个class然后包含了所有的后来的类。现在我们为我们管理的结点添加结点定义,我们创建了一个文件叫做node.pp用来存储结点,但要在site.pp中引入nodes.pp node 'puppetmaster.testing.com' inherits basenode {} node 'web.testing.com' inherits webserver {} node 'db.testing.com' inherits dbserver {} node 'mail.testing.com' inherits mailserver {} Managing Users and Groups 一旦我们创建了结点,我们就要为我们的结点创建用户和组,Puppet推荐所有的用户和组作为虚拟的资源被创建,虚拟的资源不自动在你结点上配置,但是需要详细的应用,你可以在类型前面加上@来确认虚拟资源. 虚拟资源对用户和组都是很有用的,一旦在Puppet上这是因为一个资源只能够被管理,所以我们不能够同时在fedora和debian classes中配置一个名为sysadmin的用户,使用虚拟资源,我们能够创建一个sysadmin用户作为一个虚拟资源,然后我们可以在每个class中进行实现,我们可以在组中做同样的事, 我们开始定义两种类型的用户,第一类用户和功能有关,比如我们的sysadmin用户,或者用户属于employees和administrators,这些用户将被分组到一个单独的叫做virt_users的class并且包含在一个virt_users.pp的文件里面。第二种用户是那些被用做应用、服务、守护进程的用户,这些用户被分到他们自己的class里面,每一个类将前缀都是user_,比如user_apache.每一个用户class 将在它的文件中被指明。 我们将组分为相同的类型,用相同的模型来管理他们,所以的组都和用户相关并且被包含到一个叫做virt_group的class中,并且存放在一个名为vitr_groups.pp的文件中。组和用户类似,均以group_前缀开头,比如group_apache.我们要把users和groups的文件夹在site.pp中进行引入,确保所有的用户和组都能被加载 import "groups/*" import "users/*" Managing Users 我们看一部分的virt_users这个类,只显示我们的两个用户 class virt_users { @user { ensure => "present", uid => "1001", gid => "1000", comment => "Jane Smith", home => "/nfs/IT/home/jsmith", shell => "/bin/bash", } @user { ensure => "present", uid => "1002", gid => "1000", comment => "Mary Jones", home => "/nfs/IT/home/mjone", shell => "/bin/bash" } } 看一些virt_groups这个类 class virt_groups { @group { “staff”: gid => "1000", ensure => present } @group { "administration": gid => "1501" ensure => present } @group { "mail_team": gid => "1502", ensure => present } } 如何实现这些用户和组呢,首先我们我们实现这个用户和组都叫做staff,他存储在我们的class staff中存在我们的classer文件夹中,文件叫做staff.pp,我们将virt_users和virt_groups的类包含进来,确保Puppet知道去哪找用户和组. class staff { include virt_users,virt_groups realize( Group["staff"], User["jsith"], User["mjones"] ) } 我们能在我们的basenode模板中确保组和用户在每个结点上都创建,结点必须继承这个模板结点。 node basenode { case $operatingsystem { fedora: { include fedroa } debian: { include debian } default: { include fedora } } include baseapps,sshd,staff } 但是我们如果也想我们的administration组也被包含在每个结点中,我们将创建一个叫做administrators的类用来实现administration的用户组然后添加用户到这个组,我们存储到一个叫做administrators.pp的文件中,放到classes文件夹中,然后我们将这个class包含到nodes.pp这个文件中的basenode类当中 class administrators inherts virt_users { realize( Group["administration"] ) User["jsmith"] {groups => "administration" } } node basenode { case $operatingsystem { fedora: { include fedora } debian: { include debian } default: { include fedora} } include baseapps, sshd, staff, administrators } 我们实现了添加用户jsmith到administration组中,为了实现他,administrators 类继承了virt_users这个类,现在administration组将被创建在所有的结点和模板上,继承basenode 结点模板将jsmith加入到组,我们也可以在其他的类中实现这些用户,比如 mjone是一个mail administrator,我们将为mail administrators创建一个类存储在class/mail_team.pp,并且把mailserver模板结点包含进去,像这样: class mail_team inherits virt_users { realize( Group["mail_team"] ) User["mjones"] { groups => "mail_team" } } node mailserver inherits basenode { include postfix include mail_team } 这里mail_team组将被实现并且被mailserver结点模板包含,我们将mjone加入了mail_team组中,现在所有的结点,现在每个结点只要继承mailserver 就实现了将mjones用户添加到了mail_team组中了。 我们也可以创建但用户和组做特殊的目的,比如一个用户和组要用来跑一个守护进程或一个应用,例子如下: class mysql_user { user{ "mysql": ensure => "present", uid => "501", gid => "501", comment => "MYSQL", home => "/var/lib/mysql", shell => "/sbin/nologin", } } class mysql_group { group { "mysql": gid => "501", ensure => present } } 当我们配置合适的服务时将user和group包含进来就行了。 File Serving 在我们在结点上配置这个服务之前,我们看一些Puppet是如何进行文件服务的,puppet能做为一个文件服务器向结点去传送文件在需要时,这个文件服务是分布式文件。 Puppet的文件服务也有一个服务器和客户端,服务器是配置初始化守护进程。客户端功能是从服务器端接受文件。你可以区别一个Puppet文件服务通过文件资源类型像这样: file { "httpd.conf": source => "puppet://puppetmaster/httpd/conf/httpd.conf" } 我们开始配置我们的文件服务,文件服务是由fileserver.conf这个配置文件控制的,默认存放在/etc/puppet文件夹中。在master守护进程启动的时候你可以使用一个--fsconfig的标志指明一个位置,象这样: puppetmasterd --fsconfig /usr/local/etc/puppet/fileserver.conf 这个配置文件中定义了文件的路径和访问控制,看下面的一个例子 [configuration] path /etc/puppet/manifests/files/configuration allow *.testing.com deny *.production.com 上面的例子中每一个path叫做一个module,这个module名字就是configuration.modules的作用就是允许Puppet去抽象出简单的文件系统的配置和路径,path指明了master中服务文件存放的地方,path中可以包含%h,%d和%H,它们分别代表客户端的主机名,域名,严格的域名,所有的被Puppet客户端使用的SSL证书都是基于主机和域名的,它允许你指明基于这个名字特别的文件去下载,如: path /etc/puppet/manifests/files/%h 当客户端请求时会根据主机名生产不同的路径 /etc/puppet/manifests/files/web/filename /etc/puppet/manifests/files/db/filename /etc/puppet/manifests/files/mail/filename 也可以通过deny,和allow去拒绝或允许客户端下载文件。 如果你想在configuration module去检索一个文件,可以这样 file { "/etc/nsswitch.conf": source => "puppet://puppetmaster.configuration/nsswitch.conf" } 我们已经从Puppet master中检索出了nsswitch.conf,我们还可以下载整个文件夹的内容 file { "/etc/pam.d": source => "puppet://puppetmaster/configuration/pam.d", recurse => "true" } 这次我们指明的是一个文件夹,并且这个文件夹在文件服务器上,recurse这个属性告诉Puppet所有的包含在原目录的文件应该被检索并且下载到目标结点上。 Modularizing Our Configuration 现在我们已经配置了基本的结点和介绍了用户管理以及文件服务,我们需要向我们的结点上添加一些服务像mail,databases,和web服务,现在我们在我们的db.testing,com结点上添加mysql服务,在mail.testing.com添加Postfix服务,在www.testing,con结点上添加apache服务 为了添加这些服务,我们开始使用Puppet更厉害的特性:modules.Modules是配置文件的集合manifest,templates ,files都能被复用并且是分布式的。比如除了为安装和管理mysql去创建单独的class。我们将创建一个mysql module。 为什么要用module呢? 如果是你配置的应用,守护进程,或函数包含很多类、文件或模板。最简单的方法就是将这些资源放到一个包里面,Modules使管理配置文件的集合更简单和更结构化。 Modules组织起来很简单。他们存储在一个指明的文件夹下,modulepath的配置在puppet.conf中指出,默认在$confdir/modules 和 /usr/share/puppet/modules目录,我们可以指出多个module路径用冒号隔开,如: modulepath $confdir/modules:/usr/share/puppet/modules: ➥ /usr/local/share/puppet/modules 引入的时候使用 import "mysql" Puppet是如何知道加载什么资源的呢,每一个module被用一个文件夹去进行组织的,调用一个init.pp的文件进行初始化,每一个module至少应该有下面的这些目录结构 module_path/ module_name/ module_name/manifests/ module_name/manifests/init.pp 当引入一个module时init.pp文件是自动处理的,init.pp应该被放到默认的module中 /etc/puppet/modules/mysql/manifests/init.pp 这个init.pp有两重的功能,它包含了一个核心的classes被用做提供一个引入类和定义的位置,它应该被放到manifests文件夹下面,下面是一个init.pp文件的例子 class mysql { package { "mysql-server": ... } service { "mysqld": ... } } 我们声明了一个叫做mysql的class在这里我们配置了package和service的资源,所以当我们引入一个module的时候,比如是mysql module,Puppet将在所以的包含mysql的路径中进行查找,它将查出来的manifests文件夹下的init.pp文件并且加载它的内容. Puppet也允许一些聪明的命名方法使使用模块更加容易,创建一个mysql名空间,使用这个名空间,比如,mysql module创建了一个名空间叫做mysql,使用这个名空间,我们能够很容易的在我们的模块中定义和引用其他的class,比如我们要在mysql module里面添加一个叫做server的class,为了做到这样,我们需要定义一个叫做mysql::server的类并且存储在一个叫做server.pp的文件中,我们使用这个模块只需要简单地这样写: include mysql::server Puppet会认出它的名空间并且会在正确的模块和文件中去加载这个类 名空间的魔法功能也可以扩展到模板和文件,为了充分利用这个特性,我们在root模块下创建了两个附加的目录,叫做templates和files,这些目录应该包含这个模块下的任意templates和任意文件,这样你的模板就能够通过指定模块名和模板的名字被引用了 template("mysql/my.cnf.erb") Puppet将会自动的在正确的模块路径下去加载需要的模板。 在你模块包含的文件能够使用Puppet的文件服务的功能,每一个模块自动创建它自己的文件服务模块,并且加载任意模块文件路径下的存储的文件,比如在你的mysql模块,文件能使用source属性被使用 source => "puppet://puppetmaster/mysql/my.cnf" 这将会到mysql模块下的文件目录下去找my.cnf,在你的fileserver.conf中,那要定义一个特殊的文件模块叫做modules,这个允许你去进行访问控制,定义这个文件模块没用path语句,限制文件访问在所有的模块中。 MySQL Module 我们看一下我们第一个module的例子,mysql,我们将在/etc/puppet/modules目录下面存储我们的结构,结构就是这样: /etc/puppet/modules/mysql /etc/puppet/modules/mysql/manifests/init.pp /etc/puppet/modules/mysql/files/my.cnf 我们包含了init.pp和一个其他的文件--配置文件 my.cnf. init.pp文件 class mysql { $packagelost = ["mysql","mysql-server","mysql-libs"] package { $packagelist: ensure => installed } file { "/etc/my.cnf": owner => "root", group => "root", mode => "0644", replace => true, source => "puppet:///mysql/my.cnf", require => $Package["mysql-libs"] } service { mysqld: enable => "true", ensure => "running", require => File["/etc/my.cnf"] } } 我们看到了我们的mysql模块包含3个资源,第一个是一个包资源,为每个结点的mysql安装了3个包,第二个资源是一个文件类型的资源,它从mater 服务器上检索my.cnf配置文件,我们在source属性中未包含master的名字,取代它的是puppet:///,这个用来告诉Puppet自己插入服务器的名字,这个文件资源也使用require属性告诉Puppet在文件检索之前必须加载mysql-libs,第三个资源是mysqld服务,它有一个require属性File["/etc/my.cnf"] Postfix Module 我们的下一个模块将安装和管理一台Postfix mail 服务器,它在PostFix模块中是这样组织结构的 /etc/puppet/modules/postfix /etc/puppet/modules/postfix/manifests/init.pp /etc/puppet/modules/postfix/manifests/postfix_files.pp /etc/puppet/modules/postfix/files/aliases.db /etc/puppet/modules/postfix/files/main.cf /etc/puppet/modules/postfix/files/master.cf 我们指出了init.pp文件像mysql模块一样,我们在init.pp中包含了所有的配置文件 init.pp class postfix { $mailadmin = "postmaster@$domain" $packagelist = ["postfix.$architecture", "postfix-pflogsumm.$architecture"] package { $packagelist: ensure => "installed" } postfix::postfix_files { "/etc/aliases.db": mode => "0640", source => "aliases.db"; "/etc/postfix/main.cf": source => "main.cf"; "/etc/postfix/master.cf": source => "master.cf" } service { "postfix": enable => "true", ensure => "running", require => Package["postfix.$architecture"] } cron { pflogsumm: hour => 2, minute => 15, user => mail, command => "/usr/sbin/pflogsumm -d yesterday /var/log/maillog | ➥ mail -s 'pflogsumm from $fqdn' $mailadmin", require => Package["postfix-pflogsumm.$architecture"] } } 我们定义了一个类叫postfix,在这个class里面我们声明一些资源和两个变量,第一个变量是$mailadmin,为每个结点定义了mail 管理员,它使用$domain这个fact去填充它的属性,第二个参数是一个包列表的数组,这个包列表使用了另一个fact。$architecture去确保每个结点都安装上了合适的包。然后我们定义了一些资源去管理我们的Postfix配置,我们首先安装的是postfix和postfix-pflogsum包,然后我们从我们的文件服务器上检索配置文件,为了做到这样,我们为创建的做了定义,我们必须定义postfix::postfix_files,它的定义如下: define postfix::postfix_files($owner = root, $group = root, $mode = 644, ➥ $source, $backup = false, $recurse = false, $ensure = file) { file { $name: mode => owner => group => backup => recurse => ensure => require => source => } $mode, $owner, $group, $backup, $recurse, $ensure, Package["postfix.$architecture"], "puppet:///postfix/$source" } 我们确保Postfix服务已经启动,最好我们添加一个cron job使形成pflogsumm报告,并发送出去,在我们的cron job中我们使用$fqdn这个fact和我们的$mailadmin变量 Apache Module 最后一个module我们将要创建的是apache模块,安装和管理apache模块,Apache模块的结构是这样的: /etc/puppet/modules/apache /etc/puppet/modules/apache/manifests/init.pp /etc/puppet/modules/apache/manifests/virtual_host.pp /etc/puppet/modules/apache/manifests/apache_files.pp /etc/puppet/modules/apache/files/httpd.conf /etc/puppet/modules/apache/templates/virtual_host.erb 我们已经定义了一个init.pp的文件,一个文件叫做vitrual_host.pp这个文件中包含一个定义允许我们创建虚拟主机,apache_files.pp文件包含了apache::apache_files这个类,它提供了一些文件管理的快捷方式.很像postfix模块的中的postfix::postfix_files这个类,在文件目录中我们也创建了httpd.conf这个配置文件。最终在我们模板目录里面我们虚拟主机的模板叫做virtual.erb。我们来看一下init.pp为这个模块包含的核心配置文件 class apache { $packagelist = ["httpd","webalizer","mod_ssl"] package { $packagelist: ensure => "installed" } apache::apache_files { "/etc/httpd/conf/httpd.conf": source => "puppet:///apache/httpd.conf" } service { "httpd": enable => "true", ensure => "running", hasrestart => "true", hasstatus => "true", require => Package["httpd"] } } 我们定义了apache 类,类中安装了我们需要的Apache包。安装了httpd.conf的配置文件,然后确保httpd服务能够启动和运行,你也会注意到我们在服务上指明了hasrestart和hasstatus的属性,这告诉Puppet服务的脚本支持restart和status的命令,这就让Puppet知道init脚本有很多功能,使其于脚本的交互更加灵活,下面我们看一下apache::apache_file的定义 define apache::apache_files($owner = root,$group = root,$mode = 0644,$source,$backup = false,$recurse = false, $ensure = $file) { file { $name: mode => $mode, owner => $owner, group => $group, backup => $backup, recurse => $recurse, ensure => $ensure, require => Package["httpd"], source => "puppet:///httpd/$source" } } 最后定义apache::virtual_host允许我们去在web服务器上创建新的虚拟主机 define apache::virtual_host($ip,$ensure = "enable") { $file = "/etc/httpd/conf.d/$name.comf" $document_root = "/var/www/html/$name" file { $file: ensure => $ensure ? { enable => present, disable => absent}, content => template("apache/virtual_host.erb"), notify => Service["httpd"] } file { $document_root: ensure => $ensure ? { enable => directory, disable => absent} , require => File["$file"] } } apache模块包含了apache::virtual_host定义并且允许我们定义一个或者多个虚拟主机,那麽我们应该怎么使用这个定义呢?我们看下面的例子 node 'web.testing.com' inherits webserver { apache::virtual_host { "test1.testing.com": ip => "192.168.0.1" } apache::virtual_host { "test2.testing.com": ip => "192.168.0.2" } } 我们定义了web.testing.com结点,我们调用apache::virtual_host定义2次,每次基于virtual_host.erb模板在/etc/httpd/conf.d/目录下创建一个配置文件,virtual_host.erb内容如下: <VirtualHost <%= ip %>> DocumentRoot <%= document_root %> ServerName <%= name %> </VirtualHost> 这个模板使用document_root和ip变量,apache::virtual_host定义如下: <VirtualHost 192.168.0.1> DocumentRoot /var/www/html/test1.testing.com ServerName test1.testing.com </VirtualHost> 然后为每一个虚拟主机的文件创建一个目录,最后触发httpd重新加载,如果你想移出一个虚拟主机,你可以这样定义: virtual_host { "test1.testing.com": ip => "192.168.0.1", ensure => disable } 这将移出test1.testing.com的配置文件和相关的目录,httpd服务将使用新的配置文件进行重新启动 |
puppet 应用
最新推荐文章于 2023-07-02 16:49:22 发布