{"id":368,"date":"2020-05-12T23:38:00","date_gmt":"2020-05-12T23:38:00","guid":{"rendered":"https:\/\/questy.org\/?p=368"},"modified":"2024-09-26T19:23:57","modified_gmt":"2024-09-26T19:23:57","slug":"scaling-puppet-community","status":"publish","type":"post","link":"https:\/\/questy.org\/index.php\/2020\/05\/12\/scaling-puppet-community\/","title":{"rendered":"Scaling Puppet Community"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large is-style-default\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"900\" height=\"491\" src=\"https:\/\/i0.wp.com\/questy.org\/wp-content\/uploads\/2020\/05\/graph-growing.png.webp?resize=900%2C491&#038;ssl=1\" alt=\"\" class=\"wp-image-441\" style=\"aspect-ratio:16\/9;object-fit:contain\" srcset=\"https:\/\/i0.wp.com\/questy.org\/wp-content\/uploads\/2020\/05\/graph-growing.png.webp?resize=1024%2C559&amp;ssl=1 1024w, https:\/\/i0.wp.com\/questy.org\/wp-content\/uploads\/2020\/05\/graph-growing.png.webp?resize=300%2C164&amp;ssl=1 300w, https:\/\/i0.wp.com\/questy.org\/wp-content\/uploads\/2020\/05\/graph-growing.png.webp?resize=768%2C419&amp;ssl=1 768w, https:\/\/i0.wp.com\/questy.org\/wp-content\/uploads\/2020\/05\/graph-growing.png.webp?w=1100&amp;ssl=1 1100w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/figure>\n\n\n\n<p><br>In my Puppet travels over the last 10 or so years, one topic has continued to arise time and again, and that has been the ability to scale Puppet Community (formerly Puppet Open Source) to thousands of nodes.<\/p>\n\n\n\n<p>While the best route to go is to use Puppet Enterprise for solid support and a team of talented engineers to help you in your configuration management journey, sometimes the right solution for your needs is to use Puppet Community. What follows is the product of my resolving to get to the bottom of the procedure and make it easy to perform repeatedly and to assist in scaling Puppet Community implementations for larger environments.<\/p>\n\n\n\n<p>Even though this article presents a somewhat rudimentary configuration, you can add PuppetDB, external instrumentation and telemetry, etc. and grow the environment to a truly large enterprise-class system.<\/p>\n\n\n\n<p><strong>The Design<\/strong><\/p>\n\n\n\n<p>The design of such an environment is highly flexible with many hundreds of potential configurations. In this specific scenario, an independent Master performing CA duties for the architecture as well as several catalog compilers placed behind a TCP Load Balancer for catalog compilation is what I plan to cover since once the specific moving parts are identified, modern System Engineering practice can be applied to the environment to expand and scale the installation as needed.<\/p>\n\n\n\n<p><strong>Architecture<\/strong><\/p>\n\n\n\n<p><strong>The Puppet Master<\/strong><\/p>\n\n\n\n<p>Every Puppet implementation has one of these.&nbsp; Whether there are extra compilers or not, the primary master is tooled to be not only a CA Master but also a&nbsp; catalog compiler in its own right. If you begin to tune the master and place it on beefy hardware, You can expect to eventually reach a limit to the number of nodes you can serve. If you add PuppetDB to the mix, there\u2019ll be different requirements, but generally speaking, you will want to offload PuppetDB to a different server so as to keep the master free to serve CA requests to the environment.<\/p>\n\n\n\n<p><strong>The Load Balancer<\/strong><\/p>\n\n\n\n<p>For this function, you simply need a TCP Load Balancer. An Elastic Load Balancer in AWS would serve nicely as would HAProxy on a large-ish machine (t2-xlarge). In short, this load balancer simply needs to be able to see that a port is up on the destination nodes in the serving pool, and then proxy the connections to a member of that pool that is in a healthy state.&nbsp; You may also wish to pull the Puppet healthcheck API endpoint:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:\/\/&lt;compiler fqdn&gt;:8140\/status\/v1\/simple\/master<\/code><\/pre>\n\n\n\n<p>At the Load Balancer to ensure the pool is healthy, and the load balancer only forwards requests to healthy catalog compilers.<\/p>\n\n\n\n<p>Note also that for the purposes of this discussion, it is assumed you have set up this load balancer and assigned the name compile.example.com to the VIP IP (10.0.100.20 below).<\/p>\n\n\n\n<p><strong>The Compilers<\/strong><\/p>\n\n\n\n<p>These are simply Puppet Server installations that have had the CA utility turned off, and you have configured client nodes to look to the master for this information. These nodes will sit behind the load balancer and take catalog requests from Puppet agents as though they were the only Puppet server and perform standard Puppet server requests (minus the CA work).<\/p>\n\n\n\n<p><strong>The Platform<\/strong><\/p>\n\n\n\n<p>My practice and work in this implementation was done in AWS. You can do the same work in Digital Ocean, Linode, or on physical hardware. The important part is not the size or location of the nodes I\u2019ve used, but the configuration I will enumerate below. As long as the configuration is maintained, results should be relatively consistent from platform to platform.<\/p>\n\n\n\n<p><strong>The Procedure<\/strong><\/p>\n\n\n\n<p>I performed this installation several times as though the setup did not have DNS resolution. By this, I mean that I did all name resolution in host files. You can easily manage these in Route 53 or you can add \u201cA Records\u201d in your own DNS serving infrastructure. The process I outline here is the former, using the host files.<\/p>\n\n\n\n<p>First, accommodate the names of your systems by laying out what the names will be and the structure of the addresses as needed. In the case of this reference implementation, the \/etc\/hosts file is as follows:<\/p>\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<pre class=\"wp-block-code\"><code>\t# Hosts File for Scaled Puppet Community Implementation\n\n\t# Master\n\t10.0.100.10\tpuppet.example.com\tpuppet\n\n\t# Compiler VIP (Load Balancer)\n\t10.0.100.20\tcompile.example.com\tcompile\n\n\t# Compilers\n\t10.0.100.30\tcompiler1.example.com\tcompiler1\n\t10.0.100.31\tcompiler2.example.com\tcompiler2\n\t10.0.100.32\tcompiler3.example.com\tcompiler3<\/code><\/pre>\n<\/div><\/div>\n\n\n\n<p>For each node I provision, I immediately configure the \/etc\/hosts file to contain this information so all nodes can reach each other by name. This is to satisfy the stated requirements of Puppet itself that name resolution\/DNS needs to be configured and functioning.<\/p>\n\n\n\n<p>Next, we need to install Puppetserver on the master. This is straightforward as mentioned in the Puppet docs here:&nbsp; https:\/\/puppet.com\/docs\/puppet\/latest\/puppet_platform.html#task-383<\/p>\n\n\n\n<p>So, on RHEL 7, you would enable the Puppet Platform repo<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo rpm -Uvh https:\/\/yum.puppet.com\/puppet6-release-el-7.noarch.rpm<\/code><\/pre>\n\n\n\n<p>Or on Ubuntu Xenial:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wget https:\/\/apt.puppetlabs.com\/puppet6-release-xenial.deb\nsudo dpkg -i puppet6-release-xenial.deb<\/code><\/pre>\n\n\n\n<p>Then you would install the PuppetServer package itself:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>yum install puppetserver<\/code><\/pre>\n\n\n\n<p>or<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apt-get install puppetserver<\/code><\/pre>\n\n\n\n<p><em>Be sure to source the newly installed profile so the following commands can be found in your path. To do so, run:<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>source \/etc\/profile.d\/puppet-agent.sh<\/code><\/pre>\n\n\n\n<p><em>before continuing to have all puppet resources needed in your path.<\/em><\/p>\n\n\n\n<p>At this point, we want to configure Puppet Server before allowing it to start to allow for alternate DNS names when signing certificate requests, which accommodates the name on the load balancer VIP as well as the individual compiler node names when you begin standing up catalog compilers. To do this, we need to edit the file \/etc\/puppetlabs\/puppetserver\/conf.d\/ca.conf. In the file\u2019s documentation it enumerates the new line we need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>allow-subject-alt-names: true<\/code><\/pre>\n\n\n\n<p>The subject-alt-name is an X.509 extension that allows various values to be associated with a certificate. In this way, we\u2019re leveraging the extension to allow all cert signing by the CA Master to allow for and associate all names of the VIP and the compilers to be accepted by the master and to be <em>acceptable<\/em> to the connecting node.<\/p>\n\n\n\n<p>The final step before starting the puppetserver is to generate a root and intermediate signing CA for the puppetserver, as it will be terminating SSL requests for the architecture. To do this, simply run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>puppetserver ca setup<\/code><\/pre>\n\n\n\n<p>Once you have added the above line and set up the CA, it is time to start the server. On both platforms, you would run the following SystemD control commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>systemctl enable puppetserver\nsystemctl start puppetserver<\/code><\/pre>\n\n\n\n<p>When puppetserver starts, it will begin behaving as a CA master, capable of both terminating SSL and compiling catalogs. The Puppet documentation for that file is obliquely referenced <a href=\"https:\/\/puppet.com\/docs\/puppet\/latest\/puppet_server_ca_cli.html#signing-certificates-with-subject-alternative-names-or-auth-extensions\" class=\"aioseop-link\">here<\/a> and weighs heavily in this configuration. <\/p>\n\n\n\n<p>At this point, the Puppet Server is running and is accepting requests for signing.<\/p>\n\n\n\n<p><strong>Your First Compiler<\/strong><\/p>\n\n\n\n<p>Next, we need to install a compiler, but we need to make sure that compiler will accept catalog compile requests but not provide CA services at all.<\/p>\n\n\n\n<p>This server needs to know about itself and its own job, where the CA Master (Puppet Master) is, and what names it has and is responsible for.\u00c2&nbsp; First, install Puppetserver on the compiler (in our example, compiler1.example.com).\u00c2&nbsp; As soon as the PuppetServer is installed, but before it is started, you need to configure the following to represent the compiler you are configuring:<\/p>\n\n\n\n<p>Edit the <em>\/etc\/puppetlabs\/puppet\/puppet.conf<\/em> and create a &#8220;main&#8221; section as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;main]\nserver=puppet.example.com\ndns_alt_names=compiler1.example.com,compiler1,compile.example.com,compile\nca_server=puppet.example.com<\/code><\/pre>\n\n\n\n<p>In this way, you\u2019re specifying all names that particular compiler is authorized to \u201canswer\u201d\u009d for, namely its own certname, it\u2019s own hostname, the load balancer\u2019s certname and its hostname portion of the certname as well.<\/p>\n\n\n\n<p>Next, you need to tell the compiler that it has specific certs. Above, you\u2019ve already told it where it\u2019s Puppet Master is <em>(server=puppet.example.com)<\/em>, You\u2019ve also told it what its own names are <em>(compiler1.example.com,compiler1,compile.example.com,compile)<\/em> which are its own host names and the host names of the VIP on the Load Balancer.&nbsp; You also need to tell the Puppet server on the compiler the values necessary for it to configure Jetty.&nbsp; Edit the <em>\/etc\/puppetlabs\/puppetserver\/conf.d\/webserver.conf<\/em><strong><em> <\/em><\/strong><em>and add these lines to the end of the top section:<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\tssl-cert: \"\/etc\/puppetlabs\/puppet\/ssl\/certs\/compiler1.example.com.pem\"\n\tssl-key: \"\/etc\/puppetlabs\/puppet\/ssl\/private_keys\/compiler1.example.com.pem\"\n\tssl-ca-cert: \"\/etc\/puppetlabs\/puppet\/ssl\/certs\/ca.pem\"\n\tssl-crl-path: \"\/etc\/puppetlabs\/puppet\/ssl\/crl.pem\"<\/code><\/pre>\n\n\n\n<p>Finally, you have to disable the local CA service on the compiler itself.&nbsp; This is accomplished by editing the file <em>\/etc\/puppetlabs\/puppetserver\/services.d\/ca.cfg<\/em>. There are two lines that need to be commented\/uncommented:<\/p>\n\n\n\n<p>The distributed version of the file has the CA enabled:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># To enable the CA service, leave the following line uncommented\npuppetlabs.services.ca.certificate-authority-service\/certificate-authority-service\n# To disable the CA service, comment out the above line and uncomment the line below\n#puppetlabs.services.ca.certificate-authority-disabled-service\/certificate-authority-disabled-service\npuppetlabs.trapperkeeper.services.watcher.filesystem-watch-service\/filesystem-watch-service<\/code><\/pre>\n\n\n\n<p>The in-line documentation tells you to comment the second line and to uncomment the 4th line to disable the CA service. Do that here so that the file looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># To enable the CA service, leave the following line uncommented\n#puppetlabs.services.ca.certificate-authority-service\/certificate-authority-service\n# To disable the CA service, comment out the above line and uncomment the line below\npuppetlabs.services.ca.certificate-authority-disabled-service\/certificate-authority-disabled-service\npuppetlabs.trapperkeeper.services.watcher.filesystem-watch-service\/filesystem-watch-service\n<\/code><\/pre>\n\n\n\n<p>Once all these components are in place on your catalog compiler, you need to connect your catalog compiler to the master in the usual fashion. First, request that your local certificate be signed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>puppet ssl bootstrap --waitforceert 60<\/code><\/pre>\n\n\n\n<p>Then, on the Master, sign the certificate request by specifying the machine\u2019s certname:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>puppetserver ca sign --certname compiler1.example.com<\/code><\/pre>\n\n\n\n<p>When you sign the certificate request, the Puppet CA Master then receives all alternative names from the compiler, and signs all names the compiler is representing. Namely, <em>compiler1.example.com, compiler1, compile.example.com, and compile<\/em>.&nbsp; This allows an agent, when connecting to compile.example.com to interact with the VIP as the catalog compiler, and it will accept any of the names it sees in that communication. When the agent connects to compile.example.com and gets forwarded to, say, compiler42.example.com, it doesn\u2019t blink because the signed cert is \u201cacceptable\u201d to the CA infrastructure you\u2019re currently interacting with.<\/p>\n\n\n\n<p>Once you have signed the catalog compiler\u2019s certificate request, then return to the catalog compiler and perform a Puppet run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>puppet agent -t<\/code><\/pre>\n\n\n\n<p>Then, turn on the puppetserver daemon and set it to start at boot:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>systemctl enable puppetserver\nsystemctl start puppetserver<\/code><\/pre>\n\n\n\n<p>At this point, the master, the catalog compiler, and the load balancer are all up and running, functioning as designed.&nbsp; The final portion is to connect a Puppet agent to this infrastructure so it works as expected.<\/p>\n\n\n\n<p><strong>Connecting Puppet Agents<\/strong><\/p>\n\n\n\n<p>On any agent, you would install puppet as you would normally by first enabling the platform repo just as we did for the Puppet Servers:<\/p>\n\n\n\n<p>On RHEL 7, you would enable the Puppet Platform repo as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo rpm -Uvh https:\/\/yum.puppet.com\/puppet6-release-el-7.noarch.rpm<\/code><\/pre>\n\n\n\n<p>Or on Ubuntu Xenial:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wget https:\/\/apt.puppetlabs.com\/puppet6-release-xenial.deb\nsudo dpkg -i puppet6-release-xenial.deb<\/code><\/pre>\n\n\n\n<p>Once the platform repos are configured, then install the puppet agent as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>yum -y install puppet-agent<\/code><\/pre>\n\n\n\n<p>On Redhat family of servers or<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apt-get install puppet-agent<\/code><\/pre>\n\n\n\n<p>On Ubuntu.&nbsp;<\/p>\n\n\n\n<p>Finally, before executing your first Puppet run, you need to edit the puppet.conf to tell the node where its resources are.&nbsp; <em>(You may wish to manage puppet.conf with Puppet in your fleet as the size grows.)<\/em><\/p>\n\n\n\n<p>Add the following lines to the puppet.conf:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>server=compile.example.com&nbsp;\nca_server=puppet.example.com<\/code><\/pre>\n\n\n\n<p>In your configuration, this will reflect your infrastructure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>server=&lt;FQDN of the Load Balancer IP&gt;\nca_server=&lt;FQDN of the Master&gt;<\/code><\/pre>\n\n\n\n<p>After you\u2019ve edited the puppet.conf, bootstrap the SSL as you did above on the compilers:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>puppet ssl bootstrap --waitforcert 60<\/code><\/pre>\n\n\n\n<p>Sign the certificate request on your Master:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>puppetserver ca sign --certname &lt;FQDN of the new agent node&gt;<\/code><\/pre>\n\n\n\n<p>Finally, on the new Puppet agent, ensure a Puppet run completes without error:<\/p>\n\n\n\n<p>On the agent node:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>puppet agent -t<\/code><\/pre>\n\n\n\n<p>If everything has been performed correctly, the Puppet agent machine will request from the Master a certificate signing. You will manually (or via autosign.conf) sign the agent\u2019s certificate request. the agent then begins the catalog upload procedure, but instead of sending that to the Master, you\u2019ve specified it should send that to your load balancer VIP instead. The Load Balancer will forward the catalog to one of the compilers in the pool and a standard Puppet run will complete for the agent.<\/p>\n\n\n\n<p>It is at this point you can follow the \u201cadding a catalog compiler\u201d procedure to scale your compile farm, or just continue to add agents to connect your fleet to the infrastructure.<\/p>\n\n\n\n<p><strong>Conclusion<\/strong><\/p>\n\n\n\n<p>If you\u2019ve completed everything above, you should now have a scalable infrastructure for Puppet Community. The master is serving CA certificate signing and the load balancer is handing off requests to individual compilers for processing. Agents are configured in such a way as to send those certificate requests to the master and catalogs to the load balancer vip, allowing for a greater volume of requests.<\/p>\n\n\n\n<p>It should be noted that no tuning is called out in this procedure, but Puppet has a great deal of interesting information that might allow you to increase capacity even more. The basic tuning guide for the Puppet Server can be found here:<\/p>\n\n\n\n<p><a href=\"https:\/\/puppet.com\/docs\/puppetserver\/latest\/tuning_guide.html\">https:\/\/puppet.com\/docs\/puppetserver\/latest\/tuning_guide.html<\/a><\/p>\n\n\n\n<p>This will give you guidelines for tuning your server related to the hardware specifications of the server, and assist you in scheduling runs, tuning parameters, and just generally ensuring the Puppet Server is operating optimally for your infrastructure needs.<\/p>\n\n\n\n<p>Jerald Sheets is the Owner and CEO of S &amp; S Consulting Group. He is a Puppet Certified Consultant with Norseman Defense Services, Performs consulting and training on the Puppet Enterprise and Community Platforms, and is a Puppet Certified Professional 2014 &amp; 2018.<\/p>\n\n\n\n<p>He can be reached via the following several methods:<br><br>Email: jsheets@ssconsultinggroup.net<br>Phone: 912-549-0272<br>Puppet Slack: @CVQuesty<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my Puppet travels over the last 10 or so years, one topic has continued to arise time and again, and that has been the ability to scale Puppet Community (formerly Puppet Open Source) to thousands of nodes. While the best route to go is to use Puppet Enterprise for solid support and a team [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[2],"tags":[],"class_list":["post-368","post","type-post","status-publish","format-standard","hentry","category-puppet-administration"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_likes_enabled":true,"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/questy.org\/index.php\/wp-json\/wp\/v2\/posts\/368","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/questy.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/questy.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/questy.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/questy.org\/index.php\/wp-json\/wp\/v2\/comments?post=368"}],"version-history":[{"count":3,"href":"https:\/\/questy.org\/index.php\/wp-json\/wp\/v2\/posts\/368\/revisions"}],"predecessor-version":[{"id":446,"href":"https:\/\/questy.org\/index.php\/wp-json\/wp\/v2\/posts\/368\/revisions\/446"}],"wp:attachment":[{"href":"https:\/\/questy.org\/index.php\/wp-json\/wp\/v2\/media?parent=368"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/questy.org\/index.php\/wp-json\/wp\/v2\/categories?post=368"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/questy.org\/index.php\/wp-json\/wp\/v2\/tags?post=368"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}