自动化运维工具之puppet简单实用

一、简介

什么是puppet编辑

   puppet是一种Linux、Unix、windows平台的集中配置管理系统,使用自有的puppet描述语言,可管理配置文件、用户、cron任务、软件包、系统服务等。puppet把这些系统实体称之为资源,puppet的设计目标是简化对这些资源的管理以及妥善处理资源间的依赖关系。

   puppet采用C/S星状的结构,所有的客户端和一个或几个服务器交互。每个客户端周期的(默认半个小时)向服务器发送请求,获得其最新的配置信息,保证和该配置信息同步。每个puppet客户端每半小时(可以设置)连接一次服务器端, 下载最新的配置文件,并且严格按照配置文件来配置服务器. 配置完成以后,puppet客户端可以反馈给服务器端一个消息. 如果出错,也会给服务器端反馈一个消息.

为什么要开发puppet

   系统管理员都喜欢自己写点小工具来让自己的工作完成的更快或者更好, 不管是在大企业管理大量的服务器还是只管理两三台机器. 但是很少人会把他们的工具发布出来. 也就是是说极少有工具能被重用,或者说很多工具就只能在所在的组织内部有用.拷贝给别的组织,他们也用不上. 也就是说,每个系统管理员,在一个新的公司,都会另起炉灶开发一套基于ssh,for循环的"系统"来帮助自己完成系统管理任务.
   开发puppet是为了让系统管理员可以相互交流和共享成熟的工具,避免重复的劳动.通过以下两个特性来实现这一目标:
   提供一个简洁的但是强大的框架来完成系统管理任务
   系统管理任务可以描述成puppet语言,因此可以相互分享代码,就像分享其他语言的代码一样,比如python, c等

   因此,作为系统管理员的你可以更快的完成工作,因为你可以用puppet来处理所有的管理细节. 甚至你还可以下载其他管理员的puppet代码来让你的工作完成的更快.

使用puppet的稳定性

   puppet与其他手工操作工具有一个最大的区别就是 puppet的配置具有稳定性,因此你可以多次执行puppet, 一旦你更新了你的配置文件,puppet就会根据配置文件来更改你的机器配置,通常每30分钟检查一次. puppet会让你的系统状态同配置文件所要求的状态保持一致. 比如你配置文件里面要求ssh服务必须开启. 假如不小心ssh服务被关闭了,那么下一次执行puppet的时候,puppet会发现这个异常,然后会开启 ssh 服务. 以使系统状态和配置文件保持一致.puppet就象一个魔术师,会让你的混乱的系统收敛到puppet配置文件所想要的状态.

   可以使用puppet管理服务器的整个生命周期,从初始化到退役.不同于传统的例如sun的Jumpstart或者redhat的Kickstart, puppet可以长年让服务器保持最新状态.只要一开始就正确的配置他们,然后再也不用去管他们.通常puppet用户只需要给机器安装好puppet并让他们运行,然后剩余的工作都由puppet来完成.

二、ruby语言简介

   Ruby,一种为简单快捷的面向对象编程(面向对象程序设计)而创的脚本语言,在20世纪90年代由日本人*(まつもとゆきひろ/Yukihiro Matsumoto)开发,遵守GPL协议和Ruby License。它的灵感与特性来自于 Perl、Smalltalk、Eiffel、Ada 以及 Lisp 语言。由 Ruby 语言本身还发展出了JRuby(Java 平台)、IronRuby(.NET 平台)等其他平台的 Ruby 语言替代品。Ruby的作者于1993年2月24日开始编写Ruby,直至1995年12月才正式公开发布于fj(新闻组)。因为Perl发音与6月诞生石pearl(珍珠)相同,因此Ruby以7月诞生石ruby(红宝石)命名。

ruby语言的特色

   完全面向对象:任何东西都是对象,没有基础类型

   变量没有类型(动态类型)

   任何东西都有值,不管是四则运算,逻辑表达式还是一个语句,都有回传值

   运算符重载

   垃圾回收

   强类型

   变量无需声明

   在Windows上,加载DLL

