learning puppet basic thing
TRANSCRIPT
Puppet { ‘basic’: }
Daehyung Lee <[email protected]>
Configuration Management Advantage
• Infrastructure as CodeTrack, Test, Deploy, Reproduce, Scale
• Reproducible setups• Scale quickly
Done for one, use on many• Aligned Environments
for development, testing, QA, production nodes• Alternatives CM toolkit
Chef, CFEngine, SaltStack, Ansible
About Puppet• The industry-leading configuration management
toolkit• An open source IT Management tool written in Ruby• Coding style is simular to Nagios• Influenced by CFEngine• Supports various OS
• Linux, Unix, OS X, Windows(>= 2.7)
Puppet Language• DSL(Declarative Domain Specific Language)
• Defines STATES (Not procedure)
• Resources, Classes, and NodesDeclaring resources(file, package, service, ...)
Groups of resources are organised in classesA class may describe everything needed to configure an entire
service or applicationNodes that serve different roles will generally get different sets
of classes
How Puppet works
Every node checks in with a puppet master
Do I look the way I'm supposed to look
I'm Puppet master
Lifecycle of a Puppet Run1. FactsThe node sends data about its state to the puppet master server.
2. CatalogPuppet uses the facts to compile a Catalog that specifies how the node should be configured.
3. ReportConfiguration changes are reported back to the Puppet Master.
4. ReportPuppet's open API can also send data to 3rd party tools.
Catalog• Static documents which contain resources and
relationships• Ruby object in memory• Transmitted as JSON and persisted to disk as YAML• Agent nodes cache their most recent catalog
if the master fails compilation, re-use cached one.
Resource Abstraction Layer
types providers
ubuntu
SLES
macosx
Windows
user
file
package
service
exec
mount
group
host
RHELCentOS
Resource declaration
• file: The resource type• ntp.conf: The title• path: An attribute• '/etc/ntp.conf': A value; in this case, a string• template('ntp/ntp.conf'): A function call that returns a value; in this case, the
template function, with the name of a template in a module as its argument
file {'ntp.conf': ensure => file, path => '/etc/ntp.conf', content => template('ntp/ntp.conf'), owner => 'root', mode => '0644', }
Relationship meta-parameters
• before: Causes a resource to be applied before the target resource.• require: Causes a resource to be applied after the target resource.• notify: Causes a resource to be applied before the target resource. The target
resource will refresh if the notifying resource changes.• subscribe: Causes a resource to be applied after the target resource. The
subscribing resource will refresh if the target resource changes.
package {'ntp': ensure => installed, before => File['ntp.conf'], } service {'ntpd': ensure => running, subscribe => File['ntp.conf'], }
Package['ntp']
Service['ntpd']
monitor
Chaining Arrows
• -> (ordering arrow): Causes the resource on the left to be applied before the resource on the right. Written with a hyphen and a greater-than sign.
• ~> (notification arrow): Causes the resource on the left to be applied first, and sends a refresh event to the resource on the right if the left resource changes. Written with a tilde and a greater-than sign.
Package['ntp'] -> File['ntp.conf'] ~> Service['ntpd']
File['ntp.conf'] Service['ntpd']Package['ntp']
Changed
Do restart
Data types
Boolean, Undef• Boolean
• trueall other strings, including “false"all numbers, including 0 and negative numbersarray, hash event though it's emptyany resource references
falseundef
• Undefnil in Ruby
Strings• Strings
single-quoted strings
double-quoted strings
Escape sequences
Good: 'C:\\Program Files(x86)\\'Bad: 'C:\Program Files(x86)\'
path => "${apache::root}/${apache::vhostdir}/${name}",
\$ — literal dollar sign \" — literal double quote \' — literal single quote \\ — single backslash \n — newline \r — carriage return \t — tab \s — space
Numbers• Numbers
$some_number = 8 * -7.992 $another_number = $some_number / 4
$product = 8 * +4 # syntax error $product = 8 * 4 # OK $product = 8 * .12 # syntax error $product = 8 * 0.12 # OK
Arrays, Hashes• Arrays
• Hashes
[ 'one', 'two', 'three' ] or [ 'one', 'two', 'three', ]
$foo = [ 'one', 'two', 'three' ] notice( $foo[1] ) # one notice( $foo[-2] ) # two
{ key1 => 'a', key2 => 'b' } or { key1 => 'a', key2 => 'b', } $mysite = { port => 80, server_name => { mirror0 => 'www.example.com', mirror1 => 'abc.example.com' }
} notice( $mysite[port] ) # 80 notice( $mysite[server_name][mirror1] ) # abc.example.com
Resource References• Resource References
# A reference to a file resource: subscribe =>
# A type with a multi-segment name: before =>
# A multi-resource reference: require => File['/etc/apache2/httpd.conf', '/etc/apache2/magic', '/etc/apache2/mime.types'], # An equivalent multi-resource reference: $my_files = ['/etc/apache2/httpd.conf', '/etc/apache2/magic', '/etc/apache2/mime.types'] require => File[$my_files]
Concat::Fragment['apache_port_header'],
File['/etc/ntp.conf'],
Types
Types
▪ http://docs.puppetlabs.com/puppet_core_types_cheatsheet.pdf▪ http://docs.puppetlabs.com/references/3.8.latest/type.html
stage
filebucket
mcx
exec
schedule_task
notify
router
ssh_authorized_key
selboolean
file
mount
cronaugeas computer
resources
host
schedule
mailaliasmaillistmacauthorization
interface
k5login
service
sshkey
package
stage tidy user vlan yumrepo
zfs zone zpool nagios_*
Core Types - File file {
}
title :
ensure =>path =>owner =>group =>mode =>content =>source =>target =>recurse =>
puppet:///modules/some_module/somefile,
template('some_module/somefile.erb'),
present | absent |file | directory | link ,eg) '/etc/inetd.conf',eg) 'root',eg) 'root',eg) '0644',
eg) '/etc/inet/inetd.conf',
true | false ,
'initd.conf':
Core Types - Packagepackage {
}
title :
ensure =>source =>
present | latest | {version} | absent | purged,eg) /tmp/abcpkg-1.1.rpm
eg) 'abcpkg':
Core Types - Serviceservice {
}
title :
ensure =>enable =>name =>status =>start =>stop =>restart =>hasrestart =>hasstatus =>
running | stopped ,
true | false ,defaults to title
true | false,
true | false,
Manually specified commands for working around bad init scripts
use stop+start instead of restartuse grepping process table instead of status
eg) 'httpd':
Core Types - notifynotify {
}
title :
message => defaults to title
eg) 'This message is getting logged':
Core Types - execexec {
}
title :
command =>path =>refreshonly =>onlyif =>unless =>
The command to run; defaults to title
eg) ['bin', 'sbin', '/usr/bin', '/usr/sbin'],true | falseonly run, the result of this command is non-zero
eg) 'run-something':
only run as notified
only run, the result of this command is zero
Core Types - croncron {
}
title :
command =>user =>hour =>minute =>
ensure =>
The command to run
eg) 'root',eg) 2,
eg) 'logrotate':
eg) 2,
present | absent,
Core Types - useruser {
}
title :
uid =>
gid =>shell =>home =>
ensure =>
eg) '507',
eg) 'admin', or '10000',eg) '/bin/bash',
eg) 'dave':
eg) '/home/dave',
present | absent,
managehome => true | false,if it's false, you need to create user's home directory manually.
Core Types - groupgroup {
}
title :
gid =>ensure =>
eg) '10000',
eg) 'admin':
present | absent,
name => defaults to title
Variable• prefixed with a $(dollar sign)• any kind of data types can be assigned
$www_base_path = "/var/www"file { "${www_base_path}/site": ensure => directory,}
$ssh_users = [ 'myself', 'someone' ]
class test { $ssh_users += [ 'someone_else' ]}
Interpolation
Appending Assignment(Allow strings, arrays, hashes)
Facter/Facts• Facter
• Puppet's cross-platform system profiling library• Discovers and reports per-node facts which are
available in you Puppet manifests as variables
• FactsFacts appears in Puppet as normal top-scope
variables
$ facter -p
Facts
http://docs.puppetlabs.com/facter/2.4/core_facts.html#summary
interfaces
facterversion
ipaddress
dhcp_servers
uptime
iphostnumber
kernelrelease
domain
lsb*
ec2_*
ipaddress6
blockdevicesarchitecture augeasversion
kernelmajorversion
filesystems
rsc_*
idhostnamehardwaremodel
fqdn
hardwareisa
macaddress
is_virtual
kernel
macosx manufacturer memory netmask network
operatingsystem* os osfamily partitions xendomains
and more...
Core Factsarchitecture eg) x86_64
domain eg) example.com
fqdn eg) node01.example.com
filesystem eg) ext4, iso9660
hostname eg) node01
ipaddress eg) 10.0.2.15
kernelrelease eg) 2.6.32-504.8.1.el6.x86_64
memorysize eg) 458.39 MB
osfamily eg) RedHat
Custom fact• You can extend facter by writing ruby code
• http://docs.puppetlabs.com/facter/2.4/custom_facts.html
Facter.add(:rubypath) do setcode 'which ruby'end
Facter.add(:rubypath) do confine :osfamily => "Windows" # Windows uses 'where' instead of 'which' setcode 'where ruby'end
External Facts• fact location
/etc/facter/facts.d• Adding external facts
$ echo "application_tier=dev" > /etc/facter/facts.d/app.txt$ facter application_tierdev
$ cat <<EOF > /etc/facter/facts.d/mydata.yamlyamlkey: - hello - worldEOF$ facter yamlkey["hello", "world"]
Conditional Statements
Conditional Statements• booleans
$x = 1$y = 2($x == $y)($x > $y)($x < $y)($x != $y)($x < $y) and !($x < $y)
Conditional Statements• Booleans • Arithmetic
$x = 1$y = 2($x == $y) ($x > $y) ($x < $y)($x != $y)($x < $y) and !($x < $y)
$x = 1$y = 2$x + $x == $y # true$x - $x # 0 $y / 2 # 1
Conditional Statements• if/elsif/else
• unless
if str2bool("$is_virtual") { warning('Not work on virtual machine') } elsif $::operatingsystem == 'Darwin' { warning('Not support yet') } else { include ntp }
unless $::memorysize > 1024 { $maxclient = 500 }
Conditional Statements• case
• selector
case $::hostname { /^www/: { include apache } /^dns/: { include bind } /^mx[1-2]/: { include mx } default: { include base }}
$rootgroup = $::osfamily ? { 'Solaris' => 'wheel', /(Darwin|FreeBSD)/ => 'wheel', default => 'root', }
Conditional Statements• in
if $::hostname in [ 'www', 'web' ] { service { 'httpd' : ensure => true } }
Templates» Documents that combine code, data, and literal text
to produce a final rendered output. » The goal of a template is to manage a complicated
piece of text with simple inputs.
Template Syntax<% Ruby codes %><%= Ruby expression %><%# comment %>
<% if @ssl -%> ## SSL directives SSLEngine on SSLCertificateFile <%= @ssl_cert %> SSLCertificateKeyFile <%= @ssl_key %><% if @ssl_chain -%> SSLCertificateChainFile <%= @ssl_chain %><% end -%>
No newline
Templates - iteration# ntp/init.pp$nameservers = [ ‘ns1.example.com’, ‘ns2.example.com’, ‘ns3.example.com’ ]
$searchdomains = [ ‘inside.example.com’, ‘outside.example.com’,
‘under.example.com’]file {“resolvconf”:
path => “/etc/resolv.conf”,mode => ‘0644’,
owner => root, group => root,
content => template('resolvconf.erb')}
# /etc/puppet/manifests/resolv.conf.erb
# resolv.conf build by Puppetdomain <%= @domain %>search <% @searchdomains.each do |domain| -%><%= domain -%><% end -%> <%= @domain %><% @nameservers.each do |server| -%>nameserver <%= server %><% end -%>
/etc/resolv.conf
# resolv.conf build by Puppetdomain example.comsearch inside.example.com outside.com under.example.com example.comnameserver ns1.example.comnameserver ns2.example.comnameserver ns3.example.com
inline_template$day_of_week = inline_template("<%= Time.now.gmtime.wday %>”)if $day_of_week ==0 { exec { ‘Do Monday task’ : # do something } } else { exec { ‘Do rest of days task’ : # do something else } }
Classes
Classes• Named blocks of Puppet code, Stored in module
# A class with no parametersclass base::linux { file { '/etc/passwd': owner => 'root', group => 'root', mode => '0644', } file { '/etc/shadow': owner => 'root', group => 'root', mode => '0440', }}
class
TypeAttribute
TypeAttribute
$variable
include other class
Defining class • Class[‘apache’]
class apache { package { ‘httpd’: ensure => latest } service { ‘httpd’: ensure => running } file { '/etc/httpd/conf': ensure => directory } file { '/etc/httpd/conf/httpd.conf': ensure => file, require => [ Package['httpd'], File['/etc/httpd.conf'], ], notify => Service['httpd'], }}
Looks OK, but Is it work on Debian?
Parameterized class• Class[‘apache’]
class apache ( $package = ‘httpd’, $service = ‘httpd’ ){ package { $package: ensure => latest } service { $service : ensure => running } …}# tests/debian.ppclass { ‘apache’ package => ‘apache2’, service => ‘apache2’,}
# tests/redhat.ppclass { ‘apache’ : }# orinclude apache
Params class • Class[‘apache::params’]
class apache::params { case $::osfamily { 'RedHat': { $package = 'httpd' $service = ‘httpd’ … } 'Debian': { $package = 'apache2' $service = ‘apache2’ … } }}
How to use it? The answer is inheritance
Class Inheritance• Use inherits keyword
class test::parent { $var = "parent" notice("var in parent is ",$var)}
class test::child inherits test::parent { $var = "child" notice("var in child is ",$var)}
class test::nephew { notice( "var in nephew is", $var)}
[root@localhost modules]# tree test/test/├── manifests│ ├── child.pp│ ├── init.pp│ ├── nephew.pp│ └── parent.pp└── tests └── inherits.pp
# manifests/init.ppclass test{ include test::child include test::nephew}
# tests/inherits.ppinclude test[root@localhost test]# puppet apply tests/inherits.pp Notice: Scope(Class[Test::Parent]): var in parent is parentNotice: Scope(Class[Test::Child]): var in child is childNotice: Scope(Class[Test::Nephew]): var in nephew is Notice: Compiled catalog for localhost in environment production in 0.06 secondsNotice: Finished catalog run in 0.01 seconds
Using params class• Class[‘apache’]
class apache ( $package = $apache::params::package, $service = $apache::params::service ) inherits apache::params { package { $package: ensure => latest } service { $service : ensure => running } …}# tests/apache.ppinclude apache
Use inheritance, If you want to override some parent’s variables
or using params class
Declaring class• include
standard way to declare classes
• requirebecome a dependency of the surrounding container
• contain (>= Puppet 3.4)http://docs.puppetlabs.com/puppet/3.7/reference/lang_containm
ent.html#containing-classes
include-like behavior-> multiple declaration is OK
class web {
}
include apacheinclude apacheinclude apacheinclude apache
class web {
}
class { ‘apache’: }include apache
Declaring class• class
Only way to declare parameterized classes
Resource-like behavior-> Only one declaration is OK
# tests/debian.ppclass { ‘apache’ package => ‘apache2’, service => ‘apache2’,}
class web {
}
include apacheclass { ‘apache’: }include apache
Define
Define• Can be evaluated multiple times with different
parameters• Once defined, acts like a new resource type
class apache::vhost ($docroot, $server_name, $server_admin) { include apache # contains Package['httpd'] and Service['httpd'] include apache::params # contains common config settings
$vhost_dir = $apache::params::vhost_dir
file { "${vhost_dir}/${server_name}.conf": content => template('apache/vhost-default.conf.erb'), owner => 'www', group => 'www', mode => '0644', require => Package['httpd'], notify => Service['httpd'], }}
# tests/vhosts.ppclass { 'apache::vhost': docroot => '/var/www/html', server_name => 'www01.abc.com', server_admin => '[email protected]',}
class { 'apache::vhost': docroot => '/var/www/html', server_name => 'www02.abc.com', server_admin => '[email protected]',}
[root@localhost apache]# puppet apply tests/http.pp Error: Duplicate declaration: Class[Apache::Vhost] is already declared in file /etc/puppet/modules/apache/tests/http.pp:5; cannot redeclare at /etc/puppet/modules/apache/tests/http.pp:11 on node localhostError: Duplicate declaration: Class[Apache::Vhost] is already declared in file /etc/puppet/modules/apache/tests/http.pp:5; cannot redeclare at /etc/puppet/modules/apache/tests/http.pp:11 on node localhost
Definedefine apache::vhost2($docroot, $servername = $title, $server_admin) { include apache # contains Package['httpd'] and Service['httpd'] include apache::params # contains common config settings
$vhost_dir = $apache::params::vhost_dir
file { "${vhost_dir}/${server_name}.conf": content => template('apache/vhost-default.conf.erb'), owner => 'www', group => 'www', mode => '0644', require => Package['httpd'], notify => Service['httpd'], }}
# tests/define.ppapache::vhost2 { 'www01.abc.com': docroot => '/var/www/html', server_admin => '[email protected]',}
apache::vhost2 { 'www02.abc.com': docroot => '/var/www/html', server_name => 'www02.abc.com', server_admin => '[email protected]',}
[root@localhost apache]# puppet apply tests/define.pp Notice: Compiled catalog for localhost in environment production in 0.28 secondsNotice: /Stage[main]/Main/Apache::Vhost2[www02.abc.com]/File[/etc/httpd/conf.d/www02.abc.com.conf]/ensure: defined content as '{md5}39cab336c208f334fde6b07ef8c33445'Notice: /Stage[main]/Apache/Service[httpd]: Triggered 'refresh' from 1 eventsNotice: Finished catalog run in 0.78 seconds
functions• Pre-defined chunks of Ruby code• Runs during compilation time
• http://docs.puppetlabs.com/references/3.7.latest/function.html• https://forge.puppetlabs.com/puppetlabs/stdlib
template eg) template('apache/vhost-default.conf.erb'),
str2bool eg) $_result = str2bool($::is_virtual)
fail eg) fail('$server_name is needed')
alert eg) alert('$server_name is empty, it’s filled with $title')
notice eg) notice( "var in parent is", $var)
Scope # /etc/puppet/modules/scope_example/manifests/init.pp class scope_example { $variable = "Hi!" notify {"Message from here: $variable":} notify {"Node scope: $node_variable Top scope: $top_variable":} }
# /etc/puppet/manifests/site.pp $top_variable = "Available!" node 'puppet.example.com' { $node_variable = "Available!" include scope_example notify {"Message from node scope: $variable":} } notify {"Message from top scope: $variable":}
$ puppet apply site.ppnotice: Message from here: Hi!notice: Node scope: Available! Top scope: Available!notice: Message from node scope:notice: Message from top scope:
namespace» Class and defined type names may be broken up
into segments» Separated by double colon(::)
» analogous to the / [slash] in a file path.
class apache { ... } class apache::mod { ... } class apache::mod::passenger { ... } define apache::vhost { ... }
apache <modulepath>/apache/manifests/init.pp
apache::mod <modulepath>/apache/manifests/mod.pp
apache::mod::passenger <modulepath>/apache/manifests/mod/passenger.pp
module• Collection of manifests, resources, files, templates,
classes, and definitions.• Can download pre-built modules from the Puppet
forge - http://forge.puppetlabs.com
$ puppet module generate daehyung-amodule$ tree daehyung-amodule/daehyung-amodule/|-- Modulefile|-- README|-- manifests| `-- init.pp|-- spec| `-- spec_helper.rb`-- tests `-- init.pp
foo/|-- manifests| |-- config.pp| |-- defaults.pp| |-- init.pp| |-- install.pp| `-- service.pp|-- templates| `-- foo-conf.erb`-- tests `-- init.pp
installing / uninstalling module
https://docs.puppetlabs.com/puppet/latest/reference/modules_installing.html
puppet man module
puppet module listpuppet module search r10k
puppet module install zack/r10k --version 2.6.5puppet module install zack-r10k-2.6.5.tar.gz --ignore-dependencies
puppet module upgrade zack/r10k --version 3.1.1
puppet module uninstall zack/r10k
Keep this in mind• Make a new module for you or company?
• Search from Puppet Forge first!
• The simple answer is NO!!!
Not mentioned in here• ENC• Hiera• Environments• r10k• etc.
• too many, I’m still learning it ;)
Demo
Simple wordpress server
Roles & Profiles• Implementation layer
(Profiles)Includes regular classesMight add resources directlycreate_resources call
• Business layer (Roles)Only includes profilesNo logic at allOne server - One role
Installation• Debian OS family
[Client] sudo apt-get install puppet[Server] sudo apt-get install puppetmaster
• RedHat OS familyRegister EPEL or puppetlabs' repository
• # rpm -ivh http://yum.puppetlabs.com/el/6/ \products/x86_64/puppetlabs-release-6-11.noarch.rpm
• [Client] yum install puppet• [Server] yum install puppet-server
Any Questions?