博文纲要:
一、基础原理介绍;
二、单机模式安装;
三、资源定义说明及应用;
四、语法详细阐述;
五、Master/Agent安装;
一、基础原理介绍
Puppet是开源的基于Ruby的系统配置管理工具依赖于C/S的部署架构。有两种使用环境单机使用、master/agent这两种环境在配置文件上没有区别可能在运行机制上有着一定的区别的。
puppet是一个IT基础设施自动化管理工具它能够帮助系统管理员管理基础设施的整个生命周期供应provisioning与cobbler中不一样这里是保证软件包安装的、配置configuration、联动orchestration及报告reporting。也就是说它可以帮助系统管理员更加方便的完成多台服务器的安装升级软件包、管理配置文件、系统服务、cron任务、添加新的配置、修复错误等重复工作和快速部署关键性的应用以及在本地或云端完成自主管理变更和快速扩展架构规模等。
puppet2.7.0之前遵循GPL协议是基于ruby语言开发的也就是说在部署时需要部署ruby环境但是在puppet2.7.0以后不再使用GPL协议而是使用apache 2.0 license开源协定。
puppet对于系统管理员是抽象的只依赖于ruby与facter进行工作所以说对于管理员只要能够配置好ruby的运行环境能够定制或者根据facter来检索各节点特性来定制好一个配置管理或者说配置文件就可以了。
puppet能够管理多达40多种的资源比如file、user、group、host、package、service、cron、exec、yum repo等等所以说适合管理整个软件生命周期。
puppet的工作模型puppet通过声明性、基于模型的方法进行IT自动化管理
定义通过puppet的声明性配置语言定义基础设置配置的目标状态
模拟强制应用改变的配置之前先进行模拟性的应用
强制自动、强制应用部署达成目标状态纠正任何偏离的配置
报告报告当下状态及目标状态的不同以及达成目标状态所进行的任何强制性改变
puppet的抽象模型三种模型
配置语言层有自己独立的配置语言;
事务层用来管理各资源之间有依赖关系的资源应用时彼此之间谁先谁后等联动的
资源抽象层用来定义管理的资源对象的并且将每一个应该定义的资源属性抽象出来而且提供了基本的实例化接口。
puppet从以下三个维度来对资源完成抽象(1)相似的资源被抽象成同一种资源类型如程序包资源、用户资源及服务资源等(2)将资源属性或状态的描述与其实现方式剥离开来如仅说明安装一个程序包而不用关心其具体是通过yum、pkgadd、ports、或者其他方式实现(3)仅描述资源的目标状态也即期望其实现的结果而不是其具体过程如“确定nginx运行起来”而不是具体描述为“运行nginx命令将其启动起来”这三个也被称作puppet的资源抽象层。
模块中包含类、文件、模板、使用样例模块是通用的调用时只能调用其中的类所以也就是说每个模块就是导入一大堆的类以及部分类所依赖的外部文件。
>>问题什么是模块 >>答案模块就是包含了一大堆.pp结尾的文件的目录。
>>问题怎么调用其中的类 >>答案定义节点每个节点上定义其要应用的类。
>>问题资源和类定义的什么地方 >>答案首先说明这个地方不是位置是保存至文件中还是数据库中。资源必须只能定义在文件中而这个定义时所在的文件叫做资源清单manifest而这些清单文件通常都以.pp结尾。
二、单机安装熟悉应用
###########首先有些系统上装有puppet将其卸载重新安装############
[root@node ~]# rpm -q puppet #查看是否安装程序
puppet-2.6.18-3.el6.noarch #版本太老已被废弃
[root@node ~]# rpm -e puppet #卸载
warning: /etc/puppet/puppet.conf saved as /etc/puppet/puppet.conf.rpmsave #默认保留了配置文件
[root@node ~]# rm -rf /etc/puppet/ #将其目录删除
###########然后下载两个程序文件#######################
puppet-2.7.23-1.el6.noarch.rpm
facter-1.7.3-1.el6.x86_64.rpm
###########升级faster#################################
[root@node ~]# rpm -Uvh facter-1.7.3-1.el6.x86_64.rpm
###########安装puppet#################################
[root@node ~]# yum -y localinstall puppet-2.7.23-1.el6.noarch.rpm
三、资源定义说明和应用
<一>定义
puppet的代码主要就是由这些资源和资源的属性构成. 每个资源都包含有类型、标题和一些其他属性的列表。
这是一个典型的resource资源的结构
type {'title':
attribute => value,
}
(1)、type定义资源的类型使用花括号标起来它是有规定的并不是自己随便写的在puppet的官方文档中有所有的可用type。
(2)、title定义标题标题可以使用单引号或双引号标记起来不同的title表示不同的资源。
(3)、attribute定义参数属性每一个参数都有一个值是用来告诉puppet怎样去配置资源的。
(4)、属性和值得结构attribute => 'value'。
(5)、每一个属性键值对后面都要有一个逗号最后一个键值对后面可以使用逗号也可以不使用。
(6)、如果一个资源只有一个属性它可以被声明成一行像这样:file { "/etc/group": owner =>"root" }
(7)、多个资源可以被配置在一个资源类型里面如
file {
"/etc/passwd":
ensure => present;
"/etc/group" :
owner => "root",
group => "root";
}
<二>应用
查看常见资源类型
1
[root@node ~]# puppet describe -l
查看资源的定义格式
1
[root@node ~]# puppet describe user
常见的资源类型的定义方式
1、使用puppet定义通知
#########查询此资源定义方式###############
[root@node ~]# puppet describe notify
#########定义资源###########################
[root@node puppets]# vim notify.pp
notify {'notice':
message => "hell puppet",
}
#########本地执行查看资源定义结果#########
[root@node puppets]# puppet apply notify.pp
notice: hell puppet #显示信息
notice: /Stage[main]//Notify[notice]/message: defined 'message' as 'hell puppet' #生成的报告
notice: Finished catalog run in 0.05 seconds #执行过程所用时间
2、使用puppet安装软件
package资源管理系统的软件包安装默认是yum源/etc/yum.repo.d/的通过puppet来自动安装的软件包均是通过yum来安装的所以需要配置好yum源,该资源的主要属性是ensure;设置该软件包应该在什么状态. installed 表示要安装该软件。
package常见定义方式
packae {"package name":
ensure => {installed|absent|latest|version|purged},
#installed 只要存在即可或present
#absent 删除无依赖当别的软件包依赖时不可删除
#latest 升级到最新版本
#version 指定安装具体的某个版本号yum list格式ensure => "版本号",
#purged 删除该包和依赖包(有风险勿用)
name => "package name", #应该省略写到title部分即可
}
实例演示
#############定义资源###############
[root@node puppets]# vim package.pp
package {'nginx':
ensure => installed,
}
############本地执行##############
[root@node puppets]# puppet apply package.pp
notice: /Stage[main]//Package[nginx]/ensure: created #安装成功
notice: Finished catalog run in 33.93 seconds #使用时间
############查看安装结果##########
[root@node puppets]# rpm -q nginx
nginx-1.0.15-5.el6.x86_64
3、使用puppet定义服务启动服务
service常见定义方式
service {"title": #title为服务名,nginx
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的状态
}
实例演示
##############卸载################
[root@node puppets]# rpm -e nginx #卸载nginx
#############定义资源#############
[root@node puppets]# vim package.pp
package {'nginx':
ensure => installed,
}
service {'nginx': #同一类资源不可重名不同资源可以使用同一名称
ensure => true,
}
#############本地执行############
[root@node puppets]# puppet apply package.pp
notice: /Stage[main]//Package[nginx]/ensure: created
notice: /Stage[main]//Service[nginx]/ensure: ensure changed 'stopped' to 'running'
notice: Finished catalog run in 31.77 seconds
#############查看监听端口########
[root@node puppets]# ss -tanl
LISTEN 0 128 *:80 *:*
小扩展如果这里没有定义资源package直接定义service若软件没有安装会报错由此可知资源间存在依赖关系那如何定义资源间的依赖关系可使用资源引用如何进行资源引用格式为类型['资源名称']注意在定义时类型的首字母必须要大写
4、定义资源间依赖关系
#######两个关键字分开定义########
before before => 资源引用 #后面的资源引用之前当前资源必须存在
require require => 资源引用 #后面的资源定义过当前资源才可启用
######示例###########################
[root@node puppets]# vim package.pp
package {'nginx':
ensure => installed,
}
service {'nginx':
ensure => true,
require => Package['nginx'],
}
############手动停止服务############
[root@node puppets]# service nginx stop
Stopping nginx: [ OK ]
############执行####################
[root@node puppets]# puppet apply package.pp
notice: /Stage[main]//Service[nginx]/ensure: ensure changed 'stopped' to 'running'
notice: Finished catalog run in 0.83 seconds
[root@node puppets]# rpm -e nginx #卸载软件
[root@node puppets]# puppet apply package.pp
notice: /Stage[main]//Package[nginx]/ensure: created
notice: /Stage[main]//Service[nginx]/ensure: ensure changed 'stopped' to 'running'
notice: Finished catalog run in 10.83 seconds
5、使用puppet定义组
group常用的定义方式
group {"title": #组名同name一样
name => "用户名", #组名可以不写默认与title一样
ensure => {present|absent}, #指定组的目标状态present 该组不存在则生成;absent 该组存在则删除
gid => "值", #定义组的ID
}
实例演示
[root@node puppets]# vim group.pp
group {'zhao':
ensure => 'present',
gid => 1001,
}
[root@node puppets]# puppet apply group.pp
notice: /Stage[main]//Group[zhao]/ensure: created #已创建
notice: Finished catalog run in 0.38 seconds
[root@node puppets]# tail -2 /etc/group #查看结果
nginx:x:492:
zhao:x:1001: #由此可看出若不定义用户名(name)会默认使用标题名
6、使用puppet定义用户
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 => '密钥', #指定用户的密码而密码只能使用密文加密方式在用户定义中密码可以通过任意的加密方式获取加密串字符串要用引号引起否则会将其中的$当做变量名引用的。最好以‘’引起来记着用单引号。
}
实例演示
###########获取密码串#############
[root@node puppets]# openssl passwd -1 -salt `openssl rand -hex 4`
Password:redhat(#默认不显示)
$1$c7d1a18e$Z2NuEe3zPmaJ9iVJt3xOg. #随机生成的密码串
[root@node puppets]# vim group.pp
group {'zhao':
ensure => 'present',
gid => 1001,
}
user {'zhao':
gid => 1001, #指明基本组
uid => 1001, #指定用户ID号
groups => ['zhang','li'], #定义附加组使用数组格式
home => '/home/zhao', #定义家目录位置不指定不会自动创建
managehome => true, #创建家目录默认为false
ensure => present,
require => Group['zhao']
password => '$1$c7d1a18e$Z2NuEe3zPmaJ9iVJt3xOg.', #添加密码串
}
##############本地执行#############
[root@node puppets]# puppet apply group.pp
notice: /Stage[main]//User[zhao]/ensure: created #用户创建成功
notice: /Stage[main]//User[zhao]/password: changed password #密码添加成功
notice: Finished catalog run in 0.58 seconds
##########查看定义的用户信息#######
[root@node puppets]# id zhao
uid=1001(zhao) gid=1001(zhao) groups=1001(zhao),500(li),501(zhang)
[root@node puppets]# ls /home #查看home文件
zhao
[zhao@node ~]$ ls #另起终端登录测试
7、使用puppet定义file资源
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, #如不加则不会删除
}
################详细参数解析###########
content content => "字符串", #把文件的内容(title或name)设置为content 参数后面的字符串, 新行,tab,空格可用 escaped syntax 表示必须有否则文件的内容为空除非你不需要
ensure ensure => {absent|present|file|directory|link}, #如果文件本来不存在是否要新建title名的文件其中present 检查该文件是否存在,如果不存在就新建title名的文件absent 文件存在,就会删除title名的文件(如果recurse => true ,就会删除目录)file 不存在就新建title名的文件directory 不存在就新建title名的目录
group group => {gid|组名}, #指定那个该文件的用户组,值可以是gid或者组名
mode mode => {rwx}, # mode用于设置文件的权限
owner owner => {用户名}, #设置文件的属主
path path => "文件的路径", #指定要管理文件的路径,必须用引号引起来, 这也是一个资源的 namevar ,通常path 等于资源的title
source source => {"puppet:///URL"|"完整的文件路径"}, #拷贝一个文件覆盖当前文件,用checksum来判断是否有必要进行复制,可以设置的值是一个引用master或agent的完整的文件路径,或者是URI,当前支持的URI只有puppet和file ; 这是一个对文件常用的操作,可以让puppet修改系统的配置文件.
recurse recurse => { true|false|inf|remote}, #设置是否以及如何进行递归操作即可以管理子目录recurse,purge和force连用用于删除子目录中不在资源控制的文件或目录
purge purge => {true|false}, #将不再资源中管理的其他数据则删除
force force => {ture|false}, # force是否可以删除文件或目录与ignore相反
ignore ignore => {文件名|正则表达式}, #当用recursion 方法复制一个目录的时候,可以用ignore来设定过滤条件,符合过滤条件的文件不被复制或删除. 使用ruby自带的匹配法则.因此shell级别的过滤表达式完全支持,例如[a-g]* 与force相反
target target => {"源文件或目录"} #是为创建链接文件的即将target的值作为源文件title的值作为目标文件。如lin -s $target $title。当且仅当ensure => link, 才可以使用
更多参数解析请参考[root@node puppets]# puppet describe file | less
实例演示
[root@node puppets]# vim file.pp
file {'/etc/nginx/nginx.conf': #在这里title定义全路径完全可以替代path
ensure => file,
source => '/etc/backup/nginx/nginx.conf', #指定文件来源位置
mode => '0640', #设置权限模型
owner => 'root', #属主
group => 'root', #属组
[root@node puppets]# puppet apply file.pp
notice: /Stage[main]//File[/etc/nginx/nginx.conf]/content: content changed '{md5}d9dfc198c249bb4ac341198a752b9458' to '{md5}b1de7b8f5f09371a466aa56a3e41abe7' #内容改变
notice: /Stage[main]//File[/etc/nginx/nginx.conf]/mode: mode changed '0644' to '0640' #权限改变
notice: Finished catalog run in 0.18 seconds
8、用puppet定义执行命令
exec常用的定义方式
exec {"title": #一般写上要执行的命令如不写则需要指定command
cwd => "目录的绝对路径", #在那个目录下执行也可不要
path => "/bin:/sbin:...", #命令执行的搜索路径,如不要则需指定命令的绝对路径
command => "执行的命令", #写上要执行的命令一般不需要可在title中写上
creates => "文件名绝对路径", #当且仅当该文件名不存在命令才被执行可不要
user => "用户名", #定义运行命令的用户。 注意如果你使用了这个参数那么任何的错误输出不会在当下被捕捉这是Ruby的一个bug
group => "组名" #定义运行命令的用户组。在不同的平台下的运行的结果无法确定由于不同用户运行命令的时候变量是不变的所以这是平台的问题而不是Ruby或Puppet的问题。
logoutput => "true|false", #是否记录输出,可取的值为truefalse和其他合法的日志等级。
onlyif => "命令", #如onlyif中命令的执行结果为0执行正确才执行title或command中的目录,与unless相反
unless => "命令", #如unless中命令的执行结果不为0执行出错才执行title或command中的目录,与onlyif相反
}
注意exec资源是不太好掌控的资源如果能用脚本实现尽量写成脚本通过file资源分发到服务器上面。然后用其他的方式来调用脚本。例如crontab。说来crontab资源��嗦一句虽然puppet提供了crontab资源但是你完全可以用file资源来把crontab任务放到/etc/cron.d目录下来实现crontab资源的管理。使用puppet的时候尽量用最简单的语法越是花哨的语法也越容易出错。
实例演示
[root@node puppets]# vim exec.pp
exec {'test':
path => '/bin:/sbin:/usr/bin:/usr/sbin', #定义环境变量
command => 'mktemp /tmp/abc.XXXX', #生成一个临时目录
command => 'chkconfig -add haproxy; chkconfig haproxy on', #添加服务启动服务中间使用分号隔开
user => 'root',
group => 'root',
}
执行
[root@node puppets]# puppet apply exec.pp
notice: /Stage[main]//Exec[test]/returns: executed successfully
notice: Finished catalog run in 8.19 seconds
查看
[root@node puppets]# ls /tmp/
abc.hzdn
9、定义通知资源更改机制
当我们更改过配置文件后有些不可能立即生效需要通知重新载入
#############两个关键字##############
notify定义在前资源中通知资源已更改请重载
subscribe定义在后资源中订阅通知若有改动立即通知我
#############定义资源################
[root@node puppets]# vim file.pp
file {'/etc/nginx/nginx.conf':
ensure => file,
source => '/etc/backup/nginx/nginx.conf',
mode => '0644',
owner => 'root',
group => 'root',
notify => Service ['nginx'],
}
service {'nginx':
ensure => running,
# subscribe => File ['/etc/nginx/nginx.conf'],
}
#############本地执行###############
[root@node puppets]# puppet apply file.pp
notice: /Stage[main]//File[/etc/nginx/nginx.conf]/content: content changed '{md5}b1de7b8f5f09371a466aa56a3e41abe7' to '{md5}95f45f10386878664af2b7ccd1536ea4'
notice: /Stage[main]//File[/etc/nginx/nginx.conf]/mode: mode changed '0640' to '0644'
notice: /Stage[main]//Service[nginx]: Triggered 'refresh' from 2 events #重新刷新服务器两次
notice: Finished catalog run in 2.94 seconds
#############查看进程数############
[root@node puppets]# ps aux | grep nginx
root 24696 0.0 0.3 96068 1908 ? Ss 22:46 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx 24697 0.0 0.5 96420 2676 ? S 22:46 0:00 nginx: worker process
nginx 24698 0.0 0.5 96420 2676 ? S 22:46 0:00 nginx: worker process #两个说明依然重新启动
资源间应用次序通知链的定义"->"用于定义次序链而"~>"用于定义通知链的他们既可以用于资源引用间也可应用于资源申报之间。
四、puppet中语法阐述
<一>puppet的变量
无论是定义还是引用puppet的变量名称必须以“$”开头
变量有其作用域因为puppet有其类、模块所以使得其作用域受限于其类、模块中而且puppet的每个变量都有两个名字简短名称和长格式完全限定名称(FQN)完全限定名称的格式为"$scope::variable"
什么是scope其作用方式是什么
(1)Scope是一个特定的代码区域用于同程序中的其它代码隔离开来。
(2)在puppet中scope可用于限定变量及资源默认属性的作用范围但不能用于限定资源名称及资源引用的生效范围。
(3)任何给定的scope都可以访问自己的内容以及接受来自其父scope、节点scope及top scope的内容。
如图所示
解析top scope仅能访问自己变量和属性默认值
节点scope能访问自己的及top scope的变量和属性默认值
example::parent,example::other和example::four能访问自己的以及节点scope和top scope的变量和默认值。
(4)如果要访问非当前scope中的变量则需要通过完全限制名称进行如$vhostdir = $apache::params::vhostdir
需要注意的是top 5、scope的名称为空因此若要引用其变量则需要使用类似“$::osfamily” 的方式进行。
<二>变量的数据类型
puppet语言支持多种数据类型以用于变量和属性的值以及函数的参数
字符型
非结构化的文本字符串可以使用引号也可以不使用
单引号中的变量不会替换而双引号中的能够进行变量替换
字符型值也支持使用转义符
数值型
可为整数和浮点数不过puppet只有在数值上下文才把数值当数值型对待其它情况下一律以字符型处理
数组
数组值为中括号中的以逗号分隔的项目列表最后一个项目后面可以有逗号
数组中的元素可以为任意可用数据类型包括hash或其它数组
数组索引为从0开始的整数也可以使用负数索引
布尔型
true和false不能加引号
if语句的测试条件和比较表达式都会返回布尔型值
另外其它数据类型也可以自动转换为布尔型如空字符串为false等
undef
从未被声明的变量的值类型即为undef
也可手动为某变量赋予undef值即直接使用不加引号的undef字符串
hash
即为外键值数据类型键和值之间使用"=>"分隔键值对儿定义在"{}"中彼此间以逗号分隔
其键为字符型数据而值可以为puppet支持的任意数据类型
访问hash类型的数据元素要使用“键”当做索引进行
正则表达式
属于puppet的非标准数据类型不能赋值给变量仅能用于有限的几个接受正则表达式的地方即接受使用"=~"及"!~"匹配操作符的位置通常包含case语句中的selector以及节点名称匹配的位置
他们不能传递给函数或用于资源属性的定义
puppet中的正则表达式支持使用(?<ENABLED OPTION>:<SUBP ATTERN>)和(?-<DISABLED OPTION>:<SUBP ATTERN>)两个特殊的符号。
例如下面的示例表示做正则表达式匹配时启用选项"i"(忽略字符大小写)但不支持使用“m”(把.当做换行符)和"x"(忽略模式中的空白字符和注释),这里定义-mx就是表示不使用m和x。
$packages = $operatingsystem ? {
/(?i-mx:ubuntu|debian)/ => 'apache2',
/(?i-mx: centos|redhat)/ => 'httpd',
}
<三>条件判断语句
在puppet2.7的版本中有三种条件判断语句if,case,selector而在puppet3.0版本中支持四种条件判断if,case,selector,unless(不常用)。
if
基本语法结构
if CONDITION {
statement
......
}
elsif CONDITION {
statement
......
}
else CONDITION {
statement
......
}
条件语句的书写方式这里的条件通常是条件表达式而这里语句可以使用的条件有变量、表达式、有返回值的函数。
例如
[root@node puppets]# vim if.pp
if $operatingsystem =~ /(?!-mx:^(centos|fedora|redhat))/{ #定义模式匹配
$webserver = 'httpd'
} elsif $operationsystem =~ /(?!-mx:^(debian|ubuntu))/{
$webserver = 'apache2'
} else {
$webserver = undef
notice ('Unkown OS')
}
package { "$webserver":
ensure => installed,
}
#############查看执行结果############
[root@node puppets]# puppet apply -d -v if.pp #详细显示执行过程
debug: Package[httpd](provider=yum): Ensuring => present
debug: Puppet::Type::Package::ProviderYum: Executing '/usr/bin/yum -d 0 -e 0 -y install httpd'
debug: Puppet::Type::Package::ProviderYum: Executing '/bin/rpm -q httpd --nosignature --nodigest --qf %{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}
'
notice: /Stage[main]//Package[httpd]/ensure: created
debug: /Stage[main]//Package[httpd]: The container Class[Main] will propagate my refresh event
debug: /Schedule[weekly]: Skipping device resources because running on a host
debug: /Schedule[puppet]: Skipping device resources because running on a host
debug: Class[Main]: The container Stage[main] will propagate my refresh event
debug: Finishing transaction 69898370485060
debug: Storing state
debug: Stored state in 0.02 seconds
notice: Finished catalog run in 71.04 seconds
debug: Finishing transaction 69898371162860
debug: Received report to process from node.magedu.com
debug: Processing report from node.magedu.com with processor Puppet::Reports::Store
case
类似于if语句case语句会从多个代码块中选择一个分支执行这跟其他编程语言中的case语句功能一致。
case语句会接受一个控制表达式和一组case代码块并执行第一匹配到控制表达式的块。
基本语法结构
case CONTROL_EXPRESS {
case1,...: {statement...}
case2,...: {statement...}
... ...
default: {statement...}
}
CONTROL_EXPRESS可以使用的条件为变量或表达式以及有返回值的函数。
case的指定方法可以为一个字符串、符合条件的值、具有某个返回值的函数调用、正则表达式。
statement可以为通知信息也可以为变量表达式。
示例演示
[root@node puppets]# vim case.pp
case $operatingsystem {
'centos','fedora','redhat': {notice("Welcome to RedHat OSFamily")}
/^(debian|ubuntu)$/: {notice("Welcome to $1 linux")}
default: {notice("Welcome, alien *_*")}
}
##############执行结果############
[root@node puppets]# puppet apply case.pp
notice: Scope(Class[main]): Welcome to RedHat OSFamily
notice: Finished catalog run in 0.03 seconds
selector
selector和case语句很相近但是和case语句不同的是selector返回的是值而不是表达式因此selector只能用于期望出现直接值plain value的地方这包含变量赋值、资源属性定义、函数参数传递、资源标题和其他selector的值和表达式中。但是selector不能用于一个已经嵌套于selector的case语句中也不能用于一个嵌套于case的case语句中。
基本语法结构
CONTROL_VARIABLE ? {
case1 => value1
case2 => value2
... ...
default => valueN
}
selectors的使用要点
1整个selector语句会被当作一个单独的值puppet会将控制变量按列出的次序与每个case这里的case不是case语句而是某种情况进行比较并在遇到一个匹配的case后将其值作为整个语句的值进行返回并忽略后面的其它case。
2控制变量与各case比较的方式与case语句相同但如果没有任何一个case与控制变量匹配时puppet在编译时将会返回一个错误因此实践中其必须提供default case。
3selector的控制变量只能是变量或有返回值的函数切记不能使用表达式。
4其各case可以是直接值(需要加引号)、变量、能调用返回值的函数、正则表达式模式或default。
5但与case语句所不同的是selector的各case不能使用列表。
6selector的各case的值可以是一个除了hash以外的直接值、变量、能调用返回值的函数或其它的selector。
<四>类的使用(classes):
classes是一堆命名的代码块里面声明了一些资源然后使用名字调用这个代码块类似以bash中的函数。
class是用于通用目标的一组资源可以使用多次在不同的节点上可能都会用到的因此它是命名的代码块在某位置创建之后可在puppet全局使用若不调用则不是使用但是注意有时候可能要指定其作用域的。这里的类类似于其他编程语言中的类的功能puppet的class可以被继承也可以包含子类。定义类的语法很简单如下
class my_class {
... puppet code ...
}
定义类:
(1)类的名称只能以小写字母开头可以包含小写字母、数字和下划线
(2)每个类都会引入一个新的变量scope这意味着在任何时候访问类中的变量时都会使用其完全限定名称不过在本地scope中的变量赋予一个新值。
(3)定义完类之后要声明要调用声明类的方法常用的有三种直接使用include函数声明类使用require来声明类类似Resource的声明方式一样定义类。
例如安装nginx服务器
#############关键字#############
声明类includerequireResource
#############定义###############
[root@node puppets]# vim class.pp
class nginx {
package {'nginx':
ensure => installed,
before => File['/etc/nginx/nginx.conf'],
}
file {'/etc/nginx/nginx.conf':
ensure => file,
source => '/etc/backup/nginx/nginx.conf',
mode => '0644',
owner => 'root',
group => 'root',
}
service {'nginx':
ensure => running,
subscribe => File ['/etc/nginx/nginx.conf'],
}
}
include nginx
###############执行#################
[root@node puppets]# puppet apply class.pp
notice: /Stage[main]/Nginx/Package[nginx]/ensure: created
notice: /Stage[main]/Nginx/File[/etc/nginx/nginx.conf]/content: content changed '{md5}d9dfc198c249bb4ac341198a752b9458' to '{md5}95f45f10386878664af2b7ccd1536ea4'
notice: /Stage[main]/Nginx/Service[nginx]/ensure: ensure changed 'stopped' to 'running'
notice: /Stage[main]/Nginx/Service[nginx]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 124.15 seconds
声明带参数的类
常用结构
class my_class (para1='val1',para2='val2'){
... puppet code ...
}
(1)同一个类在不同的OS上可能会略有不同因此需要通过获取相应系统的fact来实现有区别对待。然而万一相应的OS没有输出类所期望的fact或者是类依赖于非fact因素时此机制将无法满足需求。此时就需要使用带参数的类来完成此类功能同时在声明类时为其参数传递相应的值即可完成传参功能。
(2)在定义在带参数的类时需要将参数声明在类名后的小括号“()”参数可以有默认值如果使用多个参数彼此间要使用逗号分隔。
在类的内部使用参数的方式同使用本地变量在声明类时可向其传递参数其方式如同定义资源的属性因此其也称为“资源属性风格的类声明”。
(3)不能在使用include声明类时向其传递参数。对于带参数的类来说如果使用其参数的默认值仍然可以使用include声明类否则就必须使用“资源属性风格的类声明”。另外如果在使用不同的参数值将某个类声明了多次最后生效的声明将很难判定。
例如在有些情况下我们安装的是nginx在有些场景下我们安装的是tengine
##############关键字###########
class这里有声明的意思
##############定义#############
[root@node puppets]# vim class2.pp
class nginxserver ($ngserver='nginx'){
package {"$ngserver":
ensure => installed,
before => File['/etc/nginx/nginx.conf'],
}
file {'/etc/nginx/nginx.conf':
ensure => file,
source => '/etc/backup/nginx/nginx.conf',
mode => '0644',
owner => 'root',
group => 'root',
}
service {'nginx':
ensure => running,
subscribe => File ['/etc/nginx/nginx.conf'],
}
}
class {'nginxserver':
ngserver => 'tengine',
}
#############执行测试#############
[root@node puppets]# puppet apply class2.pp
err: /Stage[main]/Nginxserver/Package[tengine]/ensure: change from absent to present failed: Execution of '/usr/bin/yum -d 0 -e 0 -y install tengine' returned 1: Error: Nothing to do #出现此错误原因是我的本地yum源中没有tengine但是说明了这种方法可以使用安装。
notice: /Stage[main]/Nginxserver/File[/etc/nginx/nginx.conf]: Dependency Package[tengine] has failures: true
warning: /Stage[main]/Nginxserver/File[/etc/nginx/nginx.conf]: Skipping because of failed dependencies
notice: /Stage[main]/Nginxserver/Service[nginx]: Dependency Package[tengine] has failures: true
warning: /Stage[main]/Nginxserver/Service[nginx]: Skipping because of failed dependencies
notice: Finished catalog run in 21.08 seconds
类的继承
在原有类的基础上再添加一些属性参数代码使其更加具体化这就叫做类的继承(使用inherits关键字)。并且一个类还可以包含多个子类。
#############关键字############
inherits继承
############定义###############
[root@node puppets]# vim class3.pp
class nginx {
package {'nginx':
ensure => installed,
}
}
class nginx::websrv inherits nginx { #继承(inherits)
file {'/etc/nginx/nginx.conf':
ensure => file,
mode => '0644',
owner => 'root',
group => 'root',
source => '/etc/backup/nginx/nginx.conf',
require => Package['nginx'],
}
service {'nginx':
ensure => running,
subscribe => File['/etc/nginx/nginx.conf']
}
}
class nginx::rproxy inherits nginx {
file {'/etc/nginx/nginx.conf':
ensure => file,
mode => '0644',
owner => 'root',
group => 'root',
source => '/etc/backup/nginx/nginx.conf',
require => Package['nginx'],
}
service {'nginx':
ensure => running,
subscribe => File['/etc/nginx/nginx.conf']
}
}
include nginx::rproxy #调用
##############执行################
[root@node puppets]# puppet apply class3.pp
notice: /Stage[main]/Nginx::Rproxy/File[/etc/nginx/nginx.conf]/content: content changed '{md5}d9dfc198c249bb4ac341198a752b9458' to '{md5}95f45f10386878664af2b7ccd1536ea4'
notice: /Stage[main]/Nginx::Rproxy/Service[nginx]/ensure: ensure changed 'stopped' to 'running'
notice: /Stage[main]/Nginx::Rproxy/Service[nginx]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 1.92 seconds
小扩展
在类中我们使用=>来引用资源也可使用+>在原有依赖资源的基础上多引用几个其他资源。
例如
class apache {
service { 'apache':
require => package['httpd'],
}
}
class apache::ssl inherits apache {
Service['apache'] {
require +> [File['httpd.pem']],File['httpd.conf']]
}
}
<五>Modules概述
到目前当前笔者的概述为止资源申报、定义类、声明类等所有功能都只能在一个manifest清单文件中实现但是这却非最有效的基于puppet管理IT基础架构的方式
实践中一般需要把manifest文件分解成易于理解的结构例如将类文件、配置文件甚至包括后面将提到的模块文件等分类存放并且通过某种机制在必要时将他们整合起来
这种机制即“模块”它有助于以结构化、层次化的方式使用puppet而puppet则基于“模块自动装载器”完成模块装载。
从另一个角度来说模块实际上就是一个按约定的、预定义的结构存放了多个文件或子目录的目录目录里的这些文件或子目录必须遵循其命名规范。
puppet会按此种规范在特定的位置查找所需的模块文件不过这些特定的目录也可以通过puppet的配置参数modulepath定义。
查询模块装载路径
[root@node ~]# puppet master --genconfig | grep modulepath
modulepath = /etc/puppet/modules:/usr/share/puppet/modules #是由冒号隔开的两个目录
查看已安装模块
[root@node ~]# puppet module list
/etc/puppet/modules (no modules installed) #显示模块搜索路径
/usr/share/puppet/modules (no modules installed)
官方给出的模块目录结构
首先模块名称MODULE NAME也就是目录名称模块名称只能以小写字母开头可以包含小写字母、数字和下划线但是不能使用“main”或“settings”作为模块名在这个目录下存在有manifests、files、templates、lib、tests、spec这些子目录
manifests是清单目录包含当前模块的所有manifest文件每一个manifest文件必须包含一个类或一个定义的类也就是说里面存放的是以.pp结尾处的文件其中必须有一个init.pp的文件这个文件只能包含一个单独的类定义且类的名称必须与模块名称相同
files目录包含一组的静态文件这些文件可被节点下载使用每个文件的访问路径遵循puppet:///modules/MODULE_NAME/filename路径格式
lib目录插件目录常用于自定义fact及自定义资源类型等
templates目录存储了manifest用到的模块文件其访问路径遵循template'ModuleName/TemplateName'格式
tests目录当前模块的使用帮助或使用范例文件类似如何声明当前模块中的类及定义的类型等
spec目录类似于tests目录的功能只不过其是为lib目录中定义的各插件提供使用范例的
查询网上提供的第三方模块前提是能够访问互联网
1
2
[root@node ~]# puppet module search apache #关于apache的模块
[root@node ~]# puppet module install MODULE_NAME #指定模块名称
使用模块的步骤
1.创建模块目录和文件的方式
1
2
mkdir -pv /etc/puppet/modules/模块名/{manifests,files,lib,templates,spac,test}
touch /etc/puppet/modules/模块名/manifests/init.pp
2.在init.pp中导入别的pp文件(也可直接在init.pp中写上puppet的语法)
1
2
vim /etc/puppet/modules/模块名/manifests/init.pp
import "*" #相当于node … { import模块名}
3.写上puppet的语法,完成某些功能最好是单独写利于管理
1
vim 模块名.pp
4.根据“模块名.pp或init.pp”的内容考虑是否需要在files目录下创建所需的文件。
实例演示
模块清单手动创建一个模块使其能够运行起来
[root@node ~]# cd /etc/puppet/modules/ #进入模块清单目录下
[root@node modules]# mkdir -pv nginx/{manifests,files,lib,templates,spac,test} #创建一个Nginx模块目录
mkdir: created directory `nginx'
mkdir: created directory `nginx/manifests'
mkdir: created directory `nginx/files'
mkdir: created directory `nginx/lib'
mkdir: created directory `nginx/templates'
mkdir: created directory `nginx/spac'
mkdir: created directory `nginx/test'
[root@node modules]# puppet module list #查看已安装的模块
/etc/puppet/modules
?..? nginx (???) #有乱码显示博主没有消除掉若看客们有办法请留言告知谢谢。
/usr/share/puppet/modules (no modules installed)
[root@node modules]# cd nginx/manifests/
[root@node manifests]# vim init.pp #编辑init.pp
class nginx {
package {'nginx':
ensure => installed,
before => File['/etc/nginx/nginx.conf'],
}
file {'/etc/nginx/nginx.conf':
ensure => file,
source => 'puppet:///modules/nginx/nginx.conf', #静态文件目录
mode => '0644',
owner => 'root',
group => 'root',
}
service {'nginx':
ensure => running,
subscribe => File ['/etc/nginx/nginx.conf'],
}
}
include nginx
[root@node manifests]# cp /etc/backup/nginx/nginx.conf /etc/puppet/modules/nginx/files/ #静态文件
[root@node manifests]# service nginx stop
Stopping nginx: [ OK ]
[root@node manifests]# rpm -e nginx
warning: /etc/nginx/nginx.conf saved as /etc/nginx/nginx.conf.rpmsave
[root@node manifests]# rm -rf /etc/nginx/
[root@node manifests]# pwd #查询当前文件
/etc/puppet/modules/nginx/manifests
[root@node manifests]# puppet apply init.pp #运行测试
notice: /Stage[main]/Nginx/Package[nginx]/ensure: created
notice: /Stage[main]/Nginx/File[/etc/nginx/nginx.conf]/content: content changed '{md5}d9dfc198c249bb4ac341198a752b9458' to '{md5}95f45f10386878664af2b7ccd1536ea4'
notice: /Stage[main]/Nginx/Service[nginx]/ensure: ensure changed 'stopped' to 'running'
notice: /Stage[main]/Nginx/Service[nginx]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 64.02 seconds
站点清单
首先安装puppet-server使其生成manifests目录(/etc/puppet/manifests),这个目录就是站点清单也就是定义各节点的清单。
node(节点)的作用区分不同的客户端并且给不同的服务端分配manifest。
一般在/etc/puppet/manifests创建一个名叫nodes.pp专门用来存放node信息的
vim /etc/puppet/manifests/nodes.pp #建立节点文件
node 'FQDN' { #FQDN 完全合格域名
变量的声明 #声明格式$变量名="值" 引用格式 ${变量名}
include class #已定义好的类class
}
实例演示
###############安装puppet-server#############
[root@node ~]# rpm -ivh puppet-server-2.7.23-1.el6.noarch.rpm
[root@node ~]# hostname #当前主机名
node.magedu.com
[root@node ~]# ping node.magedu.com #测试通信
PING node.magedu.com (172.16.18.6) 56(84) bytes of data.
64 bytes from node.magedu.com (172.16.18.6): icmp_seq=1 ttl=64 time=0.300 ms
64 bytes from node.magedu.com (172.16.18.6): icmp_seq=2 ttl=64 time=0.035 ms
##############定义类###############
[root@node manifests]# pwd
/etc/puppet/modules/nginx/manifests
[root@node manifests]# vim init.pp
class nginx {
}
[root@node manifests]# vim nginxserver.pp
class nginx::nginxserver inherits nginx {
package {'nginx':
ensure => installed,
before => File['/etc/nginx/nginx.conf'],
}
file {'/etc/nginx/nginx.conf':
ensure => file,
source => 'puppet:///modules/nginx/nginx.conf',
mode => '0644',
owner => 'root',
group => 'root',
}
service {'nginx':
ensure => running,
subscribe => File ['/etc/nginx/nginx.conf'],
}
}
[root@node manifests]# cp nginxserver.pp tengine.pp
[root@node manifests]# vim tengine.pp
class nginx::tengine inherits nginx {
package {'tengine':
ensure => installed,
before => File['/etc/nginx/nginx.conf'],
}
file {'/etc/nginx/nginx.conf':
ensure => file,
source => 'puppet:///modules/nginx/nginx.conf',
mode => '0644',
owner => 'root',
group => 'root',
}
service {'nginx':
ensure => running,
subscribe => File ['/etc/nginx/nginx.conf'],
}
}
###############编辑节点文件#############
[root@node manifests]# cd /etc/puppet/manifests/
[root@node manifests]# vim nodes.pp
node 'node.magedu.com'{ #编辑节点信息由于当前没有使用节点使用本机。
include nginx::nginxserver #加载类
}
###############nginx服务################
[root@node manifests]# service nginx stop
Stopping nginx: [ OK ]
[root@node manifests]# rpm -e nginx
warning: /etc/nginx/nginx.conf saved as /etc/nginx/nginx.conf.rpmsave
[root@node manifests]# rm -rf /etc/nginx/
#############测试执行一###########
[root@node manifests]# puppet apply nodes.pp
notice: /Stage[main]/Nginx::Nginxserver/Package[nginx]/ensure: created
notice: /Stage[main]/Nginx::Nginxserver/File[/etc/nginx/nginx.conf]/content: content changed '{md5}d9dfc198c249bb4ac341198a752b9458' to '{md5}95f45f10386878664af2b7ccd1536ea4'
notice: /Stage[main]/Nginx::Nginxserver/Service[nginx]/ensure: ensure changed 'stopped' to 'running'
notice: /Stage[main]/Nginx::Nginxserver/Service[nginx]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 13.79 seconds
##########测试执行二#############
###服务###
[root@node manifests]# service nginx stop
Stopping nginx: [ OK ]
[root@node manifests]# rpm -e nginx
warning: /etc/nginx/nginx.conf saved as /etc/nginx/nginx.conf.rpmsave
[root@node manifests]# rm -rf /etc/nginx/
###更改###
[root@node manifests]# vim nodes.pp
node 'node.magedu.com'{
include nginx::tengine #调用teengine类
}
###执行###
[root@node manifests]# puppet apply nodes.pp
err: /Stage[main]/Nginx::Tengine/Package[tengine]/ensure: change from absent to present failed: Execution of '/usr/bin/yum -d 0 -e 0 -y install tengine' returned 1: Error: Nothing to do
notice: /Stage[main]/Nginx::Tengine/File[/etc/nginx/nginx.conf]: Dependency Package[tengine] has failures: true
warning: /Stage[main]/Nginx::Tengine/File[/etc/nginx/nginx.conf]: Skipping because of failed dependencies
notice: /Stage[main]/Nginx::Tengine/Service[nginx]: Dependency Package[tengine] has failures: true
warning: /Stage[main]/Nginx::Tengine/Service[nginx]: Skipping because of failed dependencies
notice: Finished catalog run in 2.28 seconds
############测试执行三############
[root@node manifests]# vim nodes.pp
node 'node1.magedu.com'{ #更改为一个无法获取信息的地址
include nginx::nginxserver
}
[root@node manifests]# puppet apply nodes.pp
Could not find default node or by name with 'node.magedu.com, node.magedu, node' on node node.magedu.com #找不到
####因此表明我们可以通过域名节点实现类的调用。
总结
puppet类不支持多重继承因此不能多次继承也不能继承自多个类。
五、Master/Agent安装:
<一>节点信息:
###########master端:############
node:172.16.18.6
[root@node ~]# hostname
node.magedu.com
###########agent端一:##########
node1:172.16.18.7
[root@node1 ~]# hostname
node1.magedu.com
<二>更改master端配置文件
[root@node ~]# cd /etc/puppet/
[root@node puppet]# ls
auth.conf fileserver.conf manifests modules puppet.conf
[root@node puppet]# puppet master --genconfig > puppet.conf #重新生成配置文件
[root@node puppet]# vim puppet.conf #修改配置文件
rundir = /var/run/puppet #645行
pidfile = $rundir/master.pid #364行
<三>初始化启动服务器端
[root@node puppet]# puppet master --no-daemonize --verbose --debug #首次启动,输出详细信息:创建CA,签发证书等;其中--no-daemonize表示工作在前端 --verbose表示显示详细信息 --debug表示调试
debug: Finishing transaction 70216505862080
info: Creating a new SSL key for ca
info: Creating a new SSL certificate request for ca
info: Certificate Request fingerprint (md5): 01:47:FD:C5:2F:8F:4F:10:EE:4B:11:94:95:1B:47:44
notice: Signed certificate request for ca
notice: Rebuilding inventory file
debug: Using cached certificate for ca
info: Creating a new certificate revocation list
info: Creating a new SSL key for node.magedu.com
debug: Using cached certificate for ca
info: Creating a new SSL certificate request for node.magedu.com
info: Certificate Request fingerprint (md5): CD:23:B4:59:94:17:F1:4A:37:46:94:59:BF:6E:8C:A0
notice: node.magedu.com has a waiting certificate request
debug: Using cached certificate for ca
debug: Using cached certificate_request for node.magedu.com
notice: Signed certificate request for node.magedu.com
notice: Removing file Puppet::SSL::CertificateRequest node.magedu.com at '/etc/puppet/ssl/ca/requests/node.magedu.com.pem'
notice: Removing file Puppet::SSL::CertificateRequest node.magedu.com at '/etc/puppet/ssl/certificate_requests/node.magedu.com.pem'
notice: Starting Puppet master version 2.7.23
debug: Finishing transaction 70216504311360
然后使用Ctrl+c取消,使用service进行启动
[root@node puppet]# service puppetmaster start
Starting puppetmaster: [ OK ]
[root@node puppet]# ss -tanl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 5 *:8140 *:* #端口已启动
<四>安装agent端
[root@node1 ~]# yum -y localinstall facter-1.7.3-1.el6.x86_64.rpm puppet-2.7.23-1.el6.noarch.rpm
<五>修改配置文件
########agent端一########
[root@node1 ~]vim /etc/puppet/puppet.conf
[main]
server = node.magedu.com #指定puppet服务器主机名
<六>初始化agent端(需要连接服务器)
########agent端一##########
[root@node1 puppet]# puppet agent --server=node.magedu.com --no-daemonize --verbose --debug
debug: Finishing transaction 69956847467440
info: Creating a new SSL key for node1.magedu.com
info: Caching certificate for ca
info: Creating a new SSL certificate request for node1.magedu.com
info: Certificate Request fingerprint (md5): 4C:7F:E3:B8:1A:24:23:4E:4E:CB:B9:ED:FF:12:D2:49
出现此命令显示说明初始化成功等待服务器端认证通过
########master端认证后,正在签署(需要一段时间)####
info: Caching certificate for node1.magedu.com
notice: Starting Puppet client version 2.7.23 #启动客户端
debug: Finishing transaction 69956847116860
debug: catalog supports formats: b64_zlib_yaml dot pson raw yaml; using pson
debug: Using cached certificate for ca
debug: Using cached certificate for node1.magedu.com
info: Caching certificate_revocation_list for ca
info: Caching catalog for node1.magedu.com
debug: Creating default schedules
info: Applying configuration version '1382754147'
debug: /Schedule[daily]: Skipping device resources because running on a host
debug: /Schedule[monthly]: Skipping device resources because running on a host
debug: /Schedule[hourly]: Skipping device resources because running on a host
debug: /Schedule[never]: Skipping device resources because running on a host
debug: /Schedule[weekly]: Skipping device resources because running on a host
debug: /Schedule[puppet]: Skipping device resources because running on a host
debug: Finishing transaction 69956847346700
debug: Storing state
info: Creating state file /var/lib/puppet/state/state.yaml
debug: Stored state in 0.01 seconds
notice: Finished catalog run in 0.04 seconds
注意:如果此前曾以其它主机名或各种原因启动过puppet客户端进程并完成过初始化(输出信息:debug: Using cached certificate for ca等),其证书文件将无法符合本此启动的需要;此时,需要先清空/var/lib/puppet/ssl/目录方可完成后续的初始化操作。
[root@node1 puppet]# rm -rf /var/lib/puppet/ssl/*
<七>签署证书
[root@node ~]# puppet cert list #查看需签署证书列表
"node1.magedu.com" (4C:7F:E3:B8:1A:24:23:4E:4E:CB:B9:ED:FF:12:D2:49)
[root@node ~]# puppet cert sign node1.magedu.com #为agent1签署证书
notice: Signed certificate request for node1.magedu.com
notice: Removing file Puppet::SSL::CertificateRequest node1.magedu.com at '/etc/puppet/ssl/ca/requests/node1.magedu.com.pem'
若有多个节点等待签署证书可使用#puppet cert sign --all 进行全部签署
<八>启动服务
[root@node1 puppet]service puppet start
至此Master/Agent端就安装完成了。