Xiaopei's DokuWiki

These are the good times in your life,
so put on a smile and it'll be alright

User Tools

Site Tools


it:puppet

puppet

  • deployment to agents and master
  • conf lang & resource abstraction layer
    • conf lang
      type { title:
        attr => value,
      }
    • RAL: facter & facts
  • transaction layer, steps:
    1. interpret & compile the confs on master
    2. communicate to agent
    3. apply the confs on agent
    4. agent report to master

Deployment

0. 安装

  • client
    $ apt-get install puppet 
  • server
    $ apt-get install  puppetmaster
     
    # 安装后 puppet master 会自动运行
    $ ps aux | grep puppet 
    puppet    3022  0.5  7.9 122900 39832 ?        Ssl  15:22   0:00 /usr/bin/ruby1.8 /usr/bin/puppet master --masterport=8140

环境设置

  • DNS
  • firewall: puppet master 使用 8140 端口

1. 配置 master

  • /etc/puppet/puppet.conf
    [master]
    certname=puppet.example.com
  • /etc/puppet/manifests/site.pp
    # The site.pp file tells Puppet where and what configuration to load for our clients.
    # Puppet will not start without the site.pp file being present.

以上两个文件是必须的, 之后便可开始服务:

$ puppet master --verbose --no-daemonize

2. 连接 agent

  • agent 增加基本配置: /etc/puppet/puppet.conf
    [main]
    server=puppet.example.com
  • agent 启动 → master 签名

3. 迭代配置

  • puppet master/agent 都不应自启动 puppet
  • puppet master 需要自启动 puppet-master
  • puppet agent 需要 cron 运行 puppet1)
    # splay 并不是好方法, 因为 splay 期间 puppet 会驻守内存
    # 按 fqdn 随机一个 60 以内的数为分, 每小时 cron 运行, 既简单, 又高效
    # (使用 --splay 在调试时发生 agent 获得不了 category, 检查发现有几个 agent 在 splaying, 怀疑问题在此)
    cron { "puppet":
        command => "/usr/sbin/puppetd --onetime --no-daemonize --logdest syslog > /dev/null 2>&1",
        user => "root",
        minute => fqdn_rand( 60 ),
        ensure => present,
    }

puppet 命令

git-like cli, 如 puppet agent, 看文档可用 man puppet-agent. 以下是常用命令:

检查配置语法

# Validate the default site manifest at /etc/puppet/manifests/site.pp
$ puppet parser validate 
 
# Validate two arbitrary manifest files:
$ puppet parser validate init.pp vhost.pp

resource

puppet resource 使用 RAL 查看/改变系统信息.

# 列出所有用户
$ puppet resource user
# ...
 
# 列出特定用户
$ puppet resource user root
# user { 'root':
# ...
 
# 修改用户(包括新建和删除)
$ puppet resource user dave ensure=present shell="/bin/zsh" home="/home/dave" managehome=true
# notice: /User[dave]/ensure: created
# user { 'dave':
#   ensure => 'present',
#   home   => '/home/dave',
#   shell  => '/bin/zsh',
# }
 
# --edit, 调用编辑器修改用户
$ puppet resource user david --edit

describe

显示资源的帮助信息. 在线帮助

# -s for Short
# -m for Meta Parameters
$ puppet describe user -s -m

apply - 以单机模式运行

# manifest example
## 1.pp
file {
     "/tmp/test":
	content => "hello\n",
	mode => 0644;
}
## 2. pp
package {
  [ "gcc" , "make" ] : # 使用数组
    ensure => installed;
}
 
# apply manifest
$ puppet apply 1.pp

运行 manifests (.pp 文件). puppet 运行流程如图.

Before being applied, manifests get compiled into a “catalog,” which is a directed acyclic graph that only represents resources and the order in which they need to be synced. All of the conditional logic, data lookup, variable interpolation, and resource grouping gets computed away during compilation, and the catalog doesn’t have any of it.

--configprint 打印配置

# 指定项
$ puppet apply --configprint modulepath
/etc/puppetlabs/puppet/modules:/opt/puppet/share/puppet/modules
 
# 所有
$ puppet apply --configprint all

以 C/S 模式运行相关的命令

