Puppet Scalability

Version 3 (John Warburton, 09/01/2010 11:49 pm)

1 1
# Puppet Scalability Notes
2 1
3 1
This document describes some of the issues when scaling puppet to
4 1
provide service for a large number of nodes.
5 1
6 1
Please see [[Using Mongrel]] for information on how to set up
7 1
Mongrel as was used in this test.
8 1
9 1
# Master Performance
10 1
11 1
As of Puppet 0.22.1, the fileserver, CA methods, and configuration
12 1
parsing methods all share resources within the puppetmasterd
13 1
process. The consequence of this setup is that a manifest with a
14 1
large number of fileserver requests will result in the fileserver
15 1
methods capitalizing the resources.
16 1
17 1
Symptoms: puppetd calls to puppetmaster.getconfig or the CA methods
18 1
seem to take an unreasonably long time, or timeout altogether.
19 1
20 1
# Splitting off the Fileserver
21 1
22 1
Since each node will typically make a single call to get their
23 1
configuration, which in turn produces a large number of file
24 1
requests, it helps dramatically if the fileserver is running in a
25 1
discrete process from the configuration server.
26 1
27 1
If you're using definitions for your remote file copies, this is
28 1
relatively painless to configure. Just configure the default port
29 1
to be something different.
30 1
31 1
I'll assume you're using something like this remotefile
32 1
definition:
33 1
34 1
    # JJM My preferred recipe to copy files from the server.
35 1
    define remotefile($source = false,
36 1
      $fileserver = false,
37 1
      $port = false,
38 1
      $basedir = "dist",
39 1
      $owner = 0, $group = 0, $mode = 640,
40 1
      $recurse = true,
41 1
      $ignore = ".svn",
42 1
      $backup = false) {
43 1
        # Some creative use of selectors to allow overrides and defaults.
44 1
        $source_real = $source ? { false => $name, default => $source }
45 1
        $fileserver_real = $fileserver ? { false => "puppet", default => $fileserver }
46 1
        case $port {
47 1
                false: { $source_uri = "puppet://$fileserver_real/$basedir/$source_real" }
48 1
                default: { $source_uri = "puppet://$fileserver_real:$port/$basedir/$source_real" }
49 1
        }
50 1
        file { $name:
51 1
                source => "$source_uri",
52 1
                ignore => $ignore,
53 1
                mode => $mode,
54 1
                owner => $owner,
55 1
                group => $group,
56 1
                recurse => $recurse,
57 1
                backup => $backup
58 1
        }
59 1
    }
60 1
61 1
So long as we use this definition whenever we copy a file from the
62 1
puppetmaster, we can easily reconfigure the system to copy files
63 1
from a discrete puppetmaster process running on a different port:
64 1
65 1
    Remotefile { port => 8145, fileserver => "puppet1.math.ohio-state.edu" }
66 1
67 1
We also need to start a second puppetmasterd on the port:
68 1
69 1
    /usr/sbin/puppetmasterd --manifest=/etc/puppet/manifests/site.pp
70 1
    --logdest=/var/log/puppet/puppetmaster.8145.log --masterport=8145
