1  概述


资源是puppet最基本的元素,每个资源的定义都具有标题、类型以及一系列的属性。puppet的特性就是处理资源与资源之间的依赖关系。任何相同类型的资源都会具有一些相同的属性。

资源定义有如下的特性: 

    a.puppet使用title在编译时期区分每个资源,使用命名变量(namevar)在执行时区分资源。 

    b.在同一类资源中title和namevar都是唯一的。 

    c.每个类型都有部分属性有默认值 

    d.如果不指定namevar,则默认赋予其title的值。

在puppet 3.6.2版本中,通过命令puppet describe --list可以查看到目前有49种资源类型,本文将结合例子介绍puppet的8种核心资源类型,如下

group: 组

user:用户

packge:程序包 

service:服务

file:文件

exec:执行自定义命令,要求幂等

cron:周期性任务计划

notify:通知

其他类型的解释见如下的文档

https://docs.puppet.com/puppet/5.2/cheatsheet_core_types.html#notify


2  八大核心资源



group:管理组


属性:

name:组名;

gid:GID;

system:是否为系统组,true OR false;

ensure:目标状态,present/absent;

members:成员用户;

例子,定义资源清单,最后一个逗号可以写也可以省略

vim group.pp
group{'mygrp':
        ensure => present,
        name => mygrp66,
        system => true,
}

测试资源情况

puppet apply -v --noop group.pp

如果测试通过,执行资源

puppet apply group.pp

注意:type必须使用小写字符;title是一个字符串,在同一类型中必须惟一;



user:管理用户


Manage users.

属性:

name:用户名;

uid: UID;

gid:基本组ID;

groups:附加组,不能包含基本组;

comment:注释; 

expiry:过期时间 ;

home:家目录; 

shell:默认shell类型;

system:是否为系统用户 ;

ensure:present/absent;

password:加密后的密码串; 

例子

例子,创建一个用户,同时指定多个附加组,用列表实现,这里假设组testgrp不存在,执行会报错,所以可以定义依赖关系,用require来定义依赖关系,这里引用的是资源,所以对应的资源要存在,假设sunnygrp组已经存在,但是还是要定义mygrp这个资源

vim user.pp 
group{'testgrp':
    ensure => present,
}
user{'myuser':
    ensure => present,
    name => 'sunny66',
    uid => 3333,
    shell => '/sbin/nologin',
    groups => [sunnygrp,testgrp],
    require => [Group['sunnygrp'],Group['testgrp']],
}
group{'sunnygrp':
    ensure => present,
    system => true,
    name => 'sunnygrp',
}

关系元参数:before/require

A before B: B依赖于A,定义在A资源中;

{
...
before => Type['B'],
...
}

B require A: B依赖于A,定义在B资源中;

{
...
require => Type['A'],
...
}

例子

依赖关系的第二种定义方法

vim  user.pp
group{'testgrp1':
    ensure => present,
    before => User['myuser1'],
}
user{'myuser1':
    ensure => present,
    name => 'sunny666',
    uid => 2222,
    shell => '/sbin/nologin',
    groups => [sunnygrp1,testgrp1],
}
group{'sunnygrp1':
    ensure => present,
    system => true,
    name => 'sunnygrp1',
    before => User['myuser1'],
}



package:管理程序包


属性:

ensure:installed, present, latest, absent, any version string (implies present)

name:包名;注意,如果不指定name,name这个名字默认就是title的名称

source:程序包来源,仅对不会自动下载相关程序包的provider有用,例如rpm或dpkg;不同操作系统的安装方法,一般不需要指定,因为会自动安装

例子,安装redis包

package{'redis':
    ensure => latest,
}



service:管理运行服务


属性:

ensure:服务是否应该运行。 有效值为“stopped”(也称为“false”),“runnig”(也称为“true”)。

enable:是否应开机启用服务。 有效值为“true”,“false”,“manual”。

name:启动的服务名称

path:用于查找init脚本的搜索路径。 多个值应该用冒号分隔或作为数组提供..脚本的搜索路径,默认path为/etc/init.d/;

hasrestart:是否支持启动功能,如果是true,则直接restart,如果如false,就执行stop后执行start来重启,默认false

hasstatus:默认true

start:手动定义启动命令;

stop:停止服务

status:服务状态

restart:手动指定*restart*命令。 如果未指定,服务将被停止,然后启动。 通常用于定义reload操作;

