前序:关于puppet
0.1puppet的工作原理:
Puppet 的工作细节分成如下几个步骤:
1、客户端puppetd 调用facter,facter 会探测出这台主机的一些变量如主机名、内存大小、IP 地址等。然后puppetd 把这些信息发送到服务器端。
2、服务器端的puppetmaster 检测到客户端的主机名,然后会到manifest 里面对应的node 配置,然后对这段内容进行解析facter 送过来的信息可以作为变量进行处理的,node 牵涉到的代码才解析,其它的代码不不解析,解析分几个过程:语法检查、然后会生成一个中间的伪代码,然后再把伪代码发给客户机。
3、客户端接收到伪代码之后就会执行,客户端再把执行结果发送给服务器。
4、服务器再把客户端的执行结果写入日志。
0.2设计架构
puppet是基于c/s架构的。服务器端保存着所有对客户端服务器的配置代码,在puppet里面叫做manifest。客户端下载manifest之后,可以根据manifest对服务器进行配置,例如软件包管理,用户管理和文件管理等等。
puppet工作过程中有两点值得注意。第一,为了保证安全,client和master之间是基于ssl和证书的,只有经master证书认证的client可以与master通信;第二,puppet会让系统保持在你所期望的某种状态并一直维持下去,如检测某个文件并保证其一直存在,保证ssh服务始终开启,如果文件被删除了或者ssh服务被关闭了,puppet下次执行时(默认30分钟),会重新创建该文件或者启动ssh服务。

第一章:puppet的安装

1.1准备工作
先设置好主机名,因为puppet的服务端和客户端的认证是靠主机来生成认证文件的,所以主机名的设置很重要。本实验主机名设置如下:
服务端:server.puppet.com
客户端:client.puppet.com
然后要调整客户端和服务端时间同步,可以在服务端建立ntp服务端,然后让客户端做时间同步,我是直接设置的日期,相差不会太就可以。
接着要设置hosts文件,要在服务端和客户端都设置,本实验设置如下:
服务端和客户端的host文件一样,vim /etc/hosts:
192.168.0.236 server.puppet.com
192.168.0.234 clietn.puppet.com

1.2安装puppet服务端和客户端
下边开始安装puppet的客户端和服务端。可以用yum安装,可以源码安装,这两个安装的方法我都测试了,但是客户端和服务端版本之间的兼容没有测试。尽量用一样的版本吧。先介绍用yum安装的方法:
centos的官方软件库里面不包含puppet包,但是在epel项目里面有包含puppet包. epel 是一个对rhel软件仓库的扩展,把一些有用的,但是rhel库没包含的软件收集在一起做成的一个软件仓库. 因此首先在centos上面安装epel:
rpm –Uvh http://download.fedora.redhat.com/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm
接着可以直接用安装puppet,
服务端:yum install puppet-server
客户端:yum install puppet
再简单说一下编译安装:
因为puppet是有ruby语言写的,所以要先安装ruby。这个直接可以yum安装。
yum install ruby
源码包安装的话服务端和客户端的安装操作是一样的,把puppet的客户端和服务端都安装了进去,只是在在启用服务的时候启用的不一样,服务端启用的是puppetmaster,客户端启用的是puppetd。接着先在下边的地址上下载相应的源码包,包括facter和puppet。http://projects.reductivelabs.com/projects/puppet/wiki/Downloading_Puppet
本实验下载的是puppet-0.25.2.tar.gz.tar.gz和puppet-0.25.2.tar.gz。
tar zxvf facter-1.5.8.tar.gz
cd puppet-0.25.2.tar.gz
ruby install.rb

再安装puppet
tar zxvf puppet-0.25.2.tar.gz
cd puppet-0.25.2.tar.gz
ruby install.rb

错误总结:
第一次认证的时候报下边的错误:
[root@client ~]# puppetd --server server1.puppet.com --test
err: Could not retrieve catalog from remote server: certificate verify failed
我做测试发现的原因有三种:
第一:是hostname没有设置好,在安装前一定要把hostname设置好,设置好之后尽量重启机器。实在不行就删了重新安装。
第二:时间不同步,时间不同步也会报认证失败的错误,可以用date设置时间,只要不是差得太多就没事。
第三:是ssl的问题,在你用的这个客户端puppet已经做过其他的机器的客户端的情况下,因为已经生成的有证书,可能会和现在的冲突,把/var/lib/puppet/ssl这个文件夹删掉之后就行了。

