Sorry everyone for the delay in getting this blog post out, sometimes life just gets in the way.
I’m excited to finally have time to start playing with Puppet’s Application Orchestration feature and write to you about my experience with it. In this blog post I am going to show you how to deploy a simple application stack with web servers, load balancers, and a database server.
First step in my adventure down the road of Application Orchestration, I started off reading about it in the Puppet Documentation. While Puppet’s documentation is fantastic, I’m the type of person who learns from real world examples to start putting the pieces together. So off to trusty Google I started looking for real world examples of people using the AO feature. Most were deploying a stack with only a single host hosting each component of the application (i.e. one db server, one web server and one load balancer). I wanted to deploy a real world scenario where we would have redundant servers for each component (in my example only one db server, might come back to change Puppet manifest to create redundant db).
I found a blog post from Yannick Struyf which showed an example of an application stack with two web servers being deployed with Puppet’s AO. I took his Puppet code and modified it where I will be deploying a very simple website stack I am calling “corp_website” with a simple hit counter built into the page keeping tracking stats stored in a MySQL database.
My Puppet module and associated Puppet code for the example stack can be found here on GitHub:
If we take a look at the “corp_website” module we need to define two service resource types called HTTP and SQL. These two ruby files can be found under the lib/puppet/type directory of the module. Lets examine these two files:
In the http.rb file we can see we are defining four parameters:
Name is a mandatory parameter which identifies the resource on the target system. The other three parameters will be used the by load balancer manifest (lb.pp) to configure the HAProxy configuration.
In the sql.rb file we are defining six parameters:
These parameters will be consumed by our web server manifest (web.pp) specifically by the configuration.php.erb template file, which is the configuration.php file for our web counter on the example website. More about writing custom service resources can be found here on Puppet’s documentation.
Next lets take a look at our manifests for our application stack. First we will define our init.pp manifest:
In the init.pp file we are declaring our application “corp_website” with our different components db, web, and lb. In order to create multiple web and load balancer servers we are using the Ruby map array method to iterate X number of times to create Http and Lb service resources with a unique name and store it in the respective $webs and $lbs variables. These variables will then be used by the Http and Lb components by a Ruby array each method further down in the manifest to create X amount of each component which will either get its default value from the init.pp file or can be overridden in the site.pp file for the environment (more on this later). More about creating application definitions can be found here on Puppet’s documentation. Next we will define our db component.
If we take a look at this manifest we start if off with a define statement, followed by some variables that will be used by the manifest with values being stored in hiera (note values are not encrypted for this example in hiera, I highly recommend in any non-sandbox environment to use eyaml in hiera to encrypt sensitive values). The rest of the manifest is pretty self explanatory as we are configuring MySQL with a database and table. At the end of the manifest is where we are producing the Sql service resource that will be consumed by our Http service resource in our next manifest.
In our web component manifest we see its again pretty straightforward, setting up Apache and PHP but first we are declaring our variables with no value that will be defined at the bottom of the manifest by the consume Sql statement. There is also the produce Http statement which the values will be used by our load balancer component. As a note I am deploying my different website components utilizing RPM packages I am storing on Gemfury in a YUM repository. The YUM configuration is being defined in my hieradata common.yaml file for the environment.
In our last component the lb manifest is utilizing the $balancemembers variable that is defined in the init.pp file, and the different variables produced in the web.pp file.
The final piece of our application stack is to declare our application instances in the environment’s site.pp file. We can see are declaring our “corp_website” application and giving it a name, this name must be changed if deploying multiple stacks of the same application. Then we are defining the number of web servers and load balancers in our application. The final piece is binding our different puppet nodes to the different components. More about declaring application instances can be found here on Puppet’s documentation.
Note: Before setting the site.pp file I would on your nodes run the following puppet command to set your agents to use the cached catalog. This is a prerequisite for using Application Orchestration.
puppet config set use_cached_catalog true –section agent
Note: You will also have to enable the Application Orchestration Service on your Puppet Enterprise server if you didn’t enable it when you installed PE. Follow these instructions from Puppet’s documentation.
Next we will need to configure Puppet to allow the invocation of puppet job commands to deploy the application. Follow the steps here on Puppet’s documentation on setting RBAC permissions and setting up token authentication if you already haven’t. After this is set you should be able to on the command line of your Puppet Enterprise Server be able to invoke the “puppet app show“ command to show the details of the “corp_website” application.
If everything seems OK then we can deploy the application with the puppet job run command:
puppet job run Corp_website[corp_website]
As we can see below the job ran successfully, in my screenshot every resource is showing unchanged because I ran the job successfully prior to my screenshot.
More about running puppet jobs can be found here in Puppet’s documentation.
I have not found any documentation on starting a puppet job through a webhook if using a Continuous Delivery tool like Jenkins or Bamboo, looks like you will have to SSH to the Puppet server from your CD server, but I found an issue where the exit code always returns “0” if the job is successful or fails. I have a request out to Puppet to see if this is a bug.
Now if you browse to your application stack if everything is successful you should see this simple webpage with the version of the website and the website counter storing stats in the MySQL database:
In conclusion I think this is a great new feature for anyone who does not have an automated way of running puppet agent runs in a specified sequential order already implemented in their application stacks. I definitely need more time playing with this tool to see if its flexible enough for all stack deployment scenarios, currently using MCollective commands initiated through SSH from my CD server.
I am thinking for my next blog post I want to build upon this post but instead of each component being on its own individual server, I want to deploy my stack on Docker containers to help cut down on the VM bloat. I have played with Docker containers on CoreOS but now I am looking how to incorporate Puppet into the mix.
Till next time, Cheers!