Makefile | 9 Workshops/PuppetWorkshop/en-US/Appendix.xml | 612 ++++ Workshops/PuppetWorkshop/en-US/Common_Content/environment_staging.png |binary Workshops/PuppetWorkshop/en-US/Common_Content/images/environment_staging.png |binary Workshops/PuppetWorkshop/en-US/Common_Content/images/title_logo.png |binary Workshops/PuppetWorkshop/en-US/Puppet_Workshop.xml | 1432 ++++++++-- Workshops/PuppetWorkshop/en-US/environment_staging.png |binary Workshops/PuppetWorkshop/en-US/images/environment_staging.png |binary en-US/Courses.xml | 3 9 files changed, 1864 insertions(+), 192 deletions(-)
New commits: commit a2022d718e70e9ea45909f9ef2cb89e993662244 Author: Jeroen van Meeuwen (Fedora Unity) kanarip@fedoraunity.org Date: Fri Oct 17 20:50:09 2008 +0200
Updates
diff --git a/Makefile b/Makefile index 5b3c298..a395170 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,14 @@ TRANSLATIONS = $(XML_LANG) $(OTHER_LANGS) COMMON_CONFIG = /usr/share/publican include $(COMMON_CONFIG)/make/Makefile.common
-html: html-en-US +int-workshop: + rm -rf en-US/Books/Workshops + mkdir -p en-US/Books/Workshops/PuppetWorkshop/ + cp -a Workshops/PuppetWorkshop/en-US/* en-US/Books/Workshops/PuppetWorkshop/ + cp -a en-US/Common_Content/Legal_Notice.xml en-US/Books/Workshops/PuppetWorkshop/Common_Content/Legal_Notice.xml + cp -a en-US/Common_Content/Conventions.xml en-US/Books/Workshops/PuppetWorkshop/Common_Content/Conventions.xml + +html: clean int-workshop html-en-US
fpeople: html rsync -aHvz --delete --progress --rsh=ssh tmp/en-US/html/ fedorapeople.org:./public_html/Courses/ diff --git a/Workshops/PuppetWorkshop/en-US/Appendix.xml b/Workshops/PuppetWorkshop/en-US/Appendix.xml index d268653..8710799 100644 --- a/Workshops/PuppetWorkshop/en-US/Appendix.xml +++ b/Workshops/PuppetWorkshop/en-US/Appendix.xml @@ -52,7 +52,7 @@ <formalpara id="PuppetWorkshop-Appendix-Terminology-nativetype"> <title>native type</title> <para> - definition + A type provided by puppet </para> </formalpara> </listitem> @@ -159,6 +159,9 @@ Deny from all </Directory>
+Listen 8140 +NameVirtualHost *:8140 + <Proxy balancer://<replaceable>master.puppetmanaged.org</replaceable>> BalancerMember http://127.0.0.1:8141 keepalive=on retry=30 </Proxy> @@ -210,11 +213,616 @@ <section id="PuppetWorkshop-Appendix-Examples-DefinedType"> <title>Example Defined Type</title> <para> - para + A defined type is a custom type you can define yourself. This can be a shortcut to a collection of resources given a set of parameters, or just a single resource, as shown in the following examples. + </para> + <formalpara> + <title>Defined type for a single resource</title> + <para> + This is an example defined type that creates a single resource, with just one parameter controlling the source of where the file comes from. + <screen>define yum::repository ($enable = true) { + file { "/etc/yum/repos.d/$name.repo": + mode => 644, + owner => "root", + group => "root", + backup => false, + links => follow, + source => $enable ? { + true => "puppet:///yum/$os/$osver/repos/$name.repo", + default => "puppet:///yum/$os/$osver/repos/$name.repo.disabled" + } + } +}</screen> + </para> + </formalpara> + <para> + You can use this defined type to manage the files in <filename>/etc/yum/repos.d/</filename>, and thus the repositories available to the YUM package manager, by calling (for example): + <screen>yum::repository { "custom": + enable => true +}</screen> + </para> + <para> + This will create a <code>File["/etc/yum/repos.d/custom.repo"]</code> and grab if from a location on the puppetmaster's fileserver where it has <code>enabled=1</code> in the repository configuration file. Should <code>$enable</code> have been set to false in the <code>Yum::Repository["custom"]</code> resource, then the file would have been grabbed from the location on the puppetmaster's fileserver where the contents would have <code>enabled=0</code>. + </para> + + <formalpara> + <title>Multiple resources in a defined type</title> + <para> + This is an example + </para> + </formalpara> + <para> + <screen>define yum::plugin($enable = false) { + file { "/etc/yum/pluginconf.d/$name.conf": + ensure => $enable ? { + true => file, + default => absent + }, + owner => "root", + group => "root", + mode => 644, + source => "puppet:///yum/plugins/$name.conf" + } + + package { "yum-$name": + ensure => $enable ? { + true => installed, + default => absent + } + } + + case $name { + "versionlock": { + file { "/etc/yum/pluginconf.d/versionlock.list": + source => "puppet:///yum/plugins/versionlock.list" + } + } + } +}</screen> </para> </section>
</appendix>
+ <appendix id="PuppetWorkshop-Appendix-GITCommitHooks"> + <title>GIT Commit Hooks</title> + <para> + The following GIT commit hooks allow you to check the manifests you commit before they are actually committed (pre-commit), and sync the puppetmasster after the changes are applied to the repo (post-commit), including posting the changes or diff to a mailing list. + </para> + <formalpara> + <title>pre-commit</title> + <para> + There is no valid pre-commit hook for GIT repositories yet. + </para> + </formalpara> + <formalpara> + <title>post-commit</title> + <para> + <screen>#!/bin/sh +# +# An hook script to mail out commit update information. +# Called by git-receive-pack with arguments: refname sha1-old sha1-new +# +# To enable this hook: +# (1) change the recipient e-mail address +# (2) make this file executable by "chmod +x update". +# + +project=$(cat $GIT_DIR/description) +[ -f $GIT_DIR/commit-list ] && \ + recipients=$(cat $GIT_DIR/commit-list) +[ -n "$recipients" ] || exit 0 + +ref_type=$(git cat-file -t "$3") + +# Only allow annotated tags in a shared repo +# Remove this code to treat dumb tags the same as everything else +# case "$1","$ref_type" in +# refs/tags/*,commit) +# echo "*** Un-annotated tags are not allowed in this repo" >&2 +# echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." +# exit 1;; +# refs/tags/*,tag) +# echo "### Pushing version '${1##refs/tags/}' to the masses" >&2 +# # recipients="release-announce@somwehere.com +# announce@somewhereelse.com" +# ;; +# esac + +# set this to 'cat' to get a very detailed listing. +# short only kicks in when an annotated tag is added +short='git shortlog' + +# see 'date --help' for info on how to write this +# The default is a human-readable iso8601-like format with minute +# precision ('2006-01-25 15:58 +0100' for example) +date_format="%F %R %z" + +# Set to the number of pathname components you want in the subject line +# to indicate which components of a project changed. +num_path_components=2 + +# Set subject +(if expr "$2" : '0*$' >/dev/null ; then + subject="Changes to '${1##refs/heads/}'" + echo "Subject: $subject" +else + base=$(git-merge-base "$2" "$3") + subject=$(git-diff-tree -r --name-only "$base" "$3" | + cut -d/ -f-$num_path_components | + sort -u | + xargs echo -n) + + commits=$(git-rev-list "$3" "^$base" | wc -l) + + if [ "$commits" -ne 1 ] ; then + subject="$commits commits - $subject" + fi + + branch="${1##refs/heads/}" + + if [ "$branch" != "master" ] ; then + subject="Branch '$branch' - $subject" + fi + + echo "Subject: $subject" +fi + +echo "To: $recipients" +echo "X-Project: $project" + +module=$(basename `readlink -f $GIT_DIR`) + +echo "X-Git-Module: $module" +echo "" + +if expr "$2" : '0*$' >/dev/null +then + # new ref + case "$1" in + refs/tags/*) + # a pushed and annotated tag (usually) means a new version + tag="${1##refs/tags/}" + if [ "$ref_type" = tag ]; then + eval $(git cat-file tag $3 | \ + sed -n '4s/tagger ([^>]*>)[^0-9]*([0-9]*).*/tagger="\1" ts="\2"/p') + date=$(date --date="1970-01-01 00:00:00 $ts seconds" +"$date_format") + echo "Tag '$tag' created by $tagger at $date" + git cat-file tag $3 | sed -n '5,$p' + echo + fi + + prev=$(git describe "$3^" | sed 's/-g.*//') + # the first tag in a repo will yield no $prev + if [ -z "$prev" ]; then + echo "Changes since the dawn of time:" + git rev-list --pretty $3 | $short + else + echo "Changes since $prev:" + git rev-list --pretty $prev..$3 | $short + echo --- + git diff $prev..$3 | diffstat -p1 + echo --- + fi + ;; + + refs/heads/*) + branch="${1##refs/heads/}" + echo "New branch '$branch' available with the following commits:" + git-rev-list --pretty "$3" $(git-rev-parse --not --all) + ;; + esac +else + case "$base" in + "$2") + git diff "$3" "^$base" | diffstat -p1 + echo + echo "New commits:" + ;; + *) + echo "Rebased ref, commits from common ancestor:" + ;; + esac + git-rev-list "$3" "^$base" | while read rev; do git-show $rev; echo ""; echo ""; done +fi) | /usr/local/bin/send-unicode-email.py $recipients +exit 0</screen> + </para> + </formalpara> + <note> + <para> + The post-commit GIT hook requires <filename>/usr/local/bin/send-unicode-email.py</filename> + </para> + </note> + </appendix> + + <appendix id="PuppetWorkshop-Appendix-SVNCommitHooks"> + <title>SVN Commit Hooks</title> + <para> + The following SVN commit hooks allow you to check the manifests you commit before they are actually committed (pre-commit), and sync the puppetmaster after the changes are applied to the repo (post-commit), including posting the changes or diff to a mailing list. + </para> + <formalpara> + <title>pre-commit</title> + <para> + <screen>#!/bin/sh +# SVN pre-commit hook to check Puppet syntax for .pp files +# Modified from http://mail.madstop.com/pipermail/puppet-users/2007-March/002034.html +REPOS="$1" +TXN="$2" +tmpfile=`mktemp` +export HOME=/tmp +SVNLOOK=/usr/bin/svnlook +$SVNLOOK changed -t "$TXN" "$REPOS" | awk '{print $2}' | grep '.pp$' | while read line +do + $SVNLOOK cat -t "$TXN" "$REPOS" "$line" > $tmpfile + if [ $? -ne 0 ]; then + echo "Warning: Failed to checkout $line" >&2 + fi + puppet --color=false --confdir=/tmp --vardir=/tmp --parseonly --ignoreimport $tmpfile >&2 + if [ $? -ne 0 ]; then + echo "Puppet syntax error in $line." >&2 + exit 2 + fi +done +res=$? +rm -f $tmpfile +if [ $res -ne 0 ]; then + exit $res +fi</screen> + </para> + </formalpara> + <formalpara> + <title>post-commit</title> + <para> + <screen>#!/bin/sh +REPOS="$1" +REV="$2" +PATH=/usr/bin:/bin +PROJECT=puppet +TO=<replaceable>to@domain.tld</replaceable> +FROM=<replaceable>from@domain.tld</replaceable> + +svn up /etc/puppet/ +svn up /var/lib/puppet/manifests/development/ +svn up /var/lib/puppet/manifests/testing/ +svn up /var/lib/puppet/manifests/production/ +svn up /var/lib/puppet/modules/development/ +svn up /var/lib/puppet/modules/testing/ +svn up /var/lib/puppet/modules/production/ + +# Note that --from is optional and if omitted the from address +# will be $COMMITTER@$hostname +# +# If no --from is used, the committer may need to be subscribed +# to the mailing list used in order for this message to be +# accepted to the mailing list +# + +svnnotify --repos-path "$REPOS" \ + --revision "$REV" \ + --smtp localhost \ + --svnlook /usr/bin/svnlook \ + --to $TO \ + <replaceable>--from $FROM </replaceable> + --subject-prefix "Puppet SVN Commit: " \ + --with-diff --subject-cx --handler HTML::ColorDiff</screen> + </para> + </formalpara> + </appendix> + + <appendix id="PuppetWorkshop-Appendix-ModuleConventions"> + <title>Module Conventions</title> + <para> + This section lists a number of conventions used by all modules on <ulink url="http://puppetmanaged.org" />, that you can use (f desireable) and modify as needed. + </para> + + <section id="PuppetWorkshop-Appendix-ModuleConventions-CodeLayout"> + <title>Code Layout</title> + <para> + A consistent layout of the code is important within a collaborative environment. This section describes a number of conventions used in the modules on puppetmanaged.org. + </para> + + <section id="PuppetWorkshop-Appendix-ModuleConventions-CodeLayout-Indentation"> + <title>Indentation</title> + <para> + <itemizedlist> + <listitem> + <formalpara> + <title>4 spaces to a tab</title> + <para> + No tabs should be used, and instead 4 space characters should be inserted to increase the indentation level. This applies to the manifests, custom facts, the configuration files (if compatible) as well as the DocBook documentation. + </para> + </formalpara> + </listitem> + <listitem> + <formalpara> + <title>Aggregated resource definitions</title> + <para> + Aggregated resource definitions, such as <code>file { [ "foo", "bar" ]: ensure => absent }</code> are indented like this:<screen>file { [ + "foo", + "bar" + ]: + ensure => absent +}</screen> + </para> + </formalpara> + </listitem> + </itemizedlist> + </para> + </section> + + </section> + + <section id="PuppetWorkshop-Appendix-ModuleConventions-Sources"> + <title>Sources</title> + <para> + The location a file is sourced from is subject to the following conditions: + <orderedlist> + <listitem> + <para> + A file should first be sourced from a domain-specific configuration tree as to allow domain administrators to maintain their own files. The exact location again is subject to the following conditions: + <orderedlist inheritnum="inherit"> + <listitem> + <para> + If the file is part of a directory structure that is going to be pulled from the source recursively, and the configuration files in that directory structure is host specific, files for such a host go into a sub-directory <code>$hostname</code>. + </para> + </listitem> + <listitem> + <para> + If the file is is a single host-specific file to be pulled from the source, the file goes into the same directoryas the original file appended with <code>.$hostname</code>. + </para> + </listitem> + </orderedlist> + </para> + </listitem> + <listitem> + <para> + A file should then be sourced from the common, shared <filename>files/</filename> file server share and is organization specific, but not necessarily domain-specific. The <filename>files/</filename> file server share allows an organization to maintain the configuration in a single source controlled tree. Again, the following conditions apply to a file location: + <orderedlist inheritnum="inherit"> + <listitem> + <para> + If the file is part of a directory structure that is going to be pulled from the source recursively, and the configuration files in that directory structure is host specific, files for such a host go into a sub-directory <code>$hostname</code>. + </para> + </listitem> + <listitem> + <para> + If the file is is a single host-specific file to be pulled from the source, the file goes into the same directoryas the original file appended with <code>.$hostname</code>. + </para> + </listitem> + </orderedlist> + </para> + </listitem> + <listitem> + <para> + The source is the default configuration available with the puppet module. + </para> + </listitem> + </orderedlist> + </para> + <para> + Such a set of conditions can maybe be shown best in an example configuration, in this case a single file: + </para> + <para> +<screen>class sudo { + file { "/etc/sudoers": + source => [ + # Organization-wide, host specific, single file + "puppet:///files/sudo/sudoers.$hostname", + + # Organization-wide + "puppet:///files/sudo/sudoers", + + # Module default + "puppet:///sudo/sudoers" + ] + } +}</screen> + </para> + <para> + If, however, a host is configured to recursively pull a directory from source, this is a better approach: + </para> + <para> +<screen>class foo { + file { "/path/to/foo/": + source => [ + # Organization-wide, host specific, directory tree + "puppet:///files/foo/$hostname/", + + # Organization-wide + "puppet:///files/foo/common/", + + # Module default + "puppet:///foo/common/" + ], + recurse => true + } +}</screen> + </para> + <para> + For common files shared between the common and host specific tree you use symbolic links from the host specific tree to the common tree. If, however, there is a million files in the common directory tree with thousands of changes every day, which makes using symbolic links not particularly sustainable, you can also specify an additional File resource in the manifest applied to the host and manage the host-specific file separate from the recursively managed directory. An example: + </para> + <para> +<screen>file { "/path/to/foo/": + source => "puppet:///foo/common/", + recurse => true +} + +file { "/path/to/foo/bar": + source => "puppet:///foo/$hostname/bar" +}</screen> + </para> + <para> + For host specific files and directory trees shared amongst a group of hosts, you use a category specific file name or directory name appendix (such as <code>.admins-only</code>) and use symbolic links between the host specific file or directory and the category specific file or directory. + </para> + + <section id="PuppetWorkshop-Appendix-ModuleConventions-Sources-Scalability"> + <title>Scalability Issues</title> + <para> + Suppose you are a large ISP (example-isp.com) running a million websites which may or may not be managed by puppet. Putting all virtualhost specific configuration files in your domain specific tree under <filename>webserver/sites/</filename> doesn't scale very well because of the large amount of files in one single directory which could be inconvenient when trying to edit those files. We've set up a domain-specific tree working around that problem by adding a <filename>webserver/sites.d/</filename> tree dividing the files into a hierarchy with makes tab-completion and finding the files needing to be edited somewhat easier. + </para> + <para> + The <filename>webserver/</filename> tree in GIT => <ulink url="http://git.puppetmanaged.org/?p=domains/example-isp.com/.git;a=tree;f=webserver" /> + </para> + </section> + + </section> + + <section id="PuppetWorkshop-Appendix-ModuleConventions-ModuleTreeLayout"> + <title>Module Tree Layout</title> + <para> + Like described on <ulink url="http://reductivelabs.com/trac/puppet/wiki">Puppet's wiki</ulink>, a module tree looks as follows: + </para> + <para> + <screen>MODULE_PATH + `- module_name/ + `- README + `- depends/ + `- files/ + `- manifests/ + `- plugins/ + `- facter/ + `- puppet/type/ + `- templates/</screen> + See also: <ulink url="http://reductivelabs.com/trac/puppet/wiki/ModuleOrganisation" /> + </para> + <para> + This is a segmentedlist: + <segmentedlist> + <title>Module Layout</title> + <segtitle>Directory</segtitle> + <segtitle>Description</segtitle> + <seglistitem> + <seg><code>MODULE_PATH</code></seg> + <seg>The path that holds the modules (see <code>modulepath</code> in <filename>/etc/puppet/puppet.conf</filename>)</seg> + </seglistitem> + <seglistitem> + <seg><filename>`- module_name/depends/</filename></seg> + <seg>Dependencies for the module</seg> + </seglistitem> + <seglistitem> + <seg><filename>`- module_name/files/</filename></seg> + <seg>Files distributed with the module. Files in this directory can be sourced by using the <code>puppet:///module_name/</code> URI</seg> + </seglistitem> + <seglistitem> + <seg><filename>`- module_name/manifests/</filename></seg> + <seg>The manifests directory for this module. Should at least hold a file called <filename>init.pp</filename></seg> + </seglistitem> + <seglistitem> + <seg><filename>`- module_name/plugins/facter/</filename></seg> + <seg>Holds custom facts for this module</seg> + </seglistitem> + <seglistitem> + <seg><filename>`- module_name/plugins/puppet/type/</filename></seg> + <seg>Holds custom puppet types for this module</seg> + </seglistitem> + </segmentedlist> + </para> + <para> + Additionally, puppetmanaged.org modules define a tree layout for some of the files needed by these modules. For example, Red Hat Enterprise Linux 3 cannot have the <code>UsePAM</code> configuration directive in <filename>/etc/ssh/sshd_config</filename>. For the SSH module, the <filename>files/</filename> directory has the following layout: + </para> + <para> + <screen>ssh/files + `- CentOS -> RedHat + `- RedHat/ + `- 3/ + `- sshd_config + `- sshd_config</screen> + </para> + <para> + whereas the module sources the <filename>/etc/ssh/sshd_config</filename> as follows: + </para> + <para><screen>file { "/etc/ssh/sshd_config": + owner => "root", + group => "root", + mode => 600, + replace => true, + source => [ + # Organization specific, OS version specific, host specific + "puppet:///files/ssh/$os/$osver/sshd_config.$hostname", + # Organization specific, OS version specific + "puppet:///files/ssh/$os/$osver/sshd_config", + + # Organization specific, OS specific, host specific + "puppet:///files/ssh/$os/sshd_config.$hostname", + # Organization specific, OS specific + "puppet:///files/ssh/$os/sshd_config", + + # Organization specific, host specific + "puppet:///files/ssh/sshd_config.$hostname", + # Organization specific + "puppet:///files/ssh/sshd_config", + + # Module default, OS version specific + "puppet:///ssh/$os/$osver/etc/ssh/sshd_config", + # Module default, OS specific + "puppet:///ssh/$os/etc/ssh/sshd_config", + # Module default + "puppet:///ssh/etc/ssh/sshd_config" + ], + notify => Service["sshd"], + require => Package["openssh-server"] +}</screen> + </para> + </section> + + <section id="PuppetWorkshop-Appendix-ModuleConventions-FileAndDirectoryPaths"> + <title>File And Directory Paths</title> + <para> + To be able to clearly distinct files from directories (and vice-versa of course), the modules use the following naming convention: + <itemizedlist> + <listitem> + <para> + Directory names end with a forward slash. + </para> + </listitem> + <listitem> + <para> + Filenames do not (however obvious). + </para> + </listitem> + </itemizedlist> + </para> + <para> + And here's why it's important. There's a few utilities that do not work if a trailing slash to a fully qualified directory name is omitted. One of these situations is shown as an example (and is not the actual code in the git module): + </para> + <para> +<screen>git::pull { [ + "foo", + "bar" + ]: + base_source => "/srv/git", + localtree => "/var/lib/puppet/modules/development" +} + +define git::pull($localtree, $base_source, $branch = 'master') { + exec { "git_pull_$name": + command => "git pull ${base_source}${name} $branch", + cwd => "${localtree}${name}" + } +}</screen> + </para> + <para> + where the <code>cwd</code> has become <filename>/var/lib/puppet/modules/developmentfoo</filename> instead of the intended <filename>/var/lib/puppet/modules/development/foo/</filename>. Also, the <code>$base_source</code> in this example is, unintentionally, <filename>/srv/gitfoo</filename>. + </para> + <para> + If the modules do not use the standard directory naming schema as set forth in this section, some modules might start omitting the forward slash (between <code>${localtree}</code> and <code>$name</code>, or between <code>${base_source}</code> and <code>${name}</code>), and others might not, which in the end leads to confusion and malfunctioning modules. + </para> + </section> + + </appendix> + +<!-- <appendix id="PuppetWorkshop-Appendix-Modules"> + <title>Modules</title> + <para> + + </para> + + <section id="PuppetWorkshop-Appendix-Modules-Puppet"> + <title>Puppet</title> + <para> + + </para> + </section> + + </appendix>--> + <xi:include href="Revision_History.xml" xmlns:xi="http://www.w3.org/2001/XInclude" /> </part> diff --git a/Workshops/PuppetWorkshop/en-US/Common_Content/environment_staging.png b/Workshops/PuppetWorkshop/en-US/Common_Content/environment_staging.png new file mode 100644 index 0000000..e2e7218 Binary files /dev/null and b/Workshops/PuppetWorkshop/en-US/Common_Content/environment_staging.png differ diff --git a/Workshops/PuppetWorkshop/en-US/Common_Content/images/environment_staging.png b/Workshops/PuppetWorkshop/en-US/Common_Content/images/environment_staging.png new file mode 100644 index 0000000..e2e7218 Binary files /dev/null and b/Workshops/PuppetWorkshop/en-US/Common_Content/images/environment_staging.png differ diff --git a/Workshops/PuppetWorkshop/en-US/Common_Content/images/title_logo.png b/Workshops/PuppetWorkshop/en-US/Common_Content/images/title_logo.png new file mode 100644 index 0000000..b611f79 Binary files /dev/null and b/Workshops/PuppetWorkshop/en-US/Common_Content/images/title_logo.png differ diff --git a/Workshops/PuppetWorkshop/en-US/Puppet_Workshop.xml b/Workshops/PuppetWorkshop/en-US/Puppet_Workshop.xml index a8ccafd..853e1f1 100644 --- a/Workshops/PuppetWorkshop/en-US/Puppet_Workshop.xml +++ b/Workshops/PuppetWorkshop/en-US/Puppet_Workshop.xml @@ -419,7 +419,7 @@ ensure => present }
file { "/etc/yp.conf": - source => "puppet://$server/files/yp.conf", + source => "puppet:///files/yp.conf", notify => Service["ypbind"], require => Package["ypbind"] } @@ -438,7 +438,7 @@ service { "ypbind": The above example is called a <emphasis>manifest</emphasis>, built out of <emphasis>types</emphasis> (package, file, service), which, once defined in a manifest, are referred to as <emphasis>resources</emphasis>. See also <xref linkend="PuppetWorkshop-Appendix-Terminology" /> </para> <para> - The configuration file for this YP client is pulled from <code>puppet://$server/files/yp.conf</code>, the puppetmaster's fileserver, which is where you put the file, possibly from within a Version Control System. + The configuration file for this YP client is pulled from <code>puppet:///files/yp.conf</code>, the puppetmaster's fileserver, which is where you put the file, possibly from within a Version Control System. </para> </section>
@@ -447,6 +447,9 @@ service { "ypbind": <para> As you can see in the <xref linkend="PuppetWorkshop-IntroductionToPuppet-WhatDoesPuppetDo-YpClientExample" />, Puppet does not just manage resources and hopes for the best, but allows one resource to require another resource (<code>File["/etc/yp.conf"] { require => Package["ypbind"] }</code>), and for one resource to notify another resource on changes (<code>File["/etc/yp.conf"] { notify => Service["ypbind"] }</code>). </para> + <para> + This capability ensures that files are placed in the right location after the appropriate package is installed, and before the service starts. This also allows changes to configuration files to notify the service so that it can reload or restart. + </para> </section>
<section id="PuppetWorkshop-IntroductionToPuppet-WriteOnceApplyManyTimes"> @@ -461,7 +464,7 @@ service { "ypbind": }
file { "/etc/yp.conf": - source => "puppet://$server/files/yp.conf", + source => "puppet:///files/yp.conf", notify => Service["ypbind"], require => Package["ypbind"] } @@ -477,13 +480,12 @@ service { "ypbind": }
node '<replaceable>node2.example.com</replaceable>' { - package { "ypbind": ensure => latest }
file { "/etc/yp.conf": - source => "puppet://$server/files/yp.conf", + source => "puppet:///files/yp.conf", notify => Service["ypbind"], require => Package["ypbind"] } @@ -499,7 +501,7 @@ node '<replaceable>node2.example.com</replaceable>' { }</screen> </para> <para> - This of course is not very efficient; One change would need to be applied many times still, and errors are easily made. Besides, it doesn't scale well. Why not group the YP Client relevant resources into a class, and then include the class with each node? Here's how: + This of course is not very efficient; One change would need to be applied many times still, and errors are easily made. Besides, it doesn't scale well. Group the YP Client relevant resources into a <xref linkend="PuppetWorkshop-PuppetTerminology-class" /> instead, and then include the class with each node: </para> <para> <screen># Example /etc/puppet/manifests/classes/yp.pp @@ -510,7 +512,7 @@ class yp::client { }
file { "/etc/yp.conf": - source => "puppet://$server/files/yp.conf", + source => "puppet:///files/yp.conf", notify => Service["ypbind"], require => Package["ypbind"] } @@ -524,9 +526,7 @@ class yp::client { ] } }</screen> -<screen># Example /etc/puppet/manifests/site.pp - -$server = "<replaceable>master.example.com</replaceable>" + <screen># Example /etc/puppet/manifests/site.pp
import "classes/*.pp"
@@ -548,8 +548,6 @@ node '<replaceable>node3.example.com</replaceable>' { <para> <screen># Example /etc/puppet/manifests/site.pp
-$server = "<replaceable>master.example.com</replaceable>" - import "classes/*.pp"
node '<replaceable>node1.example.com</replaceable>' { @@ -598,8 +596,6 @@ node '<replaceable>node3.example.com</replaceable>' { <para> <screen># Example /etc/puppet/manifests/site.pp
-$server = "<replaceable>master.example.com</replaceable>" - import "classes/*.pp"
node '<replaceable>node1.example.com</replaceable>' { @@ -614,8 +610,26 @@ node '<replaceable>node3.example.com</replaceable>' { include desktop }</screen> </para> + <para> + Note that nodes, like classes, allow a simple form of inheritance, but can only inherit from one other node. As such, you can also use: + </para> + <para> + <screen>node desktop { + include yp::client +} + +node '<replaceable>node1.example.com</replaceable>' inherits desktop { +} + +node '<replaceable>node1.example.com</replaceable>' inherits desktop { +} + +node '<replaceable>node1.example.com</replaceable>' inherits desktop { +}</screen> + </para>
</section> + </chapter>
<chapter id="PuppetWorkshop-PuppetTerminology"> @@ -624,7 +638,7 @@ node '<replaceable>node3.example.com</replaceable>' { First, some clarification on the terminology used in this documentation. See also <xref linkend="PuppetWorkshop-Appendix-Terminology" /> <itemizedlist> <listitem> - <formalpara> + <formalpara id="PuppetWorkshop-PuppetTerminology-class"> <title>class</title> <para> A class is a collection of resources applied to a node with a single include statement. It groups together a comprehensible set of resources. A class <emphasis>ypclient</emphasis> would manage the <code>File["/etc/nsswitch.conf"]</code>, <code>File["/etc/yp.conf"]</code>, <code>Package["ypbind"]</code>, and <code>Service["ypbind"]</code> resources. @@ -714,6 +728,9 @@ FIXME: </listitem> </itemizedlist> </para> + <para> + More definition of terms are in <xref linkend="PuppetWorkshop-Appendix-Terminology" /> + </para> </chapter>
<chapter id="PuppetWorkshop-HowPuppetWorks"> @@ -760,7 +777,13 @@ FIXME: </para> </formalpara> <para> - <screen># <userinput>puppetca --sign <fqdn></userinput></screen> + <screen># <userinput>puppetca --sign <replaceable><Certificate-CN></replaceable></userinput></screen> + </para> + <para> + To refuse a certificate, or clean a signed certificate, use: + </para> + <para> + <screen># <userinput>puppetca --clean <replaceable><Certificate-CN></replaceable></userinput></screen> </para> </listitem> <listitem> @@ -969,7 +992,7 @@ debug: Calling fileserver.describe</screen> A webserver capable of performing as a frontend SSL reverse proxy load balancer, such as the Apache HTTPd webserver. </para> <para> - <screen># <userinput>yum install httpd</userinput></screen> + <screen># <userinput>yum install httpd mod_ssl</userinput></screen> </para> </listitem> <listitem> @@ -1019,6 +1042,8 @@ debug: Calling fileserver.describe</screen> <title>Configuring the Puppetmaster</title> <para> The configuration file for puppet and puppetmaster is <filename>/etc/puppet/puppet.conf</filename>. It is a file in INI-like format with sections, keys and values. There's 4 sections of interest, + </para> + <para> <itemizedlist> <listitem> <formalpara> @@ -1061,140 +1086,146 @@ debug: Calling fileserver.describe</screen> <title>Relevant Settings For The First Run</title> <para> For the first run of the puppetmaster, the following settings require configuration: - <itemizedlist> - <listitem> - <formalpara> - <title>[main]</title> - <para> - The locations where puppet seeks it's configuration and puts it's transitional data. The most important setting is <literal>vardir</literal>, which should be set to <filename>/var/lib/puppet/</filename>. Further settings include: - <itemizedlist> - <listitem> - <para> - <code>logdir = /var/log/puppet/</code> - </para> - </listitem> - <listitem> + </para> + </formalpara> + <para> + <itemizedlist> + <listitem> + <formalpara> + <title>[main]</title> + <para> + The locations where puppet seeks it's configuration and puts it's transitional data. The most important setting is <literal>vardir</literal>, which should be set to <filename>/var/lib/puppet/</filename>. Further settings include: + <itemizedlist> + <listitem> + <para> + <code>logdir = /var/log/puppet/</code> + </para> + </listitem> + <listitem> + <para> + <code>rundir = /var/run/puppet/</code> + </para> + </listitem> + <listitem> + <para> + <code>ssldir = $vardir/ssl/</code> + </para> + </listitem> + </itemizedlist> + </para> + </formalpara> + <note> + <para> + If you used a package to install puppet, the defaults should work, but may not comply with your backup strategy. It is the upstream puppet package that cannot cater to each and every distribution or operating system it is available for, and therefore has a set of defaults that will work, but will need to be changed on most platforms. + </para> + </note> + </listitem> + <listitem> + <formalpara> + <title>[puppetmasterd]</title> + <para> + <itemizedlist> + <listitem> + <formalpara> + <title>certname</title> <para> - <code>rundir = /var/run/puppet/</code> + The puppetmaster certificate's Common Name (CN), for which by default the system's hostname is used. The hostname of the system is a pretty reasonable value. </para> - </listitem> - <listitem> + </formalpara> + </listitem> + <listitem> + <formalpara> + <title>certdnsnames</title> <para> - <code>ssldir = $vardir/ssl/</code> + A colon (<literal>:</literal>) seperated list of DNS names resolving to the puppetmaster. Include here: </para> - </listitem> - </itemizedlist> - </para> - </formalpara> - <note> - <para> - If you used a package to install puppet, the defaults should work, but may not comply with your backup strategy. It is the upstream puppet package that cannot cater to each and every distribution or operating system it is available for, and therefore has a set of defaults that will work, but will need to be changed on most platforms. - </para> - </note> - </listitem> - <listitem> - <formalpara> - <title>[puppetmasterd]</title> - <para> - <itemizedlist> - <listitem> - <formalpara> - <title>certname</title> - <para> - The puppetmaster certificate's Common Name (CN), for which by default the system's hostname is used. The hostname of the system is a pretty reasonable value. - </para> - </formalpara> - </listitem> - <listitem> - <formalpara> - <title>certdnsnames</title> - <para> - A colon (<literal>:</literal>) seperated list of DNS names resolving to the puppetmaster. Include here: - <orderedlist> - <listitem> - <para> - The short hostname of the system, using the output of: <screen># <userinput>hostname -s</userinput></screen> - </para> - </listitem> - <listitem> - <para> - <literal>puppet</literal> - </para> - </listitem> - <listitem> - <para> - <literal>puppet</literal>, followed by the DNS domain name of the system, using the output of <screen># <userinput>dnsdomainname</userinput></screen> - </para> - </listitem> - <listitem> - <para> - Any other hostname or fully qualified domain name you want to use for the puppetmaster. - </para> - </listitem> - </orderedlist> - </para> - </formalpara> - </listitem> - </itemizedlist> - </para> - </formalpara> - </listitem> - <listitem> - <para> - Another setting to check is whether or not this puppetmaster is going to be the Certificate Authority <screen>[puppetmasterd] - ca = <replaceable>true</replaceable></screen> - The default is often set to <code>true</code>. + </formalpara> + <para> + <orderedlist> + <listitem> + <para> + The short hostname of the system, using the output of: <screen># <userinput>hostname -s</userinput></screen> + </para> + </listitem> + <listitem> + <para> + <literal>puppet</literal> + </para> + </listitem> + <listitem> + <para> + <literal>puppet</literal>, followed by the DNS domain name of the system, using the output of <screen># <userinput>dnsdomainname</userinput></screen> + </para> + </listitem> + <listitem> + <para> + Any other hostname or fully qualified domain name you want to use for the puppetmaster. + </para> + </listitem> + </orderedlist> + </para> + </listitem> + </itemizedlist> </para> - </listitem> - <listitem> - <para> - Whether or not to use autosigning of certificates, using - <screen>[puppetca] + </formalpara> + </listitem> + <listitem> + <para> + Another setting to check is whether or not this puppetmaster is going to be the Certificate Authority <screen>[puppetmasterd] + ca = <replaceable>true</replaceable></screen> + The default is often set to <code>true</code>. + </para> + </listitem> + <listitem> + <para> + Whether or not to use autosigning of certificates, using + <screen>[puppetca] autosign = <replaceable>false</replaceable></screen> - The default is to <emphasis>not</emphasis> use autosigning. Only applicable if <code>puppetca</code> is set to <code>true</code>. - </para> - </listitem> - </itemizedlist> - </para> - </formalpara> + The default is to <emphasis>not</emphasis> use autosigning. Only applicable if <code>puppetca</code> is set to <code>true</code>. + </para> + </listitem> + </itemizedlist> + </para> <formalpara> <title>Other Relevant Settings</title> <para> The following settings require review before the puppetmaster is going in production. - <itemizedlist> - <listitem> - <para> - A list of environments using a comma seperated list, in - <screen>[puppetmasterd] + </para> + </formalpara> + <para> + <itemizedlist> + <listitem> + <para> + A list of environments using a comma seperated list, in + <screen>[puppetmasterd] environments = <replaceable>development,testing,production</replaceable></screen> - </para> - <para> - <emphasis>See also</emphasis>: <xref linkend="PuppetWorkshop-HowToUsePuppet-Environments" /> - </para> - </listitem> - <listitem> - <para> - Whether or not to use reporting, and what reporting to use (tagmail, store, rrdgraph). To configure the types or reports that should be used by the puppetmaster, use a comma separated list without spaces, in: - <screen>[puppetmasterd] + </para> + <para> + <emphasis>See also</emphasis>: <xref linkend="PuppetWorkshop-HowToUsePuppet-Environments" /> + </para> + </listitem> + <listitem> + <para> + Whether or not to use reporting, and what reporting to use (tagmail, store, rrdgraph). To configure the types or reports that should be used by the puppetmaster, use a comma separated list without spaces, in: + <screen>[puppetmasterd] reports = <replaceable>tagmail,store,rrdgraph</replaceable></screen> - </para> - <para> - <emphasis>See also</emphasis>: <xref linkend="PuppetWorkshop-OtherThingsToDoWithPuppet-TweakingReporting" /> - </para> - </listitem> - <listitem> - <para> - The location of tagmail.conf, in order to map tags you give to resources to email addresses the reports should be sent to; <screen>[main] + </para> + <para> + <emphasis>See also</emphasis>: <xref linkend="PuppetWorkshop-OtherThingsToDoWithPuppet-TweakingReporting" /> + </para> + </listitem> + <listitem> + <para> + The location of tagmail.conf, in order to map tags you give to resources to email addresses the reports should be sent to; <screen>[main] tagmap = <replaceable>/path/to/tagmail.conf</replaceable></screen> - for reporting changes applied to puppets, via email. - </para> - <para> - <emphasis>See also</emphasis>: <xref linkend="PuppetWorkshop-OtherThingsToDoWithPuppet-TweakingReporting" /> - </para> - </listitem> - </itemizedlist> - </para> - </formalpara> + for reporting changes applied to puppets, via email. + </para> + <para> + <emphasis>See also</emphasis>: <xref linkend="PuppetWorkshop-OtherThingsToDoWithPuppet-TweakingReporting" /> + </para> + </listitem> + </itemizedlist> + </para> </section>
<section id="PuppetWorkshop-SettingUpPuppet-Configuration-Puppetmaster-Sitepp"> @@ -1203,11 +1234,9 @@ debug: Calling fileserver.describe</screen> Create a minimal <code>site.pp</code> in <filename>/etc/puppet/manifests/site.pp</filename> for the puppetmaster to parse on it's initial startup. Below is an example. </para> <screen># -# site.pp for any domain +# Example site.pp #
-$server = "<replaceable>master.puppetmanaged.org</replaceable>" - # The default node
node default { @@ -1231,51 +1260,343 @@ node default { <para> The webserver is going to listen on port 8140, the default port for the puppetmaster to listen for clients. It is going to forward traffic (after being decrypted) to the puppetmaster on 127.0.0.1:8141. </para> + <para> + Setting up the webserver requires you install <application>httpd</application> and <application>mod_ssl</application>. If these are not installed already, use: + </para> + <para> + <screen># <userinput>yum install httpd mod_ssl</userinput></screen> + </para> + <para> + Refer to <xref linkend="PuppetWorkshop-Appendix-ExampleSSLFrontendReverseProxyLoadBalancerConfiguration" /> for more an example VirtualHost configuration for an SSL frontend reverse proxy load balancer. + </para> </section>
- <section id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer"> - <title>Configuring the Database Server</title> + </section> + + </chapter> + + <chapter id="PuppetWorkshop-LanguageTutorial"> + <title>Language Tutorial</title> + <para> + The language puppet uses has a strong focus on making the specification of types as easy as possible within a heterogeneous set of resources shared over all the systems you are managing. + </para> + + <section id="PuppetWorkshop-LanguageTutorial-Resources"> + <title>Resources</title> + <para> + Resource are built by using a type and specifying a list of attributes to that type. Each type has a specific list of supported attributes. You can find the complete list of native types and their attributes on <ulink url="http://reductivelabs.com/trac/puppet/wiki/TypeReference" /> + </para> + <para> + A simple example resource is: + </para> + <para> + <screen>file { "/etc/passwd": + owner => "root", + group => "root", + mode => 644 +}</screen> + </para> + <para> + Any system on which this resource is managed will use it to verify that the <filename>/etc/passwd</filename> is owned by the root user and group, and has permissions 644. The field before the colon, in this case "/etc/passwd", is the resource's title, which can be used to refer to the resource later in the manifest. + </para> + <para> + For simple resources that don't vary much between systems, a single title to refer to is sufficient. Many system resources though vary from system to system. The OpenSSH Client package for example is called <application>openssh-client</application> on Debian systems, while it is <application>openssh-clients</application> on Fedora systems. To create the package resource to get the OpenSSH Client package installed through a manifest, Puppet allows you to specify: + </para> + <para> + <screen>package { "openssh-clients": + ensure => installed, + name => $operatingsystem ? { + "Debian" => "openssh-client", + default => "openssh-clients" + } +}</screen> + </para> + <important> + <para> + Note that the name of this resource is now conditional, and thus virtually unpredictable from within the rest of the manifests, but still if you wanted to require the OpenSSH Client package you do not need to conditionally require the resource, but instead can use the title of the resource, "openssh-clients". + </para> + </important> + <para> + Even more complex resources are file resources that are located in different paths on different operating systems, such as the configuration file for the OpenSSH Client package. + </para> + <para> + <screen>file { "/etc/ssh/ssh_config": + path => $operatingsystem ? { + "Solaris" => "/usr/local/etc/ssh/ssh_config", + "Darwin" => "/etc/ssh_config", + "Debian" => "/etc/openssh/ssh_config", + default => "/etc/ssh/ssh_config" + }, + require => Package["openssh-clients"] +}</screen> + </para> + <para> + The <emphasis>title</emphasis> for this resource is "/etc/ssh/ssh_config", while the path of the file managed is conditional to the value of the <code>$operatingsystem</code> variable. The <code>$operatingsystem</code> variable in this case is a fact describing the operating system name. Note that the title of the resource is used as a path if the path parameter had not been seperately configured. + </para> + <para> + Also note that it is the title of the <code>Package["openssh-clients"]</code> used in the reference to the OpenSSH Client package resource. + </para> + <note> + <para> + To refer to a resource, you can use it's title, or specify the magic attribute "alias". + </para> + </note> + + <section id="PuppetWorkshop-LanguageTutorial-ResourceDefaults"> + <title>Resource Defaults</title> + <para> + You can specify resource defaults so that the default parameter is always used when you make a resource by capitalizing the resource and not specifying a title. With the exec type for example, the command either must use a fully qualified path, or the path parameter to the exec type must be included. + </para> + <para> + <screen>Exec { + path => [ + "/bin/", + "/usr/bin/", + "/usr/local/bin/", + "/sbin/", + "/usr/sbin/", + "/usr/local/sbin/" + ] +}</screen> + </para> + <para> + This makes the following work + </para> + <para> + <screen>exec { "echo this works": }</screen> + </para> + <para> + While normally you would have to specify (in each exec resource): + </para> + <para> + <screen>exec { "/bin/echo this works" }</screen> + </para> + <para> + Needless to say, <application>echo</application> does not exist in <filename>/bin/</filename> on all operating sytems you may want to manage. + </para> <para> - A database can be configured to store the configuration distributed by the puppetmaster, and applied by the puppet. This is optional, and creates some overhead to the original purpose of configuring the puppets, but provides the opportunity to create overviews of applied classes to nodes, and a complete inventory of <emphasis>facts</emphasis> for all nodes. similarities / exemptions. + This type of defaults you would normally store in a file named after the type you specify the defaults for, in the directory <filename>utils/</filename> beneath the <filename>manifests/</filename> directory. </para>
- <section id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-SQLite3"> - <title>SQLite3</title> + <section id="PuppetWorkshop-LanguageTutorial-ResourceDefaults-ConditionalResourceDefaults"> + <title>Conditional Resource Defaults</title> <para> - SQLite(3) is a file based, light SQL database which is suitable for small databases. Depending on the size of the organization or priority you give to storing configs, generally speaking using SQLite(3) is not very suitable. In addition, SQLite3 isn't easily queried either manually or automatically. To setup SQLite3, provide the following settings in <filename>/etc/puppet/puppet.conf</filename>: + To facilitate the use of different defaults per operating system for example, you can set the resource defaults conditionally, using either <code>if-then-else</code> statements, or using <code>case</code> statements. </para> <para> - <screen>[puppetmasterd] - reports = store[,tagmail,rrdgraph] - storeconfigs = true - dbadapter = sqlite3 - dblocation = /var/lib/puppet/storeconfigs.sqlite</screen> + Case statements let you set the resource defaults as follows: + </para> + <para> + <screen>case $operatingsystem { + "Darwin": { + Exec { + path => [ + "/foo/bin/", + "/bar/sbin/", + (...etc...) + ] + } + } + "Solaris": { + Exec { + path => [ + "/baz/bin/", + "/baz/sbin/", + (...etc...) + ] + } + } + default: { + Exec { + path => [ + "/bin/", + "/usr/bin/", + (...etc...) + ] + } + } +}</screen> </para> + <important> + <para> + Resource defaults are not global, but apply to every resource in the current scope. If you define the resource defaults within a class, then the resources within that class and all sub-classes will have defaults set. The only way you can currently specify global defaults is to define them outside of any classes. + </para> + </important> + </section>
- <section id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-MySQL"> - <title>MySQL</title> + </section> + + <section id="PuppetWorkshop-LanguageTutorial-ResourceCollections"> + <title>Resource Collections</title> + <para> + There is two ways you can combine multiple resources: Classes and definitions. Classes are only interpreted once per node. Definitions on the other hand can be used as custom types and are meant to be interpreted more then once, each time with different parameters. + </para> + <section id="PuppetWorkshop-LanguageTutorial-ResourceCollections-Classes"> + <title>Classes</title> + <para> + Classes are introduced by using the <code>class</code> keyword, and support a simple form of inheritence. Subclasses are allowed to override resources defined in parent classes. + </para> + <para> + <screen>class unix { + file { [ + "/etc/passwd", + "/etc/group" + ]: + owner => root, + group => root, + mode => 644 + } +} + +class freebsd inherits unix { + File["/etc/passwd"] { + group => "wheel" + } + File["/etc/group"] { + group => "wheel" + } +}</screen> + </para> <para> - MySQL of course is much more scalable, and you can query it manually or automatically. + You can use the <code>undef</code> keyword when overriding a resource to make the child class act as if the value had never been set in the parent: </para> <para> - <screen>[puppetmasterd] - reports = store[,tagmail,rrdgraph] - storeconfigs = true - dbadapter = mysql - dbserver = 127.0.0.1 - dbuser = <replaceable>puppet</replaceable> - dbpassword = <replaceable>puppet</replaceable> - [dbsocket = /var/lib/mysql/mysql.sock] - </screen> + <screen>class freebsd inherits unix { + File["/etc/passwd"] { + group => undef + } +}</screen> + </para> + <para> + In this example, nodes which include the unix class will have the password file forced to group root, while nodes including freebsd would have the password file group ownership left unmodified. + </para> + <para> + It is also possible to add additional values to resource parameters using the <literal>+></literal> operator: + </para> + <para> + <screen>class apache { + service { "apache": + require => Package["httpd"] + } +} + +class apache-ssl inherits apache { + # host certificate is required for SSL to function + Service["apache"] { + require +> File["apache.pem"] + } +}</screen> + </para> + <para> + The above effectively makes the <code>require</code> parameter for the <code>Service["apache"]</code> resource in the <code>apache-ssl</code> class equal to <code>[ Package["httpd"], File["apache.pem"] ]</code>. + </para> + <para> + You can add multiple values by separating each value with commas: + </para> + <para> + <screen>class apache { + service { "apache": + require => Package["httpd"] + } +} + +class apache-ssl inherits apache { + Service["apache"] { + require +> [ + File["apache.pem"], + File["/etc/httpd/conf/httpd.conf"] + ] + } +}</screen> + </para> + <para> + The above would make the require parameter in the apache-ssl class equal to <code>[ Package["httpd"], File["apache.pem"], File["/etc/httpd/conf/httpd.conf"] ]</code>. + </para> + <para> + Like resources, you can also require a class, like so: + </para> + <para> + <screen>class apache { + service { "apache": + require => Class["squid"] + } +}</screen> + </para> + <para> + In this case, the <code>Class["squid"]</code> will need to be applied successfully before the <code>Service["apache"]</code> resource is applied on the puppet. + </para> + <formalpara> + <title>Namespacing</title> + <para> + Classes such as the <code>apache</code> class in one of the forementioned examples, sub-classed by the <code>apache-ssl</code> class, can also be defined within another class: + </para> + </formalpara> + <para> + <screen>class apache { + service { "apache": + require => Package["httpd"] + } + + class ssl inherits apache { + Service["apache"] { + require +> [ + File["apache.pem"], + File["/etc/httpd/conf/httpd.conf"] + ] + } + } +}</screen> + </para> + <para> + In this case, using the ssl subclass of apache would become <literal>include apache::ssl</literal>. </para> </section>
- <section id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-Postgresql"> - <title>PostgreSQL</title> + <section id="PuppetWorkshop-LanguageTutorial-ResourceCollections-Definitions"> + <title>Definitions</title> + <para> + Definitions are very similar to classes, but are introduced with the <code>define</code> keyword, and do not allow inheritance. Definitions also take parameters, which classes do not. + </para> + <para> + <screen>class yum { + define repository($enable = true) { + file { "/etc/yum.repos.d/$name.repo": + mode => 644, + owner => "root", + group => "root", + backup => false, + links => follow, + source => $enable ? { + true => [ + "puppet:///yum/$os/$osver/repos/$name.repo" + ], + default => [ + "puppet:///yum/$os/$osver/repos/$name.repo.disabled" + ] + } + } + } +}</screen> + </para> <para> - PostgreSQL is even more scalable then MySQL + This definition can be used as follows: </para> + <para> + <screen>node '<replaceable>node1.example.com</replaceable>' { + yum::repository { "<replaceable>custom</replaceable>": + enable => true + } +}</screen> + </para> + <para> + Now, <code>node1.example.com</code> gets a file <filename>/etc/yum.repos.d/custom.repo</filename> from <filename>puppet:///yum/$os/$osver/repos/custom.repo</filename>. + </para> + <note> + <para> + The above example makes use of <code>$os</code> and <code>$osver</code> variables you have to set first. See <xref linkend="PuppetWorkshop-BestPractices-SettingOsAndOsver" /> for more details. + </para> + </note> </section>
</section> @@ -1287,35 +1608,470 @@ node default { <chapter id="PuppetWorkshop-HowToUsePuppet"> <title>How To Use Puppet</title> <para> - This is a first section + Now that we've set up Puppet, let's see how we can use puppet. </para>
<section id="PuppetWorkshop-HowToUsePuppet-Modules"> <title>Using Modules</title> <para> - About using modules + Modules are collections of manifests, files, templates, plugins and possibly documentation on how to use the module, and have a special position in Puppet that allows them to be loaded automatically. Modules live in <filename><replaceable>$confdir</replaceable>/modules/</filename> by default, so you will need to change that using the <code>modulepath</code> setting in <filename>/etc/puppet/puppet.conf</filename>: </para> + <para> + <screen>[puppetmasterd] + modulepath = /var/lib/modules/</screen> + </para> + <para> + If you are to have multiple environments (development, testing and production for example), you would move modules to <filename>/var/lib/puppet/modules/<replaceable>environment</replaceable>/</filename>. For more information on configuring environments see <xref linkend="PuppetWorkshop-HowToUsePuppet-Environments-SettingUpEnvironments" />. Modules are comprised of the following: + </para> + <formalpara> + <title><filename><replaceable>module_name</replaceable>/manifests/init.pp</filename></title> + <para> + This file is mandatory and defines the manifest for the module. The <filename><replaceable>module_name</replaceable>/manifests/init.pp</filename> should at least define 1 class with the same name as the module to allow automatically loading the module. + </para> + </formalpara> + <formalpara> + <title><filename><replaceable>module_name</replaceable>/files/</filename></title> + <para> + The tree of files this module may distribute to clients. Note that this tree should only contain the modules default, which would represent the best default for all of the systems in the organization. If the manifest uses files from the module, address the files as follows from the manifest: + </para> + </formalpara> + <para> + <screen>source => "puppet:///<replaceable>module_name</replaceable>/path/to/file"</screen> + </para> + <para> + So, for example, if the file to distribute is <filename><replaceable>module_name</replaceable>/files/foo</filename>, the <code>source</code> value would be: + </para> + <para> + <screen>source => "puppet:///<replaceable>module_name</replaceable>/foo</screen> + </para> + <para> + For more information on using multiple sources, see <xref linkend="PuppetWorkshop-BestPractices-UsingMultipleSources" />. For more information on fileserver operations, see <xref linkend="PuppetWorkshop-HowToUsePuppet-Fileserver" />. + </para> + <formalpara> + <title><filename><replaceable>module_name</replaceable>/templates/</filename></title> + <para> + Templates are basically files with conditional content, or little programs spitting out what the actual file should look like given a set of variables, conditions and logical statements. Templates should go into the <filename><replaceable>module_name</replaceable>/templates/</filename> directory, and can therefrom used as follows: + </para> + </formalpara> + <para> + <screen>content => template('<replaceable>module_name</replaceable>/<replaceable>template_filename</replaceable>')</screen> + </para> + <para> + Example with <filename>webserver/templates/virtualhost.conf.erb</filename>: + </para> + <para> + <screen>content => template('webserver/virtualhost.conf.erb')</screen> + </para> + <formalpara> + <title><filename><replaceable>module_name</replaceable>/plugins/puppet/type/</filename></title> + <para> + The location to put custom types to be distributed with this module. Requires <code>pluginsync</code> to be set to <code>true</code> in the puppet configuration. + </para> + </formalpara> + <formalpara> + <title><filename><replaceable>module_name</replaceable>/plugins/puppet/provider/</filename></title> + <para> + The location to put custom providers to be distributed with this module. Requires <code>pluginsync</code> to be set to <code>true</code> in the puppet configuration. + </para> + </formalpara> + <formalpara> + <title><filename><replaceable>module_name</replaceable>/plugins/facter/</filename></title> + <para> + The location to put custom facts to be distributed with this module. Requires <code>pluginsync</code> to be set to <code>true</code> in the puppet configuration. + </para> + </formalpara> + <formalpara> + <title><filename><replaceable>module_name</replaceable>/documentation/</filename></title> + <para> + Just an optional placeholder for documentation you may want to distribute along with the module, if you are to share the module with the community. + </para> + </formalpara> + </section> + + <section id="PuppetWorkshop-HowToUsePuppet-Environments"> + <title>Environments</title> + <para> + Environments aid in staging developments and not breaking the production. There's three environments you would want to consider using: + </para> + <formalpara> + <title>development</title> + <para> + The development environment is where you stage developments in your manifests, modules and plugins. You would apply the development environment to a small number of non-critical systems, that maybe are dedicated to the sole purpose of puppet development. These development systems would not necessarily be managed extensively with puppet, and may just apply only the classes you are making changes to. There is no use in applying the <code>foo</code> class to a development system if you are making changes to the <code>bar</code> module or class. + </para> + </formalpara> + <formalpara> + <title>testing</title> + <para> + The testing stage would be the stage where your basic development is finished, and you test the changes in a broader scope. Again, these systems should be non-critical systems, but these systems may also not be dedicated to puppet testing. In the testing stage, you would want to make sure all puppet classes and modules are applied correctly when used in combination with one another. For example, conflicts between modules and/or classes could be caused by duplicate resource definitions, or incorrect inter-module or inter-class dependencies (<code>require =></code>, <code>notify =></code>, <code>before =></code> and <code>subscribe =></code> statements). Although most of the dependencies are used in the development stage as well, using the testing stage makes sure N+X modules cooperate before you destroy your production. + </para> + </formalpara> + <para> + Obviously, the testing stage is entirely optional. One could decide to not use the testing stage the way it is subscribed here, but instead assign a couple of (power-)user desktops and have these users provide feedback on the changes applied, or not use the testing stage at all. + </para> + <formalpara> + <title>production</title> + <para> + The production stage is the most important stage in the entire process of staging changes. The production environment is where most of the systems within your orgnization are managed in, and as such, changes applied to the production environment need to be proven stable (or system and services may be interrupted). + </para> + </formalpara> + <para> + <inlinemediaobject> + <objectinfo> + <title>An overview of how changes are applied and staged</title> + </objectinfo> + <imageobject> + <imagedata format='PNG' fileref="images/environment_staging.png" /> + </imageobject> + </inlinemediaobject> + </para> + + <section id="PuppetWorkshop-HowToUsePuppet-Environments-SettingUpEnvironments"> + <title>Setting Up Environments</title> + <para> + Setting up the environments requires you to configure the available environments in <filename>/etc/puppet/puppet.conf</filename>. The relevant settings are: + </para> + <para> + <screen>[puppetmasterd] + environments = <replaceable>development,testing,production</replaceable></screen> + </para> + <para> + Per environment, create a section similar to the following: + </para> + <para> + <screen>[development] + manifest = /var/lib/puppet/manifests/development/site.pp + modulepath = /var/lib/puppet/modules/development + +[testing] + manifest = /var/lib/puppet/manifests/testing/site.pp + modulepath = /var/lib/puppet/modules/testing + +[production] + manifest = /var/lib/puppet/manifests/production/site.pp + modulepath = /var/lib/puppet/modules/production</screen> + </para> + <para> + Make sure you put the files and directories in place before restarting the puppetmaster service. + </para> + </section> + </section>
<section id="PuppetWorkshop-HowToUsePuppet-VirtualResources"> <title>Virtual Resources</title> <para> - paragraph + Using virtual resources is a way to avoid duplicate definitions in your manifests if you define a virtual resource in parent classes, inherit the parent classes in sub-classes, and realize() the virtual resource in the sub-class. + </para> + <para> + An example where you can use virtual resources is a webserver class: + </para> + <para> + <screen>class webserver { + package { "httpd": + ensure => installed + } + + class ssl { + package { [ + "httpd", + "mod_ssl" + ]: + ensure => installed + } + } +}</screen> + </para> + <para> + Now, if you were to <code>include webserver</code> on a system, everything is well. However, some other class may require the <code>webserver::ssl</code> class to be applied to the system as well: + </para> + <para> + <screen>class mail { + class server { + # Include webserver::ssl for secure webmail capacity + include webserver::ssl + + webserver::virtualhost { "mail.$domain": + enable => true, + certificate => true + } + + package { "squirrelmail": + ensure => installed + } + } +}</screen> + </para> + <para> + This would result in a duplicate definition of the <code>Package["httpd"]</code> resource. Such is easily prevented by making sure the <code>Package["httpd"]</code> resource is only defined once: + </para> + <para> + <screen>class webserver { + @package { [ + "httpd", + "mod_ssl" + ]: + ensure => installed + } + + realize(Package["httpd"]) + + class ssl inherits webserver { + realize( Package["httpd"], + Package["mod_ssl"]) + } +}</screen> + </para> + <para> + A more complex but also more appropriate example can be found at <ulink url="http://git.puppetmanaged.org/?p=puppet;a=blob;f=manifests/init.pp" />. Note that in the puppet module linked from puppetmanaged.org, the definition and use of virtual resources is a necessity. </para> </section>
<section id="PuppetWorkshop-HowToUsePuppet-Plugins"> <title>Using Plugins</title> <para> - About the use of plugins + Using plugins (e.g. custom types, facts, functions and providers) gives you even more control over the behaviour of puppet and extend what it can do. An example custom fact that exposes the version of Python installed is: + </para> + <para> + <screen># From <ulink url="http://reductivelabs.com/trac/puppet/wiki/Recipes/PythonVersion" /> +require 'facter' + +pythonversion = nil +if FileTest.exists?("/usr/bin/python") + pythonversion = %x{python -V 2>&1}.split(" ")[1] +end + +Facter.add("pythonversion") do + setcode do + pythonversion + end +end + +Facter.add("pythonmmversion") do + pythonmmversion = nil + if pythonversion != nil + pythonversionsplit = pythonversion.split(".") + pythonmmversion = pythonversionsplit[0] + "." + pythonversionsplit[1] + end + setcode do + pythonmmversion + end +end</screen> </para> + <para> + Using this new custom fact you can create conditional statements in manifests or templates using <code>$pythonverion</code> or <code>$pythonmmversion</code>. + </para> + </section>
- <section id="PuppetWorkshop-HowToUsePuppet-Environments"> - <title>Environments</title> + <section id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM"> + <title>Using Manifests from a SCM</title> <para> - paragraph + Using manifests from a Source Control Management system. </para> + + <section id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM-SingleTree"> + <title>Using a Single Tree</title> + <para> + Using a single tree in case you want to use environment staging and module support is considered a bad idea, but can still be done. The layout of such a repository can be one of the following: + </para> + <section id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM-SingleTree-UsingSVN"> + <title>Using SVN</title> + <para> + Using SVN allows Windows users to contribute to the repository and/or make edits to files in the repository, which of course has it's advantages. + </para> + <para> + The layout of an SVN repository would be as follows: + </para> + <para> + <screen><replaceable>/path/to/repository/</replaceable> + `- trunk/ + `- branches/ + `- testing/ + `- production/</screen> + </para> + <para> + <filename>trunk/</filename> would represent the development branch in this case. Continuing with <filename>trunk/</filename>, the repository layout could look as follows: + </para> + <para> + <screen>trunk/ + `- conf/ + `- files/<footnote><para>Used for files distributed without them being integrated into a module</para></footnote> + `- modules/ + `- module1/ + `- files/ + `- manifests/ + `- plugins/ + `- templates/ + `- module2/ + `- (etc) + `- manifests/ + `- site.pp + `- classes/ + `- nodes/ + `- utils/ + `- (etc)</screen> + </para> + <para> + Checking out the SVN repository (sub-directories) in each various location on the puppetmaster would look like + </para> + <para> + <screen># svn co http://server/svn/puppet/trunk/conf/ /etc/puppet/<footnote><para>Note that checking out the puppet configuration from <code>trunk/</code> like this almost certainly results in you having to manage the puppetmaster with puppet; See <ulink url="http://puppetmanaged.org/documentation/puppet-module/" /> for more information.</para></footnote> +# svn co http://server/svn/puppet/trunk/modules/ \ + /var/lib/puppet/modules/development/ +# svn co http://server/svn/puppet/trunk/manifests/ \ + /var/lib/puppet/manifests/development/ +# svn co http://server/svn/puppet/branches/testing/modules/ \ + /var/lib/puppet/modules/testing/ +# svn co http://server/svn/puppet/branches/testing/manifests/ \ + /var/lib/puppet/manifests/testing/ +# svn co http://server/svn/puppet/branches/production/modules/ \ + /var/lib/puppet/modules/production/ +# svn co http://server/svn/puppet/branches/production/manifests/ \ + /var/lib/puppet/manifests/production/ + </screen> + </para> + <para> + See <xref linkend="PuppetWorkshop-Appendix-SVNCommitHooks" /> for how to make the puppetmaster update it's sources when you commit changes to the SVN repository. + </para> + </section> + + <section id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM-SingleTree-UsingGIT"> + <title>Using GIT</title> + <para> + It is currently not possible to use a single GIT tree to configure all of your puppet environment without requiring manual (or automatic) copying of files and directories from within a copy of the source repository into the location where puppet expects them to be. + </para> + </section> + + </section> + + <section id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM-MultipleTrees"> + <title>Multiple Trees</title> + <para> + Multiple trees is more efficient because you can merge changes from one stage to another on a per-module basis, rather then merging entire branches with all configuration, manifests, modules and so forth. Additionally, using multiple trees allows you to upstream modules integrated into your own set of modules. + </para> + <para> + Using one SCM tree for each module allows you to set access control accordingly, and stage changes per module instead of globally. + </para> + </section> + + <section id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM-ModulesFromUpstream"> + <title>Modules From Upstream</title> + <para> + You can use one or more modules from an upstream provider such as: + <itemizedlist> + <listitem> + <para> + <ulink url="http://puppetmanaged.org/" /> + </para> + </listitem> + <listitem> + <para> + <ulink url="http://git.black.co.at/" /> + </para> + </listitem> + <listitem> + <para> + <ulink url="http://reductivelabs.com/trac/puppet/wiki/Recipes" /> + </para> + </listitem> + </itemizedlist> + </para> + <para> + Using either a single tree or multiple trees as described above; the environment staging though does not apply to most of the modules available from upstream providers. If the upstream provider does provide branches for each of your environments, you would still only want to use the production or equivalent branch. + </para> + <para> + If you want to use a module from an upstream provider without the use of the branches for staging, you can specify a colon-seperated list to the <code>modulepath</code> setting for your environments to have puppet search through the list of paths. Such would look like: + </para> + <para> + <screen>[puppetmasterd] + environments = <replaceable>development,testing,production</replaceable> + +[development] + manifest = /var/lib/puppet/manifests/development/site.pp + modulepath = /var/lib/puppet/modules/upstream/:/var/lib/puppet/modules/development/ + +[testing] + manifest = /var/lib/puppet/manifests/testing/site.pp + modulepath = /var/lib/puppet/modules/upstream/:/var/lib/puppet/modules/testing/ + +[production] + manifest = /var/lib/puppet/manifests/production/site.pp + modulepath = /var/lib/puppet/modules/upstream/:/var/lib/puppet/modules/production/</screen> + </para> + <para> + In the example configuration, you would clone, checkout and pull the modules you use from an upstream provider to <filename>/var/lib/puppet/modules/upstream/<replaceable>module_name</replaceable>/</filename>. + </para> + + </section> + + </section> + + <section id="PuppetWorkshop-HowToUsePuppet-Fileserver"> + <title>Fileserver</title> + <para> + Puppet's fileserver, again a core component in being able to apply configuration management to your systems, needs to be configured appropriately to allow files, templates, plugins and facts to be distributed to your clients. + </para> + + <section id="PuppetWorshop-HowToUsePuppet-Fileserver-Operations"> + <title>Fileserver Operations</title> + <para> + Using <filename>/etc/puppet/fileserver.conf</filename>, you can define fileserver <emphasis>mounts</emphasis>. + </para> + <para> + Each fileserver mount has a <code>path</code> attribute and <code>allow</code> and/or <code>deny</code> statements. The <code>path</code> attribute describes where the files are on the local filesystem, and <code>allow</code> and <code>deny</code> statements allow you to apply access control to these fileserver mounts. Note that <code>deny</code> always has precedence over <code>allow</code>, and that the order of <code>allow</code> and <code>deny</code> does therefor not matter. + </para> + <para> + Also note, that the wildcard matches used in <code>allow</code> and <code>deny</code> match DNS, or IP addresses. You can also use CIDR notations for the latter. + </para> + <para> + Additionally, the special <code>[modules]</code> mount does not use the <code>path</code> attribute but instead figures out the filesystem path based on the <code>modulepath</code> setting in <filename>/etc/puppet/puppet.conf</filename>, taking the environment used into account as well. + </para> + <para> + <screen># This file consists of arbitrarily named sections/modules +# defining where files are served from and to whom + +# Define a section 'files' +# Adapt the allow/deny settings to your needs. Order +# for allow/deny does not matter, allow always takes precedence +# over deny + +[facts] + path /var/lib/puppet/facts + allow * + +[files] + path /var/lib/puppet/files + allow *.example.com + deny *.evil.example.com + +[modules] + allow *</screen> + </para> + <para> + The <code>source =></code> parameter in resources now let's you use the following sources: + <itemizedlist> + <listitem> + <formalpara> + <title><filename>puppet:///files/<replaceable>path/to/file</replaceable></filename></title> + <para> + Resulting in the file being looked for in <filename>/var/lib/puppet/files/<replaceable>path/to/file</replaceable></filename>. + </para> + </formalpara> + </listitem> + <listitem> + <formalpara> + <title><filename>puppet:///<replaceable>module_name</replaceable>/<replaceable>path/to/file</replaceable></filename></title> + <para> + Resulting in the file being looked for in the path for module <replaceable>module_name</replaceable>, subdirectory <filename>files/</filename>, <filename><replaceable>path/to/file</replaceable></filename>. + </para> + </formalpara> + <para> + So, <filename>puppet:///sudo/sudoers</filename> would result in <filename>/var/lib/puppet/modules/<replaceable>$environment</replaceable>/sudo/files/sudoers</filename>. + </para> + </listitem> + </itemizedlist> + </para> + </section> </section>
</chapter> @@ -1323,7 +2079,7 @@ node default { <chapter id="PuppetWorkshop-TroubleshootingPuppet"> <title>Troubleshooting Puppet</title> <para> - This section is about troubleshooting the manifests, puppetmaster and puppet. + This section is about troubleshooting the manifests, the puppetmaster and the puppet. </para>
<section id="PuppetWorkshop-TroubleshootingPuppet-Manifests"> @@ -1411,17 +2167,20 @@ node default { <section id="PuppetWorkshop-TroubleshootingPuppet-ThePuppetmaster"> <title>The Puppetmaster</title> <para> - The puppetmaster + The puppetmaster service is configured using <filename>/etc/sysconfig/puppetmaster</filename>, which is also where you can specify what the log destination is, and whether the puppetmaster service should run in verbose or debug mode. Both would be using the <code>PUPPETMASTER_EXTRAOPTS</code> variable. </para>
<section id="PuppetWorkshop-TroubleshootingPuppet-ThePuppetmaster-DebuggingThePuppetmaster"> <title>Debugging The Puppetmaster</title> <para> - Running the puppetmaster in debug mode generates a lot of output. To run the puppetmaster in debug mode, stop the puppetmaster service and type: + Running the puppetmaster in debug mode generates a lot of output. You may want to redirect the output to a file and read the file to prevent the available scrollback buffer from filling up too fast. To run the puppetmaster in debug mode, stop the puppetmaster service and type: </para> <para> <screen># <userinput>puppetmasterd --debug --no-deamonize [--servertype mongrel --masterport 8141]</userinput></screen> </para> + <para> + Or run the service in verbose or debug mode by editing <filename>/etc/sysconfig/puppetmaster</filename> and adding <code>--verbose</code> or <code>--debug</code> to <code>PUPPETMASTER_EXTRAOPTS</code>. + </para> </section>
</section> @@ -1429,7 +2188,7 @@ node default { <section id="PuppetWorkshop-TroubleshootingPuppet-ThePuppet"> <title>The Puppet</title> <para> - para + Debugging the puppet can be tricky, because it is the puppetmaster that parses the manifests first, and so if the puppetmaster fails parsing the manifests, the puppet client won't get any changes to it's manifests. If files or templates have changed on the fileserver, these will obviously be changed on the puppet as well, but if a template uses a new variable from the manifest, the template will fail to parse and the error message will occur on the puppetmaster. </para>
<section id="PuppetWorkshop-TroubleshootingPuppet-ThePuppet-DebuggingThePuppet"> @@ -1446,41 +2205,193 @@ node default { <chapter id="PuppetWorkshop-OtherThingsToDoWithPuppet"> <title>Other Things To Do With Puppet</title> <para> - This is a first section + There's way more you can do with puppet. Actually there's so much you can do with puppet it does not fit in a workshop type of course. </para>
+ + <section id="PuppetWorkshop-OtherThingsToDoWithPuppet-StoreConfigurationsInADatabase"> + <title>Store Configurations In A Database</title> + <para> + A database can be configured to store the configuration distributed by the puppetmaster, and applied by the puppet. This is optional, and creates some overhead to the original purpose of configuring the puppets, but provides the opportunity to create overviews of applied classes to nodes, and a complete inventory of <emphasis>facts</emphasis> for all nodes. similarities / exemptions. + </para> + + <section id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-SQLite3"> + <title>SQLite3</title> + <para> + SQLite(3) is a file based, light SQL database which is suitable for small databases. Depending on the size of the organization or priority you give to storing configs, generally speaking using SQLite(3) is not very suitable. In addition, SQLite3 isn't easily queried either manually or automatically. To setup SQLite3, provide the following settings in <filename>/etc/puppet/puppet.conf</filename>: + </para> + <para> + <screen>[puppetmasterd] + reports = store[,tagmail,rrdgraph] + storeconfigs = true + dbadapter = sqlite3 + dblocation = <replaceable>/var/lib/puppet/storeconfigs.sqlite</replaceable></screen> + </para> + </section> + + <section id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-MySQL"> + <title>MySQL</title> + <para> + MySQL of course is much more scalable, and you can query it manually or automatically. A simple query can give you all webservers in the organization: + </para> + <para> + <screen>$ mysql -p puppet -e 'SELECT hosts.name + FROM resources INNER JOIN hosts + ON resources.host_id = hosts.id + WHERE resources.title = "webserver" + GROUP BY name;' + ++---------------------------+ +| name | ++---------------------------+ +| app1.genomicscenter.nl | +| elwood.kanarip.com | +| master.puppetmanaged.org | +| open.the-cave-of-steef.nl | +| pinky.kanarip.com | +| server.ogd.nl | +| vito.kanarip.com | ++---------------------------+ +7 rows in set (0.02 sec) + +</screen> + </para> + <para> + To configure storing the configurations, use the following settings in <filename>/etc/puppet/puppet.conf</filename>: + </para> + <para> + <screen>[puppetmasterd] + storeconfigs = true + dbadapter = mysql + dbserver = <replaceable>127.0.0.1</replaceable> + dbuser = <replaceable>puppet</replaceable> + dbpassword = <replaceable>puppet</replaceable> + [dbsocket = /var/lib/mysql/mysql.sock] + </screen> + </para> + </section> + + <section id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-Postgresql"> + <title>PostgreSQL</title> + <para> + PostgreSQL is even more scalable then MySQL as it's multi-master, but most importantly you may already have PostgreSQL running in your organization, and may just want to add puppet's configuration store to that infrastructure. + </para> + <para> + To add the configuration store to PostgreSQL, use the following settings in <filename>/etc/puppet/puppet.conf</filename>: + </para> + <para> + <screen>[puppetmasterd] + storeconfigs = true + dbadapter = postgresql + dbuser = <replaceable>puppet</replaceable> + dbpassword = <replaceable>password</replaceable> + dbserver = <replaceable>localhost</replaceable> + dbname = <replaceable>puppet</replaceable></screen> + </para> + <para> + And create the database: + </para> + <para> + <screen># <userinput>su - postgres</userinput> +$ <userinput>psql template1</userinput> +template1=# <userinput>create database <replaceable>puppet</replaceable>;</userinput> +CREATE DATABASE +<userinput>create user <replaceable>puppetuser</replaceable> with unencrypted password '<replaceable>password</replaceable>';</userinput> +CREATE ROLE +template1=# <userinput>grant create on database <replaceable>puppet</replaceable> to <replaceable>puppetuser</replaceable>;</userinput> +</screen> + </para> + </section> + + </section> + <section id="PuppetWorkshop-OtherThingsToDoWithPuppet-TweakingReporting"> <title>Tweaking Reporting</title> <para> - paragraph + Reports can be sent out to various email addresses as a notification on errors or changes applied to the systems. This not only helps in keeping track of changes being applied, but can also help you keep your manifests clean, attending to each error that may otherwise have passed by unnoticed. </para> - </section> + <para> + To enable reporting, use the following settings in <filename>/etc/puppet/puppet.conf</filename>: + </para> + <para> + <screen>[main] + tagmap = /etc/puppet/tagmail.conf
- <section id="PuppetWorkshop-OtherThingsToDoWithPuppet-WritingCustomTypes"> - <title>Writing Custom Types</title> +[puppetmasterd] + reports = tagmail + +[puppet] + reportfrom = puppet-reports@<replaceable>domain.tld</replaceable></screen> + </para> + <para> + This will cause the puppetmaster to look at <filename>/etc/puppet/tagmail.conf</filename>, which maps tags to email addresses that need to be notified on changes applied to systems tagged with the appropriate keywords. + </para> + <formalpara> + <title>Using tags in your manifests</title> + <para> + You can use tags in your manifests like so: + </para> + </formalpara> + <para> + <screen>class ssh { + tag("security") + file { "...": + (...) + } +}</screen> + </para> + <para> + To have notifications on changes to systems including the ssh class sent to security-admins@domain.tld, use the following tagmap entry: + </para> + <para> + <screen>ssh: ssh-admins@domain.tld +security: security-admins@domain.tld</screen> + </para> + <para> + A catch-all can be specified to have notifications on all changes sent to an emailaddress or multiple email addresses: + </para> <para> - paragraph + <screen>all: system-admins@domain.tld</screen> </para> </section>
<section id="PuppetWorkshop-OtherThingsToDoWithPuppet-WritingCustomFacts"> <title>Writing Custom Facts</title> <para> - paragraph + Writing custom facts, like writing custom types, functions or providers, is done in Ruby, using the interfaces and objects offered by Puppet. + </para> + </section> + + <section id="PuppetWorkshop-OtherThingsToDoWithPuppet-WritingCustomTypes"> + <title>Writing Custom Types</title> + <para> + <ulink url="http://reductivelabs.com/trac/puppet/wiki/CreatingCustomTypes" /> </para> </section>
<section id="PuppetWorkshop-OtherThingsToDoWithPuppet-WritingCustomFunctions"> <title>Writing Custom Functions</title> <para> - paragraph + <ulink url="http://reductivelabs.com/trac/puppet/wiki/WritingYourOwnFunctions" /> + </para> + </section> + + <section id="PuppetWorkshop-OtherThingsToDoWithPuppet-WritingCustomProviders"> + <title>Writing Custom Providers</title> + <para> + <ulink url="http://reductivelabs.com/trac/puppet/wiki/ProviderDevelopment" /> </para> </section>
<section id="PuppetWorkshop-OtherThingsToDoWithPuppet-StoreconfigsReportingAndPuppetview"> <title>Storeconfigs, Reporting and Puppetview</title> <para> - paragraph + Using Puppet's storeconfig capability and standard reporting, in combination with rrdgraphing, you can use applications such as <application>Puppetview</application> to create nice overviews of results of your configuration management as applicable to your systems. +<!-- + FIXME + + Elaborate +//--> </para> </section>
@@ -1489,8 +2400,153 @@ node default { <chapter id="PuppetWorkshop-BestPractices"> <title>Best Practices</title> <para> - This is a first section + Using Puppet starting in a small(er) scale, scaling up to more and more systems in your organization to become managed with puppet, you might encounter some challenges and to prevent you end up with a giant mess of manifests, files, templates, needing to search (with <code>grep</code> and <code>find</code> for example) where it is exactly you need to configure the next change, or have you use configuration items that need to be changed as soon as your organization introduces the next generation operating systems or distribution version, here's a set of <emphasis>Best Practices</emphasis>, or actually Tips & Tricks. </para> + + <section id="PuppetWorkshop-BestPractices-SettingOsAndOsver"> + <title>Setting $os and $osver</title> + <para> + The <code>$operatingsystem</code> and <code>$operatingsystemrelease</code>, <code>$operatingsystemversion</code> or <code>$lsbdistrelease</code> variables relate directly to specific operating system resources, configuration file locations, package names and most importantly, settings you can or cannot use in the configuration files for specific applications<footnote><para>Enterprise Linux 3 distributions for example have OpenSSH Server versions that do not use, and are incompatible with, the <code>UsePAM</code> setting in <filename>/etc/ssh/sshd_config</filename>. In these cases, you would want the configuration file for EL-3 to come from a different location then for the EL-4 and EL-5 distributions that do take <code>UsePAM</code> because of the OpenSSH Server package versions they ship.</para></footnote>. Using the <code>$operatingsystem</code>, <code>$operatingsystemrelease</code>, <code>$operatingsystemversion</code> and/or <code>$lsbdistrelease</code> variables (set from Facter) help you determine the Operating System (Distribution) and Version. + </para> + <para> + However, some distributions have Facter set <code>$operatingsystemrelease</code>, while others set <code>$lsbdistrelease</code>. If these variables need to be used in manifests, you really do not want to use these both because in your manifests all you are interested in is the Operating System Version. Using the following snippet of code in <filename>site.pp</filename> enables you to use <code>$os</code> and <code>$osver</code> throughout your manifests. + </para> + <para> + <screen># Get facts and give them a good, good name +$os = $operatingsystem + +case $os { + "Fedora", "CentOS", "RedHat": { + $osver = $lsbdistrelease + } + "Debian", "SuSE', "OpenSuSE": { + $osver = $operatingsystemrelease + } + "Darwin": { + $osver = $operatingsystemrelease + } +}</screen> + </para> + </section> + + <section id="PuppetWorkshop-BestPractices-UsingMultipleSources"> + <title>Using Multiple Sources</title> + <para> + para + </para> + </section> + + <section id="PuppetWorkshop-BestPractices-GroupProfiles"> + <title>Group Profiles</title> + <para> + Grouping <emphasis>profiles</emphasis> that define certain resources to be applied to the systems you manage can help you in giving and keeping structure in the ever-growing configuration tree you use. + </para> + <para> + We know of organizations that run the following deterministic set of profiles: + </para> + <para> + <itemizedlist> + <listitem> + <formalpara> + <title>Classes</title> + <para> + Organization specific classes not eligible to become a module. Note that these are mostly very simple classes, managing a single resource, or merely defining a set of variables per-class to be used throughout the manifests. + </para> + </formalpara> + <para> + Using classes to manage multiple resources is strongly discouraged as these are commonly eligible to become modules. However, when using a module from upstream changing that module to fit your needs introduces the risk of conflicts when trying to update the module from upstream. Therefor, classes can be used to override and/or extend classes from modules. + </para> + <para> + Classes are stored in <filename>classes/*.pp</filename>, where the name of the actual file represents the class defined in the file. + </para> + </listitem> + <listitem> + <formalpara> + <title>Domains</title> + <para> + Domains are most commonly used within organizations that manage branch offices, or merge with other organizations, or Puppet setups that manage more then one organization at a time. + </para> + </formalpara> + <para> + puppetmanaged.org runs multiple organizations with each organization maintaining their own domain specific configuration tree and manifests. + </para> + <para> + CustomerA has 3 branch offices and uses domains to define branch office specific settings. + </para> + <para> + Defined domains should go in <filename>domains/*.pp</filename> where the actual filename is the most descriptive common denominator between all systems in a domain. + </para> + </listitem> + <listitem> + <formalpara> + <title>Groups</title> + <para> + Grouping systems allow you to control a lot more with a lot less. Less is more, sort of. Groups can include <emphasis>servers</emphasis>, <emphasis>desktops</emphasis>, <emphasis>laptops</emphasis>. + </para> + </formalpara> + <para> + Given a group of servers you can imagine SELinux needs to be enabled on all of these, and the firewall needs to be up and running. Create a group servers in <filename>groups/server.pp</filename> and include the class defined or inherit the node used. + </para> + </listitem> + <listitem> + <formalpara> + <title>Modules</title> + <para> + Modules that need settings or modules that need to be pulled (updated) from upstream. For older versions of Puppet (< 0.24.4), you can state "imports" here as well. + </para> + </formalpara> + <para> + explanation + </para> + </listitem> + <listitem> + <formalpara> + <title>Nodes</title> + <para> + The nodes you manage, one per file. These oughta be actual nodes, not abstraction levels where you inherit from. + </para> + </formalpara> + <para> + explanation + </para> + </listitem> + <listitem> + <formalpara> + <title>Services</title> + <para> + para + </para> + </formalpara> + <para> + explanation + </para> + </listitem> + <listitem> + <formalpara> + <title>Utils</title> + <para> + para + </para> + </formalpara> + <para> + explanation + </para> + </listitem> + <listitem> + <formalpara> + <title>Users</title> + <para> + para + </para> + </formalpara> + <para> + explanation + </para> + </listitem> + </itemizedlist> + </para> + </section> + </chapter>
<xi:include href="Appendix.xml" xmlns:xi="http://www.w3.org/2001/XInclude" /> diff --git a/Workshops/PuppetWorkshop/en-US/environment_staging.png b/Workshops/PuppetWorkshop/en-US/environment_staging.png new file mode 100644 index 0000000..e2e7218 Binary files /dev/null and b/Workshops/PuppetWorkshop/en-US/environment_staging.png differ diff --git a/Workshops/PuppetWorkshop/en-US/images/environment_staging.png b/Workshops/PuppetWorkshop/en-US/images/environment_staging.png new file mode 100644 index 0000000..e2e7218 Binary files /dev/null and b/Workshops/PuppetWorkshop/en-US/images/environment_staging.png differ diff --git a/en-US/Courses.xml b/en-US/Courses.xml index 00287a7..b250580 100644 --- a/en-US/Courses.xml +++ b/en-US/Courses.xml @@ -94,7 +94,6 @@ </listitem>
//--> - </itemizedlist> </para> </formalpara> @@ -182,4 +181,6 @@ <xi:include href="Books/Linux/RHCETraining/Course.xml" xmlns:xi="http://www.w3.org/2001/XInclude" /> <xi:include href="Books/Linux/RHCETraining/Manual.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="Books/Workshops/PuppetWorkshop/Puppet_Workshop.xml" xmlns:xi="http://www.w3.org/2001/XInclude" /> + </set>