10 KiB
layout | title | tag | ||
---|---|---|---|---|
post | Intro to Puppet: The Bare Minimum |
|
Last month, some of my coworkers were looking for a brief introduction to Puppet. Puppet is a type of configuration manager for your servers. It allows you to create definitions of your server that can then be automatically maintained. Puppet is mostly self documenting so it makes it easy to know what your servers are doing while giving you a great way to automate setting up large numbers of servers.
This is that brief talk. All code is available on Github in my puppet-walkthru repository. You will need Git, Virtual Box and Vagrant installed. To begin, clone the repository and launch the Vagrantfile:
git clone https://github.com/atomaka/puppet-walkthru.git
cd puppet-walkthru
vagrant up
This will setup a virtual machine on your computer with Puppet installed. All code can be found on the virtual machine in the /vagrant directory.
vagrant ssh
sudo su cd /vagrant
You are now ready to work through the first example.
1. Managing Users
Puppet maintains state on your computer using what are referred to as resources. The built-in resources provided by Puppet provide a good start. In example one, you can see how to use a Puppet resource to add and remove a user.
user { 'tm':
ensure => present,
}
user { 'fowlks':
ensure => absent,
}
You can run this code on your virtual machine with
puppet apply manifests/1-user-type.pp
. Afterward, you should notice that the
user "tm" exists on your system.
The user resource type manages local users on your system. This works on a wide variety of systems, although some do not support some of the more specific features. In this example, we make sure the user "tm" is present on the system and make sure the user "fowlks" is not present.
ensure is a keyword for all Puppet resources. present and absent are the most common values although some resource types have others. ensure will make sure that definition exists on your server and absent will obviously do the opposite.
2. Managing Files
Managing files is one of the most common tasks for server administration and Puppet offers many ways to handle this. We’ll explore these in the next example.
file { '/tmp/test1.txt':
ensure => present,
content => 'Hello',
}
file { '/tmp/test2.txt':
ensure => present,
source => '/vagrant/files/test2.txt',
}
$something = "Hello"
file { '/tmp/test3.txt':
ensure => present,
content => template('/vagrant/templates/test3.txt.erb'),
}
Run this on your virtual machine using puppet apply manifests/2-file-type.pp
and you should be alerted that three files were created. You can verify this by
viewing the contents of the tmp directory with ls /tmp
.
The first file resource simply creates a file at the specified location that says "Hello." Unfortunately, this isn’t very useful since we do not want to have to type our entire cnfiguration file in our Puppet definition. The second resource is slightly more useful. This allows us to copy a file from our Puppet repository to a specified location.
Finally, we can also create templates. The last example uses a file from our
repository and copies it to the specified location. However, we can also include
variables that can be used in our file. In this case, we set a variable to
something and it is then displayed in the file: You said: Hello
. The contents
of $something
are used in the file.
3. Installing Packages
The last common task we’ll look at is installing packages. Puppet provides a way to define which packages can be installed. By default, this uses your distributions built-in package manager although there are ways to specify various providers. Our example shows the most basic usage.
package { 'vim':
ensure => present,
}
package { 'alpine-pico':
ensure => absent,
}
Try to open vim and you will notice it cannot run. Once you run this code with
puppet apply manifests/3-package-type.pp
, the vim package will then be
present.
4. Ordering (or lack thereof)
The trickest thing for beginners to Puppet is dealing with its non-deterministic behavior. This is easier to show than explain.
notify { 'First': }
notify { 'Second': }
notify { 'Third': }
notify { 'Fourth': }
notify { 'Fifth': }
notify { 'Sixth': }
notify { 'Seventh': }
When run, you would expect this to spit out First, Second, …, Seventh in order.
Invoke this code with puppet apply manifests/4-order-example.pp
and be
surprised at the results. The order of the code is much different than what is
in the file. Furthermore, if you were to add notify { 'Eighth': }
the ordering
might change completely.
5. But I Need Order
But there are dependencies when setting up systems. Puppet allows for this, you just are required to explicitly define them. The biggest advantage here is that if one line of dependencies fails, your entire configuration does not. It takes some getting used to and can be frustrating, but it is worth it.
notify { 'First': }
notify { 'Second':
require => Notify['First'],
}
notify { 'Third':
require => Notify['Second'],
}
notify { 'Fourth':
require => Notify['Third'],
}
notify { 'Fifth':
require => Notify['Fourth'],
}
notify { 'Sixth':
require => Notify['Fifth'],
}
notify { 'Seventh':
require => Notify['Sixth'],
}
By using the require
parameter, we have have forced ordering. If you run this
code with puppet apply manifests/5-ordered-example.pp
, you will see the order
you expected in example number four.
6. Know Your Environment
Puppet also provides a way for you to know about the system that the Puppet code is running on with a system called Facter.
notify { "${::osfamily}": }
notify { "${::ipaddress}": }
notify { "${::uptime}": }
When run with puppet apply manifests/6-facts-example.pp
,
this code
will display the information about the virtual machine you are running on. We
will look at why this is useful later.
7. Doing Something Useful
Now that we have seen some quick forced examples of how to use Puppet, we now have enough knowledge to do something that is actually useful. Using Puppet, we can configure an entire service. If you are not familiar, NTP is a networking protocol for time management. It is useful for mainitaining the same system time across all of your servers. And we can use Puppet to install it!
package { 'ntp':
ensure => present,
}
file { '/etc/ntp.conf':
ensure => present,
require => Package['ntp'],
source => '/vagrant/files/ntp.conf.debian',
}
service { 'ntp':
ensure => running,
enable => true,
subscribe => File['/etc/ntp.conf'],
}
When running this code with puppet apply manifest/7-full-example.pp
, you
should notice three things happen. First, the ntp package will be installed.
Since we are on Ubuntu, this is done using apt-get. Secondly, a configuration
file was copied from our Puppet repository to the location specified. Finally,
the ntp service was started.
Install, configure, start is one of the most common patterns in Linux/UNIX systems administration and we can easily automate it with Puppet.
Something to note is our use of subscribe when using the service resource type. This makes sure that the ntp service is restarted only if the configuration file has changed.
7. Managing Multiple Operating Systems
Before this section, be sure to reset what we did in the previous example by running bash support/cleanup7.sh. We just need to uninstall ntp and our config file so we can do it all again.
Unfortunately, our environments are never uniform and we are stuck dealing with different versions of operating systems. Luckily, we have tools that we can use to deal with it. We touched on this in section six, but now we will actually use them to install ntp again. This time, our code will work on both Debian and RedHat family Linux distributions.
case $::osfamily {
'RedHat': {
$service = 'ntpd'
$conf = 'ntp.conf.redhat'
}
'Debian': {
$service = 'ntp'
$conf = 'ntp.conf.debian'
}
}
notify { 'OS Information':
message => "${::osfamily}: Setting service to ${service} and conf to ${conf}",
before => Package['ntp'],
}
package { 'ntp':
ensure => present,
}
file { '/etc/ntp.conf':
ensure => present,
require => Package['ntp'],
source => "/vagrant/files/${conf}",
}
service { $service:
ensure => running,
enable => true,
subscribe => File['/etc/ntp.conf'],
}
When on our Ubuntu virtual machine, running this code with
puppet apply manifest/8-independent-example.pp
will setup NTP just as we did
in example seven. However, this code can also run on RedHat / CentOS machines
without any adjustments.
This is handled using the facts we discussed in section six. We check to see
what distribution we are using with $::osfamily
and make choices based on
that. Since the service and the config file are different on RedHat, we assign
variables to these and adjust them as needed.
You now have enough knowledge to get started with Puppet!