pull-based! Usually, agents are configured to periodically fetch a catalog and apply it, and the master controls what goes into that catalog.

  1. server 启动
    # 编辑 /etc/hosts
    192.168.0.234	master.example.com
     
    $ ntpdate 0.pool.ntp.org
     
    # 结束后台运行 master
    $ service puppetmaster stop 
     
    # 前台运行 master
    $ puppet master --verbose --no-daemonize
  2. client 启动
    # 编辑 /etc/hosts
    192.168.0.234	master.example.com
     
    $ ntpdate 0.pool.ntp.org
     
    # 运行 agent
    $ puppet agent --noop −−test --onetime −−server master.example.com
    # --onetime, 只运行一次 
    # --noop, dry run 
    # --test:
    #   Enable the most common options used for testing. These are 'onetime',
    #   'verbose', 'ignorecache', 'no-daemonize', 'no-usecacheonfailure',
    #   'detailed-exit-codes', 'no-splay', and 'show_diff'.
    # --splay, Puppet’s default configuration asks that each node check-in every 30 minutes. An option called ‘splay’ can add a random configurable lag to this check-in time, to further balance out check-in frequency.
     
    # 如果指定 --server 不 work, 则应修改 /etc/puppet/puppet.conf
    [agent]
        server = master.example.com
     
    # info: Creating a new SSL certificate request for ubuntu
    # info: Certificate Request fingerprint (md5): B7:30:40:28:17:5C:B7:B8:4D:AD:5B:F8:C5:1D:C8:D5
    # 但还未得到 server 签名, 会报如下连接失败错误
    # notice: Did not receive certificate
    # err: Could not request certificate: Connection refused - connect(2)
  3. server 对 client 签名
    # 列表
    $ puppet cert list
      ubuntu (B7:30:40:28:17:5C:B7:B8:4D:AD:5B:F8:C5:1D:C8:D5)
    # 签名个别
    $ puppet cert sign ubuntu
    notice: Signed certificate request for ubuntu
    notice: Removing file Puppet::SSL::CertificateRequest ubuntu at '/var/lib/puppet/ssl/ca/requests/ubuntu.pem'
     
    # 签名所有
    $ puppet cert sign --all

puppet module

puppet 从 3 开始自带 puppet module 命令, 功能包括:

  • 从 Forge 下载/安装模块
  • 新建模块
  • build & test 模块

用现成的: Forge

# 查找 iptables 相关的 module
$ puppet module search iptables
 
# 在本地 puppet 配置中安装 iptables module
$ cd /etc/puppet/modules/
$ puppet module install bobsh/iptables
$ tree iptables/
# 此模块会安装新 type iptables
$ vi iptables/lib/puppet/type/iptables.rb

Forge 上一个问题是没有打分/排名, 对此有人在 serverfault 提问 Puppet Packages, Puppet Forge, and good Puppet code?, 最佳回答说去社区看

自己写

内容
步骤
$ puppet module generate site-firewall
# 使用 puppet module 生成 site-firewall 模块的模板
# site-firewall 只是个名字, 其中 dash 以前是 author, dash 以后是实际模块/类名. 如下:
# Modulefile:
name   'site-firewall'
author 'site'
# manifests/init.pp
class firewall {
  ...
}
# 而 - 以后虽然叫 firewall 但 puppet 不会根据名字生成真的 firewall 内容
 
# 写...
 
# 打包 firewall
$ cd site-firewall/
$ puppet module build
 
# 安装到 puppet
$ mv pkg/site-firewall-0.0.1 /etc/puppet/modules/firewall
 
# 测试
$ puppet apply -e 'include firewall' --noop
 
# 应用
$ puppet apply -e 'include firewall'
 
# 检查
$ vi /etc/iptables.rules
$ iptables -L INPUT -n

基础

master

nodes

modules

separating data from puppet code - hiera

每次修改 hiera.yaml 后, 需要重启 puppet master, init.d 重启 puppet master 后一定要用 ps 看一看, 很可能 puppet master 未成功关闭!!!

refs

控制台

  • dashboard: Puppet Dashboard can be used as an External Node Classifier (ENC) as well as a reporting tool, and is moving towards being an integration interface for a variety of new Puppet functions including audit and inventory capabilities
  • foreman: The Foreman has a stronger focus on provisioning and data center management and already includes some inventory capabilities.

agent

运行模式

自定义 facter

xp-foo/lib/facter/

要想 facter 分发到各 node, 需在 puppet master 和 nodes 的 puppet.conf 中添加以下内容:

[main]
pluginsync = true

