Deploy app with Puppet on Azure
In this hands-on lab you will deploy the Parts Unlimited MRP App (PU MRP App), a Java application, using Puppet from Puppet Labs.
Puppet is a configuration management system that allows you to automate the provisioning and configuring of machines, by describing the state of your Infrastructure as Code. Implementing Infrastructure as Code is an important pillar of good DevOps practice.
DevOps MPP Course Source:
This lab is used in the following courses.
- DevOps200.2x: Infrastructure as Code - Module 4 Azure IaaS and PaaS, Environment Configuration and Deployment, and Optimization.
- AZ-400T05: Implementing Application infrastructure - Module 4: Third Party and Open Source Tool integration with Azure. The lab compliments the AZ-400 series of courses to help you prepare for the AZ-400 Microsoft Azure DevOps Solutions certification exam.
Pre-requisites:
- An SSH client, such as PuTTY.
- An active Azure subscription.
Lab Tasks:
- Task 1: Provision a Puppet Master and Node in Azure using Azure Resource Manager templates
- Task 2: Install Puppet Agent on the node
- Task 3: Configure the Puppet Production Environment
- Task 4: Test the Production Environment Configuration
- Task 5: Create a Puppet Program to describe the prerequisites for the PU MRP app
- Task 6: Run the Puppet Configuration on the Node
Estimated Lab Time:
- Approx. 90 minutes
Lab environment
In this lab you will use the following two machines.
-
A machine known as a Node will host the PU MRP app. The only task you will perform on the node is to install the Puppet Agent. The Puppet Agent can run on Linux or Windows. For this lab, we will configure the Node in a Linux Ubuntu Virtual Machine (VM).
-
A Puppet Master machine. The rest of the configuration will be applied by instructing Puppet how to configure the Node through Puppet Programs, on the Puppet Master. The Puppet Master must be a Linux machine. For this lab, we will configure the Puppet Master in a Linux Ubuntu VM.
Instead of manually creating the VMs in Azure, we will use an Azure Resource Management (ARM) template.
Task 1: Provision a Puppet Master and Node in Azure using Azure Resource Manager templates (both inside Linux Ubuntu Virtual Machines)
-
To provision the required VMs in Azure using an ARM template, select the Deploy to Azure button, and follow the wizard. You will need to log in to the Azure Portal.
The VMs will be deployed to a Resource Group along with a Virtual Network (VNET), and some other required resources.
Note: You can review the JSON template and script files used to provision the VMs in the Puppet lab files folder on GitHub.
-
When prompted specify a Subscription, Location, and Resource Group for deploying your VM resources. Provide admin usernames and passwords, as well as a unique Public DNS Names for both machines.
Consider the following guidelines.
- Subscription.
< your Azure subscription >
. - Resource group.
< a unique resource group name >
. For examplepuprgek01
. Create a new Resource Group during the deployment process, to demarcate the resources that you set up in this lab. Remove the resources created in this lab by deleting the Resource Group. - Location. Select a region to deploy the VMs to. For example
West Europe
orEast US
. - Pm Admin Username. The Pm refers to the Puppet Master VM, and Mrp refers to the Node VM. Choose the same Admin Username for both Pm and Mrp VMs. For example,
azureuser
. - Pm Admin Password. Set the same Admin Password for both VMs, and for the Pm Console. For example,
Passw0rd0134
- Pm Dns Name For Public IP. Include the word master in the Puppet Master DNS name, to distinguish it from the Node VM. You could include the word node in the Node DNS name, if you wish, for example
partsmrpnode
. Create unique DNS names for both VMs by adding your initials as a suffix. For example,partspuppetmasterek01
andpartsmrpnodeek01
Make a note of the location region, as well as any usernames and passwords you set for the VMs.
Use the checkbox to agree to the Azure Marketplace terms and conditions. Select the Purchase button. Allow about 10 minutes for each deployment to complete.
- Subscription.
-
When the deployment completes, select the Resource Groups blade from the Azure Portal menu. Choose the name of Resource Group that you created, cd-puppet-hol for example.
-
Within the Resource Group Overview pane, beside Deployments, select the 1 succeeded message to view the deployment details. The status of the
Microsoft.Template
resource should indicate that the deployment has succeeded.Choose the
Microsoft.Template
resource, listed in the Deployment Name column. -
There is a list of the deployed resources in the Microsoft.Template - Overview pane, below the Your deployment is complete message. Select the Puppet Master VM from the list, for example
partspuppetmasterek01
. -
Make a note of the DNS name value, listed inside the Overview pane for the Puppet Master Virtual Machine. The DNS name value is in the format
machinename
.region
.cloudapp.azure.com
. For example,partspuppetmasterek01.northeurope.cloudapp.azure.com
. -
Append
https://
to the beginning of the DNS name to create a URL for the Puppet Master’s public DNS Address. The resulting URL should be in the formathttps
://machinename
.region
.cloudapp.azure.com
. For example,https://partspuppetmasterek01.northeurope.cloudapp.azure.com
.Using
https
, nothttp
, visit the URL in a web browser.Override the certificate error warning messages, and visit the webpage. It is safe to ignore these error messages for the purposes of this lab. The prompts that you see, and steps required to access the URL, may depend on the web browser you use.
-
If the Puppet configuration has succeeded, you should see the Puppet Master Console sign in webpage.
Note: The lab requires several ports to be open, such as the Puppet Server port, the Puppet Console port, SSH ports, and the PU MRP app port on the
partsmrp
VM. The ARM template opens these ports on the VMs for you. You can look through the deployment JSON template to view the port configuration details in the PuppetPartsUnlimitedMRP.json file on GitHub. -
Log in to the Puppet Master Console with the following credentials.
- user name =
admin
- Password = Pm Console Password you specified earlier in this lab. For example,
Passw0rd0134
Note: You cannot log into the Puppet Master Console with the username and password you specified earlier in this lab. You must login using the built in
admin
account instead, as shown. - user name =
-
If your log in is successful, you will be redirected to the Puppet Configuration Management Console webpage which is similar in appearance to the following screenshot.
Task 2: Install Puppet Agent on the node
You are now ready to add the Node to the Puppet Master. Once the Node is added, the Puppet Master will be able to configure the Node.
-
Get the Puppet Master internal DNS name. On the Puppet Configuration Management Console webpage, go to Nodes > Unsigned Certificates. The page that loads will show an
Add node
command. Make a note of the command, we will run it in Step 4 of this lab task.For example, the Puppet Master machine name is
partspuppetmasterek01.northeurope.cloudapp.azure.com
, and the command we need to run will be similar to the following command.curl -k https://partspuppetmasterek01.irblmudbrloe5hz001blu2g34f.ax.internal.cloudapp.net:8140/packages/current/install.bash | sudo bash
-
In Azure Portal, from the left menu, choose All Resources > partsmrpnodeek01 (or whatever name you specified for the Node / partsmrp VM). Make a note of the DNS name value for the Node VM.
-
Establish an SSH connection to the Node VM. In the following example, we will connect to the Node VM using the PuTTy SSH client.
Specify the Node DNS name as the destination Host Name.
If prompted, choose Yes to add the SSH key to PuTTy’s cache.
Log in with username and password credentials that you specified in Task 1. For example,
azureuser
andPassw0rd0134
. -
Run the
Add Node
command on the node.Enter the
Add Node
command into the SSH terminal, which you noted earlier in Step 1. The command begins withcurl...
. Run the command.Wait for the command to install the Puppet Agent and any dependencies on the Node. The command takes two or three minutes to complete.
From here onwards, you will configure the Node from the Puppet Master only.
-
Accept the pending Node request.
Return to the Puppet Configuration Management Console. Refresh the Unsigned Certificates webpage (where you previously got the Node install command). You should see a pending unsigned certificate request. Choose Accept to approve the node.
This is a request to authorize the certificate between the Puppet Master and the Node, so that they can communicate securely.
-
Goto the Nodes tab in the Puppet Configuration Management Console. It may take a few minutes to configure the Node / partsmrp VM, before it is visible in the Puppet Configuration Management Console. When the Node is ready, you should see the following nodes listed in the Puppet Configuration Management Console.
- Puppet Master. For example,
partspuppetmasterek01
- Node / partsmrp. For example,
partsmrpnodeek01
The nodes are also listed in the Puppet Configuration Management Console under Configuration > Overview.
Note: You can automate the Puppet Agent installation and configuration process on an Azure VM using the Puppet Agent extension from the Azure Marketplace.
There are also a series of PowerShell cmdlets for provisioning, enabling, and disabling the Puppet Extension Handler on Windows VMs. This provides a command-line interface for deploying Puppet Enterprise Agents to Windows VMs in Azure. For details see the Puppet PowerShell Cmdlets for Azure Guide.
- Puppet Master. For example,
Puppet modules and programs
The Parts Unlimited MRP application (PU MRP App) is a Java application. The PU MRP App requires you to install and configure MongoDB and Apache Tomcat on the Node / partsmrp VM. Instead of installing and configuring MongoDB and Tomcat manually, we will write a Puppet Program that will instruct the Node to configure itself.
Puppet Programs are stored in a particular directory on the Puppet Master. Puppet Programs are made up of manifests that describe the desired state of the Node(s). The manifests can consume modules, which are pre-packaged Puppet Programs. Users can create their own modules or consume modules from a marketplace that is maintained by Puppet Labs, known as The Forge.
Some modules on The Forge are supported officially, others are open-source modules uploaded from the community. Puppet Programs are organized by environment, which allows you to manage Puppet Programs for different environments such as Dev, Test and Production.
For this lab, we will treat the Node as if it were in the Production environment. We will also download modules from The Forge, which we will consume in order to configure the Node.
Task 3: Configure the Puppet Production Environment
The template we used to deploy Puppet to Azure also configured a directory on the Puppet Master for managing the Production environment. The Production Directory is in /etc/puppetlabs/code/environments/production
.
-
Inspect the Production modules.
Connect to the to the Puppet Master via SSH, with the PuTTy client for example. Use the Change Directory command
cd
to change into the Production Directory/etc/puppetlabs/code/environments/production
.cd /etc/puppetlabs/code/environments/production
Use the list command
ls
to list the contents of the Production Directory. You will see directories namedmanifests
andmodules
.- The
manifests
directory contains descriptions of machines that we will apply to Nodes later in this lab. - The
modules
directory contains any modules that are referenced by the manifests.
- The
-
Install additional Puppet Modules from The Forge.
We will install modules from The Forge that are needed to configure the Node / partsmrp. Run the following commands in a terminal with an SSH connection to the Puppet Master.
sudo puppet module install puppetlabs-mongodb sudo puppet module install puppetlabs-tomcat sudo puppet module install maestrodev-wget sudo puppet module install puppetlabs-accounts sudo puppet module install puppetlabs-java
Note: The
mongodb
andtomcat
modules from The Forge are supported officially. Thewget
module is a user module, and is not supported officially. Theaccounts
module provides Puppet with Classes for managing and creating users and groups in our Linux VMs. Finally, thejava
module provides Puppet with additional Java functionality. -
Create a custom module.
Create a custom module named
mrpapp
in the Production/ Modules Directory on the Puppet Master. The custom module will configure the PU MRP app. Run the following commands in a terminal with an SSH connection to the Puppet Master.Use the Change Directory command
cd
to change into the Production/ Modules Directory/etc/puppetlabs/code/environments/production/modules
.cd /etc/puppetlabs/code/environments/production/modules
Run the
module generate
commands to create themrpapp
module.sudo puppet module generate partsunlimited-mrpapp
This will start a wizard that will ask a series of questions as it scaffolds the module. Simply press
enter
for each question (accepting blank or default) until the wizard completes.Running list command
ls -la
should show a list of the modules in the directory~/production/modules
, including the newmrpapp
module.Note: The
ls -la
combined commands will list the contents of a directory (i.e.ls
), using a long list format (i.e.-l
), with hidden files shown (i.e.-a
). -
The
mrpapp
module will define our Node’s configuration.The configuration of Nodes in the Production environment is defined in a
site.pp
file. Thesite.pp
file is located in the Production \ Manifests directory. The.pp
filename extension is short for Puppet Program.We will edit the
site.pp
file by adding a configuration for our Node.Run the following command in an SSH terminal to the Puppet Master, which will open the
site.pp
in the Nano Text Editor.sudo nano /etc/puppetlabs/code/environments/production/manifests/site.pp
Scroll to the bottom of the file and edit the
node default
section. Edit it to look as follows:node default { class { 'mrpapp': } }
Note: Make sure that you remove the comment tag
#
before the Class definition, as instructed. -
Press
ctrl + o
, thenenter
, and thenctrl + x
, to write the changes you made to thesite.pp
file, save the changes, and close the file.The edits that you made to the
site.pp
file instruct Puppet to configure all Nodes with themrpapp
module, by default. Themrpapp
module (though currently empty) is in the Modules directory of the Production environment, so Puppet will know where to find the module.
Task 4: Test the Production Environment Configuration
Before we describe the PU MRP app for the Node fully, test that everything is hooked up correctly by configuring a dummy file in the mrpapp
module. If Puppet executes and creates the dummy file successfully, then everything is configured and working correctly. We can then set up the mrpapp
module properly.
-
Edit the
init.pp
file.The entry point for the
mrpapp
module is theinit.pp
file, in themrpapp/manifests
directory, on the Puppet Master. Run the following command in an SSH terminal to the Puppet Master, to open theinit.pp
file for editing with Nano.sudo nano /etc/puppetlabs/code/environments/production/modules/mrpapp/manifests/init.pp
You can either delete all of the boiler-plate comments or ignore them. Scroll down to the
Class: mrpapp
declaration. Make the following edits.class mrpapp { file { '/tmp/dummy.txt': ensure => 'present', content => 'Puppet rules!' } }
Note: Make sure that you remove the comment tag
#
before the Class definition, as instructed.Press
ctrl + x
, theny
, and thenenter
to save your changes to the file.Note: Classes in Puppet Programs are not like Classes in Object Oriented Programming. Classes in Puppet Programs define a resource that is configured on a Node. By adding the
mrpapp
Class (or resource) to the init.pp file, we have instructed Puppet to ensure that a file exists on the path/tmp/dummy.txt
, and that the file has the content “Puppet rules!”. We will define more advanced resources within themrpapp
Class as we progress through this lab. -
Test the dummy file.
To test our setup, establish an SSH connection to the Node / partsmrp VM (using the PuTTy client, for example). Run the following command in an SSH terminal to the Node.
sudo puppet agent --test --debug
By default, Puppet Agents query the Puppet Master for their configuration every 30 minutes. The Puppet Agent then tests its current configuration against the configuration specified by the Puppet Master. If necessary, the Puppet Agent modifies its configuration to match the configuration specified by the Puppet Master.
The command you entered forces the Puppet Agent to query the Puppet Master for its configuration immediately. In this case, the configuration requires the
/tmp/dummy.txt
file, so the Node creates the file accordingly.You may see more output in your terminal than is shown in the previous screenshot. We used the
--debug
switch for learning purposes, to display more information as the command executes. You can remove the--debug
switch to receive less text output in the terminal if you wish.You can also use the
cat
command on the Node, to verify the presence of the file/tmp/dummy.txt
on the Node, and to inspect the file’s contents. The “Puppet rules!” message should be displayed in the terminal.cat /tmp/dummy.txt
-
Correct configuration drift.
By default, Puppet Agent runs every 30 minutes on the Nodes. Each time the Agent runs, Puppet determines if the environment is in the correct state. If it is not in the correct state, Puppet reapplies Classes as necessary. This process allows Puppet to detect Configuration Drift, and fix it.
Simulate Configuration Drift by deleting the dummy file
dummy.txt
from the Node. Run the following command in a terminal connected to the Node to delete the file.sudo rm /tmp/dummy.txt
Confirm that the file was deleted from the Node successfully by running the following command on the Node. The command should produce a No such file or directory warning message.
cat /tmp/dummy.txt
Re-run the Puppet Agent on the Node with the following command.
sudo puppet agent --test
The re-run should complete successfully, and the file should now exist on the Node again. Verify that the file is present on the Node by running the following command on the Node. Confirm that the “Puppet rules!” message is displayed in the terminal.
cat /tmp/dummy.txt
You can also edit the contents of the file
dummy.txt
on the Node. Re-run thesudo puppet agent --test
command, and verify that the contents of the filedummy.txt
have been reverted to match the configuration specified on the Puppet Master.
Task 5: Create a Puppet Program to describe the prerequisites for the PU MRP app
We have hooked the Node (partsmrp) up to the Puppet Master. Now we can write the Puppet Program to describe the prerequisites for the PU MRP app.
In practice, the different parts of a large configuration solution are typically split into multiple manifests or modules. Splitting the configuration across multiple files is a form of Modularization, and promotes better organization and reuse of code.
For simplicity, in this lab, we will describe our entire configuration in a single Puppet Program file init.pp
, from inside the mrpapp
module that we created earlier. In Task 5, we will build up our init.pp
step-by-step.
Note: You can access a completed
init.pp
file on Microsoft’s PU MRP GitHub repository. Copy theinit.pp
file contents from GitHub, or from the Task 5 sub-sections that follow, and paste them into your localinit.pp
file.
Tomcat requires the presence of a Linux User and a Group on the Node, to access and run Tomcat services. The default name that Tomcat uses for both the User and Group is Tomcat
. There are a number of ways to create this User and Group, but we will add the User and Group to the Node VM by adding a separate Class to the init.pp
file.
In addition to editing the init.pp
file, we will make the following modifications.
- Edit the file
war.pp
. Editing thewar.pp
file allows us to deploy the PU MRP app more easily. - Edit the permissions on the directory
~/tomcat7/webapps
for extracting.war
files, at the end of Task 5. Editing these permissions will allow the.war
file to be extracted automatically, when the Tomcat service is restarted as the.pp
file runs.
Note: All of the actions required to create our Puppet Program will be explained clearly, as we step through the Task 5 sub-sections that follow. If you encounter problems, see the Potential issues and troubleshooting section at the end of this lab.
Task 5.1 Configure MongoDB
Once MongoDB is configured, we want Puppet to download a Mongo script that contains data for our PU MRP application’s database. We will include this as part of our MongoDB setup. To implement these requirements, add a Class to configure MongoDB.
On the Puppet Master, use the following command to open the init.pp
file, inside the mrpapp
module, for editing.
sudo nano /etc/puppetlabs/code/environments/production/modules/mrpapp/manifests/init.pp
Add the following configuremongodb
Class to the bottom of the file.
class configuremongodb {
include wget
class { 'mongodb': }->
wget::fetch { 'mongorecords':
source => 'https://raw.githubusercontent.com/Microsoft/PartsUnlimitedMRP/master/deploy/MongoRecords.js',
destination => '/tmp/MongoRecords.js',
timeout => 0,
}->
exec { 'insertrecords':
command => 'mongo ordering /tmp/MongoRecords.js',
path => '/usr/bin:/usr/sbin',
unless => 'test -f /tmp/initcomplete'
}->
file { '/tmp/initcomplete':
ensure => 'present',
}
}
Press ctrl + o
, then enter
to save the changes to the file without exiting.
Let’s examine the configuremongodb
Class.
- Line 1. We create a Class (resource) called
configuremongodb
. - Line 2. We include the
wget
module so that we can download files viawget
. - Line 3. We invoke the
mongodb
resource (from themongodb
module we downloaded earlier). This installs Mongo DB using defaults defined in the Puppet Mongo DB module. That is all we have to do to install Mondo DB! - Line 5. We invoke the
fetch
resource from thewget
module, and call the resourcemongorecords
. - Line 6. We set the source of the file we need to download from PU MRP GitHub.
- Line 7. We set the destination where the file must be downloaded to.
- Line 10. We use the built-in Puppet resource
exec
to execute a command. - Line 11. We specify the command to execute.
- Line 12. We set the path for the command invocation.
- Line 13. We specify a condition using the keyword
unless
. We only want this command to execute once, so we create a tmp file once we have inserted the records (Line 15). If this file exists, we do not execute the command again.
Note: The
->
notation on Lines 3, 9 and 14 is an Ordering Arrow. The ordering arrow tells Puppet that it must apply the left resource before invoking the right resource. This allows us to specify order, when necessary.
Task 5.2 Configure Java
Add the following configurejava
Class, below the configuremongodb
Class, to set up our Java requirements.
class configurejava {
include apt
$packages = ['openjdk-8-jdk', 'openjdk-8-jre']
apt::ppa { 'ppa:openjdk-r/ppa': }->
package { $packages:
ensure => 'installed',
}
}
Press ctrl + o
, then enter
to save the changes to the file without exiting.
Let’s examine the configurejava
Class.
- Line 2. We include the
apt
module, which will allow us to configure new Personal Package Archives (PPAs). - Line 3. We create an array of packages that we need to install.
- Line 5. We add a PPA.
- Lines 6 to 8. We tell Puppet to ensure that the packages are installed. Puppet expands the array and installs each package in the array.
Note: We cannot use the Puppet
package
target to install Java, because this will only install Java 7. We add the PPA and use theapt
module to get Java 8.
Task 5.3 Create User and Group
Add the following createuserandgroup
Class, to specify the Linux Users and Groups to be used for configuring, deploying, starting and stopping services.
class createuserandgroup {
group { 'tomcat':
ensure => 'present',
gid => '10003',
}
user { 'tomcat':
ensure => 'present',
gid => '10003',
home => '/tomcat',
password => '!',
password_max_age => '99999',
password_min_age => '0',
uid => '1003',
}
}
Press ctrl + o
, then enter
to save the changes to the file without exiting.
Let’s examine the createuserandgroup
Class.
- Line 1. We call the
createuserandgroup
Class that we defined at the start of theinit.pp
file. We defined this Class to use the User and Group resources. - Lines 3 to 6. We use the
group
command to create the GroupTomcat
. We use the parameter valueensure = > present
to check if theTomcat
Group exists, and if not we create it. We then assign agid
value to the Group. This assignment is not mandatory, but it helps manage Groups across multiple machines. - Lines 8 to 17. We use the
user
command to create the UserTomcat
. We use theensure
command to check if theTomcat
User exists, and if not we create it. We assigngid
anduid
values to help manage Users. Then we set theTomcat
user’s home directory path. We also designate the User password values. We have left the password value open and unspecified, for ease of use. In practice you should implement strict password policies.
Note: The commands
user
andgroup
are made available via thepuppetlabs-accounts
module, which we installed on the Puppet Master earlier.
Task 5.4 Configure Tomcat
Add the following configuretomcat
Class to configure tomcat
.
class configuretomcat {
class { 'tomcat': }
require createuserandgroup
tomcat::instance { 'default':
catalina_home => '/var/lib/tomcat7',
install_from_source => false,
package_name => ['tomcat7','tomcat7-admin'],
}->
tomcat::config::server::tomcat_users {
'tomcat':
catalina_base => '/var/lib/tomcat7',
element => 'user',
password => 'password',
roles => ['manager-gui','manager-jmx','manager-script','manager-status'];
'tomcat7':
catalina_base => '/var/lib/tomcat7',
element => 'user',
password => 'password',
roles => ['manager-gui','manager-jmx','manager-script','manager-status'];
}->
tomcat::config::server::connector { 'tomcat7-http':
catalina_base => '/var/lib/tomcat7',
port => '9080',
protocol => 'HTTP/1.1',
connector_ensure => 'present',
server_config => '/etc/tomcat7/server.xml',
}->
tomcat::service { 'default':
use_jsvc => false,
use_init => true,
service_name => 'tomcat7',
}
}
Press ctrl + o
, then enter
to save the changes to the file without exiting.
Let’s examine the configuretomcat
Class.
- Line 1. We create a Class (resource) called
configuretomcat
. - Line 2. We invoke the
tomcat
resource from the Tomcat module we downloaded earlier. - Line 3. We require the
Tomcat
User and Group to be available, before we can configure Tomcat, so we mark the Classcreateuserandgroup
as required. - Lines 6 to 9. We install the Tomcat package from the Tomcat module we downloaded earlier. The package name is
Tomcat7
, so we call it here. We designate the home directory where we will place our default installation. Then, we specify that it is not from source; this is required when installing from a downloaded package. We could install directly from a web source, if we wished. We also install thetomcat7-admin
package. We will not use thetomcat7-admin
package in this lab, but it can be used to provide the Tomcat Manager Web User Interface. - Lines 13 to 23. We create two usernames for Tomcat to use. These are created within the Tomcat application for managing and configuring Tomcat. They are created within, and called from, the file
/var/lib/tomcat7/conf/tomcat-users.xml
. You can open the file, and view its contents, if you wish. - Lines 26 to 31. We configure the Tomcat connector, define the protocol to use and the port number. We also ensure that the connector is present, to allow it to serve requests on the ports that we set. We also specify the connector properties for Puppet to write to the Tomcat
server.xml
file. Again, you can open the file, and view its contents, on the Puppet Master at/var/lib/tomcat7/conf/server.xml
. - Lines 35 to 38. We configure the Tomcat service, which is our default installation. As there is only one installation of Tomcat, we specify the default value that we are applying the configuration to. We ensure the service is running, and the start the service using the
use_init
value. Then, we specify the service connector name astomcat7
for the PU MRP app.
Task 5.5 Deploy a WAR File
The PU MRP app is compiled into a .war
file that Tomcat uses to serve pages. We need to specify a resource to deploy a .war
file for the PU MRP app site.
Return to the Puppet SSH session, and edit the init.pp
file by adding the following deploywar
Class to the bottom of the file.
class deploywar {
require configuretomcat
tomcat::war { 'mrp.war':
catalina_base => '/var/lib/tomcat7',
war_source => 'https://raw.githubusercontent.com/Microsoft/PartsUnlimitedMRP/master/builds/mrp.war',
}
file { '/var/lib/tomcat7/webapps/':
path => '/var/lib/tomcat7/webapps/',
ensure => 'directory',
recurse => 'true',
mode => '777',
}
}
Press ctrl + o
, then enter
to save the changes to the file without exiting.
Let’s examine the deploywar
Class.
- Line 1. We create a Class (resource) called
deploywar
. - Line 2. We tell Puppet to ensure that
configuretomcat
has completed, before invoking thedeploywar
Class. - Line 5. We set the
catalina base
directory so that Puppet deploys the.war
to our Tomcat service. - Line 6. We use the Tomcat module’s
war
resource to deploy our.war
from thewar_source
. - Line 9. We call the
file
Class, which comes from theTomcat
module. Thefile
Class allows us to configure files on our system. - Line 10. We specify the path to the web directory for our PU MRP app.
- Line 11. We ensure a directory exists on the path we specified, before applying our configuration.
- Line 12. We specify that the configuration should be applied recursively, so it will affect all files and sub-directories within the
~/tomcat7/webapps/
directory. - Line 13. We designate a
777
permission on the all files and sub-directories within the~/tomcat7/webapps/
directory. It is very important to set these permissions to allow read, write and execute permissions on all content within the~/tomcat7/webapps/
directory. Setting777
permissions is acceptable in this lab. You should always set more restrictive permissions according to each user, group and access level in a Production environment.
Note: Typically, the following actions are needed to deploy a
.war
file
- copy the
.war
file to the Tomcat/var/lib/tomcat7/webapps
directory- set the correct permissions on the
webapps
directory- specify values for
unpackWARS
andautoDeploy
in the Tomcat server config file at/var/lib/tomcat7/conf/server.xml
- list a valid and available user in the Tomcat file
/var/lib/tomcat7/conf/tomcat-users.xml
Tomcat will extract and then deploy the
.war
when the service starts. This is why we restart the service in theinit.pp
file. When you complete this lab, you can open and view the contents of the Tomcat filesserver.xml
andtomcat-users.xml
.
Task 5.6 Start the Ordering Service
The PU MRP app calls an Ordering Service, which is a REST API that manages orders in the Mongo DB. The Ordering Service is compiled to a .jar
file. We need to copy the .jar
file file to our Node. The Node then runs the Ordering Service, in the background, so that it can listen for REST API requests.
To ensure that the Ordering Service runs, add the following orderingservice
Class to the bottom of the init.pp
file.
class orderingservice {
package { 'openjdk-7-jre':
ensure => 'installed',
}
file { '/opt/mrp':
ensure => 'directory'
}->
wget::fetch { 'orderingsvc':
source => 'https://raw.githubusercontent.com/Microsoft/PartsUnlimitedMRP/master/builds/ordering-service-0.1.0.jar',
destination => '/opt/mrp/ordering-service.jar',
cache_dir => '/var/cache/wget',
timeout => 0,
}->
exec { 'stoporderingservice':
command => "pkill -f ordering-service",
path => '/bin:/usr/bin:/usr/sbin',
onlyif => "pgrep -f ordering-service"
}->
exec { 'stoptomcat':
command => 'service tomcat7 stop',
path => '/bin:/usr/bin:/usr/sbin',
onlyif => "test -f /etc/init.d/tomcat7",
}->
exec { 'orderservice':
command => 'java -jar /opt/mrp/ordering-service.jar &',
path => '/usr/bin:/usr/sbin:/usr/lib/jvm/java-8-openjdk-amd64/bin',
}->
exec { 'wait':
command => 'sleep 20',
path => '/bin',
notify => Tomcat::Service['default']
}
}
Press ctrl + o
, then enter
to save the changes to the file without exiting.
Let’s examine the orderingservice
Class.
- Line 1. We create a Class (resource) called
orderingservice
. - Lines 2 to 4. We install the Java Runtime Environment (JRE) required to run the application using Puppet’s
package
resource. - Lines 6 to 8. We ensure that the directory
/opt/mrp
exists (Puppet creates it if it does not exist). - Lines 9 to 14. We
wget
the Ordering Service binary, and place it in/opt/mrp
. - Line 12. We specify a cache directory, to ensure that the file is only downloaded once.
- Lines 15 to 19. We stop the
orderingservice
, but only if it is running already. - Lines 20 to 24. We stop the
tomcat7
service, but only if it is running already. - Lines 25 to 28. We start the Ordering Service.
- Lines 29 to 33. We sleep for 20 seconds, to give the Ordering Service time to start up, before
notifying
thetomcat
service. These actions trigger a refresh on the service. Puppet will re-apply the state that we defined for the service (i.e. start it, if it is not running already).
Note: We need to wait after running the
java
command, since this service needs to be running before we can start Tomcat. Otherwise, Tomcat will occupy the port that the Ordering Service needs to listen on for incoming API requests.
Task 5.7 Complete the mrpapp Resource
Go to the top of the file init.pp
. Modify the mrpapp
Class by deleting the content that relates to the dummy.txt
file which we tested earlier in the lab. Add the following.
class mrpapp {
class { 'configuremongodb': }
class { 'configurejava': }
class { 'createuserandgroup': }
class { 'configuretomcat': }
class { 'deploywar': }
class { 'orderingservice': }
}
Press ctrl + x
, then y
, and then enter
to save your changes to the file.
The modifications to the mrpapp
Class specify which Classes are needed by the init.pp
file, in order to run our resources.
Note: After you have made all of the necessary modifications to the
init.pp
file, it should be identical to the completedinit.pp
file on Microsoft’s PU MRP GitHub repository. If you prefer, you can copy theinit.pp
file contents from GitHub and paste them into your localinit.pp
file.
Task 5.8 Configure .war file extracton permissions
Part of the configuration we specified in our init.pp
file involves copying the .war
file for our PU MRP app to the directory /var/lib/tomcat7/webapps
.
Tomcat automatically extracts .war
files from the webapps
directory. However, in our lab we will set full read, write and access permissions in the file war.pp
, to ensure that the file extracts sucessfully.
Note: More details about configuration editing are availble on the page Resource Type: file.
On the Puppet Master, run the following command to open the war.pp
file for the Tomcat module in the Nano editor.
sudo nano /etc/puppetlabs/code/environments/production/modules/tomcat/manifests/war.pp
Modify the permissions at the bottom of the war.pp
file, by changing the mode from mode => '0640'
to mode => '0777'
.
When you have finished editing, it should look like the following.
file { "tomcat::war ${name}":
ensure => file,
path => "${_deployment_path}/${_war_name}",
owner => $user,
group => $group,
mode => '0777',
subscribe => Archive["tomcat::war ${name}"],
}
}
}
Press ctrl + x
, then y
, and then enter
to save your changes and close the file.
Let’s examine this section of the war.pp
file.
- Line 1. Specifies the file name, which comes with the Tomcat module
tomcat::war
. - Line 2. Ensures the file is present before acting on it.
- Line 3. Defines the path to the web directory, which we previously set to
/var/lib/tomcat7/webapps
. This is where the.war
file for our PU MRP app will be copied to, as we specified in theinit.pp
file. - Line 4 to 5. Set the User and Group owners of the files.
- Line 6. Set the file permissions mode to
0777
, which allows read, write and execute permissions to everyone. You should not use 777 permissions in a Production environment because you may expose your environment to risk and malicious code.
Task 6: Run the Puppet Configuration on the Node
-
Re-run the Puppet Agent.
Return to, or re-establish, your SSH session on the Node/ partsmrp VM. Force Puppet to update the Node’s configuration with the following command.
sudo puppet agent --test
Note: This first run may take a few moments, as there is a lot to download and install. The next time that you run the Puppet Agent, it will verify that the existing environment is configured correctly. This verification process will take less time than the first run, because the services will be installed and configured already.
-
Verify that Tomcat is running correctly.
Append the port number
9080
to the DNS address URL for the Node/ partsmrp VM, for examplehttp://partsmrpnodeek01.westeurope.cloudapp.azure.com
:9080
You can get the DNS address URL from the Public IP resource for the Node, in Azure Portal (just as you did when you got the URL of the Puppet Master earlier).
Open a web browser and browse to port
9080
on the Node/ partsmrp VM. Once open in the web browser, you should see the Tomcat Confirmation webpage.Note: Use the
http
protocol, and nothttps
. -
Verify that the PU MRP app is running correctly.
Check that the configuration is correct by opening a web browser to the PU MRP app. In your web browser, append
/mrp
to the end of DNS address URL you used in Step 2. For example,http://partsmrpnodeek01.westeurope.cloudapp.azure.com:9080
/mrp
.You can also get the DNS name for the Node/ partsmrp VM in Azure Portal.
The PU MRP app Welcome webpage should be displayed in your web browser.
Explore the PU MRP app to confirm that it functions as intended. For example, select the Orders button, in your web browser, to view the Orders page.
Potential issues and troubleshooting
-
You may encounter errors on first run of the configuration, related to a Java package used in the Mongo DB installation, as shown below. Re-run the configuration on the Node with the command
sudo puppet agent --test --debug
. The second run should complete without error. If more errors occur, the--debug
switch will help you locate the problem(s). Work through the problems, and fix them one-by-one, until the configuration completes without error.Error: Execution of '/usr/bin/apt-get -q -y -o DPkg::Options::=--force-confold install openjdk-7-jre' returned 100: Reading package lists... Building dependency tree... Reading state information... Package openjdk-7-jre is not available, but is referred to by another package. This may mean that the package is missing, has been obsoleted, or is only available from another source
-
Manually typing code or copying and pasting into a terminal from the code on this page may induce errors. After you have made all of the necessary modifications to the
init.pp
file, it should be identical to the completedinit.pp
file on Microsoft’s PU MRP GitHub repository. If you have not already done so, copy theinit.pp
file contents from GitHub and paste them into your localinit.pp
file. Then, try re-running the configuration on the Node with the commandsudo puppet agent --test
to resolve errors.
Summary
In this lab you created the Puppet infrastructure needed to deploy the PU MRP app, deployed the application to a Node and managed Configuration Drift. To do so you completed the following tasks:
- Provisioned a Puppet Master and Node in Azure using Azure Resource Manager (ARM) templates (both inside Linux Ubuntu Virtual Machines)
- Installed Puppet Agent on the Node
- Configured the Puppet Production Environment
- Tested the Production Environment Configuration
- Created a Puppet Program to describe the environment for the PU MRP app
- Ran the Puppet Configuration on the Node