三、puppet的基本应用

1、puppet的资源

   如果把OS的所有配置,如用户账号,特定的文件、文件所述的目录、运行的服务、程序包已经cron任务等,看作是许多独立原子单元的集合的话,这些所谓的“单元”就是“资源”,不过,这些资源在其大小、复杂程度已经声明周期的跨度上等多个维度上可能会各不相同

   通常来说,类属于同一种资源的属性是相近的,如用户账号则有其用户名、UID、GID等组成。但,即便是同一种资源,其在不要OS上的实现方式却又可能个不相同,例如,在windows上和linux上启动和体***务的方式相去甚远

   因此,puppet从以下三个维度来都资源完成抽象

       相似的资源被抽象成同一种资源“类型,”如程序包资源、用户资源及服务资源等

       将资源属性或状态的描述与其实现方式剥离开来,如仅说明安装一个程序包而不用关系其具体是yum、pkgadd、prots或其他方式实现

       仅描述资源的目标状态,也即期望其实现的结果,而不是其具体过程,如“确定nginx运行起来”而不是具体描述为“运行nginx命令将启动起来”

   这三个也被称为puppet的资源抽象层(RAL)。RAL由type(类型)和provide(提供者,即不同的OS上的特定实现)组成

2.puppet资源结构

   在为puppet定义一个资源时,需要为其指定所属的类型和资源标题,并同时配置一系列的属性和对应的值。puppet通过特有的语言来描述和管理资源。

   这种语法被称作为“资源申报(resource declaration)”,它是  puppet语言的核心组成部分,在定义中,仅描述了资源的目标状态而没有提到为达到目标所需要采取的任何步骤。

    puppet有很多内置的资源类型,而通过安装插件还可以继续新增额外的类型,可以通过puppet官方的类型参考页面(http://docs.puttetlabs.com.references/latest/type.html)获取详细的信息。也可以通过“puppet describe ”命令来获取puppet当前所支持的类型列表及每种类型的详细信息。

3.puppet的安装

   puppet的安装可以使用源码安装,也可以使用rpm(官方提供)、epel源、官方提供的yum仓库来安装(通过下载官方提供的rpm包可以指定官方的yum仓库)

   puppet现在常用的版本有两个,分别为2.7系列和3系列,本处使用的为2.7系列,下面使用到的命令可能在3系列不适用

   本处使用的为epel源中提供的版本

1
[root@node1 ~]# yum install puppet -y

4.定义资源

   如前所述,资源是puppet用于模型化系统配置的基础单元,每个资源都从某个角度描述了系统属性,如某程序包必须安装或某用户必须移除等,在puppet,用于完成此类功能的代码也即“资源申报”

   type { ‘title’:

   atttibue    => value,    

}

   资源的文件统一以.pp结尾

   在定义时,资源类型必须使用小写字母,而资源名称仅是一个字符串,但要求在同一类型中期必须唯一

5.puppet资源资源介绍

       1)package:puppet管理软件包

           puppet支持使用的软件包管理器:yum,rpm,apt,prots,gem,msi,dpkg,pkg

           常用的参数:

           ensure:程序包的目标状态,值有present(installed)、absent(不存在)、purged、held、latest

           name:资源的名称,即软件包的名字,可以省略,如果省略,将继承title的值

           provide:软件包管理器

           source:指定程序包文件路径

           install_options:安装选项,最常用的是通过INATALLDIR来制定安装目录