Examples

  • 一个简单的 facter:
    Facter.add("home") do
      setcode do
        ENV['HOME']
      end
    end
  • 跨平台问题可使用 confine 方法:
    Facter.add("timezone") do
      # confile 类似 assert(?)
      # 只在 operatingsystem 为 debian 时运行
      confine :operatingsystem => :debian
      setcode do
        File.readlines("/etc/timezone").to_a.last
      end
    end
  • …或使用 Facter:
    Facter.add("timezone") do
      setcode do
        if Facter.value(:operatingsystem) =~ /Debian|Ubuntu/
          File.readlines("/etc/timezone").to_a.last
        else
          tz = Time.new.zone
        end
      end
    end

测试

# mkdir -p ~/lib/ruby/facter
# export RUBYLIB=~/lib/ruby
# cp ~/xp-foo/facts/home.rb $RUBYLIB/facter
# facter home
/root

tips

  • 检查重启
    cron { 'reboot_check':
      command => 'if [ -f /var/run/reboot-required ]; then echo 'Server is required to and is going to reboot' | mail admin@example.com; /sbin/reboot;  fi;',
      user => root,
      hour => 0,
      minute => 38,
    }
    
    if [ -f /var/run/reboot-required ]; then
        echo 'Server is required to and is going to reboot' | mail admin@example.com
        /sbin/reboot
    fi
    
  • visudo
    augeas { “dev_sites”:
      context => “/files/etc/sudoers”,
      changes => [
        "set spec[user = '%devs']/user %devs”,
        “set spec[user = '%devs']/host_group/host ALL”,
        “set spec[user = '%devs']/host_group/command[1] \”/etc/init.d/apache2 restart\”",
        “set spec[user = '%devs']/host_group/command[2] \”/etc/init.d/apache2 reload\”",
        “set spec[user = '%devs']/host_group/command[3] \”/etc/init.d/apache2 start\”",
        “set spec[user = '%devs']/host_group/command[1]/tag NOPASSWD”,
      ],
    }

puppet 语法

  • Resources – Individual configuration items
  • Files – Physical files you can serve out to your agents
  • Templates – Template files that you can use to populate files
  • Nodes – Specifies the configuration of each agent
  • Classes – Collections of resources
  • Definitions – Composite collections of resources

资源

basic

# 让/etc/passwd的权限保持644, 并且属于root用户和root用户组
file { # 类型
  "/etc/passwd": # title 
    name => "/etc/passd", # 对哪个文件操作, 默认等于 title, 可省略
    owner => root,
    group => root,
    mode => 644;
}

条件

if

facter 不返回 boolean, 返回的是 string!!!

if $is_virtual == 'true' {
  warning('Tried to include class ntp on virtual machine; this node may be misclassified.')
}
elsif $operatingsystem == 'Darwin' {
  warning('This NTP module does not yet work on our Mac laptops.')
}
else {
  include ntp
}
if condition {
  block of code
}
elsif condition {
  block of code
}
else {
  block of code
}

The blocks of code for each condition can contain any Puppet code.

if $is_virtual == 'true' {
  service {'ntpd':
    ensure => stopped,
    enable => false,
  }
}
else {
  service { 'ntpd':
    name       => 'ntpd',
    ensure     => running,
    enable     => true,
    hasrestart => true,
    require => Package['ntp'],
  }
}
case
# e.g. 1
case $operatingsystem {
  centos: { $apache = "httpd" }
  # Note that these matches are case-insensitive.
  redhat: { $apache = "httpd" }
  debian: { $apache = "apache2" }
  ubuntu: { $apache = "apache2" }
  default: { fail("Unrecognized operating system for webserver") }
  # "fail" is a function. We'll get to those later.
}
package {'apache':
  name   => $apache,
  ensure => latest,
}
 
# e.g. 2
case $ipaddress_eth0 {
  /^127[\d.]+$/: { 
    notify {'misconfig': 
      message => "Possible network misconfiguration: IP address of $0",
    } 
  }
}
 
# e.g. 3
case $operatingsystem {
  centos, redhat: { $apache = "httpd" }
  debian, ubuntu: { $apache = "apache2" }
  default: { fail("Unrecognized operating system for webserver") }
}
Selectors
$apache = $operatingsystem ? {
  centos                => 'httpd',
  redhat                => 'httpd',
  /(?i)(ubuntu|debian)/ => "apache2-$1",
    # (Don't actually use that package name.)
  default               => undef,
}

资源间的关系

service { 
  "sshd":
    subscribe => File[sshdconfig],
}

