我通过伯乐在线翻译了一个Puppet简明教程,一共分为四部分,这是第四部分。
原文地址:http://blog.jobbole.com/89214/
本文由 伯乐在线 - Wing 翻译,黄利民 校稿。未经许可,禁止转载!
英文出处:Manuel Kiessling。欢迎加入翻译组。
在 《用 Puppet 搭建易管理的服务器基础架构(3)》中,我们创建了第一个可重用的、真实的Puppet模块:apache2。在第四部分以及之后的章节中,我们会停止使用示例,转而开始构建一个具有完整特性基础架构,包括用户和权限管理、Nagios监控、文件服务器、负载均衡、web服务器、数据库、VPN以及开发系统等等。
通过Puppet管理用户和组账号
在我工作的公司,我会在使用Puppet来管理我以及和我一起在Linux机器上工作的同事的Unix用户账号。由于只有几个人需要使用shell或者ssh的方式来访问系统,因此这种方式可以很好得工作。万一你需要支持大量的用户,每个用户都在机器上有他/她自己的账号,那么一个类似LDAP的解决方案可能更加合适。
但如果你像我一样,只有少数的软件开发人员和系统管理员需要访问系统,并且一个集中式的目录服务会过于复杂,那么通过Puppet来管理这些用户账号就非常简单可行。
对于通过Puppet进行管理的每一台机器上的清单来说,用户和用户组管理所使用的清单是一个很典型的示例。这种方式下每个用户都可以预期他/她会在每一台机器上都有自己可用的账号(当然在每个系统里面可能会有不同的权限)。你没有必要在多个用户之间分享root或者其他任何Unix账号——sudo会去为每个账号分配合适的权限。
使用集中式的配置管理会在这里显示它的威力:我们无论何时向基础设施中添加一个新机器,每个用户都可以预期他/她在新机器中的账号已经建立,包括一些个人配置,诸如Bash设置等等。同样,SSH密钥的管理也会从一件麻烦事变得小菜一碟。
在你的基础设施中,为用户ID和用户组ID定义一个简单但严格的规则集,是很有意义的。
在我的示例中,规则集看起来就像这样:
- 内部系统管理员:UID/GID在1000到1999之间
- 外部系统管理员:UID/GID在2000到2999之间
- SFTP账号:UID/GID在3000到3999之间
在我们的Ubuntu系统中,Ubuntu在安装过程中会创建用户ubuntu,它已经使用了UID/GID 1000,但如我们在后面所见,我们总可以通过Puppet来管理它。
如我之前所说,我假设你有一个基础设施,只有很少的用户会需要Unix账号来访问你的机器。在这种情况下,为每个用户创建一个Puppet模块是有意义的(和将所有用户清单放到一个模块相比)。通过这种方式,你可以创建简单或者复杂的账号模块,这些模块可以很好的共存,而不会最终变成一个混乱的模块。并且,你还可以在必要的时候在节点上“混合”不同的账号。例如Bob和Mary都需要访问你的web服务器,但只有Mary需要访问数据库,那么我们可以通过简单的将节点映射到模块的方式,来实现这一特性。如果你有一个包含10个用户的用户组,它需要访问所有的机器,我们不需要将每个账号映射到每个节点上,而只需要将这10个用户模块放到一个类中即可,我们会在后面对此进行详细说明。
第一个用户模块
那么,这在实际中看起来是什么样子呢?让我们针对已经存在的ubuntu账号,创建第一个用户模块。为了让模块容易被识别,我们应该在所有的用户模块的名字添加“user-”前缀——因此,我们需要为模块创建如下所示的目录结构:
1
2
3
|
On the puppetserver VM ~ # sudo mkdir -p /etc/puppet/modules/user-ubuntu/manifests
|
用户账号和它所在的用户组可以通过两个专门的块进行维护:user和group。我们可以像这样使用它们:
/etc/puppet/modules/user-ubuntu/manifests/init.pp on puppetserver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class user-ubuntu { user { "ubuntu":
comment => "ubuntu,,,",
home => "/home/ubuntu",
shell => "/bin/bash",
uid => 1000,
gid => 1000,
managehome => "true",
password => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',
groups => ["adm", "cdrom", "sudo", "dip", "plugdev", "lpadmin", "sambashare"]
}
group { "ubuntu":
gid => 1000,
}
} |
如你所见,这基本上反应了用户的创建过程,就好像它已经在系统中存在一样。请注意密码哈希码是放在单引号里面,而不是像其它参数那样放在双引号里面——这是因为对于Puppet来说,双引号中的$符号有特殊的含义。
像以前一样,为了应用这个新的清单(确切得说,是我们创建的新模块),我们需要在节点中声明这个新模块:
/etc/puppet/manifests/site.pp on puppetserver
1
2
3
4
5
6
|
node "puppetclient" { include user-ubuntu
include apache2
} |
我们从输出可以看出,这并没有改变任何事情(好吧,在你看来,这可能不是真的——密码可能已经生成了不同的哈希码,而且它会更新——尽管如此,最终你还是会有一个名为ubuntu的用户,它的密码也是ubuntu)。
使用Puppet管理SSH密钥
对于一个非常初级的用户管理来说,user块可以很好的工作,但我们肯定想要更多。Puppet可以用来管理SSH密钥,从而真正消除大量的“管理痛点”。让我在puppetserver系统上为root用户创建一个新的私有/公有SSH密钥对,然后将公有密钥添加到puppetclient系统的ubuntu用户的authorized_keys 文件中。
为此,我们首先生成一个新的密钥对,并将公有密钥输出到控制台上:
On the puppetserver VM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
~# sudo ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory '/root/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: e8:5e:ae:f4:be:1c:a1:79:da:a4:94:50:20:dd:7e:b3 root@puppetserver The key's randomart image is: +--[ RSA 2048]----+ | .... | | .... | | .. | | ...o | | . ..So | | o +E. | | B = | | + @ . | | =oB. | +-----------------+ ~# sudo cat /root/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT root@puppetserver |
(当然,在生成你自己系统的密钥时,最终你会得到一个不同的密钥对)。
Puppet还有一些代码用来管理用户的已经授权的SSH密钥,我们按照如下方式使用,来对新生成的密钥进行授权:
/etc/puppet/modules/user-ubuntu/manifests/init.pp on puppetserver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class user-ubuntu { user { "ubuntu":
comment => "ubuntu,,,",
home => "/home/ubuntu",
shell => "/bin/bash",
uid => 1000,
gid => 1000,
managehome => "true",
password => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',
groups => ["adm", "cdrom", "sudo", "dip", "plugdev", "lpadmin", "sambashare"]
}
group { "ubuntu":
gid => 1000,
}
ssh_authorized_key { "default-ssh-key-for-ubuntu":
user => "ubuntu",
ensure => present,
type => "ssh-rsa",
key => "AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT",
name => "root@puppetserver",
}
} |
正如我们期望的那样,这会在puppetclient系统上为ubuntu用户生成如下的authorized_keys文件:
/home/ubuntu/.ssh/authorized_keys on puppetclient
1
2
3
4
|
# HEADER: This file was autogenerated at Tue Apr 22 21:58:56 +0200 2014 # HEADER: by puppet. While it can still be managed manually, it # HEADER: is definitely not recommended. ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT root@puppetserver |
使用宏来避免重复
现在,虽然所有的一切都非常好,但如果查看最终的清单文件,我们会发现大量的重复信息:
/etc/puppet/modules/user-ubuntu/manifests/init.pp on puppetserver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class user-ubuntu { user { "ubuntu":
comment => "ubuntu,,,",
home => "/home/ubuntu",
shell => "/bin/bash",
uid => 1000,
gid => 1000,
managehome => "true",
password => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',
groups => ["adm", "cdrom", "sudo", "dip", "plugdev", "lpadmin", "sambashare"]
}
group { "ubuntu":
gid => 1000,
}
ssh_authorized_key { "default-ssh-key-for-ubuntu":
user => "ubuntu",
ensure => present,
type => "ssh-rsa",
key => "AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT",
name => "root@puppetserver",
}
} |
在创建用户时,真正需要的信息包括用户名、UID、密码哈希码、用户组以及SSH密钥。但是在我们的清单中,我们需要提供用户名6次、UID 3次。现在假设一下你要通过这种方式管理20个用户。幸运的是,我们有更好的解决方案。
Puppet允许我们定义自己的参数化宏(被称为defines),它可以让我们通过提供不同值的方式来多次重用清单文件,而无需一遍又一遍的重复写出整个清单文件。
我们首先来创建一个新模块来存储我们的宏:
/etc/puppet/modules/macro-useradd/manifests/init.pp on puppetserver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
define macro-useradd ( $name, $uid, $password, $groups, $sshkeytype, $sshkey ) { $username = $title
user { "$username":
comment => "$name",
home => "/home/$username",
shell => "/bin/bash",
uid => $uid,
gid => $uid,
managehome => "true",
password => "$password",
groups => $groups,
}
group { "$username":
gid => $uid,
}
ssh_authorized_key { "default-ssh-key-for-$username":
user => "$username",
ensure => present,
type => "$sshkeytype",
key => "$sshkey",
name => "$username",
}
} |
上述清单代码中,除了“$username=$title”这一行外,其他内容可能都是自解释的。在Puppet中的每一个块总会有一个标题——它是一个跟在大括号后面的一个字符串,并在结束后会有一个冒号:
1
2
3
4
5
6
7
8
9
10
11
|
file { "/this/is/a/title": ...
} group { "and-so-is-this". ...
} package { "yet-another-title": ...
} |
我们用标题来唯一标识一个块语句的。对于一个给定的节点,你不可以有多个指定类型的块带有相同的标题——也就是说,你不可以为/foo/bar文件声明两个file块。并且,正如你在这些示例中所看到的的那样,当块被执行时,标题经常被用作执行时所需要的一个值——例如,在file块中,标题被设置成块应该处理的文件的路径。
我们在自己的宏中也进行同样的处理:对于传入define的标题,我们期望它是一个Unix用户名,并且在我们的宏的块语句中,也将其作为对应的用户名进行使用。
请注意在我们的宏中,在处理参数时是如何使用引号的:对于数字类型的值,例如uid和gid,以及数组类型的值,例如groups,不需要引号,但对于字符串类型的值,例如comment和key,是需要引号的。我们在上面提过密码哈希码由于包含了$字符,因此需要将其放在单引号中,来防止对参数开头的$进行解析。我们稍后会看到,当调用宏的时候,情况依然如此。在宏中,我们需要再次使用双引号——这会导致$password参数会被解析,但参数中的内容(包含$字符的密码哈希码)就不会再被解析了。
既然我们已经定义了宏,接下来我们需要重写user-ubuntu清单,并在清单内使用宏:
/etc/puppet/modules/user-ubuntu/manifests/init.pp on puppetserver
1
2
3
4
5
6
7
8
9
10
11
12
|
class user-ubuntu { macro-useradd { "ubuntu":
name => "ubuntu",
uid => "1000",
password => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',
groups => ["adm", "cdrom", "sudo", "dip", "plugdev", "lpadmin", "sambashare"],
sshkeytype => "ssh-rsa",
sshkey => "AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT"
}
} |
我们需要再次运行sudo puppet agent –verbose –no-daemonize –onetime,但在目标机器上不会有任何变化,这是因为我们仅仅重构了清单,并没有改变它的逻辑。
增加更多用户
我们的重构生成了一个非常简洁的user-ubuntu清单,如果使用宏,那么添加大量用户就会变得非常直接。在下面的示例中,我们会添加两个用户:Mary和Bob。在这里我会直接重用ubuntu用户的密码哈希码和SSH密钥:
/etc/puppet/modules/user-mary/manifests/init.pp on puppetserver
1
2
3
4
5
6
7
8
9
10
11
12
|
class user-mary { macro-useradd { "mary":
name => "Mary",
uid => "1001",
password => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',
groups => ["sudo"],
sshkeytype => "ssh-rsa",
sshkey => "AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT"
}
} |
/etc/puppet/modules/user-bob/manifests/init.pp on puppetserver
1
2
3
4
5
6
7
8
9
10
11
12
|
class user-bob { macro-useradd { "bob":
name => "Bob",
uid => "1002",
password => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',
groups => [],
sshkeytype => "ssh-rsa",
sshkey => "AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT"
}
} |
管理用户权限
Puppet不仅能处理用户账户,而且还能处理账号的超级用户权限,让我们使用上面创建的两个账号来演示这一点。目标是能够集中定义哪个用户在哪台机器上有哪些权限。
正如你在用户清单中所见,Mary是sudo用户组中的成员,但Bob并不是。这已经表明Mary有能力以超级用户的权限在机器上执行任何一个命令。然而,我们并不像给Bob超级用户权限——我们只是想将他的超级用户权限限制在puppetclient虚拟机系统上,在其它机器上,只需要为其设置普通用户权限。单独的组关系并不能解决这个问题——为此,我们需要一个模块来处理所有机器上的sudoers文件。
首先,我们创建这个模块所需的目录结构:
On the puppetserver VM
1
2
|
~# sudo mkdir -p /etc/puppet/modules/sudo/manifests ~# sudo mkdir -p /etc/puppet/modules/sudo/files/etc |
然后,我们为模块创建清单文件:
/etc/puppet/modules/sudo/manifests/init.pp on puppetserver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class sudo { package { "sudo":
ensure => present,
}
file { "/etc/sudoers":
owner => "root",
group => "root",
mode => 0440,
source => "puppet://$puppetserver/modules/sudo/etc/sudoers",
require => Package["sudo"],
}
} |
我们需要将清单所管理的文件/etc/sudoers添加到模块的file文件夹下:
/etc/puppet/modules/sudo/files/etc/sudoers on puppetserver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
Defaults env_reset Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" # Host alias specification # User alias specification # Cmnd alias specification # User privilege specification root ALL=(ALL:ALL) ALL # Members of the admin group may gain root privileges %admin ALL=(ALL) ALL # Allow members of group sudo to execute any command %sudo ALL=(ALL:ALL) ALL # Bob has superuser privileges on puppetclient only bob puppetclient=(ALL:ALL) ALL |
在这里,我们的想法是在我们的基础设施的所有机器上只保留一份sudoers文件。这样我们可以通过集中的方式进行权限管理,即使每台机器都使用它本地的/etc/sudoers副本来检查用户权限。
在我们部署这些改动之前,我们需要将新的清单文件映射到节点上。我们会把所有的user模块和sudo模块放到一个新的类中,这样一旦我们向Puppet基础设施中添加更多的节点时,可以避免一些重复工作:
/etc/puppet/manifests/site.pp on puppetserver
1
2
3
4
5
6
7
8
9
10
11
|
class users { include user-ubuntu
include user-mary
include user-bob
include sudo
} node "puppetclient" { include users
include apache2
} |
现在,我们终于可以应用这些改变:
On the puppetclient VM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
~# sudo puppet agent --verbose --no-daemonize --onetime info: Caching catalog for puppetclient info: Applying configuration version '1399957763' info: FileBucket adding {md5}1b00ee0a97a1bcf9961e476140e2c5c1 info: /Stage[main]/Sudo/File[/etc/sudoers]: Filebucketed /etc/sudoers to puppet with sum 1b00ee0a97a1bcf9961e476140e2c5c1 notice: /Stage[main]/Sudo/File[/etc/sudoers]/content: content changed '{md5}1b00ee0a97a1bcf9961e476140e2c5c1' to '{md5}c5de61ca64ad4ef2e85e37d728d1be9f' notice: /Stage[main]/User-mary/Macro-useradd[mary]/Group[mary]/ensure: created notice: /Stage[main]/User-mary/Macro-useradd[mary]/User[mary]/ensure: created notice: /Stage[main]/User-mary/Macro-useradd[mary]/Ssh_authorized_key[default-ssh-key-for-mary]/ensure: created notice: /Stage[main]/User-bob/Macro-useradd[bob]/Group[bob]/ensure: created notice: /Stage[main]/User-bob/Macro-useradd[bob]/User[bob]/ensure: created notice: /Stage[main]/User-bob/Macro-useradd[bob]/Ssh_authorized_key[default-ssh-key-for-bob]/ensure: created notice: Finished catalog run in 0.47 seconds |
总结
在这一部分中,我们演示了如何在不使用集中式目录服务的前提下,通过Puppet来部署一个集中式的用户和权限管理解决方案。我们还学习了如果使用宏来简化那些复杂的、需要被使用多次的Puppet模块。
在接下来的第五部分中,我们会去查看Puppet如何集成Nagios,并演示一个通过Puppet构建的可管理的服务器基础设施如何在不增加额外管理负担的情况下,对服务器进行监控。
(伯乐在线注:本系列的第四篇,原作者完成于2014年3月,虽然说了会有第五部分,不过目前还没有在他博客看到有。)