71 1
72 1
With this configuration, remotefile objects will use port 8145 by
73 1
default, greatly reducing load on the "main" puppetmaster process
74 1
listening on 8140.
75 1
76 1
# Centralised Puppet Infrastructure
77 1
78 3 John Warburton
**\<WARNING\>** 
79 3 John Warburton
80 3 John Warburton
This does not currently work for 0.25.x nor 2.6.x (see Issue #3143 
81 3 John Warburton
and Issue #3770 and related issues in Issue #3640 for progress on resolution). 
82 3 John Warburton
Until the issues are resolved, it is suggested to use one machine as the CA, 
83 3 John Warburton
if you can accept the fact that its a single point of failure for creating new certificates.
84 3 John Warburton
85 3 John Warburton
**\</WARNING\>** 
86 3 John Warburton
87 1
As we are deploying puppet infrastructure in multiple sites it was
88 1
important for us to have a centralized management for puppet, but a
89 1
non centralized "service", we wanted to have the option to switch
90 1
between puppet masters, and did not like the idea of a single CA
91 1
for all of our infrastructure. The main reason against a common ca
92 1
was, as we are really spread over the world and its common to
93 1
install 50+ servers in a time frame of a few hours, we didn't want
94 1
to introduce any type of dependencies.
95 1
96 1
Additionally, revoking works better this way, and well... we just
97 1
wanted to make it work.
98 1
99 1
Using our solution, you could also use real root CA, your company
100 1
root or self sign certificate, in some cases it could make sense
101 1
not to use a self sign if you want to reuse the certificates for
102 1
Apache, ldap etc.
103 1
104 1
Since all of our puppet masters are managed as well, we have one
105 1
root puppet master (i.e. puppet master of the puppet masters), we
106 1
called it the puppeteer. The puppeteer installation is like a
107 1
regular puppet master installation.
108 1
109 1
Please note that webrick is at this time (0.24.4) unable to handle
110 1
the certs in a correct way to get this setup working. As such, you
111 1
will need to use something else to handle your SSL connections such
112 1
as Apache. Also, you must be using a fairly recently version of
113 1
puppet for the client to support it (0.24.4 works good).
114 1
115 1
We are using Apache + Mongrel: on all puppet masters you should
116 1
have something like that in your Apache configuration (that's just
117 1
the ssl part):
118 1
119 1
    <VirtualHost *:8140>
120 1
        SSLEngine on
121 1
        SSLCipherSuite          SSLv2:-LOW:-EXPORT:RC4+RSA
122 1
        SSLCertificateFile      /var/lib/puppet/ssl/certs/your.fqdn.com.pem
123 1
        SSLCertificateKeyFile   /var/lib/puppet/ssl/private_keys/your.fqdn.com.pem
124 1
        SSLCertificateChainFile /var/lib/puppet/ssl/certs/ca.pem
125 1
        SSLCACertificateFile    /var/lib/puppet/ssl/certs/ca.pem
126 1
        SSLCARevocationFile     /var/lib/puppet/ssl/ca/ca_crl.pem
127 1
        SSLVerifyClient         optional
128 1
        SSLVerifyDepth          3
129 1
        SSLOptions              +StdEnvVars
130 1
    
131 1
        RequestHeader set X-SSL-Subject %{SSL_CLIENT_S_DN}e
132 1
        RequestHeader set X-Client-DN %{SSL_CLIENT_S_DN}e
133 1
        RequestHeader set X-Client-Verify %{SSL_CLIENT_VERIFY}e
134 1
    
135 1
    <Location />
136 1
137 1
Then, let your second puppet master (the second level of the
138 1
certificate chain) request a certificate from the puppeteer. Setup
139 1
an openssl.cnf file (just store it somewhere) with the following
140 1
content (adjust for your needs):
141 1
142 1
    HOME                    = .
143 1
    RANDFILE                = $ENV::HOME/.rnd
144 1
    [ ca ]
145 1
    default_ca      = CA_default
146 1
    [ CA_default ]
147 1
    dir             = /var/lib/puppet/ssl
148 1
    new_certs_dir   = $dir/ca/signed
149 1
    crl_dir         = $dir/ca
150 1
    database        = $dir/index
151 1
    certificate     = $dir/ca/ca_crt.pem
152 1
    serial          = $dir/ca/serial
153 1
    crl             = $dir/ca/ca_crl.pem
154 1
    private_key     = $dir/ca/ca_key.pem
155 1
    RANDFILE        = $dir/private/.rand
156 1
    x509_extensions = usr_cert
157 1
    unique_subject  = no
158 1
    name_opt        = ca_default
159 1
    cert_opt        = ca_default
160 1
    default_crl_days= 30
161 1
    default_days    = 3650
162 1
    default_md      = sha1
163 1
    preserve        = no
164 1
    policy          = policy_anything
165 1
    [ policy_match ]
166 1
    countryName             = match
167 1
    stateOrProvinceName     = match
168 1
    organizationName        = match
169 1
    organizationalUnitName  = optional
170 1
    commonName              = supplied
171 1
    emailAddress            = optional
172 1
    [ policy_anything ]
173 1
    countryName             = optional
174 1
    stateOrProvinceName     = optional
175 1
    localityName            = optional
176 1
    organizationName        = optional
177 1
    organizationalUnitName  = optional
178 1
    commonName              = supplied
179 1
    emailAddress            = optional
180 1
    [ req ]
181 1
    default_bits            = 2048
182 1
    default_keyfile         = ./ca/ca_key.pem
183 1
    default_md              = sha1
184 1
    prompt                  = no
185 1
    distinguished_name      = root_ca_distinguished_name
186 1
    x509_extensions = v3_ca
187 1
    string_mask = nombstr
188 1
    [ root_ca_distinguished_name ]
189 1
    commonName = XXXXXXXX
190 1
    [ usr_cert ]
191 1
    basicConstraints=CA:FALSE
192 1
    subjectKeyIdentifier=hash
193 1
    authorityKeyIdentifier=keyid,issuer:always
194 1
    nsCaRevocationUrl               = https://puppeteer.your.domain.com/ca_crl.pem
195 1
    [ v3_req ]
196 1
    basicConstraints = CA:FALSE
197 1
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment
198 1
    [ v3_ca ]
199 1
    subjectKeyIdentifier=hash
200 1
    authorityKeyIdentifier=keyid,issuer:always
201 1
    basicConstraints = critical,CA:true
202 1
    keyUsage = keyCertSign, cRLSign
203 1
    [ crl_ext ]
204 1
    authorityKeyIdentifier=keyid:always,issuer:always
205 1
206 1
On the puppet master then copy this file to your new puppet master
207 1
(e.g. /tmp):
208 1
209 1
    puppetmaster=fqdn
210 1
    /usr/bin/perl -p -i -e "s/XXXXXXXX/$puppetmaster/" /tmp/openssl.cnf
211 1
    /usr/bin/openssl req -new -nodes -key /var/lib/puppet/ssl/ca/ca_key.pem -config /tmp/openssl.cnf -out /tmp/${puppetmaster}.csr -passin file:/var/lib/puppet/ssl/ca/private/ca.pass
212 1
213 1
Copy the ${puppetmaster}:/tmp/${puppetmaster}.csr back to the
214 1
puppeteer. On the puppeteer:
215 1
216 1
    touch /var/lib/puppet/ssl/index
217 1
    # Sign this request with the puppeteer's CA keys
218 1
    /usr/bin/openssl ca -config openssl.cnf -extfile openssl.cnf -extensions v3_ca -in ${puppetmaster}.csr -out ${puppetmaster}.pem -passin file:/var/lib/puppet/ssl/ca/private/ca.pass -batch
219 1
    
220 1
    # Push the new certificate into place on the puppetmaster
221 1
    scp ${puppetmaster}.pem ${puppetmaster}:/var/lib/puppet/ssl/ca/ca_crt.pem
222 1
223 1
In your installation process append the content of the puppeteer's
224 1
\~puppet/ssl/ca/ca\_crt.pem to /var/lib/puppet/ssl/certs/ca.pem on
225 1
the client
226 1
227 1
Now you should be able to use any puppet master that was signed
228 1
this way.