1.3配置c/s模式的puppet的实验环境
Puppet的的客户端和服务端是靠ssl链接的,在服务端有一个自签名的根证书,在安装软件的时候自动生成。每个客户端的证书要经过根证书签名才能和服务器连接。所以首先要在客户端执行下面的命令来请求服务器签名证书。
puppet --server server.puppet.com --test
执行上面的命令,客户端将生成证书,并且把证书签名请求发到服务器端。登录到服务
器端,执行下面的命令查看是否有客户端的证书请求:
puppetca –list 这个查看没有签名的证书
client.puppet.com
如果看到了客户端的证书请求,用下面的命令对所有证书请求签名:
puppetca –s client.puppet.com
或者puppetca -s –a 这条命令是对所有为签名的请求签名。
签过名之后可以看到:
[root@server ~]# puppetca --list --all
+ client.puppet.com (B2:9D:E5:CB:7A:4A:09:CD:33:44:25:BF:1B:C3:FA:56)
+ server.puppet.com (8F:FE:EE:AB:1E:E8:BA:52:7F:30:59:6A:94:84:05:87)
如果出现这些就说明签名完成。
服务端:
[root@server ~]# md5sum /var/lib/puppet/ssl/ca/signed/client.puppet.com.pem
f833cbbd129456d3e4df5daec0b612b8 /var/lib/puppet/ssl/ca/signed/client.puppet.com.pem
客户端:
[root@client ~]# md5sum /var/lib/puppet/ssl/certs/client.puppet.com.pem
f833cbbd129456d3e4df5daec0b612b8 /var/lib/puppet/ssl/certs/client.puppet.com.pem
上边揭黑部分的两串代码相同,说明客户端和服务端的认证签名完成。
注意:
如果想重新生成签名可以先在服务端用puppetca –clean client.puppet.com清除服务端的证书,然后在客户端删除/var/lib/puppet这个文件夹,删除之后再认证的话可以重新生成这个文件夹。

1.4puppet客户端和服务端的启动和调试
服务端的启动:
Yum安装的:service puppetmaster start
编译安装的:service puppetmasterd start
或者 /usr/bin/ruby /usr/sbin/puppetmasterd
客户端启动:service puppet start
如果服务端和客户端按上述方法启动,每个半个小时,会进行一次自动同步。

下边说调试的时候用到的两个命令:
服务端:puppetmasterd -d --no-daemonize -v –trace 这个命令可以看到服务端的和客户端同步的信息,注意的是用这个的时候puppetmaster是没开的。
客户端:puppet --server server.puppet.com –test 用这个命令来同步服务端对客户端的配置

第二章:puppet的使用方法

2.1:资源
资源属性
before
用于控制不同对象(资源)的执行顺序关系,表示某个对象(资源)在另一个对象之后发生(require与之相反,它表示之前发生)。
subscribe
检测某个资源,当它发生变化时,该资源会重新加载。需要注意的是,目前支持subscribe的资源只有exec,service和mount。
 