title 为数组的资源

file {
  ["/etc/passwd","/etc/hosts"]:
    owner => root ,
    group => root ,
    mode => 644;
}

默认值

# 放在某 manifest 文件顶部, 便对此 manifest 所有 file 有效 
File {
  owner=>root,
  mode=>644;
}
# 默认值可以被后面的设置覆盖。

依赖

file {
  "/etc/apache2/port.conf":
    content=>"80",
    require=>Package["apache2"];
}
package{
  "apache2":
    ensure=>installed;
}

class 和 define

class

class ssh {
  file {
    "/etc/ssh/sshd_config":
      source => "puppet://$fileserver/ssh/sshd_config.cfg";
  }
  package {
    "ssh":
      ensure=>installed;
  }
  service{
    "ssh":
      ensure=>running;
  }
}

define

define svn_repo($path) {
  exec {
    "/usr/bin/svnadmin create $path/$title":
      unless => "/bin/test -d $path";
  }
}
 
svn_repo {
  puppet_repo:
    path => "/var/svn_puppet"
}
 
svn_repo {
  other_repo:
    path => "/var/svn_other"
}

node

FIXME “node include class” 跟 nagios 配置结构类似, 是否有办法在写 puppet 配置时, 自动生成 nagios 配置?

node 'host1.example.com'{
  include ssh
}
 
node 'host2.example.com'{
  include apache,mysql,php
}
 
# 还可以定义一个 default node
 
# node 可有属性
node 'host4.example.com'{
  $networktype="tele"
  $nagioscheckport="80,22,3306"
  include ssh,apache,mysql
}

变量和数组

$(doller 符)定义变量, 变量的内容用双引号括起来

$test="hello,guys"
file{
  "/tmp/test":
    content=>$test;
}

方括号定义数组, 数组的内容由逗号分割

define php::pear() {
  package{ 
    "`php−${name}": 
      ensure => installed
  }
}
 
php::pear { 
  ['ldap','mysql','ps','snmp','sqlite','tidy','xmlrpc']: 
    # title 为数组, name 默认为 title, 无其他属性
}

模块

一个模块就是一个/etc/puppet/modues目录下面的一个目录和它的子目录, 在puppet的主文件site.pp里面用import modulename可以插入模块。新版本的puppet可以自动插入/etc/puppet/modues目录下的模块。引入模块, 可以结构化代码, 便于分享和管理

一个模块目录下面通常包括三个目录, files, manifests, templates 。

manifests 里面必须要包括一个init.pp的文件, 这是该模块的初始文件, 导入一个模块的时候, 会从init.pp开始执行。可以把所有的代码都写到init.pp里面, 也可以分成多个pp文件, init 再去包含其他文件。

files目录是该模块的文件发布目录

templates 目录包含erb模型文件, 这个和file资源的template属性有关

几个常用的资源

file

  • owner,group,mode,content
  • source, 从其他url复制文件内容
    source=>"puppet://${fileserver}/lvs/${corp}.${idc}.keepalived.conf"
  • template, 利用template,可以通过erb模板生成文件内容,erb模板可以使用变量,而且还可以对变量进行计算和操作
    file{
    	"/etc/squid/squid.conf":
    		mode=>0644,
    		content=>template("squid/squid.conf.erb");
    }
     
    # squid.conf.erb
    cache_mem <%= Integer( vmx_memsize.to_i * 0.45 ) %> MB
    visible_hostname <%= fqdn %>
  • 644 = 755 For Directories, 不用额外 +x 2)

package

管理系统的软件包安装

  • ensure, 表示软件需要安装时, 既可以用 installed 又可以用 present, 我用 installed, 因为 present 可能产生“保持现状”的歧义
    package{
    	["vim","iproute","x−window−system"]:
    		ensure=>installed;
    	["pppoe","pppoe−conf"]:
    		ensure=>absent;
    }

service

保证 /etc/init.d 目录下的服务执行脚本执行什么命令

service{
	"ssh":
		ensure=>running;
	"nfs":
		ensure=>stoped;
}

exec

在执行puppet的时候,调用shell执行一条shell语句

exec{
	"delete config":
		path=>"/bin:/usr/bin",
		command=>"rm /etc/ssh/ssh_config";
}

notify

notify {"I'm notifying you.":}
# equals
notify {'some title':
  message => "I'm notifying you.",
}

cron

定时检查重启机制. FIXME 重启前后应有 notification