1
2
3
4
5
6
7
8
9
10
11
[root@node1 script]# rpm -q nginx
package nginx is not installed
package 'nginx':
        ensure => installed,
        name   => nginx,
}
[root@node1 script]# puppet apply test.pp
notice: /Stage[main]//Package[nginx]/ensure: created
notice: Finished catalog run in 26.11 seconds
[root@node1 script]# rpm -q nginx
nginx-1.0.15-5.el6.x86_64

       2)service:用于定义服务的状态等

           常用的参数

           ensure:服务的额目标状态,值有true(running)和false(stopped)

           enable:是否开机自动启动,值有true和false

           name:服务名称,可以省略,如果省略,将继承title的值

           path:服务脚本路径,默认为/etc/init.d/下

           start:定制启动命令

           stop:定制关闭命令

           restart:定制重启命令

           status:定制状态

1
2
3
4
5
6
7
8
9
service { 'nginx':
        ensure => true,
        enable => true,
}
[root@node1 script]# puppet apply test.pp
notice: /Stage[main]//Service[nginx]/ensure: ensure changed 'stopped' to 'running'
notice: Finished catalog run in 1.52 seconds
[root@node1 script]# service nginx status
nginx (pid  37956is running...

      3)file:管理文件、目录、软链接;生成文件内容;管理文件权限、属性;也可以通过source属性到指定位置下载文件;通过recurse属性来获取目录

           常用参数:

           ensuce:目标状态,值有*absent*, *present*, *file*, 和*directory*.

           backup:通过filebacket资源来备份文件,值通常为filebucket资源的名称

           content:文件内容,生成方式有三种(content,source,target),三者彼此互斥

           source:通过制定的url下载文件至本地,获取文件格式为:puppet:///modules/MODULE_NAME/file_names

           target:为符号链接指定目标

           links:文件为符号连接,值为“follow”,“manage”

           path:文件路径,必须使用双引号

           mode:定义权限,通常为8进制数字

           owner: 定义文件的属主

           group:定义文件的属组

           force:强制执行删除文件、链接或目录、仅用于ensure为absent时

           purge:清除指定目录中存在的,但未在资源中定义的文件

           resurce:目录递归,值为true,false,inf,remote

           replace:替换,本地存在的文件与资源中指定的文件内容不同时是否执行替换,默认为否

1
2
3
4
5
6
7
8
9
10
11
12
file {'fstab.link':
        ensuce => present,
        target => '/etc/fstab',
        links  => 'follow',
        path   => "/tmp/fstab.link"
}
[root@node1 script]# puppet apply test1.pp
notice: /Stage[main]//File[fstab.link]/ensure: created
notice: Finished catalog run in 0.04 seconds
[root@node1 script]# ll /tmp/
total 8
-rw-r--r--  1 root root    0 Apr 23 23:18 fstab.link

       4)exec: 执行命令,通常在不得不用时才使用,慎用,通常用于完成puppet自身无法完成的功能

           常用的参数:

           command:要执行的命令,通过为命令文件的完整路径

           path:命令搜索路径

           group:执行命令的组

           user:执行命令的用户

           onlyif:0,表示仅在命令的状态返回值为0时才执行此命令

           refresh:定义接受的其他资源的通知时,则要重新执行此命令

           refreshonly:仅被当被依赖的资源发生改变时才被触发

           tries:尝试次数,默认为1

           try_sleep:多次尝试之间的时间间隔

1
2
3
4
5
6
7
8
9
10
exec {'mkdir abc':
        command => 'mkdir /tmp/abc',
        path    => '/bin:/sbin:/usr/bin:/usr/sbin',
        }
[root@node1 script]# puppet apply test2.pp
notice: /Stage[main]//Exec[mkdir abc]/returns: executed successfully
notice: Finished catalog run in 0.11 seconds
[root@node1 script]# ll /tmp/
total 12
drwxr-xr-x  2 root root 4096 Apr 23 23:29 abc

       5)group:管理系统上的用户组

           常用参数:

           ensure:目标状态,present,absent

           name:组名

           gid:GID

           system:系统组    