name
指定了要对那个文件操作,默认情况下,name都等于title,所以很多时候name是可以省略的。但是name却可以通过判定操作系统自己选择合适的值。看下边的例子:

  1. file { 
  2. "proftp": 
  3. name => "/usr/local/etc/proftpd.conf", 
  4. source => "puppet://$fileserver/proftp/proftpd.conf", 
这样的话,别的资源在引用的时候就可以可以直接用proftp,不用写长长的路径了。

alias
给资源的title一个别名,这样在引用的时候就可以直接用alias后边的名字,对一些别名比较长的,代码可以简化很多。看下边的例子:

  1. file { 
  2. "/usr/local/etc/proftpd.conf": 
  3. source => "puppet://$fileserver/proftp/proftpd.conf", 
  4. alias => "proftp" 

 

资源:
常用的资源主要有以下几个:
file:文件管理
package:软件包管理
service:系统服务管理
cron:配置定时任务
exec:运行shell命令
定义一个资源,需要指定资源的类型和title。看下例:
 

  1. file { 
  2. “/etc/password”: 
  3. name => “/etc/passwd”, 
  4. owner => root, 
  5. group => root, 
  6. mode => 644; 
  7. 上边代码:file是指定资源的类型,第二行的”/etc/password”是资源的title,title的作用是让puppet能唯一识别这个资源。后边的三个就不用说了。 

下一个例子:

  1. file { 
  2. "/etc/ssh/sshd_config": 
  3. mode => 0644, owner => root , group => root, 
  4. source => puppet://$fileserver/ssh/sshd_config ", 
  5. Service { 
  6. “sshd”: 
  7. subscribe => File[/etc/sshd/sshdconfig], 


上边代码第二行和第三行已经解释过了,第四行soucrce的意思是指定文件的来源,指定的来源是puppet://$fileserver/ssh/sshd_config.sarge或者puppet://$fileserver/ssh/sshd_config.etch,$fileserver这个变量应该是事先定义好的,一般是在主配置文件的site.pp中定义,后边的ssh是在/etc/puppet/fileserver.conf中定义的。下边的service也是作为一种资源,subscribe检测某资源,当它发生变化的时候,该资源会重新加载。后边的file是引用上边的,file的第一个字母必须要大写。

再看一个例子:

  1. package { "ntp": 
  2. ensure => installed, 
  3. service { "ntpd": 
  4. ensure => "running", 
  5. require => Package["ntp"], 
上边的例子第一部分是说安装ntp这个包,用的yum安装的,下边的require是说这个服务依赖于ntp包的安装。

2.2类
类的作用是把一组资源收集在一个盒子里面,一起使用,例如把sshd和他的配置文
件做成一个ssh类:
 

  1. class sshd { 
  2. file { 
  3. "/etc/ssh/sshd_config": 
  4. mode => 0644, owner => root , group => root, 
  5. source => "puppet://$fileserver/ssh/sshd_config", 
  6.  
  7. service { "sshd": 
  8. ensure => running, 
  9. subscribe => File["/etc/ssh/sshd_config"], 


这样在调用的时候就可以很方便。
2.3节点:
puppet如何区分不同的客户端,并且给不同的服务端分配manifest呢?puppet使用node资源做这件事情,node 后面跟客户端的主机名,例如:

  1. node 'client.puppet.com' { 
  2. include ntp,vsftpd,sshd 


2.4变量和数组
puppet也和其他语言一样,支持变量和数组,puppet用$符号定义变量,变量的内容
用双引号括起来。例如:
$fileserver = "server.puppet.com"
puppet利用方括号来定义数组,数组的内容由逗号分割,例如下面的例子:

  1. [ "apache2" , " httpd " , " ssh " ] 

2.5模块
一个模块就是一个/etc/puppet/modules目录下面的一个目录和它的子目录,在puppet的主文件site.pp里面用
import “modulename”
通过引入模块,可以结构化代码,便于分享和管理,比如关于apache的所有配置都写到apache模块下面。一个模块目录下面通常包括三个目录:files, manifests, templates。
manifests 里面必须要包括一个init.pp的文件,这是该模块的初始(入口)文件,导入一个模块的时候,会从init.pp开始执行。可以把所有的代码都写到init.pp里面,也可以分成多个pp文件,init 再去包含其他文件。
files目录是该模块的文件发布目录,puppet提供一个文件分发机制,类似rsync的模块。
templates 目录包含erb模型文件,这个和file资源的template属性有关。
puppet安装好以后,modules目录是没有的,自己建立一个就行,然后在里面可以新增加你的模块。
2.6 puppet配置文件结构树参考

1、所有的配置文件最后都要被主site.pp引用。
主线是:1 class目录下的类.pp=>服务的主配置文件init.pp=>引用模块的mudule.pp
2 node文件夹下服务器分组node节点.pp(例如C.pp)=>node文件夹下site.pp
3 主配置site.pp把1的mudule.pp 和2的node文件夹下site.pp 去“ import”。
2、但是site.pp,不是唯一的,我们可以建无数的site.pp,需要在/etc/puppet/manifests目录下

2.7编程实例:
根据这个思路,我们来建立一个ssh模块。

mkdir –p /etc/puppet/modules/sshd/manifests
mkdir –p /etc/puppet/modules/sshd/files

vim /etc/puppet/ fileserver.conf

  1. [ssh] 
  2. path /etc/puppet/modules/sshd/files 
  3. allow 192.168.0.0/24 
  4.  
  5. vim /etc/puppet/modules/sshd/manifests/init.pp 
  6. class sshd { 
  7. file { 
  8. "/etc/ssh/sshd_config": 
  9. mode => 0644, owner => root , group => root, 
  10. source => "puppet://$fileserver/ssh/sshd_config", 
  11.  
  12. service { "sshd": 
  13. ensure => running, 
  14. subscribe => File["/etc/ssh/sshd_config"], 

cp /etc/ssh/sshd_config /etc/puppet/modules/sshd/files/

vim /etc/puppet/manifests/site.pp

  1. $fileserver = "server.puppet.com" 
  2. import "modules.pp" 
  3. import "nodes.pp" 
  4.  
  5. vim /etc/puppet/manifests/modules.pp 
  6. import "sshd" 
  7.  
  8. vim /etc/puppet/manifests/nodes.pp 
  9. node 'client.puppet.com' { 
  10. include sshd 


注意:
使用节点的时候,尽量把所有的配置写成类,节点里面定义好变量和包含相应的类就可以了。保证代码的简洁。


对file类型title的测试:

  1. file { 
  2. "/var/proftp": 
  3. recurse => "true" 递归检索 
  4. source => "puppet://$fileserver/proftp/proftpd/autoproftp", 


如果source后边跟的是文件,那么执行之后客户端上这个文件的名字就是title。
如果source后边跟的是目录,要拷贝目录下的内容,应该加上recurse => "true",的结果是把把source指定的目录下的所有内容,拷贝到title目录下的。如果不用
recurse => "true"这个,客户端只会增加一个title这样的目录,里边没有内容。
如果客户端有title这目录或者文件的话,和服务端的source冲突的话,执行结果是不拷贝source源的内容到客户端。