cron { 'reboot_check':
  command => 'if [ -f /var/run/reboot-required ]; then /sbin/reboot; fi;',
  user => root,
  hour => 0,
  minute => 38,
}
# crontab -l
# 3 3 * * * if [ -f /var/run/reboot-required ]; then /sbin/reboot; fi;

puppet module 书写规范

my_module — This outermost directory’s name matches the name of the module.
    manifests/ — Contains all of the manifests in the module.
        init.pp — Contains a class definition. This class’s name must match the module’s name( class my_module{...}).
        other_class.pp — Contains a class named ( class my_module::otherclass{...}).
        my_defined_type.pp — Contains a defined type named my_module::my_defined_type.
        implementation/ — This directory’s name affects the class names beneath it.
            foo.pp — Contains a class named my_module::implementation::foo.
            bar.pp — Contains a class named my_module::implementation::bar.
    files/ — Contains static files, which managed nodes can download
    lib/ — Contains plugins, like custom facts and custom resource types.
    templates/ — Contains templates, which can be referenced from the module’s manifests.
    tests/ — Contains examples showing how to declare the module’s classes and defined types.

my_module/manifests/ 的组织

  1. 必须要有 init.pp, 其中必须定义与模块名相同的 class, 如 class my_module {…}
  2. 模块中的其他类需要加命名空间, 如 other_class.pp 定义 my_module::other_class, sub_dir/foo.pp 定义 my_module::sub_dir::foo

文件

  1. 引用文件可用 file {'/etc/foo.conf': ... source => "puppet:///modules/my_module/some_file" ...}, 它会指向 my_module/files/some_file

模版

  1. facts, 全局变量和本作用域内的变量在模版文件中可直接使用(不需加 $)
  2. 以上三种在模版中前缀 @ 也可使用(as instance variables)
  3. 其他作用域的变量, 需用 scope 对象的 lookupvar 方法调用, 如 scope.lookupvar('apache::user')
  4. 调用模版形如 file {'/etc/foo.conf': … content ⇒ template('my_module/foo.conf.erb'), …}, 它会指向 my_module/templates/foo.conf.erb.
  5. 模版不光能用在 file 资源的 content 属性上, 几乎能用在所有有需要的地方, template() 方法只是 evaluates the template and turns it into a string
  6. 如果调用多个模版, 如template('foo/one.erb', 'foo/two.erb'), 最终值为各模版求值结果再合并

使用参数

  1. 定义类时, 可设置参数列表, 而 node 在使用类时便可对参数赋值, 如
# /etc/puppetlabs/puppet/modules/paramclassexample/manifests/init.pp
# 定义 class 时, 可设置参数列表
class paramclassexample ($value1, $value2 = "Default value") {
  notify {"Value 1 is ${value1}.":}
  notify {"Value 2 is ${value2}.":}
}
 
# ~/learning-manifests/paramclass1.pp
# 使用该 class 资源, 并对两个参数赋值
class {'paramclassexample':
  value1 => 'Something',
  value2 => 'Something else',
}
 
# ~/learning-manifests/paramclass2.pp
# 使用该 class 资源, 只对一个参数赋值, 另一个使用默认值
class {'paramclassexample':
  value1 => 'Something',
}

资源的组织

使用 metaparameters for ordering

每类资源都有自己的一系列属性, 但还有一系列属性是试用于所有类型的资源的, 这些属性叫做 metaparameters.

用来整理资源间关系的元属性有 before, require, notify, and subscribe, 他们都接受形如 Type['title'](资源类型首字母大写3)!方括号!) 的资源引用(也支持数组), 例如 require ⇒ File['/tmp/test1'].

Before and Require

before is used in the earlier resource, and lists resources that depend on it; require is used in the later resource and lists the resources that it depends on. These two metaparameters are just different ways of writing the same relationship.

# before
    file {'/tmp/test1':
      ensure  => present,
      content => "Hi.",
      before  => Notify['/tmp/test1 has already been synced.'],
      # (See what I meant about symbolic titles being a good idea?)
    }
 
    notify {'/tmp/test1 has already been synced.':}
 
# require
    file {'/tmp/test1':
      ensure  => present,
      content => "Hi.",
    }
 
    notify {'/tmp/test1 has already been synced.':
      require => File['/tmp/test1'],
    }
notify and subscribe

The notify and subscribe metaparameters make dependency relationships the way before and require do, but they also make refresh relationships. A few resource types4) can be “refreshed”, if Puppet makes any changes to that resource, it will send a refresh event to the later resource, which will react accordingly.