1
2
3
4
5
6
7
8
9
10
group { 'wangfeng':
        ensure => present,
        name   => wangfeng,
        gid    => 1000,
}
[root@node1 script]# puppet apply test3.pp
notice: /Stage[main]//Group[wangfeng]/ensure: created
notice: Finished catalog run in 0.12 seconds
[root@node1 script]# tail -1 /etc/group
wangfeng:x:1000:

       6)user:管理用户

           常用参数:

           ensure:目标状态,present,absent

           name:用户名

           uid:用户uid

           system:系统用户

           home:用户家目录

           shell:用户默认shell

           gid:用户的gid

           password:密码,使用加密后密码

           magagehome: 是否创建家目录,默认为false

1
2
3
4
5
6
7
8
9
10
user { 'testuser':
        ensure => present,
        name   => testuser,
        uid    => 600,
        home   => '/home/user',
        shell  => '/bin/bash',
        gid    => 1000,
        password => '$1$557750f4$l7Kg7QcRc8k8jgHZ/IYEn/',
        managehome => true,
}

       7)cron:定义周期性任务

           常见属性

           ensure:目标状态,present,absent

           command:命令或脚本

           environment:运行时的环境变量

           hour:小时

           mouth:月

           monthday:日

           weekday:周

           minute:分

           name:名称

           user: 默认为root

1
2
3
4
5
6
7
8
cron { 'ntpdate':
        ensure => present,
        command => '/usr/sbin/ntpdate "time.windows.com" &> /dev/null',
        minute  => '*/5',
}
[root@node1 script]# crontab -l
# Puppet Name: ntpdate
*/5 * * * * /usr/sbin/ntpdate "time.windows.com" &> /dev/null

       8)notify:调试输出

            常用参数:

            message:信息

            name:信息名称

6.puppet资源引用

       使用Type['title'],首字母必须大写

7.puppet的特殊属性

       Enurse:用于控制支援什邡存在元参数,用于定资源间的依赖关系,及应用次序,通知机制等,其有四个资源“require”、“before”、“notify”、“subscribe”

       require:表示需要依赖于某个资源,如上面用服务和状态的定义应该为状态应该依赖于服务安装成功,则可以写成如下所示

1
2
3
4
5
6
7
8
9
package 'nginx':
        ensure => installed,
        name   => nginx,
}
service { 'nginx':
        ensure => true,
        enable => true,
        require => Package['nginx'],
}

       before:表示应该先执行本资源,在执行别的资源,如上面用服务和状态的定义应该为状态应该依赖于服务安装成功,则可以写成如下所示

1
2
3
4
5
6
7
8
9
service { 'nginx':
        ensure => true,
        enable => true,
}
package 'nginx':
        ensure => installed,
        name   => nginx,
        before => Service ['nginx'],
}

       notify: 表示将当前资源的变动信息通知给别的资源,为通知的发出者。如上面用服务和状态的定义应该为状态应该依赖于服务安装成功,则可以写成如下所示

1
2
3
4
5
6
7
8
9
service { 'nginx':
        ensure => true,
        enable => true,
}
package 'nginx':
        ensure => installed,
        name   => nginx,
        notify => Service ['nginx'],
}

       subscribe:表示定义某资源的变动信息,为通知的接收者,如上面用服务和状态的定义应该为状态应该依赖于服务安装成功,则可以写成如下所示

1
2
3
4
5
6
7
8
9
package 'nginx':
        ensure => installed,
        name   => nginx,
}
service { 'nginx':
        ensure => true,
        enable => true,
        subscribe => Package['nginx'],
}

   依赖关系还可以使用->和~>来表示

   -> 表示后资源需要依赖前资源

   ~> 表示前资源变动通知后资源调用

