puppet总结
一、基础知识
1.Puppet是开源的基于Ruby的系统配置管理工具,依赖于C/S的部署架构。Puppet这样的自动化配置管理工具可以帮助系统管理员更加方便的完成多台服务器的升级软件包、管理配置文件、系统服务、cron任务、添加新的配置、修复错误等重复工作。
2.Puppet的运作是典型的C/S模式,一个Server,多个Client。这个Server被称作master,而Client被叫作agent,也就是指属于master的node(节点)。在每个instance上,无论它是master还是agent,Puppet都作为daemon程序常驻进程,agent们通过SSL标准与master建立加密认证连接,而master就作为接收上级(也就是用户)的命令并向agent们发送命令的指挥官。
3.Puppet的语法允许你创建一个单独脚本,用来在你所有的目标主机上建立一个用户。所有的目标主机会依次使用适用于本地系统的语法解释和执行这个模块。
4.在大规模的生成环境中,如果只有一台puppetmaster会忙不过来的,因为puppet是用ruby写的,ruby是解析型语言,每个客户端来访问,都要解析一次,当客户端多了就忙不过来,所以需要扩展成一个服务器组。puppetmaster可以看作一个web服务器,实际上也是由ruby提供的web服务器模块来做的。因此可以利用web代理软件来配合puppetmaster做集群设置。
5. 用puppet,可以运行一个服务器端,然后每个客户端通过ssl证书连接服务器,得到本机器的配置列表,然后更加列表的来完成配置工作,所以如果硬件配置好,在一天之内配置好上千上万台机器是很容易实现的事情,前提得大部分机器配置类似。
6. puppet是一种Linux、Unix平台的集中配置管理系统,所谓配置管理系统,就是管理机器里面诸如文件,用户,进程,软件包这些资源,其设计目标是简化对这些资源的管理以及妥善处理资源间的依赖关系。puppet使用一种描述性语言来定义配置项,配置项中被称为”资源”,描述性语言可以声明你的配置的状态---比如声明一个软件包应该被安装或者一个服务应该被启动。
7.在中心的Server上安装puppet-server包,并运行puppetmasterd进程;在被管理机上安装puppet包,并运行puppetd进程。另外,在每台主机上配置好自己的hostname,之后每台机器要以hostname区分。
8. puppet的服务器端保存着所有的对客户端服务器的配置代码,在puppet里面叫做manifest. 客户端下载manifest之后,可以根据manifest对服务器进行配置,例如软件包管理,用户管理和文件管理等等。这样就把日常的系统管理任务代码化了,代码化的好处是可以分享,保存,避免重复劳动,也可以快速恢复以及快速的大规模部署服务器。同时,manifest可以的根据客户端服务器的配置情况(硬件和软件)来动态生成。
Puppet 结构
9. puppet的代码主要就是由这些资源和资源的属性构成。
10. puppet有两种执行模式,一是直接运行puppetd file.manifest,二是puppetd --serverpuppetmaster.server.com;前面一种是直接读取file.mainfest文件进行配置,后一种是从服务端下载manifest进行配置。
11. 编写一个manifest文件/tmp/1.pp,内容如下:
f i l e {
" /tmp/ t es t " :
content=>"h e l l o \n" ,
mode => 0644;
}
然后执行puppetd /tmp/1.pp ;执行完成以后,将会在/tmp目录下面生成一个文件test,文件内容是"hello";第一行的file表明是什么类型的资源,第二行的"/tmp/test"叫做这个资源的title;用来代表这个资源,后面两行设置这个资源的属性。
12.
package {
[ " gcc" , "make" ] :
ensure => i ns t a l l e d ;
}
这是配置一个包资源,包是gcc和make, 第三行是指定这两个包的属性,在这里是installed,表示要安装这两个软件包。再次提醒:不同的资源有不同的属性,但是又有一些属性是所有资源都共有的,例如tag,这种属性叫做元属性。
13. puppetmaster的第一个执行的代码是在/etc/puppet/manifest/site.pp因此这个文件必须存在,而且其他的代码也要通过代码来调用. 现在,建立一个最简单的site.pp文件,内容如下
node default {
f i l e { "/tmp/temp1 . t x t " :
content =>" h e l l o " ; }
}
二、原理
Puppet是一个C/S架构的配置管理工具,在中央服务器上安装puppet-server软件包(被称作Puppet master)。在需要管理的目标主机上安装puppet客户端软件(被称作Puppet Client)。当客户端连接上Puppet master后,定义在Puppet master上的配置文件会被编译,然后在客户端上运行。每个客户端默认每半个小时(可以设置runinterval=30)和服务器进行一次通信,确认配置信息的更新情况。如果有新的配置信息或者配置信息已经改变,配置将会被重新编译并发布到各客户端执行。也可以在服务器上主动触发一个配置信息的更新,强制各客户端进行配置。如果客户端的配置信息被改变了,它可以从服务器获得原始配置进行校正。
1)客户端通过facter收集客户端信息并发送至服务端
2)连接服务端并请求catalog日志
3)请求节点(node)的信息
4)从服务器端接收节点(node)的实例
5)编译代码(包括语法检查等工作)
6)查询是否有exported 虚拟资源
7)如有,则从数据库接收虚拟资源
8)接收完整的catalog日志
9)存储catalog日志到数据库
10)客户端接收完整的catalog日志
Puppet的工作细节分成如下几个步骤:
1、客户端puppetd调用facter,facter会探测出这台主机的一些变量如主机名、内存大小、IP地址等。然后puppetd把这些信息发送到服务器端。
2、服务器端的puppetmaster检测到客户端的主机名,然后会到manifest里面对应的node配置,然后对这段内容进行解析,facter送过来的信息可以作为变量进行处理的,node牵涉到的代码才解析,其它的代码不不解析,解析分几个过程:语法检查、然后会生成一个中间的伪代码,然后再把伪代码发给客户机。
3、客户端接收到伪代码之后就会执行,客户端再把执行结果发送给服务器。
4、服务器再把客户端的执行结果写入日志。
实验效果图:
三、基本安装
注意:要在安装软件以前先设置主机名,因为生成证书的时候要把主机名写入证书,如果证书生成好了再改主机名,就连不上,这是很多初学者遇到问题。每个客户端的证书要经过根证书签名才能和服务器连接。
系统配置:centos i386 5.x最小化安装+ Developtool
服务端ip: 192.168.10.1hostname:master.perofu.com
客户端ip: 192.168.10.3hostname:client.perofu.com
准备情况:ruby必须是1.8.5的,以上的不兼容。
一、 服务器端安装
1.
echo"192.168.10.1 master.perofu.com" >>/etc/hosts
echo "192.168.10.3 client.perofu.com">>/etc/hosts
2.
hostnamemaster.perofu.com
3.
vi/etc/sysconfig/network
HOSTNAME=master.perofu.com
4.安装ruby1.8.5,(1.8.6)不支持。如果需要查看帮助文档,才需要安装ruby-rdoc ruby-irb
yum -y install rubyruby-libs ruby-rdoc ruby-irb
5.安装NTP同步时间,统一master和client上的时间(每天凌晨5点10分同步time.nist.gov,并将 Linux 时间写入 BIOS时)
yum install ntp -y
chkconfig --level 35ntpd on
crontab -e
10 5 * * * root/usr/sbin/ntpdate time.nist.gov ; /sbin/hwclock �Cw
service crond restart
ntpdate pool.ntp.org;hwclock �Cw
6.安装facter
tar -axffacter-latest.tgz -C /usr/local/src/
cd/usr/local/src/facter-1.6.8/
ruby install.rb
7.安装puppet
tar -axfpuppet-2.6.3.tar.gz -C /usr/local/src/
cd/usr/local/src/puppet-2.6.3/
ruby install.rb
mkdir -p /etc/puppet/manifests
cp conf/auth.conf /etc/puppet/
cp conf/redhat/fileserver.conf /etc/puppet/
cp conf/redhat/puppet.conf /etc/puppet/
cp conf/redhat/server.init /etc/init.d/puppetmaster
chmod +x /etc/init.d/puppetmaster
chkconfig --add puppetmaster
chkconfig puppetmaster on
puppetmasterd --mkusers#生成pupput用户,#如出现错误,则执行groupadd puppet;useradd -g puppet puppet
mkdir -p /var/lib/puppet/rrd
chown puppet:puppet /var/lib/puppet/rrd
/etc/init.d/puppetmaster start#启动
netstat -anplt |gerp :8140#是否已启动
8.测试
②puppetca -s -a#为所有的客户端签证书,仅对某个客户端第一次使用
④vi /etc/puppet/manifests/site.pp
file {"/tmp/testfile":
ensure =>present,
mode => 644,
owner => root,
group => root
}
/etc/init.d/puppetmaster restart#第一次创建site.pp,必须重启
二、 客户端安装
1.
echo"192.168.10.1 master.perofu.com" >>/etc/hosts
echo "192.168.10.3 client.perofu.com">>/etc/hosts
2.
hostnameclient.perofu.com
3.
vi/etc/sysconfig/network
HOSTNAME=client.perofu.com
4.安装ruby1.8.5,(1.8.6)不支持。如果需要查看帮助文档,才需要安装ruby-rdoc ruby-irb
yum -y install rubyruby-libs ruby-rdoc ruby-irb
5.安装NTP同步时间,统一master和client上的时间(每天凌晨5点10分同步time.nist.gov,并将 Linux 时间写入 BIOS时)
yum install ntp -y
chkconfig --level 35 ntpd on
crontab -e
10 5 * * * root/usr/sbin/ntpdate time.nist.gov ; /sbin/hwclock �Cw
service crond restart
ntpdate pool.ntp.org; hwclock �Cw
6.安装facter
tar -axf facter-latest.tgz -C /usr/local/src/
cd /usr/local/src/facter-1.6.8/
ruby install.rb
7.安装puppet
tar -axf puppet-2.6.3.tar.gz -C /usr/local/src/
cd /usr/local/src/puppet-2.6.3/
ruby install.rb
mkdir -p /etc/puppet
cp conf/auth.conf /etc/puppet/
cp conf/namespaceauth.conf /etc/puppet/
cp conf/redhat/puppet.conf /etc/puppet/
cp conf/redhat/client.init /etc/init.d/puppet
chmod +x /etc/init.d/puppet
chkconfig --add puppet
chkconfig puppet on
puppetd --mkusers#如出现错误,则执行groupaddpuppet;useradd -g puppetpuppet
mkdir -p /var/lib/puppet/rrd
chown puppet:puppet /var/lib/puppet/rrd
/etc/init.d/puppet start
8.测试(服务器的第8步先执行)
①puppetd --test--server master.perofu.com#向服务器发送证书请求,等待服务器的签收
③puppetd --test--server master.perofu.com#签过之后,再次向服务器发起请求
出现以下内容,表示服务器和客户端能正常通讯
notice: Finishedcactlog run in 0.02 seconds
⑤puppetd --test--server master.perofu.com
#请求服务器的/etc/puppet/manifests/下的内容
⑥ll /tmp/testfile#如有,则表示测试成功
四、语法
1.puppet的代码主要就是由这些资源和资源的属性构成. 每个资源都包含有类型(type),标题(title)和一些其他属性的列表。
这是一个典型的resource(资源)的结构:
type { "title":
attribute=>"value",
...
attribute=> "value",
}
2.type是有规定的,并不是自己随便写的,在Puppet的官方文档里有所有可用的type:augeas、computer、cron、exec、file、filebucket、group、host、interface、k5login、macauthorization、
mailalias、maillist、mcx、mount、nagios_command、nagios_contact、nagios_contactgroup、nagios_host、nagios_hostdependency、nagios_hostescalation、nagios_hostextinfo、nagios_hostgroup、nagios_service、nagios_servicedependency、nagios_serviceescalation、nagios_serviceextinfo、nagios_servicegroup、nagios_timeperiod、notify、package、resource、router、schedule、selboolean、selmodule、service、ssh_auth、rized_key、sshkey、stage、tidy、user、vlan、yumrepo、zfs、zone、zpool
3.不同的title(标题),表示不同的资源;冒号前的那一部分,就是资源的标题(title);在标题后面,紧跟的是特定的Attributes(属性),属性用来告诉Puppet如何去配置资源。
4. 属性和值的结构:attribute =>"value",
5. 每一个属性键值对后面都要有一个逗号,最后一个属性键值对后面可以使用逗号也可以使用分号。
6. 如果一个资源只有一个属性,它可以被声明成一行,像这样:
file { "/etc/group": owner =>"root" }
7. 每个资源写多行的结构。多个资源可以被配置在一个资源类型里面如:
file {
"/etc/passwd":
ensure => present;
"/etc/group" :
owner => "root",
group => "root";
}
8. 首字母大写的资源类型永远是一个引用,而小写的是一个声明。由于资源只能被声明一次,所以重复两次相同的声明会导致错误。这是Puppet保证你的配置模块化的重要特性之一。
9.除了每个资源类型的属性外,Puppet同时具有称作元参数(Metaparameters)的全局属性。元参数可以与任何资源类型协作。
10.有时你需要为一组资源指定一个默认的参数值;Puppet使用一个首字母大写并且没有标题(title)的资源格式来实现这个功能。例如,在下面的例子中,我们将为所有的命令设定一个默认路径:
Exec { path =>"/usr/bin:/bin:/usr/sbin:/sbin" }
exec { "echo thisworks": }
这个代码段中的第一个声明为可执行(exec)资源提供了一个默认值;资源Exec需要一个绝对路径或者能够找到可执行程序的路径。这样减少了代码量,同时,在需要时这个路径能被单独的资源覆盖。通过这种方法,你可以为整个配置文件指定一个默认路径,然后在需要的时候覆盖掉这个值。
11.在Puppet中,资源的默认值对任何资源均生效。
12.类的关键字是class,它的内容由一对大括号包裹。下面这个例子创建了一个用于管理两个独立文件的类:
class unix {
file {
"/etc/passwd":
owner => "root",
group => "root",
mode=> 644;
"/etc/shadow":
owner => "root",
group => "root",
mode=> 440;
}
}
13.定义使用和类相同的基本形式,不同的是它们使用关键字define(而不是class),并且定义支持参数但不支持继承。就像之前所提到的,定义可以接收参数并在相同的系统上多次重用。比如我们可能会在一个系统内创建多个版本库,这里可以使用定义而不是类。下面是一个例子:
define svn_repo($path) {
exec { "/usr/bin/svnadmin create $path/$title":
unless => "/bin/test -d $path",
}
}
svn_repo { puppet_repo: path => "/var/svn_puppet" }
svn_repo { other_repo:path => "/var/svn_other" }
注意变量是怎么在定义中使用的。我们使用$符号表示变量。注意上面的变量$title。这里有一点专业知识,在Puppet0.22.3及以后的版本中,定义可以分别使用变量$title和$name表示标题和名字。默认情况下,$title和$name被设置成相同值,不过你可以设置一个标题值,同时将一个不同的名字值作为参数进行传递。$title和$name只在定义中生效,类或其他资源均不生效。
<一>变量和数组
1.变量
puppet用$符号定义变量,变量的内容。
声明格式:$变量名="值"
引用格式: ${变量名}
$test=" hello , guys "
file {"/tmp/ test " :
content => ${test};
}
2.数组
puppet利用方括号来定义数组,数组的内容由逗号分割,分别用双引号括起来
[ "apache2" , " httpd " , " ssh "]
<二>资源:
定义一个资源,需要指定资源的type(类型)和资源的title(标题)
这是一个典型的resource(资源)的结构:
type { "title1",…,"title":
attribute => "value",
...
attribute=> "value",
}
看一个例子:
file{
"/etc/passwd":
name =>"/etc/passd",
owner => root,
group => root,
mode => 644;
}
上面的代码让/etc/passwd的权限保持644,并且属于root用户和root用户组,file是指定资源的类型是"file"类型,第二行的"/etc/passwd"是资源的title, title的作用是让puppet能唯一标识这个资源。第三行的name指定了要对那个文件操作,默认情况下,name都等于title,所以很多时候name是可以省略的。这点要注意。看下面的例子:
file{
"sshdconfig":
name =>$operatingsystem ? {
solaris =>"/usr/local/etc/ssh/sshd_config",
default =>"/etc/ssh/sshd_config",
},
owner => root,
group => root,
mode => 644,
}
资源的title是sshdconfig,但是name却可以通过判定操作系统自己选择合适的值。这样,当其他的资源要依赖sshdconfig的时候,只需要说明依赖sshdconfig就行,不需要关心文件到底在什么路径下面。例如下面的代码:
service{"sshd":
subscribe =>File[sshdconfig],
}
指定了一个sshd的服务,这个服务如果发现文件资源sshdconfig 有变动,就会自己reload配置文件。是不是很方便呢?注意上面的subscribe后面的File,第一个字母要大写,定义资源关系的时候,这里的字母要大写。
通常,在puppet代码里面可能会定义很多相同的资源,可以用[]把所有资源的title写在一起,例如:
file{
["/etc/passwd","/etc/hosts"]:
owner => root,
group => root,
mode => 644;
}
你可能已经发现了,每次定义文件的时候如果都输入mode,owner,group会很繁琐,因此你可以在puppet的site.pp的开头定义资源的默认值。定义资源的默认值需要把资源的第一个资源大写。例如下面的代码让所有的file资源的mode是644,owner是root。
File{owner => root,mode => 644;}
默认值可以被后面的设置覆盖。
在puppet里面可以定义资源之间的关系,例如前面提到的,如果sshdconfig文件如果有修改,sshd服务就重启。puppet里面还有另一个资源关系,依赖。例如资源A依赖资源B,如果资源B不存在,资源A就不被执行。定义资源依赖的属性是requre 。例如:
file{
"/etc/apache2/port.conf":
content =>"80",
require =>Package["apache2"];
}
package{
"apache2":
ensure =>installed;
}
file资源设置port.conf的内容为80,但是在设置file资源之前,要求apache2这个软件包配置好了(即file资源依赖Package["apache2"])。
<三>资源的类型:
puppet的资源是由具体的系统管理任务抽象而来的,系统管理中遇到什么任务,就会有相应的资源类型,目前puppet支持的资源类型有:
file文件/目录的管理
package软件包的管理(yum install|remove)
service某服务的管理(start|restart|stop|status)
croncrontab定时任务的管理
user添加用户
group添加用户组
execshell命令
notify资源之间的通讯
<四>常用资源:
puppet管理不同的资源,是利用不同的provider来管理的。例如管理package资源,在debian上面是用的apt-get,在redhat上面是用的yum 。在这里,apt,yum就是provider。在定义资源的时候,可以明确指定provider。但是通常都是puppet自己探测。
file资源
file {"title":#一般是文件名
ensure =>present|absent|file|directory|link
content =>"content",#文件内容(必须有,否则,文件的内容为空)
user =>"username",
group =>"groupname",
mode => 权限,#四位八进制数
path =>"title",
source =>"puppet:///URL",
#指定到master上文件的绝对路径或agent上本地文件绝对路径
target =>#指定目标文件,用于ln -s $target $title
recurse => true,#递归
purge => true,#将不再资源中管理的其他数据,删除
force => true,#如不加,则不会删除
}
contentcontent => "字符串",
把文件的内容(title或name)设置为content 参数后面的字符串, 新行,tab,空格可用 escaped syntax 表示(必须有,否则,文件的内容为空,除非你不需要)
ensureensure => {absent|present|file|directory|link},
如果文件本来不存在是否要新建title名的文件,
present,检查该文件是否存在,如果不存在就新建title名的文件,
absent, 文件存在,就会删除title名的文件(如果recurse => true ,就会删除目录).
file, 不存在就新建title名的文件
directory, 不存在就新建title名的目录
groupgroup => {gid|组名},
指定那个该文件的用户组,值可以是gid或者组名
modemode =>{rwx},
mode用于设置文件的权限(数字)r=4,w=2,x=1
ownerowner => {用户名},
设置文件的属主
pathpath => "文件的路径",
指定要管理文件的路径,必须用引号引起来, 这也是一个资源的namevar ,通常path 等于资源的title
sourcesource => {"puppet:///URL"|"完整的文件路径"},
拷贝一个文件覆盖当前文件,用checksum来判断是否有必要进行复制,可以设置的值是一个引用master或agent的完整的文件路径,或者是URI,当前支持的URI只有puppet和file ; 这是一个对文件常用的操作,可以让puppet修改系统的配置文件.
backupbackup => {|.文件名|false},
决定文件的内容在被修改前是否进行备份. 利用filebucket对文件进行备份,按文件的md5sum进行归类,便于恢复文件的时候找到文件.可以把文件备份到 puppet 客户端,也可以通过设置backpup => bucket_name 把文件备份到网络上的其他机器. 如果backup的值是一个点号”.”开头的字符串,puppet会把文件备份在同一目录下,备份文件的扩展名就是 bakcup里面的那个字符串.如果设置backup => false , 该文件不做备份.
recurserecurse => { true|false|inf|remote},
设置是否以及如何进行递归操作,即可以管理子目录(recurse,purge和force连用,用于删除子目录中,不在资源控制的文件或目录)
purgepurge => {true|false},
将不再资源中管理的其他数据,则删除
forceforce => {ture|false},
force是否可以删除文件或目录,与ignore相反
ignoreignore => {文件名|正则表达式},
当用recursion 方法复制一个目录的时候,可以用ignore来设定过滤条件,符合过滤条件的文件不被复制或删除.使用ruby自带的匹配法则.因此shell级别的过滤表达式完全支持,例如[a-g]*, 与force相反
targettarget => {"源文件或目录"}
是为创建链接文件的,即将target的值,作为源文件,title的值,作为目标文件。如lin -s $target $title。当且仅当ensure => link,才可以使用
checksumchecksum => {md5|mtime|time|timestamp},
怎样检查文件是否被修改,这个状态用来在复制文件的时候使用, 这里有几种检测方式,包括md5,mtime,time,timestamp等.默认的检测是用md5,
linkslink => {follow|manage},
定义操作符合链接文件.文件拷贝的时候,
follow,会拷贝文件的内容,而不是只拷贝符合链接本身,
manage ,会拷贝符合链接本身.
recurselimitrecurselimit => {数字},
递归的深度,设置的值可以匹配/^[0-9]+$/.
replacereplace => {true|false},
是否覆盖已经存在的文件。可以设置的值是(true,yes),(false,no),注,true和yes一样,false和no是一样。
selrange文件内容是否属于SElinux哪个组成部分,只适于开启了Selinux的机器。
selrole文件所属的SeLinux 角色
seltype文件所属的Selinuxtype
seluser文件所属的Selinux user
sourceselect选择拷贝目录级别,默认,source是递归的。
type检查文件是否只读。
例:
file资源在puppet里面用的挺多,属性包括大家已经属性的owner,group,mode,content等等。file还有两个重要的命令,source和template.通常,一个文件的内容可以由content属性来包含固定的内容,但是也可以用source命令来从其他url复制文件内容。目前puppet只支持puppet这个url,表示从puppet的fileserver去下载文件内容。例如:
source =>"puppet://${fileserver}/lvs/${corp}.${idc}.keepalived.conf "
其中fileserver后面的lvs表示是lvs模块的files目录这个路径。正如前面提到的一样。用source就可以把很多配置文件放到puppet服务器端统一管理。
file资源的另一个template命令是一个功能强大的命令。利用template,可以通过erb模板生成文件内容,erb模板可以使用变量。而且还可以对变量进行计算和操作。这是puppet强大的地方,举一个例子,你配置两台squid服务器,两台服务器的内存不一样,那么在squid.conf里面有关内存的配置命令就要根据硬件配置来设置。在过去,你只能手工去判定和修改,现在puppet自己搞定。看下面的代码:
file {
"/etc/squid/squid.conf":
mode => 0644,
content =>template("squid/squid.conf.erb");
}
这里的template里面的"squid/squid.conf.erb"表示的路径是squid模块下面templates目录
下的squid.conf.erb这个路径。看看squid.conf.erb里面的部分内容1
cache_mem <%=Integer(vmx_memsize.to_i*0.45) -%> MB
visible_hostname <%=fqdn %>
在这里,cache_mem设置成总内存的45%大小,visible_hostname 设置成主机名。更多有
趣的功能也可以实现。
在使用puppet过程中,有时有个类需要使用到多个file 资源. 而这些资源有相同的属性,例如,用户和组相同,权限相同,你还是每次都照样写吗?这里给大家提供一个小的技巧.其实这里我们可以给file 设置默认的属性.就不用重复写了.大大地简化我们的代码.
例如:
File { ##这里的大写,表示默认属性,下面的两个文件,属主和组都会被设置为root且权限为644
ensure => "present",
owner => "root",
group => "root",
mode => 644,
}
file { "/etc/cobbler/modules.conf":
content => template("cobbler/modules.conf"),
}
file { "/etc/cobbler/dhcp.template":
content => template("cobbler/dhcp.template"),
}
其实我们可以再优化下写法,如下:
File{
ensure => "present",
owner => "root",
group => "root",
mode => 644,
}
file { "/etc/cobbler/modules.conf":
content => template("cobbler/modules.conf"),
"/etc/cobbler/dhcp.template":
content => template("cobbler/dhcp.template"),
}
package资源
1.package资源的属性
packae {"package name":
ensure => {present|absent|latest|version|purged},
#present只要存在即可,或installed
#absent删除(无依赖),当别的软件包依赖时,不可删除
#latest升级到最新版本
#version指定安装具体的某个版本号(yum list),格式:ensure => "版本号",
#purged删除该包,和依赖包(有风险,勿用)
name =>"package name",#应该省略,写到title部分即可
}
package资源管理系统的软件包安装,默认是yum源(/etc/yum.repo.d/)的,通过puppet来自动安装的软件包均是通过yum来安装的,所以需要配置好yum源,该资源的主要属性是ensure;设置该软件包应该在什么状态.installed 表示要安装该软件,也可以写成present;absent 表示反安装该软件,pureged 表示干净的移除该软件,latest 表示安装软件包的最新版本.
ensure=> {installed|absent|pureged|latest}
installed:title的软件必须安装好了
absent:title的软件是需要卸载的
pureged:title的软件是需要全部删除,深度卸载,删除所有配置文件和依赖包,有潜在风险,慎用
latest:title的软件是需要被更新到最新
2.package资源的应用
例如:
package{
["vim","iproute","x-window-system"]:
ensure => installed;
["pppoe","pppoe-conf"]:
ensure => absent;
}
安装vim等包,删除pppoe,pppoe-conf包。如果你的系统安装的是编译的软件包,建议你
打包成操作系统的包格式,建立你自己的软件仓库。
package {"screen":
ensure => latest,
}
Package { ensure => "installed" }#设置默认属性
package { "screen": } package { "strace": }package { "sudo": }
请注意,第一个P是大写,这就是意味着对软件包管理设置了一个全局的的参数,即如果没有提供参数,那么默认就是要installed,即安装
service资源
1.service资源的属性
service{"title":#title为服务名,mysqld
ensure =>{running|stopped},#当前service的状态
enable =>{true|false},#service是否开机启动
{status|start|stop|restart}=> "cmd ",
#指定命令的路径,当且仅当,启动脚本不在/etc/init.d/下的,才需要,一般用于源码包安装的,且命令不再/etc/init.d/下的
hasrestart =>{true|false},
#重启service的步骤,true->restart;false->stop,start
hasstatus =>{true|false},
#是从命令行查询还是从进程表(有没有该进程)中,查询service的状态
}
2.service资源的应用
service{"httpd":
ensure =>running,
start =>"/usr/local/apache2/bin/apachectl start",
}
service{"httpd":
ensure =>running,
restart=> "/usr/local/apache2/bin/apachectl restart",
hasrestart=> "true",
}
exec资源
exec {"title":
#一般写上要执行的命令,如不写,则需要指定command
cwd => "目录的绝对路径",
#在那个目录下执行,也可不要
path =>"/bin:/sbin:...",
#命令执行的搜索路径,如不要,则需指定命令的绝对路径
command => "执行的命令",
#写上要执行的命令,一般不需要,可在title中写上
creates => "文件名(绝对路径)",
#当且仅当,该文件名不存在,命令才被执行,可不要
user => "用户名",
#定义运行命令的用户。 注意如果你使用了这个参数,那么任何的错误输出不会在当下被捕捉,这是Ruby的一个bug
logoutput =>"true|false",
#是否记录输出,可取的值为:true,false和其他合法的日志等级。
onlyif => "命令",
#如onlyif中命令的执行结果为0(执行正确),才执行title或command中的目录,与unless相反
unless => "命令",
#如unless中命令的执行结果不为0(执行出错),才执行title或command中的目录,与onlyif相反
}
puppet执行外部命令,多次反复用这个方式执行命令是有威胁性的,因此建议对执行的命令进行加锁或者类似的处理.
建议每个exec资源的名字最好是唯一的.
建议属性值的目录、命令或文件,最好是绝对路径
puppet exec 参数介绍
command:command => "执行的命令",
将会被执行的命令,必须为被执行命令的绝对路径,或者得提供该命令的搜索路径。如果命令被成功执行,所有的输出会被记录在实例的正常(normal)日志里,但是如果命令执行失败(既返回值与我们所指定的不同),那么所有的输出会在错误(err)日志中被记录。这个是exec资源类型的名变量(namevar)。
creates:creates => "文件名(绝对路径)",
指定命令所生成的文件。如果提供了这个参数,那么命令只会在所指定的文件不存在的情况的被执行:
cwd:cwd => "目录的绝对路径",
指定命令执行的目录。如果目录不存在,则命令执行失败。
useruser => "用户名",
定义运行命令的用户。 注意如果你使用了这个参数,那么任何的错误输出不会在当下被捕捉,这是Ruby的一个bug。
If you are using Puppet tocreate this user, the exec will automatically require the user, as long as itis specified by name
onlyifonlyif => "命令",
如onlyif中命令的执行结果为0(执行正确),才执行title或command中的目录,与unless相反。例如:
exec { "logrotate":path => "/usr/bin:/usr/sbin:/bin", onlyif => "test `du/var/log/messages | cut -f1` -gt 100000" }
只有在test返回true的时候logrotate才会被运行。
需要注意的是onlyif定义的命令跟主命令遵循同样的规则,也就是说如果path没有被设置的话,需要使用绝对路径。
除此之外,onlyif还可以接受数组做为其值,例如:
onlyif =>["test -f /tmp/file1", "test -f /tmp/file2"]
上面的代码限定了只有在所有数组中的条件返回true时exec才会被执行。
unlessunless => "命令",
如unless中命令的执行结果不为0(执行出错),才执行title或command中的目录,与onlyif相反。例如:
exec {"/bin/echo root >> /usr/lib/cron/cron.allow":
path =>"/usr/bin:/usr/sbin:/bin",
unless => "grep root/usr/lib/cron/cron.allow 2>/dev/null"
}
上面这段代码先用grep在cron.allow文件(Solaris系统中)中找root,如果没有找到,就写入root。
要注意的是这个参数里的命令跟主命令遵循同样的规则,也就是说如果path没有被设置的话,需要使用绝对路径。
pathpath => "/bin:/sbin:...",
命令执行的搜索路径。如果path没有被定义,命令需要使用绝对路径。路径可以以数组或以冒号分隔的形式来定义。
logoutputlogoutput => "true|false",
是否记录输出。默认会根据exec资源的日志等级(loglevel) 来记录输出。若定义为on_failure,则仅在命令返回错误的时候记录输出。可取的值为:true,false和其他合法的日志等级。
env:
我们不建议使用这个参数,请使用‘environment’。这一部分还未完成。
environment
为命令设定额外的环境变量。要注意的是如果你用这个来设定PATH,那么PATH的属性会被覆盖。多个环境变量应该以数组的形式来设定。
group
定义运行命令的用户组。在不同的平台下的运行的结果无法确定,由于不同用户运行命令的时候,变量是不变的,所以这是平台的问题,而不是Ruby或Puppet的问题。
refresh
定义如何更新命令。当exec收到一个来自其他资源的事件时,默认只会重新执行一次命令。不过这个参数允许你定义更新时执行不同的命令。
refreshonly
该属性可以使命令变成仅刷新触发的,也就是说只有在一个依赖的对象被改变时,命令才会被执行。仅当命令与其他对象有依赖关系时,这个参数才有意义。当你要触发某个行为时,会显得很有用:
# Pull down the main aliases file file {"/etc/aliases": source =>"puppet://server/module/aliases" } # Rebuild the database, but onlywhen the file changes exec { newaliases: path => ["/usr/bin","/usr/sbin"], subscribe => File["/etc/aliases"],refreshonly => true }
要注意的是只有subscribe和notify可以促发行为,而不是require,所以在使用refreshonly时,只有同时使用subscribe或notify才有意义。有效的值为true, false。
returns
指定返回的代码。如果被执行的命令返回了其他的代码,一个错误(error)会被返回。默认值是0,可以定义为一个由可以接受的返回代码组成的数组或单值。
timeouttimeout => "秒数",
命令运行的最长时间。如果命令运行的时间超过了timeout定义的时间,那么这个命令就会被终止,并作为运行失败处理。当定义为负值时就会取消运行时间的限制。timeout的值是以秒为单位的。
exec资源在不到万不得已的时候不要去用,简单说来exec资源就是在执行puppet的时
候,调用shell执行一条shell语句,例如:
exec {"delete config":
path => "/bin:/usr/bin",
command => "rm /etc/ssh/ssh_config";
}
exec可以用path指定命令执行的预搜索路径,create属性表明该exec将创建一个文件,当下一次puppet执行的时候,如果发现了这个文件,就不再执行这个exec资源。
exec资源是不太好掌控的资源,如果能用脚本实现,尽量写成脚本通过file资源分发到服务器上面。然后用其他的方式来调用脚本。例如crontab。说来crontab资源,罗嗦一句,虽然puppet提供了crontab资源,但是你完全可以用file资源来把crontab任务放到/etc/cron.d目录下来实现crontab资源的管理。使用puppet的时候,尽量用最简单的语法,越是花哨的语法也越容易出错。
cron资源
1.cron资源的属性
minute hour month monthday weekday command
cron {"title":
ensure =>{present|absent},
#决定该计划任务的目标状态,present 如该cron不存在,则添加之;absent 如该cron已存在,则删除之
command => "命令",#欲执行的命令或脚本路径,也可不写,默认是title
user => "用户",#执行该cron的用户身份
minute => "值",#同crontab,不写,代表*
hour => "值",
month => "值",
monthday => "值",
weekday => "值",
}
除了用户和command两个参数以外,其他的参数都是可选项.
ensure ensure => {present|absent},
决定该计划任务的目标状态,
present 如该cron不存在,则添加;
absent 如该cron已存在,则删除之
commandcommand => "命令",
欲执行的命令或脚本路径,也可不写,默认是title
useruser => "用户",
把该crontab加到那个用户的crontab列表,默认是运行puppet的用户
minuteminute => "值",
运行crontab的分钟,可设置成0-59,同crontab,不写,代表*
hourhour => "值",
运行crontab的小时,可设置成0-23
monthdaymonthday => "值",
一个月份中的日子,1-31
monthmonth => "值",
设置crontab运行的月份,1-12
weekdaweekday => "值",
运行crontab的星期数,0-7,周日是为0或7.
2.cron资源的应用
cron {"logrotate":
command => “/usr/sbin/logrotate”,
user => root,
hour => 2,
minute => 0
}
上述例子执行的结果就是在crontab中增加了一行:
crontab �Ce �Cu root
0 2 * * * /usr/sbin/logrotate
user资源
1.user资源的属性
user {"title":#用户名,同name一样
name => "用户名",#用户名,可以不写,默认与title一样
ensure => {present|absent},
#指定用户的目标状态present该用户不存在则生成;absent 该用户存在则删除
uid => "值",
gid => "值",
groups => "组名",#groups该用户所属用户组名,可为列表
home => "绝对路径",
#该用户分配的家目录,如没有managehome =>true, 则不会创建家目录
managehome => {true|false},
#是否自动生成家目录,默认为false,表示,即使指定home,也不会创建;true表示创建home的目录
shell => "shell的绝对路径",#用户登录后默认分配的shell
password => '密钥',
#指定用户的结过MD5加密码后的密码(grub-md5-crypt)。最好以‘’引起来,记着,用单引号,因为经过MD5加密后可能会出$符号。
}
namename => "用户名",
指定创建的用户名,可以不写,默认与title一样
ensureensure =>{present|absent},
指定用户的目标状态。
present 该用户不存在则生成;
absent 该用户存在则删除
uiduid => "值",
用户的uid
gidgid => "值",
用户的gid
groupsgroups => "组名",
groups该用户所属用户组名,可为列表
homehome => "绝对路径",
该用户分配的家目录,如没有managehome => true, 则不会创建家目录
managehomemanagehome => {true|false},
是否自动生成家目录,默认为false,表示,即使指定home,也不会创建;true表示创建home的目录
shellshell => "shell的绝对路径",
用户登录后默认分配的shell
passwordpassword => '密钥',
指定用户的结过MD5加密码后的密码(grub-md5-crypt)。最好以‘’引起来,记着,用单引号,因为经过MD5加密后可能会出$符号。
2.user资源的应用
①.我要创建一个名为test的用户.
代码示例
user { "test":
uid => 2000,
gid => 2000,
home => "/home/test",
shell => "/bin/bash";
}
注意:puppet在创建用户的时候,并不会创建用户的home目录。
②.比如test用户离职了,需要删除test用户。
代码示例:
user { "test":
ensure => "absent",
}
③. 创建test用户并管理用户目录
代码示例
user { "test":
ensure => "present",
managehome => true,
}
④.创建test用户,且让test用户是属于sa组。
代码示例
user { "test":
ensure => "present",
managehome => true,
groups => sa,
}
注意,用户所属的SA组要先创建。
关于如何创建组,在接下来的会为大家讲,这里给举个例子.
代码示例:
group { "sa":
ensure => "present",
gid => 3000,
}
⑥
user {
"test":
ensure=> present,
shell =>"/bin/bash",
home=> "/home/test",
managehome => true,
comment =>"www.mysqlops.com test by sky addtest",
password => '$1$Nnh0M0$t9s7Bbwx2fFer6IP/QGdA0',
}
使用grub-md5-crypt生成密码
总结:用户和密码跟系统管理员关系密切,管理用户的切不可马虎。方便快捷,实用的管理才是王道,今天运维自动化管理
<二>引用资源
当我们引用一个资源时,需要大写资源类型的首字母,例如File[sshdconfig]。当看到一个大写的资源类型,需要明白那实际上就是一个资源引用。小写字母是用来定义的。资源只能被定义一次,重复定义相同的资源会导致错误。
1.资源的引用:
基本格式:Type ["title",…, "title"]
注:Type:表示资源的类型,且第一个字母必须大写
title:表示该资源的title,多个title,可用逗号分隔,必须存在
常用于require => Type ["title",…,"title"]
表示require之后的引用,必须先存在或正确执行
2.例子
①file { 'sshdconfig':
path => $operatingsystem ? {
solaris => '/usr/local/etc/ssh/sshd_config',
default => '/etc/ssh/sshd_config',
},
owner => 'root',
group => 'root',
mode=> '0644',
}
service { 'sshd':
subscribe => File['sshdconfig'],
}
<五>资源默认值
为某类资源指定一个默认的参数。使用没有标题的大写资源首字母方式。
格式:Type { 属性 => 值,…, 属性 =>值}
注:Type:表示资源的类型,且第一个字母必须大写,不需要title
例如,为所有的执行命令设置默认的path参数:
Exec { path => '/usr/bin:/bin:/usr/sbin:/sbin' }
exec { 'echo this works': }
第一行代码为exec资源提供一个默认的path参数。exec资源需要一个完整的限定路径或者能够通过path参数找到具体的可执行文件。资源定义时若有必要可以覆盖path的默认设置。这种情况下我们可以为所有的配置指定一个默认path参数,特殊情况时覆盖它就可以了。
Puppet默认值支持所有的资源类型。
默认值不是全局的 - 它只在当前范围和当前范围下面有效。如果你想为所有的配置设置默认值,唯一选择是在任意类的外面定义它们。
<六>资源之间相互关系
①puppet 资源之间简单依赖关系
puppet 资源之间简单依赖关系,主要是有下面的三个参数。
before:在某个资源之前执行
after: 在某个资源之后执行
require: 某个资源必须存在或者正确执行后,才执行相应的资源。常用
为了便于理解,下面就给大家看个代码示例:
代码示例:
file{"/etc/ssh/sshd_config":
.....
require =>Package["openssh-server"],#引用package {"openssh-server":…}
}
package { “openssh-server”:
……
before => File["/etc/ssh/sshd_config"],
}
上面例 子我们可以知道:/etc/ssh/sshd_config必须在openssh-server这个软件包安装后,才会生成。
after和before相反,就不举例说明了。那大家会有个问题,那/etc/ssh/sshd_config有改动的时候,那要怎么办呢,接下来就为大家介绍,puppet资源之间触发更新动作。
class admin::ntp {
package {"ntp":
ensure =>installed,
require =>File["/etc/ntp.conf"],
}
service {"ntp":
ensure=> running,
require =>Package["ntp"],
}
file {"/etc/ntp.conf":
source=>"puppet:///modules/admin/ntp.conf",
notify=> Service["ntp"],
require => Package["ntp"],
}
}
Package["ntp"] ->File["/etc/ntp.conf"] ~> Service["ntp"]
②puppet 资源之间触发更新
puppet资源之间触发更新主要是由下面二个参数。在执行Services,Exec资源中非常有用。
notify:用来通知某个资源进行更新。
subscribe:该资源有更新时,通知另一个资源执行相应的动作。
其实这两个效果参数效果是一样的。接下来看下代码示例,更好的理解上面的两个参数。
file{"/etc/ssh/sshd_config":
.....
notify =>Service[sshd],
}
service{"sshd":
......
subscribe=> File["/etc/ssh/sshd_config"],
}
上面的例子,我们可以看到,当/etc/ssh/sshd_config文件发生更新时,就会通知sshd服务,进行reload.
大家可以接合上面的两个例子来看,可以看到整个流程是这样的,sshd服务运行,需要/etc/ssh/sshd_config配置文件,而/etc/ssh/sshd_config是要由openssh-server软件包安装来生成。
<七>类(class)
类可以把多个相关的资源定义在一起,组成一个类,一起使用,例如把sshd和他的配置文件做成一个ssh类,其他的地方要用到就直接包含ssh类就可以了,方便写出更简洁的代
码,便于维护。类可以继承。少用
class 是一个资源的集合,它代表节点上一个单独的配置项目,SSH服务或者NFS包,class在一个结点上仅仅形成1次(即在相同的系统上仅能使用一次),因为已经完成的配置应该仅仅存在一次
1.定义类
类定义以class关键字开始,内容放在花括号里面
class 类名 {
type { "title ":
attribute =>"value",
...
attribute => "value",
}
...(各种资源)
type { "title ":
attribute =>"value",
...
attribute => "value",
}
}
2.继承
类也有用基类(父类)和子类来实现简单的继承和覆盖的模型,一个子类可以继承基类的值并且能够实现它一个或者多个值对基类的值进行覆盖.
子类的作用:完成某个任务,基本同父类的作用,但只是某些参数的不同
class 类名(新建)inherits 父类名(已存在){
Type["title"]{attribute =>"value",}
}
注:Type:表示资源的类型,且第一个字母必须大写,且必须存在于父类中
{attribute => "value",}:表示修改父类的某些属性的值
①{attribute => "value",…, attribute=> "value",}表示修改父类的某些属性的值
②{attribute=> undef,…,attribute=> undef,}表示取消父类的某个属性的值
③{attribute+> "value"}
{[attribute +>"value",…, attribute +> "value",]}表示增加父类没有的某些属性的值或者中概括([])中的数组值
3. 禁用资源
为继承和覆盖最常见的用法是禁止服务或者其它资源:
class apache::disabled inherits apache {
Service["apache2"] {
enable => false,
ensure => stopped,
}
}
4.实例
①class unix {
file { "/etc/password":
owner => "root",
group => "root",
mode=> 644;
}
file{ "/etc/shadow":
owner => "root",
group => "root",
mode=> 440;
}
}
②
class freebsd inherits unix {
File["/etc/passwd"] { group => undef }
}
在上面的例子中,包含类unix的节点的password文件的组名将被设置为“wheel”,而包含类freebsd的节点的password文件的组名则不会被设置(既保持原来的值,不去修改)。
③
你可以同时覆盖多个值,比如:
class freebsd inherits unix {
File["/etc/passwd","/etc/shadow"] { group =>wheel }
}
④
可以使用操作符‘+>’(‘再赋值’)来追加资源的参数:
class apache {
service { "apache": require => Package["httpd"] }
}
class apache-ssl inherits apache {
#host certificate is required for SSL to function
Service[apache] { require +> File["apache.pem"] }
}
上面的例子中使第二个类依赖了所有第一个类所依赖的包,同时增加对包'apache.pem'的依赖。
当追加多个依赖时,使用中括号和逗号:
class apache {
service { "apache": require => Package["httpd"] }
}
class apache-ssl inherits apache {
Service[apache] { require +> [ File["apache.pem"],File["/etc/httpd/conf/httpd.conf"] ] }
}
⑤
class myclass {
class nested {
file { "/etc/passwd":
owner => "root",
group => "root",
mode => 644;
}
}
}
class anotherclass {
include myclass::nested
}
在这个例子中,在外层类中的嵌套类可以通过在名为anotherclass的类中以myclass::nested包括进来。在这里顺序很重要,如果要让这个例子正确的运行的话,myclass类一定要在anotherclass类被求值之前被求值。
<八>函数(定义)
定义使用和类相同的基本形式,不同的是它们使用关键字define(而不是class),并且定义支持参数但不支持继承。就像之前所提到的,定义可以接收参数并在相同的系统上多次重用
注:使用define最好在定义时,使用变量,负责就可以使用class
1.无变量
①define 函数名() {
type{ "title ":
attribute => "value",
...
attribute=> "value",
}
...(各种资源)
type{ "title ":
attribute => "value",
...
attribute=> "value",
}
}
②有变量
define 函数名(变量名1,...,变量名n) {#格式:$var
type{ "title ":
attribute => "变量名",#格式:${var},下同
...
attribute=> "变量名",
}
...(各种资源)
type{ "title ":
attribute => "变量名",
...
attribute=> "变量名",
}
}
③在class中定义define
class 类名 {
define 函数名 (变量名1,...,变量名n) {
type{ "title ":
attribute => "变量名",#格式:${var},下同
...
attribute=> "变量名",
}
...(各种资源)
type{ "title ":
attribute => "变量名",
...
attribute=> "变量名",
}
}
}
2.引用define
①一般情况(无class)
函数名 {
变量名 => "值",
...
变量名 => "值",
}
②class
类名::函数名 {
变量名 => "值",
...
变量名 => "值",
}
3.实例
①定义了一个definition 用来执行一个脚本去配置一个新的主机.
define newip ( $ip ) {
exec { "/sbin/ifconfig" $title $ip ":
}
}
#引用
newip { eth0:
ip => "11.11.11.11"
}
#或
newip(11.11.11.11) {"eth0":
}
我们创建了一个definition叫做newip并且有一个参数叫做$ip,内部定义了我们用来执行资源类型内部二进制代码,这里是ifconfig 命令,我们已经指出变量$ip和使用的另一个变量$title($name)
②同上,不过使用了类class
class virtuals {
define newip ( $ip ) {
exec { "/sbin/ifconfig $title$ip":
}
}
}
virtuals::newip { eth0:
ip => "11.11.11.11",
}
③声明多个参数列表,以逗号分割:
define webapp( $domain, $path, $platform ){
...
}
webapp { "mywizzoapp":
domain=>"mywizzoapp.com",
path=>"/var/www/apps/mywizzoapp",
platform => "Rails",
}
④创建了一个函数(define),并只添加了一个参数,参数又
是实例的名字.但是,我们也可以添加任何我们想要的参数,因此我们只要在函数
中进行声明.
define tmpfile( $greeting ) {
file { "/tmp/$name":
content => $greeting,
}
}
在声明实例资源的时候,并传递相应的变量值.
tmpfile{ "foo": greeting =>"Hello, world" }
⑤为每一个参数指定一个默认的值
define config_file(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,
source => "puppet:///$source"
}
}
config_file { "/etc/vnc.conf":
source => "vnc/vnc.conf",
mode => "0640"
}
我们创建了config_file定义, 然后对其应用,这跟函数调用差不多
类和定义的创建过程都很相似(虽然类不接收参数),不过他们使用起来非常不同。
定义是用来定义在一个主机上包含多个实例的可重用对象的,所以定义不能包含任何只能有一个实例的资源。比如,多次使用同一个定义不能创建相同的文件。
另一方面,类是独一无二的――无论你包含它们多少次,你只会得到资源的一个副本。
大多数时候,服务会被定义成类,服务的包,配置文件以及正在运行的服务都会被定义在类中,因为通常在每一个主机上它们都只有一个副本。(这些有时被惯称为“服务――包――文件”)。
定义是被用来管理类似虚拟主机这样可以有许多的资源,或者使用一个可重用的封装来包装一些简单的信息,以此来减少代码量。
<十>模块
如果是你配置的应用,守护进程,或函数包含很多类、文件或模板。最简单的方法就是将这些资源放到一个包里面,Modules使管理配置文件的集合更简单和更结构化。
使用模块的步骤:
1.创建必要的模块目录及文件
mkdir -p /etc/puppet/modules/模块名/{manifests,files,templates}
touch/etc/puppet/modules/模块名/manifests/init.pp
2.在init.pp中导入别的pp文件(也可直接在init.pp中写上puppet的语法)
vi/etc/puppet/modules/模块名/manifests/init.pp
import"*"#相当于node … { import模块名}
3.写上puppet的语法,完成某些功能,最好是单独写,利于管理
vi 模块名.pp
4.根据“模块名.pp”的内容,考虑是否需要在files目录下创建,所需的文件。
5.定义一个节点,并使用新的模块(即/etc/puppet/modules/模块名
vi/etc/puppet/manifests/site.pp
node 节点名 {
include模块名
}
注:
/etc/puppet/modules/模块名/manifests:存放该模块的所有配置文件,格式:文件名.pp
/etc/puppet/modules/模块名/files:存放该模块中所需要的文件
/etc/puppet/modules/模块名/templates:存放模板文件的目录,文件格式:文件名.erb
/etc/puppet/modules/模块名/manifests/init.pp:init.pp文件是该模块的初始文件,导入一个模块的时候,会从init.pp开始执行。可以把所有的代码都写到init.pp里面,也可以分成多个pp文件,init再去包含其他文件(import "*.pp")
Modules组织起来很简单。他们存储在一个指明的文件夹下,modulepath的配置在puppet.conf中指出,默认在$confdir/modules和 /usr/share/puppet/modules目录,我们可以指出多个module路径用冒号隔开,如:
modulepath = /etc/uppet/modules:/usr/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语句,限制文件访问在所有的模块中。
最重要的一件事可以去做,那就是把puppet 代码组织成模块,以更易于维护并使puppet的代码结构更加清晰。一个模块可以是一个简单的分组相关的东西:例如:web服务器模块可以包括一个web服务器必需要的一切:Apache的配置文件,虚拟主机模板,和必要的puppet代码去部署这些。
分离成模块,使得代码更容易重新使用和共享代码,也是最合乎逻辑的方式组织代码。在这个例子中,我们将创建一个模块用来管理memcached.memcache是一个内存缓存系统与web应用程序常用在一起。
1.明确你的模块路径,模块路径是在puppet.conf里设置,默认值是/etc/puppet/modules.如果你的代码已经使用的版本控制系统,像之前我建议你的方式去做,然后使用你的工作副本目录用来部署到/etc/puppet/moudules/,并替代原目录。
memcached实例
# puppet --genconfig |grep modulepath
modulepath =/etc/puppet/modules:/usr/share/puppet/modules
# cd /etc/puppet/modules
2. 创建memcached目录:
# mkdir memcached
3. 在memcached目录下创manifests和files两个目录:
# cd memcached
# mkdir manifests files
4. 在manifests目录,创建init.pp文件,init.pp文件内容如下:
vi manifests/init.pp
import "*"
5. 在manifests目录,创建另外一个文件名称为memcached.pp,其内容如下:
class memcached {
package { "memcached":
ensure => installed,
}
file { "/etc/memcached.conf":
source =>"puppet:///modules/memcached/memcached.conf",
}
service { "memcached":
ensure => running,
enable => true,
require => [Package["memcached"],
File["/etc/memcached.conf"] ],
}
}
6. 切换到files目录,创建memcached.conf文件,其内容如下:
-m 64
-p 11211
-u nobody
-l 127.0.0.1
7. 定义一个节点,并使用新的模块(即/etc/puppet/modules/memcached)。
node cookbook {
include memcached
}
8.运行puppet 测试新的配置
# puppet agent --test
info: Retrieving plugin
info: Caching catalog forcookbook.bitfieldconsulting.com
info: Applying configuration version'1300361964'
notice:/Stage[main]/Memcached/Package[memcached]/ensure: ensure
changed 'purged' to 'present'
...
info:/Stage[main]/Memcached/File[/etc/memcached.conf]:
Filebucketed /etc/memcached.conf to puppetwith sum a977521922a151
c959ac953712840803
notice:/Stage[main]/Memcached/File[/etc/memcached.conf]/content:
content changed'{md5}a977521922a151c959ac953712840803' to '{md5}f
5c0bb01a24a5b3b86926c7b067ea6ba'
notice: Finished catalog run in 20.68seconds
# service memcached status
* memcached is running
puppet 会自动寻找并自动加载init.pp这个文件,这是模块在所有导入的类的时候必须的,
在我们的例子:
import "*"
memcache类是定义在memcached.pp这个文件里,这将是由 init.pp加载。现在的话,我们在结点上执行类:
include memcached
在memached类中,我们提到了memcached.conf文件:
file { "/etc/memcached.conf":
source =>"puppet:///modules/memcached/memcached.conf",
}
正如我们在puppet的文件服务器中自定义挂载点里所看到的片断。上面的source参数告诉puppet所要寻找的源文件路径。
MODULEPATH/
memcached/files/memcached.conf
学习着去热爱模块,因为他们让你管理puppet的生活会轻松许多,模块并不复杂。然而,实践和经验会帮助我们判断何时应该组织划分为模块,以及如何更好的安排你的模块结构,这里有一些帮助你正确上路的技巧。
模板
模板作为模块的一部分,如果你需要使用模板,可以把模板放到MODULE_NAME/templates目录下,用法可以参考这样:
file { "/etc/memcached.conf":
content =>template("memcached/memcached.conf"),
}
puppet 会在下面路径寻找文件:
MODULEPATH/memcached/templates/memcached.conf
Facts,functions(函数),types(类型)和providers
模块可以包含自定义的Facts,自定义函数,和自定义类型以及providers.如需有关这些的详细信息,请参阅外部工具和puppet的生态系统(ecosystem)
使用标准的命名约定
给模块和类取个合适且简单明了的名称是非常有用的,尤其是别人要维护你的代码的时候,或者有其他人需要阅读并使用你的代码工作的时候.
1. puppet的模块名以他们所管理的软件或者服务名:例如:apache或者haproxy
2. 以模块的功能或者所提供的服务来命名类名,例如:apache::vhost或者rails::dependencies.
译者说明 apache就是所提供的服务,vhost是功能,中间以::分格.
3.如果模块内提供禁止提供某服务 ,就可以命名为disabled.例如:一个用于停止apache的类应该被命令为
apache::disabled.
译者说明:比如iptables服务,有时需要开启,或者需要关闭,那么就分成两个类, iptables::disabled和iptables::enable.
4. 如果一个节点需要提供多种服务,请在节点定义定义后为每个服务执行所需要的类,或者导入模块.
node server014 inherits server {
include puppet::server
include mail::server
include repo::gem
include repo::apt
include zabbix
}
5.管理用户的模块应该命名为user.
6.在用户模块里,声明你的虚拟用户类名为user::virtual
7.在用户模块里,为特定的用户群体可以设置为子类,子类应该被命名为用户组.举例,
user::sysadmins或者user::contractors.
8.如果你需要覆盖特定节点上的一个类或者服务,可以使用继承,继承类的话前缀是子类的
名称.举例,如果一个节点名称为cartman 需要特定的ssh配置,你想覆盖ssh类,可以这样做:
class cartman_ssh inherits ssh {
[ override config here ]
}
9.当你需要运行puppet为不同的服务布署配置文件时,完整的配置文件名以服务开头,使用点为分隔符,
后面为文件功能.举例:
Apache的初始化脚本:apache.init
Rails的snippet的定时处理日志配置文件:rails.logrotate
mywizzoapp的Nginx的虚拟主机配置文件:mywizzoapp.vhost.nginx
standalone 服务的mysql配置文件:standalone.mysql
<十一>模板
puppet管理的时候,我们会根据业务进行划分,将其分为如web、db、memcache等,为了方便统一管理,这个时候,在puppet实战中我们就可以使用模板,比如上面,有web类型,db类型的,memcache类型的,新业务上线的时候,这时候运维自动化,运用puppet就非常有意义,我们使用pupet模板,可以节约我们很多时间和精力,又大大地减少犯错的几率的。接下来就为大家介绍,puppet管理中什么是模板,如何使用模板,运用模板,这些都是实际生产中积累。
1.puppet进阶之ERB基础
puppet管理中,puppet通过erb(embedded Ruby)支持模板,模板是为资源设置统一的格式,puppet 模板主要是用于文本文件,且模板的文件名必须以erb结尾,例如nginx配置文件等.
①. puppet管理中如何在模板中使用变量?
答:使用这种格式<%= name %>,变量name必须先定义.
②. puppet管理默认模板是路径是?
答:默认是/var/lib/puppet/templates.
③. puppet管理中如何查找当前模板路径
答:可以使用如下命令:puppet�Cconfigprinttemplatedir
④. puppet实战中puppet模板是如何存放?
答:一般都是放在模块名下的templates目录下。例如模块名nginx,那么模板一般会放在nginx/templates下。
puppet管理apache的虚拟主机模板创建实例
1. 创建模块名,以及templates,files目录。
mkdir -p/etc/puppet/modules/apache/{manifests,templates,files}
2. 创建/etc/puppet/modules/apache/manifests/init.pp内容为import “*”.
echo 'import "*" '>/etc/puppet/modules/apache/manifests/init.pp
3. 创建/etc/puppet/modules/apache/manifests/apache.pp,类名为apache
class apache {
package { "http":
ensure => present,
}
services { "http":
......
require =>Package["http"],
}
define apache_conf ($sitedomain) {
file { "httpd_conf":
......
content =>template("apache/httpd.conf.erb"),
require =>package["httpd"],
}
}
}
5. 创建/etc/puppet/modules/apache/templates/httpd.conf.erb
(1)复制一个标准的httpd.conf为模板。
cp httpd.conf/etc/puppet/modules/apache/templates/httpd.conf.erb
(2)根所实际情况修改/etc/puppet/modules/apache/templates/httpd.conf.erb
以下是贴出部分以供示例:
<VirtualHost *:80>
ServerName <%= sitedomain %>
ServerAdmin admin@<%= sitedomain %>
DocumentRoot /var/www/<%= sitedomain%>
ErrorLog logs/<%= sitedomain%>-error_log
CustomLog logs/<%= sitedomain%>-access_log common
</VirtualHost>
6. 检查模板是否有错误
erb -x -T '-'/etc/puppet/modules/apache/templates/httpd.conf.erb | ruby -c
7. 测试,使用一个node ,名为c2.inanu.net 进行测试
node 'c2.inanu.net' {
import"apache"#可省略,在init.pp中,已有import “*”
include "apache"
apache::apache_conf { "inanu":
sitedomain =>"inanu.net",
}
}
8. 在客户端运行puppet �Ctest �Cserverpuppetmaster
puppet --test --server c1.inanu.net
注意第 7 步中 apache::apache_conf,我在虚拟机里测试的时候,需要添加的类名,但在实际生产中不需要添加,可能是版本差异,另外下面几点需要注意下:
1. puppet 模板中,也可以使用数组,这时常用的循环遍历。
示例:
$values = [val1, val2, otherval]
<% values.each do |val| -%>
Some stuff with <%= val %>
<% end -%>
2. puppet模板中也可以使用条件语句
示例:
<% if broadcast != "NONE"%> broadcast <%= broadcast %> <% end %>
3. 模板也可以作为变量
示例:
$template = 'apache/vhost.erb'
4. 测试变量是否定义
示例:
<% if has_variable?("myvar") then%>
myvar has <%= myvar %> value
<% end %>
5. 变量的作用域
<%= scope.lookupvar('apache::user')%>
puppet 管理使用模板总结
puppet 管理过程中会遇到各种问题,而puppet 模板是方便,快捷,容易处理,这样,我们在puppet 运维自动化过程充分利用puppet 模板减少出错的机会的,puppet模板上面介绍的比较少,在写的过程中可以使用ERB测试别忘记,要按照puppet指南规范代码。更多可以参阅官方:http://docs.puppetlabs.com/guides/templating.html
<十二>节点
node(节点)的作用:区分不同的客户端,并且给不同的服务端分配manifest。
1.习惯:
实际生成中,不建议直接在site.pp中定义node,因为当node过多的时候,不利于阅读和理解,建议创建一个名叫nodes.pp,专门用来存放node信息的
vi/etc/puppet/manifests/site.pp
import "nodes.pp"
#建立节点文件
touch /etc/puppet/manifests/nodes.pp
2.节点的定义:
①默认节点的定义:表示没有定义的节点都是使用default。default的作用可以在多台nodes(节点) 需要执行相同的操作的时候,省去多次书写node(节点名称),带来方便。
node default{
变量的声明#声明格式:$变量名="值"引用格式: ${变量名}
include 类名,...,类名#已定义好的类class
}
②客户端节点的定义:
node '客户端的主机名' {
变量的声明#声明格式:$变量名="值"引用格式: ${变量名}
include 类名,...,类名#已定义好的类class
}
3.节点的继承(不讲,同类的继承inherits)
4.实例;
node 'test01.test.com' {
file {"/tmp/temp1.txt" :
content=> "hello world.
died,java."
}
}
<十三>使vim支持puppet的pp文件的语法
1.在用户根目录创建目录
mkdir -p~/.vim/{syntax,ftdetect}
2.创建puppet.vim文件
vim~/.vim/syntax/puppet.vim
" puppetsyntax file
" Filename:puppet.vim
" Language:puppet configuration file
" Copied fromthe cfengine, ruby, and perl syntax files
" For version5.x: Clear all syntax items
" For version6.x: Quit when a syntax file was already loaded
if version <600
syntax clear
elseifexists("b:current_syntax")
finish
endif
syn regionpuppetDefine start="^\s*\(class\|define\|site\|node\)"end="{" contains=puppetDefType,puppetDefName,puppetDefArguments
syn keywordpuppetDefType class define site node inherits contained
syn keywordpuppetInherits inherits contained
syn regionpuppetDefArguments start="(" end=")"contains=puppetArgument
syn matchpuppetArgument "\w\+" contained
syn matchpuppetArgument "\$\w\+" contained
syn matchpuppetArgument "'[^']+'" contained
syn matchpuppetArgument '"[^"]+"' contained
syn matchpuppetDefName "\w\+" contained
syn matchpuppetInstance "\w\+\s*{"contains=puppetTypeBrace,puppetTypeName,puppetTypeDefault
syn matchpuppetTypeBrace "{" contained
syn matchpuppetTypeName "[a-z]\w*" contained
syn matchpuppetTypeDefault "[A-Z]\w*" contained
syn matchpuppetParam "\w\+\s*=>" contains=puppetTypeRArrow,puppetParamName
syn matchpuppetParamRArrow "=>" contained
syn matchpuppetParamName "\w\+" contained
syn matchpuppetVariable "$\w\+"
syn matchpuppetVariable "${\w\+}"
syn matchpuppetParen "("
syn matchpuppetParen ")"
syn matchpuppetBrace "{"
syn matchpuppetBrace "}"
syn regionpuppetString start=+"+ skip=+\\\\\|\\"+ end=+"+contains=puppetVariable
syn keywordpuppetBoolean true false
syn keywordpuppetKeyword import inherits include
syn keywordpuppetControl case default
" commentslast overriding everything else
syn matchpuppetComment "\s*#.*$" contains=puppetTodo
syn keywordpuppetTodo TODO NOTE FIXME XXX contained
" Define thedefault highlighting.
" For version5.7 and earlier: only when not done already
" For version5.8 and later: only when an item doesn't have highlighting yet
if version >=508 || !exists("did_puppet_syn_inits")
if version <508
letdid_puppet_syn_inits = 1
command -nargs=+HiLink hi link <args>
else
command -nargs=+HiLink hi def link <args>
endif
HiLinkpuppetVariable Identifier
HiLinkpuppetBoolean Boolean
HiLink puppetTypeIdentifier
HiLinkpuppetDefault Identifier
HiLinkpuppetKeyword Define
HiLink puppetTypeDefsDefine
HiLinkpuppetComment Comment
HiLinkpuppetString String
HiLink puppetTodoTodo
" HiLinkpuppetBrace Delimiter
" HiLinkpuppetTypeBrace Delimiter
" HiLinkpuppetParen Delimiter
HiLinkpuppetDelimiter Delimiter
HiLink puppetControlStatement
HiLinkpuppetDefType Define
HiLinkpuppetDefName Type
HiLinkpuppetTypeName Statement
HiLinkpuppetTypeDefault Type
HiLinkpuppetParamName Identifier
HiLinkpuppetArgument Identifier
delcommand HiLink
endif
let b:current_syntax= "puppet"
3.创建ftdetect/puppet.vim
vim~/.vim/ftdetect/puppet.vim
auBufRead,BufNewFile *.pp setfiletype puppet
4.测试
vim/etc/puppet/manifests/site.pp
五、配置、管理、使用
puppet命令
1.puppet.comf文件详解
①[main]命名空间选项(全局配置):
confdir 配置文件目录,默认在/etc/puppet
vardir 动态数据目录,默认在/var/lib/puppet
logdir 日志目录,默认在/var/log/log
rundir puppet PID目录,默认在/var/run/puppet
statedir state目录,默认在$vardir/state
statefile state文件,默认在$statedir/state.yaml
ssldir SSL证书目录,默认在$vardir/ssl
trace 发生错误时显示跟踪信息,默认false
filetimeout 检测配置文件状态改变的时间周期,单位秒,默认15秒
syslogfacility 指定syslog功能为user级,默认为daemon级
②[puppetmasterd]命名空间选项(服务器的配置):
user 后台进程执行的用户
group 后台进程执行的组
mainfestdir mainfests文件存储目录,默认为$confdir/mainfests
mainfest mainfest站点文件的名字,默认为site.pp
bindaddress 后台进程绑定的网卡地址接口
masterport 后台进程执行的端口,默认为8140
③[puppet]命名空间选项(客户端的配置):
server puppet puppet服务器名,默认为puppet
runinterval seconds puppet应用配置的时间间隔,默认1800秒(0.5小时)
puppetdlockfie file puppet lock文件位置,默认$statedir/puppetdlock
puppetport port 后台进程执行的端口,默认8139
2.文件服务配置文件(fileserver.conf)的详解:
[files]
path /var/lib/puppet/files
allow 121.14.1.*
allow 60.28.228.0/24
allow *.house.sina.com.cn
deny *.sina.com.cn
path定义文件存放路径,通过allow/deny来控制访问权限。
3. puppet命令集(建议使用puppet 类型参数)
①puppet 用于执行用户所写独立的mainfests文件
语法:
puppet [-h|--help][-V|--version] [-d|--debug] [-v|--verbose][--detailed-exitcodes] [-l|--logdest]
用法:
# puppet -l /tmp/manifest.log manifest.pp
②puppetd 运行在被管理主机上的客户端程序
语法:
puppetd[-D|--daemonize|--no-daemonize][-d|--debug] [--disable] [--enable][-h|--help] [--fqdn ] [-l|--logdestsyslog||console][-o|--onetime] [--serve ][-t|--test][--noop][-V|--version] [-v|--verbose][-w|--waitforcert ]
用法:
# puppetd �Cserver puppet.domain.com
③puppetmasterd 运行在管理机上的服务器程序
语法:
puppetmasterd[-D|--daemonize|--no-daemonize][-d|--debug] [-h|--help][-l|--logdest |console|syslog] [--nobucket] [--nonodes][-v|--verbose][-V|--version]
用法:
# puppetmasterd
④puppetca puppet认证程序
语法:
puppetca[-a|--all] [-h|--help][-V|--version] [-d|--debug] [-v|--verbose] [-g|--generate] [-l|--list][-s|--sign] [-r|--revoke][-p|--print] [-c|--clean] [--verify] [host]
用法:
# puppetca �Cl
# puppetca -s culain.madstop.com
#puppetca -a -s#为所有的客户端进行认证
⑤puppetrun 用于连接客户端,强制运行在本地配置文件下
语法:
puppetrun [-a|--all][-c|--class ] [-d|--debug] [-f|--foreground] [-h|--help] [--host ] [--no-fqdn][--ignoreschedules] [-t|--tag ] [--test] [-p|--ping]
用法:
# puppetrun -p 10 �Chost host1 �Chost host2 -t remotefile -twebserver
⑥filebucket 客户端用于发送文件到puppet file bucket的工具
语法:
filebucket [-h|--help][-V|--version] [-d|--debug] [-v|--verbose] [-l|--local] [-r|--remote][-s|--server ] [-b|--bucket ] …
用法:
# filebucket -b /tmp/filebucket /my/file
⑦ralsh 转换配置信息到puppet配置代码
语法:
ralsh [-h|--help] [-d|--debug] [-v|--verbose] [-e|--edit] [-H|--host] [-p|--param] [-t|--types] type
用法:
# ralsh user luke
user {
‘luke’:
home => ‘/home/luke’,
uid => ‘100′,
ensure => ‘present’,
comment => ‘Luke Kanies,,,’,
gid => ‘1000′,
shell => ‘/bin/bash’,
groups => ['sysadmin','audio','video','puppet']
}
⑧puppetdoc 打印puppet参考文档
语法:
puppetdoc [-a|--all] [-h|--help] [-o|--outputdir ] [-m|--mode ][-r|--reference <[type]|configuration|..>] [manifest-file]
用法:
# puppetdoc -r type > /tmp/type_reference.rst
# puppetdoc �Coutputdir /tmp/rdoc �Cmode rdoc /path/to/manifests
# puppetdoc /etc/puppet/manifests/site.pp
附:我的puppetmaster的配置文件
[main]
vardir= /var/puppet
logdir =/var/log/puppet
rundir =/var/run/puppet
ssldir = $vardir/ssl
confdir = /etc/puppet
[puppetd]
classfile =$vardir/classes.txt
# Where puppetdcaches the local configuration.An
# extensionindicating the cache format is added automatically.
# The default valueis '$confdir/localconfig'.
localconfig =$vardir/localconfig
[puppetmasterd]
environment =production
reports =store,log,mydefine
logdir =/var/log/puppet
modulepath =/etc/puppet/modules:/usr/share/puppet/modules
config =/etc/puppet/puppet.conf
manifestdir =/etc/puppet/manifests
manifest = /etc/puppet/manifests/site.pp
fileserverconfig =/etc/puppet/fileserver.conf
templatedir =/etc/puppet/templates
reportdir =/var/lib/puppet/reports
autosign=true#自动认证客户端
clientyamldir =/var/lib/puppet/client_yaml
客户端的配置文件
[main]
vardir = /var/lib/puppet
logdir =/var/log/puppet
rundir =/var/run/puppet
ssldir = $vardir/ssl
[puppetd]
server =pu.server.com
authconfig =/etc/puppet/namespaceauth.conf
classfile =$vardir/classes.txt
localconfig =$vardir/localconfig
puppetdlog =/var/log/puppet/puppetd.log
runinterval = 600
report = true#这个默认是false如果不填写就没有报表了!
reportserver =pu.server.com
常用命令:
1.#puppetmasterd --genconfig >/etc/puppet/puppet.conf.default
2.#puppetmasterd -d -v --no-daemonize
3.#puppetd -v -d --no-daemonize