Chaining
  • -> for ordering
  • ~> for notification
    file {'/tmp/test1':
      ensure  => present,
      content => "Hi.",
    }
 
    notify {'after':
      message => '/tmp/test1 has already been synced.',
    }
 
    File['/tmp/test1'] -> Notify['after']
Autorequire

puppet 会对某些资源做自动的依赖判断, 比如某目录该目录下的文件. 非重点.

Autorequire

package/file/service

    package { 'openssh-server':
      ensure => present,
      before => File['/etc/ssh/sshd_config'],
    }
 
    file { '/etc/ssh/sshd_config':
      ensure => file,
      mode   => 600,
      source => '/root/learning-manifests/sshd_config',
    }
 
    service { 'sshd':
      ensure     => running,
      enable     => true,
      hasrestart => true,
      hasstatus  => true,
      subscribe  => File['/etc/ssh/sshd_config'],
    }

一个综合的例子

ntp/manifests/init.pp
# = Class: ntp
# 
# ...
# 
# == Parameters: 
#
# $servers:: An array of NTP servers, with or without +iburst+ and 
#            +dynamic+ statements appended. Defaults to the OS's defaults.
# $enable::  Whether to start the NTP service on boot. Defaults to true. Valid
#            values: true and false. 
# $ensure::  Whether to run the NTP service. Defaults to running. Valid values:
#            running and stopped. 
# 
# == Requires: 
# 
# Nothing.
# 
# == Sample Usage:
#
#   class {'ntp':
#     servers => [ "ntp1.example.com dynamic",
#                  "ntp2.example.com dynamic", ],
#   }
#   class {'ntp':
#     enable => false,
#     ensure => stopped,
#   }
#
## 应使用 RDoc 格式写注释
# 
class ntp ($servers = undef, $enable = true, $ensure = running) {
# 参数列表
 
  case $operatingsystem {
    centos, redhat: { 
      $service_name    = 'ntpd'
      $conf_template   = 'ntp.conf.el.erb'
      $default_servers = [ "0.centos.pool.ntp.org",
                           "1.centos.pool.ntp.org",
                           "2.centos.pool.ntp.org", ]
    }
    debian, ubuntu: { 
      $service_name    = 'ntp'
      $conf_template   = 'ntp.conf.debian.erb'
      $default_servers = [ "0.debian.pool.ntp.org iburst",
                           "1.debian.pool.ntp.org iburst",
                           "2.debian.pool.ntp.org iburst",
                           "3.debian.pool.ntp.org iburst", ]
    }
  }
 
  if $servers == undef {
    $servers_real = $default_servers
  }
  else {
    $servers_real = $servers
  }
  # 这里之所以用 $servers_real, 是因为 puppet 不允许对变量重复赋值
 
  package { 'ntp':
    ensure => installed,
  }
 
  service { 'ntp':
    name      => $service_name,
    ensure    => $ensure,
    enable    => $enable,
    subscribe => File['ntp.conf'],
  }
 
  file { 'ntp.conf':
    path    => '/etc/ntp.conf',
    ensure  => file,
    require => Package['ntp'],
    content => template("ntp/${conf_template}"),
  }
}

自定义资源类型

  • You define a type with the define keyword, and the definition looks almost exactly like a parameterized class. You need:
    • A name
    • A list of parameters (in parentheses, after the name)
      • (Defined types also get a special $title parameter without having to declare it, and its value is always set to the title of the resource instance. Classes get this too, but it’s less useful since a class will only ever have one name.)
    • And a collection of resources.
  • In modules, like with classes, each defined type should go in its own file in a module’s manifests/ directory, and the same rules for namespacing apply
  • To avoid duplicate definition error, both the title and the name (or namevar) of every resource in the definition are somehow derived from a unique parameter (often the $title) of the defined type.
  • If there’s a singleton resource that has to exist for any instance of the defined type to work
define planfile ($user = $title, $content) {
  file {"/home/${user}/.plan":
    ensure  => file,
    content => $content,
    mode    => 0644,
    owner   => $user,
    require => User[$user],
  }
}
 
user {'nick':
  ensure     => present,
  managehome => true,
  uid        => 517,
}
planfile {'nick':
  content => "Working on new Learning Puppet chapters. Tomorrow: upgrading the LP virtual machine.",
}

参数类型检查