8.puppet的变量

     puppet的变量名称以“$”开头,赋值操作符为“=”,应用变量值为“”,或者什么都不写

     puppet的变量可以接受的数据类型

       布尔型:true和false,不能加引号,if语句的测试条件和比较表达式都会返回布尔型值,另外,其他数据类型也可以自动转换为布尔型,如空字符串为false等

       undef:从未声明的变量的值类型即为undef,也可以手动为某变量赋予undef值,即直接使用不加引号的undef字符串

       字符型:非结构化的文本字符串,可以使用引号,也可以不用。单引号中的变量不会替换,而双引号中的能够进行变量替换;字符型也支持使用转移符

       数值型:可为整数或浮点数,不过,puppe只有在数值上下文才把数值当数值对待,其他清理下一律以字符型处理

       数组:数组值为中括号“[]”中的以逗号分隔的项目列表,最后一个项目后面可以有逗号;数组中的袁术可以为任何可用数据类型,包括hash或其他数组,属组索引为从0开始的整数,也可以使用负数索引

       hash:即为外键值数据类型,键和值之间使用“=>”分隔,键值对定义在“{ }”中,彼此间以逗号分隔;其键位字符型数据,而值可以为puppet支持的任意数据类型,访hash类型的数据元素要使用“键”当作索引进行

       正则表达式:属于puppet的非标准数据类型,不能赋值给变量,仅能用于有限的几个接收正则表达式的地方,即接受使用“=~”及“!~”匹配操作符的位置,通常包括case语句中的selector,已经节点名称匹配的位置,他们不能传递给函数或用于资源属性的定义

        facter变量:可以通过facter查看

        内置变量

           agent端:$environment,$clientcert,$clentbversion

           server端:$servername,$serverip,$serverversion

      puppet中的正则表达式支持使用(?<ENABLED OPTION>:<SUNPATTERN>)和(?-<DISABLED OPTION>:<SUNPATTERN>)两个特殊的符号,如下面的示例,表示做正则表达式匹配时启用选项“i(忽略字符大小写)”,但不支持使用“m(把.当作换行符)”和启用“x(忽略模式中的空白字符和注释)”

1
2
3
4
$packages = $operatingsystem ? {
      /(?i-mx:ubuntu|debian)/        => 'apache2',
      /(?i-mx:centos|fedora|redhat)/ => 'httpd',
    }

9.puppet的判断语句

       puppet的判断语句主要有三种分别为if,case,selector

       puppet的操作符有:比较操作符,布尔操作符,算术操作符,其应用于shell相识

       if语句分为单分支,双分支和多分支        

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
单分支:
    if CONDITION {
        statement
        ...
    }
双分支:
    if CONDITION {
        statement
        ...
    }
    else {
        statement
        ...
    }
多分支:
    if CONDITION {
        statement
        ...
    }
    elsif CONDITION {
        statement
        ...
    }
    else {
        statement
        ...
    }

   例如,判断一个系统的OS,并输出“welcome to OS server”,

1
2
3
4
5
6
7
if $operatingsystem  == /^(?i-mx:centos|fedora|redhat)/ {
    notice "welcome to redhat OSFamily"
}elsif $operatingsystem  == ubuntu {
    notice "welcome to ubuntu server"
}else {
    notice "nokown server"
}

   case语句的语法

1
2
3
4
5
6
case CONTROL_EXPRESS {
        case1,...: { statement... }
        case2,...:{ statement... }
        ... ...
        default:{ statement... }
    }

   上面的例子可以写为    

1
2
3
4
5
6
case $operatingsystem {
     'Solaris':          { notice("Welcome to Solaris") }
     'RedHat''CentOS': { notice("Welcome to RedHat OSFamily") }
     /^(Debian|Ubuntu)$/:{ notice("Welcome to $1 linux") }
     default:            { notice("Welcome, alien *_*") }
   }

   selector语句的用法    

1
2
3
4
5
6
CONTROL_VARIABLE ? {
    case1 => value1
    case2 => value2
    ...
    default => valueN
}

   同样,上面的例子可以写为

1
2
3
4
5
6
7
8
9
$welcome =$operatingsystem ? {
                /^(?i-mx:centos|fedora|redhat)/ => 'redhat OSFamily',
                /^(?i-mx:ubuntu)/        => 'ubuntu',
                /^(?i-mx:debian)/        => 'debebian',
        }
