一.puppet介绍
- (1)什么是puppet
puppet是一种重量级自动化运维工具,实现自动化运维以及能够帮助系统管理员管理基础设施的整个生命周期。因此基于puppet可以实现自动化重复任务、快速部署关键性应用以及本地或云端完成主动管理变更和快速扩展架构规模等
适用系统:Linux ,Unix,Windows
和ansible的区别:ansible属于轻量级自动化运维系统,在一般的小型企业中应用广泛,而puppet属于重量级运维工具- (2)puppet开发语言
puppet有自己的开发语言,遵循GPL协议(2.7.0-),基于ruby语言开发
2.7.0以后使用(Apache 2.0 license)
(3)puppet作用
可以实现配置文件,设置用户,制定cron计划任务,安装软件包,管理软件系统等功能。puppet把这些系统实体称之为资源,puppet的设计目标是简化对这些资源的管理以及妥善处理资源间的依赖关系。puppet可以管理40多种资源
例:file、group、user 、host、package、service、cron、exec、yum repo等- (3)puppet工作结构
puppet采用C/S星状的结构,所有的客户端和一个或几个服务器交互。每个客户端周期的(默认半个小时)向服务器发送请求,获得其最新的配置信息,保证和该配置信息同步。每个puppet客户端每半小时(可以设置)连接一次服务器端, 下载最新的配置文件,并且严格按照配置文件来配置客户端. 配置完成以后,puppet客户端可以反馈给服务器端一个消息. 如果出错,也会给服务器端反馈一个消息.
下载地址:https://yum.puppet.com
官网地址:https://www.puppet.com- (4)puppet的工作模型
1.单工作模型:手动应用清单(manifest)
下载:yum install puppet(epel源),在centos6中的epel源没有该安装包
主程序:/usr/bin/puppet
配置文件:/etc/puppet/puppet.conf
2.master/agent模型
有agent端周期性的向master发送应用请求清单并应用于本地
下载:
master端: yum install puppet puppet-server
agent端:yum install puppet
二.puppet基本应用(单模型)
(1)puppet的简单命令
- help : Display Puppet help.
- apply: Apply Puppet manifests locally
describe Display help about resource types
搜狗截图20171126152439.png- agent: The puppet agent daemon
- master: The puppet master daemon
module: Creates, installs and searches for modules on the Puppet Forge
搜狗截图20171126153009.png
(2)资源的定义
向资源类型的属性赋值来实现,可称为资源类型实例化,定义了资源实例的文件(清单),manifest
- 定义资源的语法:
type {'title':
attribute1 => value1,
attribute2 => value2,
}
注意事项:type必须是小写字符,title是一个字符串,在同一类型中必须唯一- 资源属性的三个特殊属性:
Namevar:简称为name
ensure:资源的目标状态
provider:指明资源的管理接口
最常用的是ensure
(3)最常见的几种资源的介绍与配置
1.group
主要属性:
name:组名
gid:组id
system:是否为系统组
ensure:目标状态
members:成员用户
示例:
vim group.pp(建议.pp结尾便于区分)
group{ 'mygrp':
ensure => present,——设置的状态
name => mygrp,——组的名字
system => true,——定义为系统组
}
配置完成后让清单生效
puppet apply -v(显示过程) --noop(干跑,不生效) group.pp
确定没错后,去掉--noop执行观察结果
2.user
属性:
name:用户名;
uid: UID;
gid:基本组ID;
groups:附加组,不能包含基本组;
comment:注释;
expiry:过期时间 ;
home:家目录;
shell:默认shell类型;
system:是否为系统用户 ;
ensure:present/absent;
password:加密后的密码串;
示例:
vim user1.pp
user{'user1':
ensure => present,
}
其他都为默认状态
vim user2.pp
user{'myuser':
ensure => present,
name => user2,
uid => 3002,
shell => '/bin/tcsh',——指定shell类型默认为/bin/bash
home => '/tmp/user1',——指定家目录
groups => mygrp,——指定附加组
}
配置完成后执行puppet apply -v user1.pp和user2.pp
查看结果
搜狗截图20171126155953.png
注意:也可以为同一个用户设置多个附加组,但是要定义之间的依赖关系才能生效
重新 vim user2.pp
group{'testgrp':
ensure => present,
}
user{'myuser':
ensure => present,
name => user2,
uid => 3002,
shell => '/bin/tcsh',——指定shell类型默认为/bin/bash
home => '/tmp/user1',——指定家目录
require => [Group['mygrp'],Group['testgrp']],——定义依赖关系时的Type的首字母必须大写
}
配置完成后还要执行 cat group.pp >> user2.pp,否则无法将mygrp设置为user2的附加组
然后执行 puppet apply -v user2.pp生效
搜狗截图20171126161400.png
引入关系元参数的概念 before/require
A before B: B依赖于A,定义在A资源中;
{
...
before => Type['B'],
...
}
B require A: B依赖于A,定义在B资源中;
{
...
require => Type['A'],
...
}
示例:vim user2.pp
1 group{'testgrp':
2 ensure => present,
3 before => User['myuser'],——表示被user{'myuser'}所依赖
4 }
5
6
7 user{'myuser':
8 ensure => present,
9 name => 'user2',
10 uid => 3002,
11 shell => '/bin/tcsh',
12 home => '/tmp/user2',
13 groups => ['mygrp', 'testgrp'],
14
15 }
16
17 group{ 'mygrp':
18 ensure => present,
19 name => 'mygrp',
20 system => true,
21 before => User['myuser'],——表示被user{'myuser'}所依赖
22 }
3.package
核心属性:
ensure: installed ,present,latest,absent
name :包名
source:程序包的安装来源
provider:指明安装方式
示例:
vim package.pp
package{'redis':
ensure => latest,
}
4.service
基本属性:
ensure
enable
name
path:目标路径
hasrestart:是否启动重启功能,用true和false来区分
restart:具体的重启的命令
start
stop
status
示例:
vim redis.pp
service {'redis':
ensure => latest,
enable => false,——开机不自启动
hasrestart => true, ——开启重启功能
restart => 'systemctl restart redis',
require => Pakage['redis']——表示依赖于package资源
}
package{'redis':
ensure => latest,
}
puppet apply -v redis.pp
表示一旦安装redis包后,自动开启redis服务
5.file资源
ensure:Whether the file should exist, and if so what kind of file it should be. Possible values are present
, absent
, file
, directory
, and link
.
file:类型为普通文件,其内容由content属性生成或复制由source属性指向的文件路径来创建;
link:类型为符号链接文件,必须由target属性指明其链接的目标文件;
directory:类型为目录,可通过source指向的路径复制生成,recurse属性指明是否递归复制;
path:文件路径;
source:源文件;
content:文件内容;
target:符号链接的目标文件;
owner:属主
group:属组
mode:权限;
atime/ctime/mtime:时间戳;
- 示例
vim file.pp
file{'/etc/redis.conf':
ensure => file,——目标状态为文件
source => '/root/redis.conf',——定义的原路径
owner => 'redis',——所有者
group => 'root',——所属组
mode => '0644',——权限
注意:因为在file中已经定义了目标路径的绝对路径,因此可以不再写path
}
为了效果明显,在执行puppet apply -v file.pp之前先cp /etc/redis.conf /root/
vim /root/redis.conf
将bind地址改为0.0.0.0
然后执行puppet apply -v file.pp
发现/etc/redis.conf中的bind地址改为0.0.0.0
实现复制功能
- 示例2:实现目录到目录的复制
vim directory.pp
1 file{'yum.repos.d':
2 ensure => directory,
3 path => '/tmp/yum.repos.d',
4 source => '/etc/yum.repos.d',
5 recurse => true,——递归功能目的是将原目录中的文件也一并复制到目标目录下
6 }
搜狗截图20171125095541.png
- 如果将文件复制到指定目录上——结果指定的目录仍然是文件
vim mulu2.pp
file {'test':
ensure => directory,
path => '/tmp/test',
source => ['/etc/issue', '/etc/group'],
}
![搜狗截图20171125094440.png](http:https://img.haomeiwen.com/i6851471/5ff334a7c03c6b6a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
显示如图仍然是文件,并且只有一个生效
* 示例3:定义link链接
vim link.pp
file{'redis.conf':
ensure => link,
path => '/tmp/redis.conf',
target =.> '/etc/redis.conf',——定义目标,path指向target定义的路径
}
puppet apply -v link.pp
搜狗截图20171125100154.png
综上我们可以定义一个综合性的清单来实现安装redis,还要完成文件的复制的功能,从而触发redis服务重启
vim redis.pp
package{'redis':
ensure => latest,
name => 'redis',
}
file{'redis.conf':
ensure =>file,
path => '/etc/redis.conf',
source => '/root/redis.conf',
mode => '0644',
owner => 'root',
group => 'redis',
require => Package['redis'],
}
servcie{'redis':
ensure => running
name => 'redis',
hasrestart => true,
restart => 'systemctl restart redis',
subscribe => File['redis.conf'],——类似require,定义依赖关系
}
- subscribe:b依赖a,并且b监控a资源的变化产生的事件
注意:也可以在file资源中加入notify => Service['redis'],(通知service资源本资源的变化)此时service中的subcribe可以注释掉
为了效果显著可以将redis删除再执行
puppet apply -v redis.pp - 在实际应用中也可将file中的require和service中的subscribe去掉,直接在package的最后加入->(表示package被file资源依赖),并在file最后加入~>(表示file资源既要先于service资源生效,并通知service资源自己因为变化而产生的事件)
- 或者在redis.pp清单中的最后加入 Package['redis']->File['redis.conf']~>Service['redis']
6.exec资源
主要实现puppet对shell的调用,完成软件的安装
属性:
command** (namevar):要运行的命令;
cwd:The directory from which to run the command.指定命令执行的目录,如果目录不存在,则命令执行失败
creates:文件路径,仅此路径表示的文件不存在时,command方才执行;
user/group:运行命令的用户身份;
path:The search path used for command execution. Commands must be fully qualified if no path is specified.
onlyif:此属性指定一个命令,此命令正常(退出码为0)运行时,当前command才会运行;
unless:此属性指定一个命令,此命令非正常(退出码为非0)运行时,当前command才会运行;
refresh:重新执行当前command的替代命令;
refreshonly:仅接收到订阅的资源的通知时方才运行;
示例1:
vim exec.pp
exec{'makedir':
command => 'mkdir /tmp/hi.dir',
path => '/bin:/bin/sbin:/usr/bin:/usr/sbin',——指定路径,否则无法执行command命令
creates => '/tmp/hi.dir',——不存在该路径时才执行command命令
}
puppet apply -v exec.pp
搜狗截图20171125110400.png
示例2
vim exec2.pp
exec{'createsuer':
command => 'useradd user7',
path => '/bin:/sbin:/usr/bin:/usr/sbin',
unless => 'id user7',——当使用id命令找不到该用户时,才执行command命令
}
搜狗截图20171125110619.png
示例3:
vim exec3.pp定义触发安装zabbix-agent,zabbix-sender的触发条件
exec{'installpkg':
command => 'yum -y install zabbix-agent zabbix-sender',
path => '/bin:/sbin:/usr/bin:/usr/sbin',
onlyif => 'yum repolist |grep -i zabbix',定义触发command执行的命令
}
vim exec4.pp 实现备份
file{'/etc/redis.conf':
source => '/root/redis.conf',
ensure => file,
}
exec{'backupfile':
command => 'cp /etc/redis.conf /backups/',
path => ['/bin','/sbin', '/usr/bin', 'usr/sbin'],
refreshonly => true,
subscribe => File['/etc/redis.conf'],
}
前提先创建backups这个目录
执行 puppet apply -v exec4.pp
7.cron资源
属性
command:要执行的任务;
ensure:present/absent;
hour:
minute:
monthday:
month:
weekday:
user:以哪个用户的身份运行命令
target:添加为哪个用户的任务
name:cron job的名称;
示例:
vim cron.pp
cron {'timesync':
command => '/usr/sbin/ntpdate 172.18.0.1 &> /dev/null',——定义同步时间命令
ensure => present,
minute => '/3',——每三分钟执行一次
user => 'root', ——以root身份运行
}
###8.notify资源
定义通知任务
属性:
message: 信息内容
name: 信息名称
示例:
vim notify.pp
notify {'helloworld':
message => 'hello everyone ,aolo',
}
搜狗截图20171126161400.png
三.puppet变量
(1)数据类型
字符型:引号可有可无;但单引号为强引用,双引号为弱引用;
数值型:默认均识别为字符串,仅在数值上下文才以数值对待;
数组:[]中以逗号分隔元素列表;
布尔型值:true, false;
hash:{}中以逗号分隔k/v数据列表; 键为字符型,值为任意puppet支持的类型;{ 'mon' => 'Monday', 'tue' => 'Tuesday', };
undef:未定义 ;
(2)正则表达式:
(?<ENABLED OPTION>:<PATTERN>)
(?-<DISABLED OPTION>:<PATTERN>)
OPTIONS:
i:忽略字符大小写;
m:把.当换行符;
x:忽略<PATTERN>中的空白字符
(?i-mx:PATTERN)
不能赋值给变量 ,仅能用在接受=或!操作符的位置;
(3)puppet的变量种类
facts:
由facter提供;top scope;
内建变量:
master端变量
$servername, $serverip, $serverversion
agent端变量
$clientcert, $clientversion, $environment
parser变量
$module_name
用户自定义变量:
变量有作用域,称为Scope;
top scope: $::var_name
node scope
class scope
使用facter -p来查看facts变量
(4)puppet流程控制语句:
if语句:
if CONDITION {
...
} else {
...
}
CONDITION的给定方式:
(1) 变量
(2) 比较表达式
(3) 有返回值的函数
示例:
vim pkg.pp
$pkgname='nginx'
package {'installpkg':
name => "$pkgname",
ensure => latest,
}
或者
$pkgname = 'nginx'
package{"$pkgname":
ensure => latest,
}
if语句
vim if.pp
if $osfamily == 'Debian' {——if $osfamily =~/(?i-mx:debian)/
$apachename = 'apache2'
} else {
$apachename = 'httpd'
}
package{"$apachename":
ensure => latest,
}
以上为双分支写法,还可以定义多分支写法
vim if2.pp
if $osfamliy == 'Debian' {
$apachename = 'apache2'
} elsif $osfamily == 'Windows'{
$apachename = 'apache'
} else {
$ apachename = 'httpd'
}
package {" $anachename":
ensure => latest,
}
case语句
case CONTROL_EXPRESSION {
case1: { ... }
case2: { ... }
case3: { ... }
...
default: { ... }
}
CONTROL_EXPRESSION:
(1) 变量
(2) 表达式
(3) 有返回值的函数
各case的给定方式:
(1) 直接字串;
(2) 变量
(3) 有返回值的函数
(4) 正则表达式模式;
(5) default
示例:
vim case.pp
case $osfamily{
"Windows" : { $webpkg = 'apache' }
/(?i-mx: debian)/: { $webpkg = 'apache2' }
default: { $webpkg = 'httpd' }
}
selector 语句
CONTROL_VARIABLE ? {
case1 => value1,
case2 => value2,
default => valueN,
}
CONTROL_VARIABLE的给定方法:
(1) 变量
(2) 有返回值的函数
各case的给定方式:
(1) 直接字串;
(2) 变量
(3) 有返回值的函数
(4) 正则表达式模式;
(5) default
注意:不能使用列表格式;但可以是其它的selecor;
示例:
vim selector.pp
$webpkg = $osfamily ? {
"Windows" => 'apache',
/(?i-mx: debian)/ => 'apache2',
default => 'htttpd',
}
package {"$webpkg":
ensure => latest,
}
class(类)语句
类:puppet中命名的代码模块,常用于定义一组通用目标的资源,可在puppet全局调用;
类可以被继承,也可以包含子类;
语法格式:
class NAME {
...puppet code...
}
class NAME(parameter1, parameter2) {
...puppet code...
}
类代码只有声明后才会执行,调用方式:
(1) include CLASS_NAME1, CLASS_NAME2, ...
(2) class{'CLASS_NAME':
attribute => value,
}
示例:
vim class.pp
class instweb{
$webpkg = $osfamily ? {
"Windows" => 'apache',
/(?i-mx:debian)/ => 'apache2',
default => 'httpd',
}
package { "$webpkg":
ensure => latest,
}
include instweb——调用class类,否则不生效
class的综合应用
vim class2.pp
class dbserver ($pkg,$srv) {
package{"$pkg":
ensure => latest,
}
service{"$srv":
ensure => running,
}
}
if $operatingsystem == "CentOS" or $operatingsystem == "RedHat"{
case $operatingsystemmajrelease {
'7': { $pkgname = 'mariadb-server' $srvname='mariadb'}
default: { $pkgname = 'mysql-server' $srvname= 'mysqld' }
}
}
class{'dbserver':——子类继承父类
pkg => "$pkgname",
srv => "$srvname",
}
class的类继承方法
首先mkdir redis.modules/
cd redis.modules/
cp /etc/redis.conf ./redis-salve.conf
cp /etc/redis.conf ./redis-master.conf
实现redis的主从复制,先在172.18.254.242上进行配置,然后将redis.pp复制到172.18.250.89(作为主节点),再讲redis.conf复制到172.18.250.223(从节点)
vim redis.pp
1 class redis {
2 package{'redis' :
3 ensure => latest,
4
5 }
6
7 service{'redis':
8
9 ensure => running,
10 enable => true,
11 hasrestart => true,
12 restart => 'systemctl restart redis ',
13 require => Package['redis'],——定义依赖关系
14 }
15
16
17 }
18 class redis::master inherits redis {
19 file{'/etc/redis.conf':
20
21 ensure => file,
22 source => '/root/redis.module/redis-master.conf',
23 owner => redis,
24 group => root,
25 require => Package['redis'],——依赖package
26 }
27
28 Service['redis'] {——在子类中为父类的资源新增属性或覆盖指定的属性的值:
29
30 restart => 'systemctl restart redis.service',
31 # require +> File['/etc.redis.conf'],——代表覆盖父类资源
32 subscribe => File['/etc/redis.conf'],
33 }
34 }
35
36
37 class redis::slave inherits redis {
38 file{'/etc/redis.conf':
39
40 ensure => file,
41 source => '/root/redis.module/redis-slave.conf',
42 owner => redis,
43 group => root,
44 require => Package['redis'],
45 }
46
47 Service['redis'] {
48
49 restart => 'systemctl restart redis.service',
50 # require +> File['/etc.redis.conf'],
51 subscribe => File['/etc/redis.conf'],
52 }
53 }
54 # include redis::master
此时在主节点上执行
只需要在最后一行中调用 include redis::master
在从节点上执行 include redis:: slave
为了效果显著在从节点上 vim redis.module/redis-slave.conf
加入slaveof 172.18.250.89 6379
在主从节点上安装puppet,执行puppet apply -v redis.conf
此时vim /etc/redis.conf(在从节点上)
搜狗截图20171125213958.png
设置成功
在主节点上redis-cli
set key '' who am i''
get key
在从节点上也能显示"who am i"
则证明主从复制成功
四.puppet模板
erb:模板语言,embedded ruby;
puppet兼容的erb语法:
https://docs.puppet.com/puppet/latest/reference/lang_template_erb.html
file{'title':
ensure => file,
path =>
content => template('/PATH/TO/ERB_FILE'),
}
文本文件中内嵌变量替换机制:
<%= @VARIABLE_NAME %>
示例:定义nginx类的调用,实现模板的调用erb
mkdir nginx.module/
mv nginx.conf nginx.conf.erb
vim nginx.conf.erb
搜狗截图20171126215917.png
vim nginx.pp
1 class nginx{
2
3 package { 'nginx':
4 ensure => latest,
5
6
7 } ->
8 file {'/etc/nginx/nginx.conf':
9 ensure => file,
10 content => template('/root/nginx.module/nginx.conf.erb'),——以content形式导入模板,相当于source
11
12
13 } ~>
14 service{'nginx':
15
16 ensure => running,
17 }
18 }
19
20 include nginx
puppet apply -v nginx.pp
执行完成后vim /etc/nginx/nginx.conf
搜狗截图20171126220340.png
在从节点上 cd redis.module/
cp redis-slave.conf redis-slave.conf.erb
vim redis.pp
class redis {
package{'redis' :
ensure => latest,
}
service{'redis':
ensure => running,
enable => true,
hasrestart => true,
restart => 'systemctl restart redis ',
require => Package['redis'],
}
}
class redis::master inherits redis {
file{'/etc/redis.conf':
ensure => file,
source => '/root/redis.module/redis-master.conf',
owner => redis,
group => root,
require => Package['redis'],
}
Service['redis'] {
restart => 'systemctl restart redis.service',
# require +> File['/etc.redis.conf'],
subscribe => File['/etc/redis.conf'],
}
}
注意:class redis::slave($masterip,$masterport='6379') inherits redis {
file{'/etc/redis.conf':
ensure => file,
注意: content => template('/root/redis.module/redis-slave.conf.erb'),
owner => redis,
group => root,
require => Package['redis'],
}
Service['redis'] {
restart => 'systemctl restart redis.service',
# require +> File['/etc.redis.conf'],
subscribe => File['/etc/redis.conf'],
}
}
#include redis::slave——注释lnclude
class{'redis::slave':
masterip => '172.18.250.89',——直接调用子类
}
此配置是为了实现自动化对redis的主从复制
因此为了使效果显著,在从节点上执行 rpm -e redis
rm -rf /etc/redis.rpmconf
此时再执行 puppet apply -v redis.conf
搜狗截图20171125213801.png
ss -ntl
搜狗截图20171125213928.png
vim /etc/redis.conf
搜狗截图20171125213958.png
网友评论