binary:启动服务,启动程序的路径

path:启动服务脚本路径

例子

启动redis,有依赖关系,假设卸掉redis,但是测试还是正常,真正执行的时候却失败了,因此启动服务脚本中定义资源package

vim service.pp
service{'redis':
    ensure => running,
    enable => true,
    hasrestart => true,
    restart => 'systemctl restart redis',
    require => Package['redis'],
}
package{'redis':
    ensure => latest,
}



file:管理文件


管理文件其内容,所有权和权限。

ensure:文件是否应该存在,如果是,应该是什么类型的文件。 可能的值是“present”,“absent”,“file”,“directory”和“link”。

file:类型为普通文件,其内容由content属性生成或复制由source属性指向的文件路径来创建;

link:类型为符号链接文件,必须由target属性指明其链接的目标文件;

directory:类型为目录,可通过source指向的路径复制生成,recurse属性指明是否递归复制;

path:文件路径;

source:源文件;

content:文件内容;

target:符号链接的目标文件; 

owner:属主

group:属组

mode:权限;

atime/ctime/mtime:时间戳;

例子

复制文件,先复制一份redis配置文件,并进行修改,然后通过puppet的file进行复制到对应主机

cp /etc/redis.conf /root、
vim file.pp
file{'/etc/redis.conf':
    ensure => file,
    source => '/root/redis.conf',
    owner => 'redis',
    group => 'root',
    mode => '0644',
}

例子

复制目录,注意,以下recurse对应的true是布尔值,不能加引号

vim file.pp
file{'yumfile':
    ensure => directory,
    path => '/tmp/yum.repos.d/',
    source => '/etc/yum.repos.d/',
    recurse => true,
}

复制文件到目录下,那么ensure为directory配置就不会生效,直接复制为一个文件,而不生成目录,即如下的例子中,只会在/tmp下生成一个名为test的文件,内容是/etc/issue的内容,而不会在/tmp/test这个目录下生成issue这个文件

file{'test':
    ensure => directory,
    path => '/tmp/test',
    source => '/etc/issue',
}

如果有多个源文件,那么只会复制一个文件,如下例子,只会复制/etc/issue这个文件,新生成的文件名为/tmp/test

file{'test':
    ensure => directory,
    path => '/tmp/test',
    source => ['/etc/issue','/etc/group'],
}

例子

创建链接

file{'redis.conf':
    ensure => link,
    path => '/tmp/redis.conf',
    target => '/etc/redis.conf',
}

通知元参数:订阅或者通知关系

A notify B:B依赖于A,接受由A触发refresh;

B subscribe A:B依赖于A,接受由A触发refresh;B监控A资源的内容变化,一定A变化,B要跟着发送对应变化

在一资源中,notify和suscribe只需要二者选一个即可

通知关系:通知相关的其它资源进行“刷新”操作;

notify

A notify B:B依赖于A,且A发生改变后会通知B;

{
...
notify => Type['B'],
...
}

subscribe

B subscribe A:B依赖于A,且B监控A资源的变化产生的事件;

{
...
subscribe => Type['A'],
...
}

例子:

安装redis,并启动redis服务,当redis的配置文件更改时,重启服务,用通知链表示表示依赖关系

vim service.pp
service{'redis':
    ensure => running,
    enable => true,
    hasrestart => true,
    restart => 'systemctl restart redis',
    subscribe => File['redis.conf'],
}
package{'redis':
    ensure => latest,
}
file{'redis.conf':
    path => '/etc/redis.conf',
    source => '/root/redis.conf',
    ensure => file,
    mode => '0644',
    owner => redis,
    group => root,
#   notify => Service['redis'],
}
Package['redis'] -> File['redis.conf'] ~> Service['redis']

以上例子,可以改成如下,->表示package先于file,~>表示file先于service,同时service订阅了file

#service with notify or subscribe
package{'redis':
    ensure => latest,
} ->
file{'redis.conf':
    path => '/etc/redis.conf',
    source => '/root/redis.conf',
    ensure => file,
    mode => '0644',
    owner => redis,
    group => root,
} ~>
service{'redis':
    ensure => running,
    enable => true,
    hasrestart => true,
    restart => 'systemctl restart redis',
}

依赖关系:当某个条件执行后,才会执行

before

require

例子:

以下例子,File['test.symlink']依赖于File['test.txt'],只有当File['test.txt']资源执行,File['test.symlink']才会被执行