notify { "$welcome":
    message => "welcome to $welcome",
}
~

10.puppet的类

      Class是用于通用目标或目的的一组资源,因此,它是命令的代码块,在某位置创建之后可在puppet全局使用。类似于其他编程语言中的类的功能,puppet的类可以继承,也可以包含子类,定义类的语法如下所示

       class my_class {

        ...puppet code ...

       }

       例如:定义一个名为nginx的类牟其中包含两类资源,一个是package类型的nginx,一个是service的nginx

1
2
3
4
5
6
7
8
9
10
11
class nginx {
package 'nginx':
        ensure => installed,
        name   => nginx,
}
service { 'nginx':
        ensure => true,
        enable => true,
        subscribe => Package['nginx'],
}
}

   需要注意的是,类的名称只能以小写字母开头,可以包含小写字母,数字和下划线,另外,每个类都会一如一个新的变量scope,这意味着在任何时候访问类中的变量时,都得使用其完全限定名称。不过,在本地scop可以重新为top scope的变量赋予一个新值

类声明

   在mainfest文件中定义的类不会直接被执行,他们需要实现声明后才能被执行。例如,把上面定义的nginx类保存下来,尝试执行

1
2
[root@node1 script]# puppet apply test.pp
notice: Finished catalog run in 0.03 seconds

   上述的命令结果显示,其什么也没有执行,因为这里仅定义来类,要声明一个类,需要顶一股哟的类的名称为参数include函数,也可以想声明一个变量一样声明一个来class { ‘class_name’},因此上面的应该写为

1
2
3
4
5
6
7
8
9
10
11
class nginx {
package 'nginx':
        ensure => installed,
        name   => nginx,
}
service { 'nginx':
        ensure => true,
        enable => true,
}
}
include nginx

   再次执行就可以了

带参数的类

    同一个类在不同的os上可能会略有不同,因此需要通过获取相应的系统的fact来实现由区别对待,然后万一响应的OS没有输出类所期望的fact或者是类依赖于非fact因素时,此机制将无法满足需求,此时就需要使用带参数的类来完成此类功能,同时,在声明类时为其参数传递响应的值及可完成传参数功能

   在定义在带参数的类是,需要将参数声明在类名后的小括号“()”,参数可以有默认值,如果使用多个参数,彼此将要使用逗号分隔,在类的内部使用参数的方式通使用本地变量

   在类传递参数时,其方式如同定义资源的属性,因此,其也成为“资源属性风格的类声明”,其语法格式为

   class { 'class_name':

       para1 => value1,

       para2 => value2,

}

   注意,不能再使用include声明类时向其传递参数,也就是说不能再include函数中为声明的类指定参数值,对于带参数的默认值,仍可以使用include声明类,否则,就必须使用“资源属性风格的类声明”,另外,如果使用不同的参数值将某类声明多次,最后生效的声明将很难判断

类的调用

   类可以基于父类调用,在调用时,应该指定通过inherits调用父类。如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class nginx {
package 'nginx':
        ensure => installed,
        name   => nginx,
}
}
class nignx::web inherits nginx {
service { 'nginx':
        ensure => true,
        enable => true,
}
}
include nignx::web
~

   执行结果

1
2
3
4
[root@node1 script]# puppet apply test9.pp
notice: /Stage[main]/Nginx/Package[nginx]/ensure: created
notice: /Stage[main]/Nignx::Web/Service[nginx]/ensure: ensure changed 'stopped' to 'running'
notice: Finished catalog run in 131.49 seconds

   =>:在子类中覆盖父类中的资源

   +>:在子类中为父类中的资源新增额外的属性


本文转自wangfeng7399 51CTO博客,原文链接:http://blog.51cto.com/wangfeng7399/1412140,如需转载请自行联系原作者


上一篇:puppet重申证书


下一篇:[redis设计与实现][5]基本数据结构——整数集合