Use Puppet Modules to Create a LAMP Stack
- Deprecated guides:
- Ubuntu 14.04
Traducciones al EspañolEstamos traduciendo nuestros guías y tutoriales al Español. Es posible que usted esté viendo una traducción generada automáticamente. Estamos trabajando con traductores profesionales para verificar las traducciones de nuestro sitio web. Este proyecto es un trabajo en curso.


Puppet modules are the building blocks of your Puppet managed servers. Modules install and configure packages, create directories, and generate any other server changes that the user includes in the module. A Puppet module aims to perform all parts of a certain task, such as downloading the Apache package, configuring all files, changing the MPM data, and setting up virtual hosts. Modules are, in turn, broken down into classes that are .pp files meant to simplify the module into various tasks and improve the module’s readability for any future users.
In this guide, you will create an Apache and a PHP module. A MySQL module will be adapted from the Puppet Lab’s MySQL module found on the Puppet Forge. These steps will create a full LAMP stack on your server and provide an overview of the various ways modules can be utilized.
Before You Begin
Set up a Puppet Master (Ubuntu 18.04) and two Puppet agents (Ubuntu 18.04 and CentOS 7) by following the steps in the Getting Started with Puppet - Basic Installation and Setup guide.
Create the Apache Module
From the Puppet Master, navigate to Puppet’s module directory and create the
apachedirectory:cd /etc/puppetlabs/code/environments/production/modules/ sudo mkdir apacheFrom within the
apachedirectory, createmanifests,templates,files, andexamplesdirectories:cd apache sudo mkdir {manifests,templates,files,examples}Navigate to the
manifestsdirectory:cd manifestsFrom here, the module will be separated into classes, based on the goals of that particular section of code. In this instance, there will be an
init.ppclass for downloading the Apache package, aparams.ppfile to define any variables and parameters,config.ppto manage any configuration files for the Apache service itself, and avhosts.ppfile to define the virtual hosts. This module will also make use of Hiera data to store variables for each node.
Create the Initial Apache Class and Parameters
From within the
manifestsdirectory, create aninit.ppfile to hold theapacheclass. This class should share its name with the module name. This file will be used to install the Apache package. Since Ubuntu 18.04 and CentOS 7 use different package names for Apache, a variable will be used:- File: /etc/puppetlabs/code/environments/production/modules/apache/manifests/init.pp
1 2 3 4 5 6 7 8class apache { package { 'apache': name => $apachename, ensure => present, } }
The
packageresource allows for the management of a package. This is used to add, remove, or ensure a package is present. In most cases, the name of the resource (apache, above) should be the name of the package being managed. Because of the difference in naming conventions, however, this resource is simply calledapache, while the actual name of the package is called upon with thenamereference.name, in this instance, calls for the yet-undefined variable$apachename. Theensurereference ensures that the package ispresent.The
params.ppfile will be used to define the needed variables. While these variables could be defined within theinit.ppfile, since more variables will need to be used outside of the resource type itself, using aparams.ppfile allows for variables to be defined inifstatements and used across multiple classes.Create a
params.ppand add the following code:- File: /etc/puppetlabs/code/environments/production/modules/apache/manifests/params.pp
1 2 3 4 5 6 7 8 9 10 11 12 13class apache::params { if $::osfamily == 'RedHat' { $apachename = 'httpd' } elsif $::osfamily == 'Debian' { $apachename = 'apache2' } else { fail ( 'this is not a supported distro.') } }
Outside of the original
init.ppclass, each class name needs to branch off ofapache. As such, this class is calledapache::params. The name after the double colon should share a name with the file.An
ifstatement is used to define the parameters, pulling from information provided by Facter, which is already installed on the Puppet master. In this case, Facter will be used to pull down the operating system family (osfamily), to discern if it is Red Hat or Debian-based.Note For the duration of this guide, when something needs to be added to the parameter list the variables needed for Red Hat and Debian will be provided, but the expanding code will not be shown. A complete copy ofparams.ppcan be viewed here.With the parameters finally defined, we need to call the
params.ppfile and the parameters intoinit.pp. To do this, the parameters need to be added after the class name, but before the opening curly bracket ({):- File: /etc/puppetlabs/code/environments/production/modules/apache/manifests/init.pp
1 2 3class apache ( $apachename = $::apache::params::apachename, ) inherits ::apache::params {
The value string
$::apache::params::valuetells Puppet to pull the values from theapachemodules,paramsclass, followed by the parameter name. The fragmentinherits ::apache::paramsallows forinit.ppto inherit these values.
Manage Configuration Files
The Apache configuration file will be different depending on whether you are working on a Red Hat- or Debian-based system. These can be viewed here: httpd.conf (Red Hat), apache2.conf (Debian).
Copy the content of
httpd.confandapache2.confin separate files and save them in thefilesdirectory located at/etc/puppetlabs/code/environments/production/modules/apache/files.Both files need to be edited to disable keepalive. You will need to add the line
KeepAlive Offthehttpd.conffile. If you do not want to change this setting, a comment should be added to the top of each file:- File: /etc/puppetlabs/code/environments/production/modules/apache/files/httpd.conf
1# This file is managed by Puppet
Add these files to the
init.ppfile, so Puppet will know where they are located on both the master server and agent nodes. To do this, thefileresource is used:- File: /etc/puppetlabs/code/environments/production/modules/apache/manifests/init.pp
1 2 3 4 5file { 'configuration-file': path => $conffile, ensure => file, source => $confsource, }
Because the configuration file is found in two different locations, the resource is given the generic name
configuration-filewith the file path defined as a parameter with thepathattribute.ensureensures that it is a file.sourceprovides the location on the Puppet master of the files created above.Open the
params.ppfile. The$conffileand$confsourcevariables need to be defined within theifstatement:- File: /etc/puppetlabs/code/environments/production/modules/apache/manifests/params.pp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17if $::osfamily == 'RedHat' { ... $conffile = '/etc/httpd/conf/httpd.conf' $confsource = 'puppet:///modules/apache/httpd.conf' } elsif $::osfamily == 'Debian' { ... $conffile = '/etc/apache2/apache2.conf' $confsource = 'puppet:///modules/apache/apache2.conf' } else { ...
These parameters will also need to be added to the beginning of the
apacheclass declaration in theinit.ppfile, similar to the previous example. A complete copy of theinit.ppfile can be seen here for reference.When the configuration file is changed, Apache needs to restart. To automate this, the
serviceresource can be used in combination with thenotifyattribute, which will call the resource to run whenever the configuration file is changed:- File: /etc/puppetlabs/code/environments/production/modules/apache/manifests/init.pp
1 2 3 4 5 6 7 8 9 10 11file { 'configuration-file': path => $conffile, ensure => file, source => $confsource, notify => Service['apache-service'], } service { 'apache-service': name => $apachename, hasrestart => true, }
The
serviceresource uses the already-created parameter that defined the Apache name on Red Hat and Debian systems. Thehasrestartattribute will trigger a restart of the defined service.
Create the Virtual Hosts Files
Depending on your systems distribution the virtual hosts files will be managed differently. Because of this, the code for virtual hosts will be encased in an if statement, similar to the one used in the params.pp class but containing actual Puppet resources. This will provide an example of an alternate use of if statements within Puppet’s code.
From within the
apache/manifests/directory, create and open avhosts.ppfile. Add the skeleton of theifstatement:- File: /etc/puppetlabs/code/environments/production/modules/apache/manifests/vhosts.pp
1 2 3 4 5 6 7 8 9 10 11class apache::vhosts { if $::osfamily == 'RedHat' { } elsif $::osfamily == 'Debian' { } else { } }
The location of the virtual hosts file on our CentOS 7 server is
/etc/httpd/conf.d/vhost.conf. This file will need to be created as a template on the Puppet master. The same needs to be done for the Ubuntu virtual hosts file, which is located at/etc/apache2/sites-available/example.com.conf, replacingexample.comwith the server’s FQDN. Navigate to thetemplatesfile within theapachemodule, and then create two files for your virtual hosts:For Red Hat systems:
- File: /etc/puppetlabs/code/environments/production/modules/apache/templates/vhosts-rh.conf.erb
1 2 3 4 5 6 7 8<VirtualHost *:80> ServerAdmin <%= @adminemail %> ServerName <%= @servername %> ServerAlias www.<%= @servername %> DocumentRoot /var/www/<%= @servername -%>/public_html/ ErrorLog /var/www/<%- @servername -%>/logs/error.log CustomLog /var/www/<%= @servername -%>/logs/access.log combined </Virtual Host>
For Debian systems:
- File: /etc/puppet/modules/apache/templates/vhosts-deb.conf.erb
1 2 3 4 5 6 7 8 9 10 11<VirtualHost *:80> ServerAdmin <%= @adminemail %> ServerName <%= @servername %> ServerAlias www.<%= @servername %> DocumentRoot /var/www/html/<%= @servername -%>/public_html/ ErrorLog /var/www/html/<%- @servername -%>/logs/error.log CustomLog /var/www/html/<%= @servername -%>/logs/access.log combined <Directory /var/www/html/<%= @servername -%>/public_html> Require all granted </Directory> </Virtual Host>
Only two variables are used in these files:
adminemailandservername. These will be defined on a node-by-node basis, within thesite.ppfile.Return to the
vhosts.ppfile. The templates created can now be referenced in the code:- File: /etc/puppetlabs/code/environments/production/modules/apache/manifests/vhosts.pp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17class apache::vhosts { if $::osfamily == 'RedHat' { file { '/etc/httpd/conf.d/vhost.conf': ensure => file, content => template('apache/vhosts-rh.conf.erb'), } } elsif $::osfamily == 'Debian' { file { "/etc/apache2/sites-available/$servername.conf": ensure => file, content => template('apache/vhosts-deb.conf.erb'), } } else { fail('This is not a supported distro.') } }
Both distribution families call to the
fileresource and take on the title of the virtual host’s location on the respective distribution. For Debian, this once more means referencing the$servernamevalue. Thecontentattribute calls to the respective templates.Note Values containing variables, such as the name of the Debian file resource above, need to be wrapped in double quotes ("). Any variables in single quotes (') are parsed exactly as written and will not pull in a variable.Both virtual hosts files reference two directories that are not on the systems by default. These can be created through the use of the
fileresource, each located within theifstatement. The completevhosts.conffile should resemble:- File: /etc/puppetlabs/code/environments/production/modules/apache/manifests/vhosts.pp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27class apache::vhosts { if $::osfamily == 'RedHat' { file { '/etc/httpd/conf.d/vhost.conf': ensure => file, content => template('apache/vhosts-rh.conf.erb'), } file { [ '/var/www/$servername', '/var/www/$servername/public_html', '/var/www/$servername/log', ]: ensure => directory, } } elsif $::osfamily == 'Debian' { file { "/etc/apache2/sites-available/$servername.conf": ensure => file, content => template('apache/vhosts-deb.conf.erb'), } file { [ '/var/www/$servername', '/var/www/$servername/public_html', '/var/www/$servername/logs', ]: ensure => directory, } } else { fail ( 'This is not a supported distro.') } }
Test and Run the Module
From within the
apache/manifests/directory, run thepuppet parseron all files to ensure the Puppet coding is without error:sudo /opt/puppetlabs/bin/puppet parser validate init.pp params.pp vhosts.ppIt should return empty, barring any issues.
Navigate to the
examplesdirectory within theapachemodule. Create aninit.ppfile and include the created classes. Replace the values for$servernameand$adminemailwith your own:- File: /etc/puppetlabs/code/environments/production/modules/apache/examples/init.pp
1 2 3 4 5$serveremail = 'webmaster@example.com' $servername = 'example.com' include apache include apache::vhosts
Test the module by running
puppet applywith the--nooptag:sudo /opt/puppetlabs/bin/puppet apply --noop init.ppIt should return no errors, and output that it will trigger refreshes from events. To install and configure apache on the Puppet master, this can be run again without
--noop, if so desired.Navigate back to the main Puppet directory and then to the
manifestsfolder (not the one located in the Apache module).cd /etc/puppetlabs/code/environments/production/manifestsIf you are continuing this guide from the Getting Started with Puppet - Basic Installation and Setup guide, you should have a
site.ppfile already created. If not, create one now.Open
site.ppand include the Apache module for each agent node. Also input the variables for theadminemailandservernameparameters. If you followed the Getting Started with Puppet - Basic Installation and Setup guide, a single node configuration withinsite.ppwill resemble the following:- File: /etc/puppetlabs/code/environments/production/manifests/site.pp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43node 'ubuntuhost.example.com' { $adminemail = 'webmaster@example.com' $servername = 'hostname.example.com' include accounts include apache include apache::vhosts resources { 'firewall': purge => true, } Firewall { before => Class['firewall::post'], require => Class['firewall::pre'], } class { ['firewall::pre', 'firewall::post']: } } node 'centoshost.example.com' { $adminemail = 'webmaster@example.com' $servername = 'hostname.example.com' include accounts include apache include apache::vhosts resources { 'firewall': purge => true, } Firewall { before => Class['firewall::post'], require => Class['firewall::pre'], } class { ['firewall::pre', 'firewall::post']: } }
If you did not follow the Getting Started with Puppet - Basic Installation and Setup guide, then your
site.ppfile should resemble the following example:- File: /etc/puppetlabs/code/environments/production/manifests/site.pp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18node 'ubupuppet.ip.linodeusercontent.com' { $adminemail = 'webmaster@example.com' $servername = 'hostname.example.com' include apache include apache::vhosts } node 'centospuppet.ip.linodeusercontent.com' { $adminemail = 'webmaster@example.com' $servername = 'hostname.example.com' include apache include apache::vhosts }
By default, the Puppet agent service on your managed nodes will automatically check with the master once every 30 minutes and apply any new configurations from the master. You can also manually invoke the Puppet agent process in-between automatic agent runs. To manually run the new module on your agent nodes, log in to the nodes and run:
sudo /opt/puppetlabs/bin/puppet agent -t
Using the MySQL Module
Many modules needed to run a server already exist within Puppet Labs’ Puppet Forge. These can be configured just as extensively as a module that you created and can save time since the module need not be created from scratch.
Ensure you are in the /etc/puppetlabs/code/environments/production/modules directory and install the Puppet Forge’s MySQL module by PuppetLabs. This will also install any prerequisite modules.
cd /etc/puppetlabs/code/environments/production/modules
sudo /opt/puppetlabs/bin/puppet module install puppetlabs-mysql
Use Hiera to Create Databases
Before you begin to create the configuration files for the MySQL module, consider that you may not want the same values to be used across all agent nodes. To supply Puppet with the correct data per node, Hiera is used. In this instance, you will be using a different root password per node, thus creating different MySQL databases.
Navigate to
/etc/puppetand create Hiera’s configuration filehiera.yamlin the mainpuppetdirectory. You will use Hiera’s default values:- File: /etc/puppetlabs/code/environments/production/hiera.yaml
1 2 3 4 5 6 7 8--- version: 5 hierarchy: - name: Common path: common.yaml defaults: data_hash: yaml_data datadir: data
Create the file
common.yaml. It will be used to define the defaultrootpassword for MySQL:- File: /etc/puppetlabs/code/environments/production/common.yaml
1mysql::server::root_password: 'password'
The
common.yamlfile is used when a variable is not defined elsewhere. This means all servers will share the same MySQL root password. These passwords can also be hashed to increase security.To use the MySQL module’s defaults you can simply add an
include '::mysql::server'line to thesite.ppfile. However, in this example, you will override some of the module’s defaults to create a database for each of your nodes. Edit thesite.ppfile with the following values:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39node 'ubupuppet.ip.linodeusercontent.com' { $adminemail = 'webmaster@example.com' $servername = 'hostname.example.com' include apache include apache::vhosts include php mysql::db { "mydb_${fqdn}": user => 'myuser', password => 'mypass', dbname => 'mydb', host => $::fqdn, grant => ['SELECT', 'UPDATE'], tag => $domain, } } node 'centospuppet.ip.linodeusercontent.com' { $adminemail = 'webmaster@example.com' $servername = 'hostname.example.com' include apache include apache::vhosts include mysql::server include php mysql::db { "mydb_${fqdn}": user => 'myuser', password => 'mypass', dbname => 'mydb', host => $::fqdn, grant => ['SELECT', 'UPDATE'], tag => $domain, } }
You can run these updates manually on each node by SSHing into each node and issuing the following command:
sudo /opt/puppetlabs/bin/puppet agent -tOtherwise, the Puppet agent service on your managed nodes will automatically check with the master once every 30 minutes and apply any new configurations from the master.
Create the PHP Module
Create the
phpdirectory in the/etc/puppetlabs/code/environments/production/modulespath, and generate thefiles,manifests,templates, andexamplesdirectories afterward:sudo mkdir php cd php sudo mkdir {files,manifests,examples,templates}Create the
init.pp. This file will use thepackageresource to install PHP. Two packages will be installed: The PHP package and the PHP extension and application repository. Add the following contents to your file:- File: /etc/puppetlabs/code/environments/production/modules/php/manifests/init.pp
1 2 3 4 5 6 7 8 9 10 11 12class php { package { 'php': name: $phpname, ensure: present, } package { 'php-pear': ensure: present, } }
Add
include phpto the hosts in yoursites.ppfile:- File: /etc/puppetlabs/code/environments/production/manifests/site.pp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22node 'ubupuppet.ip.linodeusercontent.com' { $adminemail = 'webmaster@example.com' $servername = 'hostname.example.com' include apache include apache::vhosts include mysql::database include php } node 'centospuppet.ip.linodeusercontent.com' { $adminemail = 'webmaster@example.com' $servername = 'hostname.example.com' include apache include apache::vhosts include mysql::database include php }
Run the following command on your agent nodes to pull in any changes to your servers.
sudo /opt/puppetlabs/bin/puppet agent -tOtherwise, the Puppet agent service on your managed nodes will automatically check with the master once every 30 minutes and apply any new configurations from the master.
You should now have a fully functioning LAMP stack on each of your Puppet managed nodes.
This page was originally published on
