atomaka.com/_posts/2014-01-19-intro-to-puppet.md

300 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
layout: post
title: 'Intro to Puppet: The Bare Minimum'
tag:
- puppet
- technical
---
Last month, some of my coworkers were looking for a brief introduction to
[Puppet](http://www.puppetlabs.com/). 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](https://github.com/atomaka/puppet-walkthru).
You will need [Git](http://www.git-scm.com/),
[Virtual Box](https://www.virtualbox.org/) and
[Vagrant](http://www.vagrantup.com/) 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](http://docs.puppetlabs.com/references/latest/type.html). The
built-in resources provided by Puppet provide a good start. In
[example one](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/1-user-type.pp),
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](http://docs.puppetlabs.com/references/latest/type.html#user)
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. Well explore these in the
[next example](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/2-file-type.pp).
```
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](http://docs.puppetlabs.com/references/latest/type.html#file)
simply creates a file at the specified location that says "Hello."
Unfortunately, this isnt 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 well look at is installing packages. Puppet provides a way
to define which
[packages](http://docs.puppetlabs.com/references/latest/type.html#package) 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](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/3-package-type.pp).
```
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](http://puppetlabs.com/blog/inside-puppet-about-determinism).
This is easier to
[show than explain](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/4-order-example.pp).
```
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](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/5-ordered-example.pp).
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](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/6-facts-example.pp)
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](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/7-full-example.pp).
If you are not familiar, [NTP](http://www.ntp.org/) 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](http://docs.puppetlabs.com/references/latest/type.html#service)
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](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/8-independent-example.pp).
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!