file{'test.txt':
path    => '/tmp/test.txt',
ensure  => file,
source  => '/etc/fstab',
}
file{'test.symlink':
path    => '/tmp/test.symlink',
ensure  => link,
target  => '/tmp/test.txt',
require => File['test.txt'],
}

以上例子可以调整如下,资源File['test.txt']执行后才会执行File['test.symlink']

file{'test.txt':
    path => '/tmp/test.txt',
    ensure => file,
    source => '/etc/fstab',
    before => File['test.symlink'],
}
file{'test.symlink':
    path => '/tmp/test.sysmlink',
    target => '/tmp/test.txt',
    ensure => link,
}



exec:执行外部命令


exec资源中的任何命令必须能够运行多次而不会造成危害,即它必须是幂等的。

**command** (*namevar*):要运行的命令;

cwd:运行该命令的目录。

creates:文件路径,仅此路径表示的文件不存在时,command方才执行;

user/group:运行命令的用户身份;

path:用于命令执行的搜索路径。 如果没有指定路径,则命令必须是绝对路径。

onlyif:此属性指定一个命令,此命令正常(退出码为0)运行时,当前command才会运行;

unless:此属性指定一个命令,此命令非正常(退出码为非0)运行时,当前command才会运行;

refresh:重新执行当前command的替代命令;

refreshonly:仅接收到订阅的资源的通知时方才运行;

例子:

创建目录

mkdir不幂等,因此定义creates,只有/tmp/hi.dir/不存在时才执行command

vim exec.pp
exec{'makedir':
    command => 'mkdir /tmp/hi.dir',
    path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin',
    creates => '/tmp/hi.dir',
}

创建用户

其中,unless表示只有id sunny88失败,那么command命令才会执行,如果id sunny88成功了,那么command命令就不会执行,即只有sunny88用户不存在的时候才会执行command命令

exec{'createuser':
    command => 'useradd sunny88',
    path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin',
    unless => 'id sunny88',
}

安装包

安装tree服务包,只有查找到sunny仓库时才会执行command

exec{'installpkg':
    command => 'yum -y install tree',
    path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin',
    onlyif => 'yum repolist | grep -i sunny',
}

newaliases是一个命令,只有当/etc/aliases文件发送变化后才会执行newaliases

file{'/etc/aliases':
    path => '/etc/aliases',
    ensure => file,
    before => Exec['newaliases'],
}
exec{'newaliases':
    path => ["/usr/local/sbin","/usr/local/bin","/usr/local","/usr/bin"],
    subscribe => File["/etc/aliases"],
    refreshonly => true,
}

例子

为redis服务器生成配置文件,当模板配置文件改变时,通知exec['backupfile'],并且将本次变化后的配置文件备份到指定路径下,这里有个问题是,第一次的配置文件将不会被备份,因此要手动备份第一次的配置文件

注意,以下的cp命令不幂等,因此复制的时候,建议将备份的文件新增名字后加上时间为后缀。为redis.conf-$(date +%F-%H-%M-%S),防止冲突

file{'/etc/redis.conf':
        source => '/root/redis.conf',
        ensure => file,
}
exec{'mkdir':
    command => 'mkdir /backups',
    path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin',
    unless => 'ls /backups',
    before => Exec['backupfile'],
}
exec{'backupfile':
    command => "cp /etc/redis.conf /backups/redis.conf-$(date +%F-%H-%M-%S)",
    path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin',
    refreshonly => true,
    subscribe => File['/etc/redis.conf'],
}



cron:安装和管理cron作业


每个由Puppet创建的cron资源都需要一个命令和至少一个周期性属性(小时,分钟,月份,月份,工作日或特殊)。

command:要执行的任务;

ensure:present/absent;

hour:

minute:

monthday:

month:

weekday:

user:以哪个用户的身份运行命令

target:添加为哪个用户的任务

name:cron job的名称;

示例:每隔三分钟执行一次计划任务

cron{'timesync':
command => '/usr/sbin/ntpdate 172.18.50.61 &> /dev/null',
ensure  => present,
minute  => '*/3',
user    => 'root',
}



notify:发送消息


将任意消息发送到代理运行时日志。

属性:

message:信息内容

name:信息名称;

例子

发出通知消息

notify{'hello':
    message => 'hello everyone,I am sunny',
    name => 'Say hello',
}

到这里,8大核心资源的定义和使用介绍完成。