使用 stdlib module 提供的 validate_array, validate_bool, validate_hash, validate_re, validate_string 方法

FAQ

运行流程

  1. 客户端 puppet agent 调用facter, facter探测出主机的一些变量, 例如主机名, 内存大小, IP 地址等。 puppet agent 把这些信息通过 SSL 连接发送到服务器端。
  2. 服务器端的 puppet master 检测客户端的主机名,然后找到 manifest里面对应的 node配置, 然后对该部分内容进行解析,facter送过来的信息可以作为变量处理,node牵涉到的代码才解析,其他没牵涉的代码不解析。 解析分为几个阶段,语法检查,如果语法错误就报错。如果语法没错,就继续解析,解析的结果生成一个中间的“伪代码”,然后把伪代码发给客户端。
  3. 客户端接收到“伪代码”,并且执行,客户端把执行结果发送给服务器。
  4. 服务器端把客户端的执行结果写入日志。

连不上服务器?

  • 要在安装软件以前先设置主机名,因为生成证书的时候要把主机名写入证书,如果证书生成好了再改主机名,就连不上,这是很多初学者遇到的问题
  • 服务器/客户端都需有校时, 否则会报以下错误: err: Could not retrieve catalog from remote server: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed. This is often because the time is out of sync on the server or client

Which versions of Ruby does Puppet support?

Ruby 1.8.5 is fully supported, Ruby 1.8.7 generally gives better performance and memory use.

Should I upgrade the client or server first?

When upgrading to a new version of Puppet, always upgrade the server first. Old clients can point at a new server but you may have problems when pointing a new client at an old server.

How do I document my manifests?

How do all of these variables, like $operatingsystem, get set?

$ facter

Facter is an independent, cross-platform Ruby library designed to gather information on all the nodes you will be managing with Puppet. It is available on all platforms that Puppet is available.

Can I access environment variables with Facter?

$ FACTER_FOO="bar"
$ export FACTER_FOO
$ facter | grep 'foo'
  foo => bar

推倒重来

$ puppet cert clean --all

nagios check puppet freshness

在 puppet master 检查

node 在成功运行 puppet agent 后会在 puppet master 留下 report, 所以 nagios 可通过检查 node report 目录 /var/lib/puppet/reports/$NODE_NAME 的 mtime 来确定 puppet agent 的新鲜度.

要注意 reports 目录皆为 drwxr-x— puppet puppet, 所以需将 nagios 加入 puppet 组才能正常检测!

$ grep puppet /etc/group
puppet:x:121:nagios

检查 mtime 推荐使用自带的 plugin check_file_age

<code>
# 定义 command
define command {
  command_name check_puppet_freshness
  # puppet 只提供 critical line 就 ok
  # command_line $USER1$/check_puppet_freshness.sh -h '$HOSTNAME$' -c '$ARG1$'
  command_line $USER1$/check_file_age "/var/lib/puppet/reports/$HOSTNAME$" -w $ARG1$ -c $ARG1$
}

# 定义 service
define service {
  hostgroup_name puppet_agents
  service_description puppet_freshness
  check_command check_puppet_freshness!7200
  # 2h 以上没 puppet 就提醒
  use generic-service
  notification_interval 0 ; set > 0 if you want to be renotified
}

在发现 check_file_age 前, 我也写了个 bash 脚本

check_puppet_freshness.sh
#!/bin/bash
 
while getopts 'h:c:' OPT
do
  case $OPT in
    h)
      host="$OPTARG";;
    c)
      critical_line="$OPTARG";;
  esac
done
 
report_base="/var/lib/puppet/reports"
host_report="$report_base/$host"
 
if [[ ! -d $host_report ]]
then
  echo "no such host: $host_report"
  exit 1
fi
 
mtime=`stat --printf=%Y  $host_report`
now=`date +%s`
age=`expr $now - $mtime`
 
if (( $age > $critical_line ))
then
  echo "host hasn't fresh for $age seconds"
  exit 2
fi
 
echo "OK, host freshed $age seconds ago"
exit 0

在 puppet agent 检查

关键是检查文件 /var/lib/puppet/state/state.yaml 的 mtime, $age = $now - $mtime;, $age 可作为 ganglia 的一项 metric, 传至 gmetad 后用 nagios check_ganglia.

参考: Blog Without An Important Name : Monitoring Puppet with Nagios

puppet agent stuck on cron run

在某些 node 中遇到过 cron 运行的 puppet agent 卡住的问题, puppet version 2.7.11, strace -p 发现一直在报 select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout). 将此现象 google 后, 找出相同问题: Bug #13038: Hung agent processes. 据问题评论, 升级到 2.7.12 后便可解决问题.

require a array variable

如果遇到以下情况:

$dependencies = ["php5-cgi", "php5-memcache", "php5-mysql"]
package { $dependencies:
  ensure => installed,
}
 
exec { "/root/foo.deb":
  # ¿ how to require $dependencies ?
}

可以逆向思维, 不用 require 用 before:

$dependencies = ["php5-cgi", "php5-memcache", "php5-mysql"]
package { $dependencies:
  ensure => installed,
  before => Exec["/root/foo.deb"]
}
 
exec { "/root/foo.deb":
  # ...
}

How to give a name to File{} for next steps whith "require" on Puppet? - Server Fault

使用 puppet 做远程备份

Override/change resource attributes

#In a node or class somewhere
service { 'elasticsearch':
  ensure => running,
}
 
#Sometime later in the code, for ex, in a node that inherits above node (also valid for classes)
Service <| title == 'elasticsearch' |> { ensure => stopped }

Blog: Puppet: Override/change resource attributes defined in included class or inherited node

Managing SSH Keys With Puppet

如何让 exec 只允许一次

exec 的 creates 选项可让 exec 仅在 creates 指定的文件不存在时运行, 虽然 creates 自己不会创建文件, 但可让 exec 创建文件以强制让其只执行一次.

    exec { "tar -xf /Volumes/nfs02/important.tar":
      cwd     => "/var/tmp",
      creates => "/var/tmp/myfile",
      # myfile is assumed to be a file inside important.tar
      path    => ["/usr/bin", "/usr/sbin"]
    }
 
    exec {'touch /tmp/reboot && reboot':
      path => ["/bin", "/usr/bin", "/usr/sbin"],
      creates => "/tmp/reboot",
    }

如何查看 exec 的输出

  exec { "route -n":
    path   => "/usr/bin:/usr/sbin:/bin:/sbin",
    logoutput => true,
  }

但输出的结果在 dashboard 中一行一条, 不好看, 更好的方法是直接 grep report:

$ grep message /var/lib/puppet/reports/the.node/201308050510.yaml

      message: "Kernel IP routing table"
      message: "Destination     Gateway         Genmask         Flags Metric Ref    Use Iface"
      message: "0.0.0.0         192.168.2.1     0.0.0.0         UG    100    0        0 eth0"
      message: "192.168.2.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0"
      message: "executed successfully"
      message: "Finished catalog run in 3.35 seconds"

Best Practices

http://docs.puppetlabs.com/guides/best_practices.html

  • Use Modules When Possible
  • Keep Your Puppet Content In Version Control
  • Naming Conventions

    Node names should match the hostnames of the nodes.

    When naming classes, a class that disables ssh should be inherited from the ssh class and be named “ssh::disabled”

  • Classes Vs Defined Types

    Classes are not to be thought of in the ‘object oriented’ meaning of a class. This means a machine belongs to a particular class of machine.

    For instance, a generic webserver would be a class. You would include that class as part of any node that needed to be built as a generic webserver. That class would drop in whatever packages, etc, it needed to do.

    Defined types on the other hand (created with ‘define’) can have many instances on a machine, and can encapsulate classes and other resources. They can be created using user supplied variables. For instance, to manage iptables, a defined type may wrap each rule in the iptables file, and the iptables configuration could be built out of fragments generated by those defined types.

    Usage of classes and defined types, in addition to the built-in managed types, is very helpful towards having a managable Puppet infrastructure.

相关工具

augeas [ɔ:'dʒi:æs]

Augeas is a configuration editing tool. It parses configuration files in their native formats and transforms them into a tree. Configuration changes are made by manipulating this tree and saving it back into native config files.

Augeas 是 redhat 的 Emerging Technologies5) 项目之一.

refs

常用文档

puppet 的讨论区在 google groups, 访问很成问题

中文 Puppet 信息可参考: xjc2694-ChinaUnix博客

3)
you only use the lowercase type name when declaring a new resource. Any other situation will always call for a capitalized type name.
4)
Of the built-in types, only exec, service, and mount can be refreshed.
5)
新兴技术, an assortment of projects developing new open source technology related to virtualization and the cloud.
it/puppet.txt · Last modified: 2014/11/26 10:28 by admin