Chapter 7. Managing Applications
 | Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it? |  |
 | --Brian W. Kernighan. |
In this chapter, we will cover the following recipes:
- Using public modules
- Managing Apache servers
- Creating Apache virtual hosts
- Creating nginx virtual hosts
- Managing MySQL
- Creating databases and users
Introduction
Without applications, a server is just a very expensive space heater. In this chapter, I'll present some recipes to manage some specific software with Puppet: MySQL, Apache, nginx, and Ruby. I hope the recipes will be useful to you in themselves. However, the patterns and techniques they use are applicable to almost any software, so you can adapt them to your own purposes without much difficulty. One thing that is common about these applications, they are common. Most Puppet installations will have to deal with a web server, Apache or nginx. Most, if not all, will have databases and some of those will have MySQL. When everyone has to deal with a problem, community solutions are generally better tested and more thorough than homegrown solutions. We'll use modules from the Puppet Forge in this chapter to manage these applications.
When you are writing your own Apache or nginx modules from scratch, you'll have to pay attention to the nuances of the distributions you support. Some distributions call the apache package httpd
, while others use apache2
; the same can be said for MySQL. In addition, Debian-based distributions use an enabled folder method to enable custom sites in Apache, which are virtual sites, whereas RPM based distributions do not. For more information on virtual sites, visit http://httpd.apache.org/docs/2.2/vhosts/.
Using public modules
When you write a Puppet module to manage some software or service, you don't have to start from scratch. Community-contributed modules are available at the Puppet Forge site for many popular applications. Sometimes, a community module will be exactly what you need and you can download and start using it straight away. In most cases, you will need to make some modifications to suit your particular needs and environment.
Like all community efforts, there are some excellent and some less than excellent modules on the Forge. You should read the README section of the module and decide whether the module is going to work in your installation. At the least, ensure that your distribution is supported. Puppetlabs has introduced a set of modules that are supported, that is, if you are an enterprise customer, they will support your use of the module in your installation. Additionally, most Forge modules deal with multiple operating systems, distributions, and a great number of use cases. In many cases, not using a forge module is like reinventing the wheel. One caveat though is that Forge modules may be more complex than your local modules. You should read the code and get a sense of what the module is doing. Knowing how the module works will help you debug it later.
How to do it...
In this example, we'll use the puppet module
command to find and install the useful stdlib
module, which contains many utility functions to help you develop Puppet code. It is one of the aforementioned supported modules by puppetlabs. I'll download the module into my user's home directory and manually install it in the Git repository. To install puppetlabs stdlib module, follow these steps:
- Run the following command:
t@mylaptop ~ $ puppet module search puppetlabs-stdlib Notice: Searching https://forgeapi.puppetlabs.com ... NAME DESCRIPTION AUTHOR KEYWORDS puppetlabs-stdlib Puppet Module Standard Library @puppetlabs stdlib stages
- We verified that we have the right module, so we'll install it with
module install
now:t@mylaptop ~ $ puppet module install puppetlabs-stdlib Notice: Preparing to install into /home/thomas/.puppet/modules ... Notice: Downloading from https://forgeapi.puppetlabs.com ... Notice: Installing -- do not interrupt ... /home/thomas/.puppet/modules └── puppetlabs-stdlib (v4.3.2)
- The module is now ready to use in your manifests; most good modules come with a
README
file to show you how to do this.
How it works...
You can search for modules that match the package or software you're interested in with the puppet module search
command. To install a specific module, use puppet module install
. You can add the -i
option to tell Puppet where to find your module directory.
You can browse the forge to see what's available at http://forge.puppetlabs.com/.
More information on supported modules is available at https://forge.puppetlabs.com/supported.
The current list of supported modules is available at https://forge.puppetlabs.com/modules?endorsements=supported.
There's more...
Modules on the Forge include a metadata.json
file, which describes the module and which operating systems the module supports. This file also includes a list of modules that are required by the module.
Note
This file was previously named Modulefile and not in JSON format; the old Modulefile format was deprecated in Version 3.6.
As we will see in our next section, when installing a module from the Forge, the required dependencies will automatically be installed as well.
Not all publically available modules are on Puppet Forge. Some other great places to look at on GitHub are:
Though not a collection of modules as such, the Puppet Cookbook website has many useful and illuminating code examples, patterns, and tips, maintained by the admirable Dean Wilson:
puppet module
command
to find and install the useful stdlib
module, which contains many utility functions to help you develop Puppet code. It is one of the aforementioned supported modules by puppetlabs. I'll download the module into my user's home directory and manually install it in the Git repository. To install puppetlabs stdlib module, follow these steps:
- Run the following command:
t@mylaptop ~ $ puppet module search puppetlabs-stdlib Notice: Searching https://forgeapi.puppetlabs.com ... NAME DESCRIPTION AUTHOR KEYWORDS puppetlabs-stdlib Puppet Module Standard Library @puppetlabs stdlib stages
- We verified that we have the right module, so we'll install it with
module install
now:t@mylaptop ~ $ puppet module install puppetlabs-stdlib Notice: Preparing to install into /home/thomas/.puppet/modules ... Notice: Downloading from https://forgeapi.puppetlabs.com ... Notice: Installing -- do not interrupt ... /home/thomas/.puppet/modules └── puppetlabs-stdlib (v4.3.2)
- The module is now ready to use in your manifests; most good modules come with a
README
file to show you how to do this.
How it works...
You can search for modules that match the package or software you're interested in with the puppet module search
command. To install a specific module, use puppet module install
. You can add the -i
option to tell Puppet where to find your module directory.
You can browse the forge to see what's available at http://forge.puppetlabs.com/.
More information on supported modules is available at https://forge.puppetlabs.com/supported.
The current list of supported modules is available at https://forge.puppetlabs.com/modules?endorsements=supported.
There's more...
Modules on the Forge include a metadata.json
file, which describes the module and which operating systems the module supports. This file also includes a list of modules that are required by the module.
Note
This file was previously named Modulefile and not in JSON format; the old Modulefile format was deprecated in Version 3.6.
As we will see in our next section, when installing a module from the Forge, the required dependencies will automatically be installed as well.
Not all publically available modules are on Puppet Forge. Some other great places to look at on GitHub are:
Though not a collection of modules as such, the Puppet Cookbook website has many useful and illuminating code examples, patterns, and tips, maintained by the admirable Dean Wilson:
modules that match the package or software you're interested in with the puppet module search
command. To install a specific module, use puppet module install
. You can add the -i
option to tell Puppet where to find your module directory.
You can browse the forge to see what's available at http://forge.puppetlabs.com/.
More information on supported modules is available at https://forge.puppetlabs.com/supported.
The current list of supported modules is available at https://forge.puppetlabs.com/modules?endorsements=supported.
There's more...
Modules on the Forge include a metadata.json
file, which describes the module and which operating systems the module supports. This file also includes a list of modules that are required by the module.
Note
This file was previously named Modulefile and not in JSON format; the old Modulefile format was deprecated in Version 3.6.
As we will see in our next section, when installing a module from the Forge, the required dependencies will automatically be installed as well.
Not all publically available modules are on Puppet Forge. Some other great places to look at on GitHub are:
Though not a collection of modules as such, the Puppet Cookbook website has many useful and illuminating code examples, patterns, and tips, maintained by the admirable Dean Wilson:
metadata.json
file, which
describes the module and which operating systems the module supports. This file also includes a list of modules that are required by the module.
Note
This file was previously named Modulefile and not in JSON format; the old Modulefile format was deprecated in Version 3.6.
As we will see in our next section, when installing a module from the Forge, the required dependencies will automatically be installed as well.
Not all publically available modules are on Puppet Forge. Some other great places to look at on GitHub are:
Though not a collection of modules as such, the Puppet Cookbook website has many useful and illuminating code examples, patterns, and tips, maintained by the admirable Dean Wilson:
Managing Apache servers
Apache is the world's favorite web server, so it's highly likely that part of your Puppetly duties will include installing and managing Apache.
How to do it...
We'll install and use the puppetlabs-apache
module to install and start Apache. This time, when we run puppet module install
, we'll use the -i
option to tell Puppet to install the module in our Git repository's module's directory.
- Install the module using
puppet modules install
:t@mylaptop ~/puppet $ puppet module install -i modules puppetlabs-apache Notice: Preparing to install into /home/thomas/puppet/modules ... Notice: Downloading from https://forgeapi.puppetlabs.com ... Notice: Installing -- do not interrupt ... /home/thomas/puppet/modules └─┬ puppetlabs-apache (v1.1.1) ├── puppetlabs-concat (v1.1.1) └── puppetlabs-stdlib (v4.3.2)
- Add the modules to your Git repository and push them out:
t@mylaptop ~/puppet $ git add modules/apache modules/concat modules/stdlib t@mylaptop ~/puppet $ git commit -m "adding puppetlabs-apache module" [production 395b079] adding puppetlabs-apache module 647 files changed, 35017 insertions(+), 13 deletions(-) rename modules/{apache => apache.cookbook}/manifests/init.pp (100%) create mode 100644 modules/apache/CHANGELOG.md create mode 100644 modules/apache/CONTRIBUTING.md ... t@mylaptop ~/puppet $ git push origin production Counting objects: 277, done. Delta compression using up to 4 threads. Compressing objects: 100% (248/248), done. Writing objects: 100% (266/266), 136.25 KiB | 0 bytes/s, done. Total 266 (delta 48), reused 0 (delta 0) remote: To puppet@puppet.example.com:/etc/puppet/environments/puppet.git remote: 9faaa16..395b079 production -> production
- Create a web server node definition in
site.pp
:node webserver { class {'apache': } }
- Run Puppet to apply the default Apache module configuration:
[root@webserver ~]# puppet agent -t Info: Caching certificate for webserver.example.com Notice: /File[/var/lib/puppet/lib/puppet/provider/a2mod]/ensure: created ... Info: Caching catalog for webserver.example.com ... Info: Class[Apache::Service]: Scheduling refresh of Service[httpd] Notice: /Stage[main]/Apache::Service/Service[httpd]: Triggered 'refresh' from 51 events Notice: Finished catalog run in 11.73 seconds
- Verify that you can reach
webserver.example.com
:[root@webserver ~]# curl http://webserver.example.com <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <html> <head> <title>Index of /</title> </head> <body> <h1>Index of /</h1> <table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr><tr><th colspan="5"><hr></th></tr> <tr><th colspan="5"><hr></th></tr> </table> </body></html>
How it works...
Installing the puppetlabs-Apache module from the Forge causes both puppetlabs-concat and puppetlabs-stdlib to be installed into our modules directory. The concat module is used to stitch snippets of files together in a specific order. It is used by the Apache module to create the main Apache configuration files.
We then defined a web server node and applied the Apache class to that node. We used all the default values and let the Apache module configure our server to be an Apache web server.
The Apache module then went and rewrote all our Apache configurations. By default, the module purges all the configuration files from the Apache directory (/etc/apache2
or /etc/httpd
depending on the distribution). The module can configure many different distributions and handle the nuances of each distribution. As a user of the module, you don't need to know how your distribution deals with the Apache module configuration.
After purging and rewriting the configuration files, the module ensures that the apache2 service is running (httpd
on Enterprise Linux (EL)).
We then tested the webserver using curl. There was nothing returned but an empty index page. This is the expected behavior. Normally, when we install Apache on a server, there are some files that display a default page (welcome.conf
on EL-based systems), since the module purged those configurations, we only see an empty page.
In a production environment, you would modify the defaults applied by the Apache module; the suggested configuration from the README is as follows:
class { 'apache':
default_mods => false,
default_confd_files => false,
}
puppetlabs-apache
module to install and start Apache. This time, when we run puppet module install
, we'll use the -i
option to tell Puppet to install the module in our Git repository's module's directory.
puppet modules install
:t@mylaptop ~/puppet $ puppet module install -i modules puppetlabs-apache Notice: Preparing to install into /home/thomas/puppet/modules ... Notice: Downloading from https://forgeapi.puppetlabs.com ... Notice: Installing -- do not interrupt ... /home/thomas/puppet/modules └─┬ puppetlabs-apache (v1.1.1) ├── puppetlabs-concat (v1.1.1) └── puppetlabs-stdlib (v4.3.2)
t@mylaptop ~/puppet $ git add modules/apache modules/concat modules/stdlib t@mylaptop ~/puppet $ git commit -m "adding puppetlabs-apache module" [production 395b079] adding puppetlabs-apache module 647 files changed, 35017 insertions(+), 13 deletions(-) rename modules/{apache => apache.cookbook}/manifests/init.pp (100%) create mode 100644 modules/apache/CHANGELOG.md create mode 100644 modules/apache/CONTRIBUTING.md ... t@mylaptop ~/puppet $ git push origin production Counting objects: 277, done. Delta compression using up to 4 threads. Compressing objects: 100% (248/248), done. Writing objects: 100% (266/266), 136.25 KiB | 0 bytes/s, done. Total 266 (delta 48), reused 0 (delta 0) remote: To puppet@puppet.example.com:/etc/puppet/environments/puppet.git remote: 9faaa16..395b079 production -> production
site.pp
:node webserver { class {'apache': } }
- the default Apache module configuration:
[root@webserver ~]# puppet agent -t Info: Caching certificate for webserver.example.com Notice: /File[/var/lib/puppet/lib/puppet/provider/a2mod]/ensure: created ... Info: Caching catalog for webserver.example.com ... Info: Class[Apache::Service]: Scheduling refresh of Service[httpd] Notice: /Stage[main]/Apache::Service/Service[httpd]: Triggered 'refresh' from 51 events Notice: Finished catalog run in 11.73 seconds
- Verify that you can reach
webserver.example.com
:[root@webserver ~]# curl http://webserver.example.com <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <html> <head> <title>Index of /</title> </head> <body> <h1>Index of /</h1> <table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr><tr><th colspan="5"><hr></th></tr> <tr><th colspan="5"><hr></th></tr> </table> </body></html>
How it works...
Installing the puppetlabs-Apache module from the Forge causes both puppetlabs-concat and puppetlabs-stdlib to be installed into our modules directory. The concat module is used to stitch snippets of files together in a specific order. It is used by the Apache module to create the main Apache configuration files.
We then defined a web server node and applied the Apache class to that node. We used all the default values and let the Apache module configure our server to be an Apache web server.
The Apache module then went and rewrote all our Apache configurations. By default, the module purges all the configuration files from the Apache directory (/etc/apache2
or /etc/httpd
depending on the distribution). The module can configure many different distributions and handle the nuances of each distribution. As a user of the module, you don't need to know how your distribution deals with the Apache module configuration.
After purging and rewriting the configuration files, the module ensures that the apache2 service is running (httpd
on Enterprise Linux (EL)).
We then tested the webserver using curl. There was nothing returned but an empty index page. This is the expected behavior. Normally, when we install Apache on a server, there are some files that display a default page (welcome.conf
on EL-based systems), since the module purged those configurations, we only see an empty page.
In a production environment, you would modify the defaults applied by the Apache module; the suggested configuration from the README is as follows:
class { 'apache':
default_mods => false,
default_confd_files => false,
}
puppetlabs-Apache module from the Forge causes both puppetlabs-concat and puppetlabs-stdlib to be installed into our modules directory. The concat module is used to stitch snippets of files together in a specific order. It is used by the Apache module to create the main Apache configuration files.
We then defined a web server node and applied the Apache class to that node. We used all the default values and let the Apache module configure our server to be an Apache web server.
The Apache module then went and rewrote all our Apache configurations. By default, the module purges all the configuration files from the Apache directory (/etc/apache2
or /etc/httpd
depending on the distribution). The module can configure many different distributions and handle the nuances of each distribution. As a user of the module, you don't need to know how your distribution deals with the Apache module configuration.
After purging and rewriting the configuration files, the module ensures that the apache2 service is running (httpd
on Enterprise Linux (EL)).
We then tested the webserver using curl. There was nothing returned but an empty index page. This is the expected behavior. Normally, when we install Apache on a server, there are some files that display a default page (welcome.conf
on EL-based systems), since the module purged those configurations, we only see an empty page.
In a production environment, you would modify the defaults applied by the Apache module; the suggested configuration from the README is as follows:
class { 'apache':
default_mods => false,
default_confd_files => false,
}
Creating Apache virtual hosts
Apache virtual hosts are created with the apache
module with the defined type apache::vhost
. We will create a new vhost on our Apache webserver called
navajo, one of the apache tribes.
How to do it...
Follow these steps to create Apache virtual hosts:
- Create a navajo
apache::vhost
definition as follows:apache::vhost { 'navajo.example.com': port => '80', docroot => '/var/www/navajo', }
- Create an index file for the new vhost:
file {'/var/www/navajo/index.html': content => "<html>\nnavajo.example.com\nhttp://en.wikipedia.org/wiki/Navajo_people\n</html>\n", mode => '0644', require => Apache::Vhost['navajo.example.com'] }
- Run Puppet to create the new vhost:
[root@webserver ~]# puppet agent -t Info: Caching catalog for webserver.example.com Info: Applying configuration version '1414475598' Notice: /Stage[main]/Main/Node[webserver]/Apache::Vhost[navajo.example.com]/File[/var/www/navajo]/ensure: created Notice: /Stage[main]/Main/Node[webserver]/Apache::Vhost[navajo.example.com]/File[25-navajo.example.com.conf]/ensure: created Info: /Stage[main]/Main/Node[webserver]/Apache::Vhost[navajo.example.com]/File[25-navajo.example.com.conf]: Scheduling refresh of Service[httpd] Notice: /Stage[main]/Main/Node[webserver]/File[/var/www/navajo/index.html]/ensure: defined content as '{md5}5212fe215f4c0223fb86102a34319cc6' Notice: /Stage[main]/Apache::Service/Service[httpd]: Triggered 'refresh' from 1 events Notice: Finished catalog run in 2.73 seconds
- Verify that you can reach the new virtual host:
[root@webserver ~]# curl http://navajo.example.com <html> navajo.example.com http://en.wikipedia.org/wiki/Navajo_people </html>
How it works...
The apache::vhost
defined type creates a virtual host configuration file for Apache, 25-navajo.example.com.conf
. The file is created with a template; 25
at the beginning of the filename is the "priority level" of this virtual host. When Apache first starts, it reads through its configuration directory and starts executing files in an alphabetical order. Files that begin with numbers are read before files that start with letters. In this way, the Apache module ensures that the virtual hosts are read in a specific order, which can be specified when you define the virtual host. The contents of this file are as follows:
# ************************************
# Vhost template in module puppetlabs-apache
# Managed by Puppet
# ************************************
<VirtualHost *:80>
ServerName navajo.example.com
## Vhost docroot
DocumentRoot "/var/www/navajo"
## Directories, there should at least be a declaration for /var/www/navajo
<Directory "/var/www/navajo">
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>
## Load additional static includes
## Logging
ErrorLog "/var/log/httpd/navajo.example.com_error.log"
ServerSignature Off
CustomLog "/var/log/httpd/navajo.example.com_access.log" combined
</VirtualHost>
As you can see, the default file has created log files, set up directory access permissions and options, in addition to specifying the listen port and DocumentRoot
.
The vhost definition creates the DocumentRoot
directory, specified as 'root' to the apache::virtual
definition. The directory is created before the virtual host configuration file; after that file has been created, a notify trigger is sent to the Apache process to restart.
Our manifest included a file that required the Apache::Vhost['navajo.example.com']
resource; our file was then created after the directory and the virtual host configuration file.
When we run curl on the new website (if you haven't created a hostname alias in DNS, you will have to create one in your local /etc/hosts
file for navajo.example.com
, or specify the host as curl -H 'Host: navajo.example.com' <ipaddress
of navajo.example.com>
), we see the contents of the index file we created:
file {'/var/www/navajo/index.html':
content => "<html>\nnavajo.example.com\nhttp://en.wikipedia.org/wiki/Navajo_people\n</html>\n",
mode => '0644',
require => Apache::Vhost['navajo.example.com']
}
[root@webserver ~]# curl http://navajo.example.com
<html>
navajo.example.com
http://en.wikipedia.org/wiki/Navajo_people
<\html>
There's more...
Both the defined type and the template take into account a multitude of possible configuration scenarios for virtual hosts. It is highly unlikely that you will find a setting that is not covered by this module. You should look at the definition for apache::virtual
and the sheer number of possible arguments.
The module also takes care of several settings for you. For instance, if we change the listen port on our navajo
virtual host from 80
to 8080
, the module will make the following changes in /etc/httpd/conf.d/ports.conf
:
Listen 80
+Listen 8080
NameVirtualHost *:80
+NameVirtualHost *:8080
And in our virtual host file:
-<VirtualHost *:80>
+<VirtualHost *:8080>
So that we can now curl on port 8080
and see the same results:
[root@webserver ~]# curl http://navajo.example.com:8080
<html>
navajo.example.com
http://en.wikipedia.org/wiki/Navajo_people
</html>
And when we try on port 80
:
[root@webserver ~]# curl http://navajo.example.com
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Index of /</title>
</head>
<body>
<h1>Index of /</h1>
<table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr><tr><th colspan="5"><hr></th></tr>
<tr><th colspan="5"><hr></th></tr>
</table>
</body>
</html>
As we can see, the virtual host is no longer listening on port 80
and we receive the default empty directory listing we saw in our earlier example.
apache::vhost
definition as follows:apache::vhost { 'navajo.example.com': port => '80', docroot => '/var/www/navajo', }
file {'/var/www/navajo/index.html': content => "<html>\nnavajo.example.com\nhttp://en.wikipedia.org/wiki/Navajo_people\n</html>\n", mode => '0644', require => Apache::Vhost['navajo.example.com'] }
[root@webserver ~]# puppet agent -t Info: Caching catalog for webserver.example.com Info: Applying configuration version '1414475598' Notice: /Stage[main]/Main/Node[webserver]/Apache::Vhost[navajo.example.com]/File[/var/www/navajo]/ensure: created Notice: /Stage[main]/Main/Node[webserver]/Apache::Vhost[navajo.example.com]/File[25-navajo.example.com.conf]/ensure: created Info: /Stage[main]/Main/Node[webserver]/Apache::Vhost[navajo.example.com]/File[25-navajo.example.com.conf]: Scheduling refresh of Service[httpd] Notice: /Stage[main]/Main/Node[webserver]/File[/var/www/navajo/index.html]/ensure: defined content as '{md5}5212fe215f4c0223fb86102a34319cc6' Notice: /Stage[main]/Apache::Service/Service[httpd]: Triggered 'refresh' from 1 events Notice: Finished catalog run in 2.73 seconds
[root@webserver ~]# curl http://navajo.example.com <html> navajo.example.com http://en.wikipedia.org/wiki/Navajo_people </html>
How it works...
The apache::vhost
defined type creates a virtual host configuration file for Apache, 25-navajo.example.com.conf
. The file is created with a template; 25
at the beginning of the filename is the "priority level" of this virtual host. When Apache first starts, it reads through its configuration directory and starts executing files in an alphabetical order. Files that begin with numbers are read before files that start with letters. In this way, the Apache module ensures that the virtual hosts are read in a specific order, which can be specified when you define the virtual host. The contents of this file are as follows:
# ************************************
# Vhost template in module puppetlabs-apache
# Managed by Puppet
# ************************************
<VirtualHost *:80>
ServerName navajo.example.com
## Vhost docroot
DocumentRoot "/var/www/navajo"
## Directories, there should at least be a declaration for /var/www/navajo
<Directory "/var/www/navajo">
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>
## Load additional static includes
## Logging
ErrorLog "/var/log/httpd/navajo.example.com_error.log"
ServerSignature Off
CustomLog "/var/log/httpd/navajo.example.com_access.log" combined
</VirtualHost>
As you can see, the default file has created log files, set up directory access permissions and options, in addition to specifying the listen port and DocumentRoot
.
The vhost definition creates the DocumentRoot
directory, specified as 'root' to the apache::virtual
definition. The directory is created before the virtual host configuration file; after that file has been created, a notify trigger is sent to the Apache process to restart.
Our manifest included a file that required the Apache::Vhost['navajo.example.com']
resource; our file was then created after the directory and the virtual host configuration file.
When we run curl on the new website (if you haven't created a hostname alias in DNS, you will have to create one in your local /etc/hosts
file for navajo.example.com
, or specify the host as curl -H 'Host: navajo.example.com' <ipaddress
of navajo.example.com>
), we see the contents of the index file we created:
file {'/var/www/navajo/index.html':
content => "<html>\nnavajo.example.com\nhttp://en.wikipedia.org/wiki/Navajo_people\n</html>\n",
mode => '0644',
require => Apache::Vhost['navajo.example.com']
}
[root@webserver ~]# curl http://navajo.example.com
<html>
navajo.example.com
http://en.wikipedia.org/wiki/Navajo_people
<\html>
There's more...
Both the defined type and the template take into account a multitude of possible configuration scenarios for virtual hosts. It is highly unlikely that you will find a setting that is not covered by this module. You should look at the definition for apache::virtual
and the sheer number of possible arguments.
The module also takes care of several settings for you. For instance, if we change the listen port on our navajo
virtual host from 80
to 8080
, the module will make the following changes in /etc/httpd/conf.d/ports.conf
:
Listen 80
+Listen 8080
NameVirtualHost *:80
+NameVirtualHost *:8080
And in our virtual host file:
-<VirtualHost *:80>
+<VirtualHost *:8080>
So that we can now curl on port 8080
and see the same results:
[root@webserver ~]# curl http://navajo.example.com:8080
<html>
navajo.example.com
http://en.wikipedia.org/wiki/Navajo_people
</html>
And when we try on port 80
:
[root@webserver ~]# curl http://navajo.example.com
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Index of /</title>
</head>
<body>
<h1>Index of /</h1>
<table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr><tr><th colspan="5"><hr></th></tr>
<tr><th colspan="5"><hr></th></tr>
</table>
</body>
</html>
As we can see, the virtual host is no longer listening on port 80
and we receive the default empty directory listing we saw in our earlier example.
apache::vhost
defined type
creates a virtual host configuration file for Apache, 25-navajo.example.com.conf
. The file is created with a template; 25
at the beginning of the filename is the "priority level" of this virtual host. When Apache first starts, it reads through its configuration directory and starts executing files in an alphabetical order. Files that begin with numbers are read before files that start with letters. In this way, the Apache module ensures that the virtual hosts are read in a specific order, which can be specified when you define the virtual host. The contents of this file are as follows:
# ************************************
# Vhost template in module puppetlabs-apache
# Managed by Puppet
# ************************************
<VirtualHost *:80>
ServerName navajo.example.com
## Vhost docroot
DocumentRoot "/var/www/navajo"
## Directories, there should at least be a declaration for /var/www/navajo
<Directory "/var/www/navajo">
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>
## Load additional static includes
## Logging
ErrorLog "/var/log/httpd/navajo.example.com_error.log"
ServerSignature Off
CustomLog "/var/log/httpd/navajo.example.com_access.log" combined
</VirtualHost>
As you can see, the default file has created log files, set up directory access permissions and options, in addition to specifying the listen port and DocumentRoot
.
The vhost definition creates the DocumentRoot
directory, specified as 'root' to the apache::virtual
definition. The directory is created before the virtual host configuration file; after that file has been created, a notify trigger is sent to the Apache process to restart.
Our manifest included a file that required the Apache::Vhost['navajo.example.com']
resource; our file was then created after the directory and the virtual host configuration file.
When we run curl on the new website (if you haven't created a hostname alias in DNS, you will have to create one in your local /etc/hosts
file for navajo.example.com
, or specify the host as curl -H 'Host: navajo.example.com' <ipaddress
of navajo.example.com>
), we see the contents of the index file we created:
file {'/var/www/navajo/index.html':
content => "<html>\nnavajo.example.com\nhttp://en.wikipedia.org/wiki/Navajo_people\n</html>\n",
mode => '0644',
require => Apache::Vhost['navajo.example.com']
}
[root@webserver ~]# curl http://navajo.example.com
<html>
navajo.example.com
http://en.wikipedia.org/wiki/Navajo_people
<\html>
There's more...
Both the defined type and the template take into account a multitude of possible configuration scenarios for virtual hosts. It is highly unlikely that you will find a setting that is not covered by this module. You should look at the definition for apache::virtual
and the sheer number of possible arguments.
The module also takes care of several settings for you. For instance, if we change the listen port on our navajo
virtual host from 80
to 8080
, the module will make the following changes in /etc/httpd/conf.d/ports.conf
:
Listen 80
+Listen 8080
NameVirtualHost *:80
+NameVirtualHost *:8080
And in our virtual host file:
-<VirtualHost *:80>
+<VirtualHost *:8080>
So that we can now curl on port 8080
and see the same results:
[root@webserver ~]# curl http://navajo.example.com:8080
<html>
navajo.example.com
http://en.wikipedia.org/wiki/Navajo_people
</html>
And when we try on port 80
:
[root@webserver ~]# curl http://navajo.example.com
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Index of /</title>
</head>
<body>
<h1>Index of /</h1>
<table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr><tr><th colspan="5"><hr></th></tr>
<tr><th colspan="5"><hr></th></tr>
</table>
</body>
</html>
As we can see, the virtual host is no longer listening on port 80
and we receive the default empty directory listing we saw in our earlier example.
apache::virtual
and the sheer number of possible arguments.
also takes care of several settings for you. For instance, if we change the listen port on our navajo
virtual host from 80
to 8080
, the module will make the following changes in /etc/httpd/conf.d/ports.conf
:
Listen 80
+Listen 8080
NameVirtualHost *:80
+NameVirtualHost *:8080
And in our virtual host file:
-<VirtualHost *:80>
+<VirtualHost *:8080>
So that we can now curl on port 8080
and see the same results:
[root@webserver ~]# curl http://navajo.example.com:8080
<html>
navajo.example.com
http://en.wikipedia.org/wiki/Navajo_people
</html>
And when we try on port 80
:
[root@webserver ~]# curl http://navajo.example.com
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Index of /</title>
</head>
<body>
<h1>Index of /</h1>
<table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr><tr><th colspan="5"><hr></th></tr>
<tr><th colspan="5"><hr></th></tr>
</table>
</body>
</html>
As we can see, the virtual host is no longer listening on port 80
and we receive the default empty directory listing we saw in our earlier example.
Creating nginx virtual hosts
Nginx is a fast, lightweight web server that is preferred over Apache in many contexts, especially where high performance is important. Nginx is configured slightly differently than Apache; like Apache though, there is a Forge module that can be used to configure nginx for us. Unlike Apache, however, the module that is suggested for use is not supplied by puppetlabs but by James Fryman. This module uses some interesting tricks to configure itself. Previous versions of this module used R.I. Pienaar's module_data
package. This package is used to configure hieradata within a module. It's used to supply default values to the nginx module. I wouldn't recommend starting out with this module at this point, but it is a good example of where module configuration may be headed in the future. Giving modules the ability to modify hieradata may prove useful.
How to do it...
In this example, we'll use a Forge module to configure nginx. We'll download the module and use it to configure virtualhosts.
- Download the
jfryman-nginx
module from the Forge:t@mylaptop ~ $ cd ~/puppet t@mylaptop ~/puppet $ puppet module install -i modules jfryman-nginx Notice: Preparing to install into /home/thomas/puppet/modules ... Notice: Downloading from https://forgeapi.puppetlabs.com ... Notice: Installing -- do not interrupt ... /home/thomas/puppet/modules └─┬ jfryman-nginx (v0.2.1) ├── puppetlabs-apt (v1.7.0) ├── puppetlabs-concat (v1.1.1) └── puppetlabs-stdlib (v4.3.2)
- Replace the definition for webserver with an nginx configuration:
node webserver { class {'nginx':} nginx::resource::vhost { 'mescalero.example.com': www_root => '/var/www/mescalero', } file {'/var/www/mescalero': ensure => 'directory''directory', mode => '0755', require => Nginx::Resource::Vhost['mescalero.example.com'], } file {'/var/www/mescalero/index.html': content => "<html>\nmescalero.example.com\nhttp://en.wikipedia.org/wiki/Mescalero\n</html>\n", mode => 0644, require => File['/var/www/mescalero'], } }
- If apache is still running on your webserver, stop it:
[root@webserver ~]# puppet resource service httpd ensure=false Notice: /Service[httpd]/ensure: ensure changed 'running' to 'stopped' service { 'httpd': ensure => 'stopped', } Run puppet agent on your webserver node: [root@webserver ~]# puppet agent -t Info: Caching catalog for webserver.example.com Info: Applying configuration version '1414561483' Notice: /Stage[main]/Main/Node[webserver]/Nginx::Resource::Vhost[mescalero.example.com]/Concat[/etc/nginx/sites-available/mescalero.example.com.conf]/File[/etc/nginx/sites-available/mescalero.example.com.conf]/ensure: defined content as '{md5}35bb59bfcd0cf5a549d152aaec284357' Info: /Stage[main]/Main/Node[webserver]/Nginx::Resource::Vhost[mescalero.example.com]/Concat[/etc/nginx/sites-available/mescalero.example.com.conf]/File[/etc/nginx/sites-available/mescalero.example.com.conf]: Scheduling refresh of Class[Nginx::Service] Info: Concat[/etc/nginx/sites-available/mescalero.example.com.conf]: Scheduling refresh of Class[Nginx::Service] Notice: /Stage[main]/Main/Node[webserver]/Nginx::Resource::Vhost[mescalero.example.com]/File[mescalero.example.com.conf symlink]/ensure: created Info: /Stage[main]/Main/Node[webserver]/Nginx::Resource::Vhost[mescalero.example.com]/File[mescalero.example.com.conf symlink]: Scheduling refresh of Service[nginx] Notice: /Stage[main]/Main/Node[webserver]/File[/var/www/mescalero]/ensure: created Notice: /Stage[main]/Main/Node[webserver]/File[/var/www/mescalero/index.html]/ensure: defined content as '{md5}2bd618c7dc3a3addc9e27c2f3cfde294' Notice: /Stage[main]/Nginx::Config/File[/etc/nginx/conf.d/proxy.conf]/ensure: defined content as '{md5}1919fd65635d49653273e14028888617' Info: Computing checksum on file /etc/nginx/conf.d/example_ssl.conf Info: /Stage[main]/Nginx::Config/File[/etc/nginx/conf.d/example_ssl.conf]: Filebucketed /etc/nginx/conf.d/example_ssl.conf to puppet with sum 84724f296c7056157d531d6b1215b507 Notice: /Stage[main]/Nginx::Config/File[/etc/nginx/conf.d/example_ssl.conf]/ensure: removed Info: Computing checksum on file /etc/nginx/conf.d/default.conf Info: /Stage[main]/Nginx::Config/File[/etc/nginx/conf.d/default.conf]: Filebucketed /etc/nginx/conf.d/default.conf to puppet with sum 4dce452bf8dbb01f278ec0ea9ba6cf40 Notice: /Stage[main]/Nginx::Config/File[/etc/nginx/conf.d/default.conf]/ensure: removed Info: Class[Nginx::Config]: Scheduling refresh of Class[Nginx::Service] Info: Class[Nginx::Service]: Scheduling refresh of Service[nginx] Notice: /Stage[main]/Nginx::Service/Service[nginx]: Triggered 'refresh' from 2 events Notice: Finished catalog run in 28.98 seconds
- Verify that you can reach the new virtualhost:
[root@webserver ~]# curl mescalero.example.com <html> mescalero.example.com http://en.wikipedia.org/wiki/Mescalero </html>
How it works...
Installing the jfryman-nginx
module causes the concat, stdlib, and APT modules to be installed. We run Puppet on our master to have the plugins created by these modules added to our running master. The stdlib and concat have facter and Puppet plugins that need to be installed for the nginx module to work properly.
With the plugins synchronized, we can then run puppet agent on our web server. As a precaution, we stop Apache if it was previously started (we can't have nginx and Apache both listening on port 80
). After puppet agent runs, we verified that nginx was running and the virtual host was configured.
There's more...
This nginx module is under active development. There are several interesting solutions employed with the module. Previous releases used the ripienaar-module_data
module, which allows a module to include default values for its various attributes, via a hiera plugin. Although still in an early stage of development, this system is already usable and represents one of the cutting-edge modules on the Forge.
In the next section, we'll use a supported module to configure and manage MySQL installations.
jfryman-nginx
module from the Forge:t@mylaptop ~ $ cd ~/puppet t@mylaptop ~/puppet $ puppet module install -i modules jfryman-nginx Notice: Preparing to install into /home/thomas/puppet/modules ... Notice: Downloading from https://forgeapi.puppetlabs.com ... Notice: Installing -- do not interrupt ... /home/thomas/puppet/modules └─┬ jfryman-nginx (v0.2.1) ├── puppetlabs-apt (v1.7.0) ├── puppetlabs-concat (v1.1.1) └── puppetlabs-stdlib (v4.3.2)
node webserver { class {'nginx':} nginx::resource::vhost { 'mescalero.example.com': www_root => '/var/www/mescalero', } file {'/var/www/mescalero': ensure => 'directory''directory', mode => '0755', require => Nginx::Resource::Vhost['mescalero.example.com'], } file {'/var/www/mescalero/index.html': content => "<html>\nmescalero.example.com\nhttp://en.wikipedia.org/wiki/Mescalero\n</html>\n", mode => 0644, require => File['/var/www/mescalero'], } }
- running on your webserver, stop it:
[root@webserver ~]# puppet resource service httpd ensure=false Notice: /Service[httpd]/ensure: ensure changed 'running' to 'stopped' service { 'httpd': ensure => 'stopped', } Run puppet agent on your webserver node: [root@webserver ~]# puppet agent -t Info: Caching catalog for webserver.example.com Info: Applying configuration version '1414561483' Notice: /Stage[main]/Main/Node[webserver]/Nginx::Resource::Vhost[mescalero.example.com]/Concat[/etc/nginx/sites-available/mescalero.example.com.conf]/File[/etc/nginx/sites-available/mescalero.example.com.conf]/ensure: defined content as '{md5}35bb59bfcd0cf5a549d152aaec284357' Info: /Stage[main]/Main/Node[webserver]/Nginx::Resource::Vhost[mescalero.example.com]/Concat[/etc/nginx/sites-available/mescalero.example.com.conf]/File[/etc/nginx/sites-available/mescalero.example.com.conf]: Scheduling refresh of Class[Nginx::Service] Info: Concat[/etc/nginx/sites-available/mescalero.example.com.conf]: Scheduling refresh of Class[Nginx::Service] Notice: /Stage[main]/Main/Node[webserver]/Nginx::Resource::Vhost[mescalero.example.com]/File[mescalero.example.com.conf symlink]/ensure: created Info: /Stage[main]/Main/Node[webserver]/Nginx::Resource::Vhost[mescalero.example.com]/File[mescalero.example.com.conf symlink]: Scheduling refresh of Service[nginx] Notice: /Stage[main]/Main/Node[webserver]/File[/var/www/mescalero]/ensure: created Notice: /Stage[main]/Main/Node[webserver]/File[/var/www/mescalero/index.html]/ensure: defined content as '{md5}2bd618c7dc3a3addc9e27c2f3cfde294' Notice: /Stage[main]/Nginx::Config/File[/etc/nginx/conf.d/proxy.conf]/ensure: defined content as '{md5}1919fd65635d49653273e14028888617' Info: Computing checksum on file /etc/nginx/conf.d/example_ssl.conf Info: /Stage[main]/Nginx::Config/File[/etc/nginx/conf.d/example_ssl.conf]: Filebucketed /etc/nginx/conf.d/example_ssl.conf to puppet with sum 84724f296c7056157d531d6b1215b507 Notice: /Stage[main]/Nginx::Config/File[/etc/nginx/conf.d/example_ssl.conf]/ensure: removed Info: Computing checksum on file /etc/nginx/conf.d/default.conf Info: /Stage[main]/Nginx::Config/File[/etc/nginx/conf.d/default.conf]: Filebucketed /etc/nginx/conf.d/default.conf to puppet with sum 4dce452bf8dbb01f278ec0ea9ba6cf40 Notice: /Stage[main]/Nginx::Config/File[/etc/nginx/conf.d/default.conf]/ensure: removed Info: Class[Nginx::Config]: Scheduling refresh of Class[Nginx::Service] Info: Class[Nginx::Service]: Scheduling refresh of Service[nginx] Notice: /Stage[main]/Nginx::Service/Service[nginx]: Triggered 'refresh' from 2 events Notice: Finished catalog run in 28.98 seconds
- Verify that you can reach the new virtualhost:
[root@webserver ~]# curl mescalero.example.com <html> mescalero.example.com http://en.wikipedia.org/wiki/Mescalero </html>
How it works...
Installing the jfryman-nginx
module causes the concat, stdlib, and APT modules to be installed. We run Puppet on our master to have the plugins created by these modules added to our running master. The stdlib and concat have facter and Puppet plugins that need to be installed for the nginx module to work properly.
With the plugins synchronized, we can then run puppet agent on our web server. As a precaution, we stop Apache if it was previously started (we can't have nginx and Apache both listening on port 80
). After puppet agent runs, we verified that nginx was running and the virtual host was configured.
There's more...
This nginx module is under active development. There are several interesting solutions employed with the module. Previous releases used the ripienaar-module_data
module, which allows a module to include default values for its various attributes, via a hiera plugin. Although still in an early stage of development, this system is already usable and represents one of the cutting-edge modules on the Forge.
In the next section, we'll use a supported module to configure and manage MySQL installations.
the jfryman-nginx
module causes the concat, stdlib, and APT modules to be installed. We run Puppet on our master to have the plugins created by these modules added to our running master. The stdlib and concat have facter and Puppet plugins that need to be installed for the nginx module to work properly.
With the plugins synchronized, we can then run puppet agent on our web server. As a precaution, we stop Apache if it was previously started (we can't have nginx and Apache both listening on port 80
). After puppet agent runs, we verified that nginx was running and the virtual host was configured.
There's more...
This nginx module is under active development. There are several interesting solutions employed with the module. Previous releases used the ripienaar-module_data
module, which allows a module to include default values for its various attributes, via a hiera plugin. Although still in an early stage of development, this system is already usable and represents one of the cutting-edge modules on the Forge.
In the next section, we'll use a supported module to configure and manage MySQL installations.
ripienaar-module_data
module, which allows a
module to include default values for its various attributes, via a hiera plugin. Although still in an early stage of development, this system is already usable and represents one of the cutting-edge modules on the Forge.
In the next section, we'll use a supported module to configure and manage MySQL installations.
Managing MySQL
MySQL is a very widely used database server, and it's fairly certain you'll need to install and configure a MySQL server at some point. The puppetlabs-mysql
module can simplify your MySQL deployments.
How to do it...
Follow these steps to create the example:
- Install the
puppetlabs-mysql
module:t@mylaptop ~/puppet $ puppet module install -i modules puppetlabs-mysql Notice: Preparing to install into /home/thomas/puppet/modules ... Notice: Downloading from https://forgeapi.puppetlabs.com ... Notice: Installing -- do not interrupt ... /home/thomas/puppet/modules └─┬ puppetlabs-mysql (v2.3.1) └── puppetlabs-stdlib (v4.3.2)
- Create a new node definition for your MySQL server:
node dbserver { class { '::mysql::server': root_password => 'PacktPub', override_options => { 'mysqld' => { 'max_connections' => '1024' } } } }
- Run Puppet to install the database server and apply the new root password:
[root@dbserver ~]# puppet agent -t Info: Caching catalog for dbserver.example.com Info: Applying configuration version '1414566216' Notice: /Stage[main]/Mysql::Server::Install/Package[mysql-server]/ensure: created Notice: /Stage[main]/Mysql::Server::Service/Service[mysqld]/ensure: ensure changed 'stopped' to 'running' Info: /Stage[main]/Mysql::Server::Service/Service[mysqld]: Unscheduling refresh on Service[mysqld] Notice: /Stage[main]/Mysql::Server::Root_password/Mysql_user[root@localhost]/password_hash: defined 'password_hash' as '*6ABB0D4A7D1381BAEE4D078354557D495ACFC059' Notice: /Stage[main]/Mysql::Server::Root_password/File[/root/.my.cnf]/ensure: defined content as '{md5}87bc129b137c9d613e9f31c80ea5426c' Notice: Finished catalog run in 35.50 seconds
- Verify that you can connect to the database:
[root@dbserver ~]# mysql Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 11 Server version: 5.1.73 Source distribution Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
How it works...
The MySQL module installs the MySQL server and ensures that the server is running. It then configures the root password for MySQL. The module does a lot of other things for you as well. It creates a .my.cnf
file with the root user password. When we run the mysql
client, the .my.cnf
file sets all the defaults, so we do not need to supply any arguments.
There's more...
In the next section, we'll show how to create databases and users.
to create the example:
- Install the
puppetlabs-mysql
module:t@mylaptop ~/puppet $ puppet module install -i modules puppetlabs-mysql Notice: Preparing to install into /home/thomas/puppet/modules ... Notice: Downloading from https://forgeapi.puppetlabs.com ... Notice: Installing -- do not interrupt ... /home/thomas/puppet/modules └─┬ puppetlabs-mysql (v2.3.1) └── puppetlabs-stdlib (v4.3.2)
- Create a new node definition for your MySQL server:
node dbserver { class { '::mysql::server': root_password => 'PacktPub', override_options => { 'mysqld' => { 'max_connections' => '1024' } } } }
- Run Puppet to install the database server and apply the new root password:
[root@dbserver ~]# puppet agent -t Info: Caching catalog for dbserver.example.com Info: Applying configuration version '1414566216' Notice: /Stage[main]/Mysql::Server::Install/Package[mysql-server]/ensure: created Notice: /Stage[main]/Mysql::Server::Service/Service[mysqld]/ensure: ensure changed 'stopped' to 'running' Info: /Stage[main]/Mysql::Server::Service/Service[mysqld]: Unscheduling refresh on Service[mysqld] Notice: /Stage[main]/Mysql::Server::Root_password/Mysql_user[root@localhost]/password_hash: defined 'password_hash' as '*6ABB0D4A7D1381BAEE4D078354557D495ACFC059' Notice: /Stage[main]/Mysql::Server::Root_password/File[/root/.my.cnf]/ensure: defined content as '{md5}87bc129b137c9d613e9f31c80ea5426c' Notice: Finished catalog run in 35.50 seconds
- Verify that you can connect to the database:
[root@dbserver ~]# mysql Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 11 Server version: 5.1.73 Source distribution Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
How it works...
The MySQL module installs the MySQL server and ensures that the server is running. It then configures the root password for MySQL. The module does a lot of other things for you as well. It creates a .my.cnf
file with the root user password. When we run the mysql
client, the .my.cnf
file sets all the defaults, so we do not need to supply any arguments.
There's more...
In the next section, we'll show how to create databases and users.
module installs the MySQL server and ensures that the server is running. It then configures the root password for MySQL. The module does a lot of other things for you as well. It creates a .my.cnf
file with the root user password. When we run the mysql
client, the .my.cnf
file sets all the defaults, so we do not need to supply any arguments.
There's more...
In the next section, we'll show how to create databases and users.
Creating databases and users
Managing a database means more than ensuring that the service is running; a database server is nothing without databases. Databases need users and privileges. Privileges are handled with GRANT
statements. We will use the puppetlabs-mysql
package to create a database and a user with access to that database. We'll create a MySQL user Drupal and a database called Drupal. We'll create a table named nodes and place data into that table.
How to do it...
Follow these steps to create databases and users:
- Create a database definition within your
dbserver
class:mysql::db { 'drupal': host => 'localhost', user => 'drupal', password => 'Cookbook', sql => '/root/drupal.sql', require => File['/root/drupal.sql'] } file { '/root/drupal.sql': ensure => present, source => 'puppet:///modules/mysql/drupal.sql', }
- Allow the Drupal user to modify the nodes table:
mysql_grant { 'drupal@localhost/drupal.nodes': ensure => 'present', options => ['GRANT'], privileges => ['ALL'], table => 'drupal.nodes'nodes', user => 'drupal@localhost', }
- Create the
drupal.sql
file with the following contents:CREATE TABLE users (id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255), body TEXT); INSERT INTO users (id, title, body) VALUES (1,'First Node','Contents of first Node'); INSERT INTO users (id, title, body) VALUES (2,'Second Node','Contents of second Node');
- Run Puppet to have user, database, and
GRANT
created:[root@dbserver ~]# puppet agent -t Info: Caching catalog for dbserver.example.com Info: Applying configuration version '1414648818' Notice: /Stage[main]/Main/Node[dbserver]/File[/root/drupal.sql]/ensure: defined content as '{md5}780f3946cfc0f373c6d4146394650f6b' Notice: /Stage[main]/Main/Node[dbserver]/Mysql_grant[drupal@localhost/drupal.nodes]/ensure: created Notice: /Stage[main]/Main/Node[dbserver]/Mysql::Db[drupal]/Mysql_user[drupal@localhost]/ensure: created Notice: /Stage[main]/Main/Node[dbserver]/Mysql::Db[drupal]/Mysql_database[drupal]/ensure: created Info: /Stage[main]/Main/Node[dbserver]/Mysql::Db[drupal]/Mysql_database[drupal]: Scheduling refresh of Exec[drupal-import] Notice: /Stage[main]/Main/Node[dbserver]/Mysql::Db[drupal]/Mysql_grant[drupal@localhost/drupal.*]/ensure: created Notice: /Stage[main]/Main/Node[dbserver]/Mysql::Db[drupal]/Exec[drupal-import]: Triggered 'refresh' from 1 events Notice: Finished catalog run in 10.06 seconds
- Verify that the database and table have been created:
[root@dbserver ~]# mysql drupal Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 34 Server version: 5.1.73 Source distribution Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show tables; +------------------+ | Tables_in_drupal | +------------------+ | users | +------------------+ 1 row in set (0.00 sec)
- Now, verify that our default data has been loaded into the table:
mysql> select * from users; +----+-------------+-------------------------+ | id | title | body | +----+-------------+-------------------------+ | 1 | First Node | Contents of first Node | | 2 | Second Node | Contents of second Node | +----+-------------+-------------------------+ 2 rows in set (0.00 sec)
How it works...
We start with the definition of the new drupal database:
mysql::db { 'drupal':
host => 'localhost',
user => 'drupal',
password => 'Cookbook',
sql => '/root/drupal.sql',
require => File['/root/drupal.sql']
}
We specify that we'll connect from localhost (we could connect to the database from another server) using the drupal user. We give the password for the user and specify a SQL file that will be applied to the database after the database has been created. We require that this file already exist and define the file next:
file { '/root/drupal.sql':
ensure => present,
source => 'puppet:///modules/mysql/drupal.sql',
}
We then ensure that the user has the appropriate privileges with a mysql_grant
statement:
mysql_grant { 'drupal@localhost/drupal.nodes':
ensure => 'present',
options => ['GRANT'],
privileges => ['ALL'],
table => 'drupal.nodes',
user => 'drupal@localhost',
}
There's more...
Using the puppetlabs-MySQL and puppetlabs-Apache module, we can create an entire functioning web server. The puppetlabs-Apache module will install Apache, and we can include the PHP module and MySQL module as well. We can then use the puppetlabs-Mysql module to install the MySQL server, and then create the required drupal databases and seed the database with the data.
Deploying a new drupal installation would be as simple as including a class on a node.
dbserver
class:mysql::db { 'drupal': host => 'localhost', user => 'drupal', password => 'Cookbook', sql => '/root/drupal.sql', require => File['/root/drupal.sql'] } file { '/root/drupal.sql': ensure => present, source => 'puppet:///modules/mysql/drupal.sql', }
mysql_grant { 'drupal@localhost/drupal.nodes': ensure => 'present', options => ['GRANT'], privileges => ['ALL'], table => 'drupal.nodes'nodes', user => 'drupal@localhost', }
- the
drupal.sql
file with the following contents:CREATE TABLE users (id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255), body TEXT); INSERT INTO users (id, title, body) VALUES (1,'First Node','Contents of first Node'); INSERT INTO users (id, title, body) VALUES (2,'Second Node','Contents of second Node');
- Run Puppet to have user, database, and
GRANT
created:[root@dbserver ~]# puppet agent -t Info: Caching catalog for dbserver.example.com Info: Applying configuration version '1414648818' Notice: /Stage[main]/Main/Node[dbserver]/File[/root/drupal.sql]/ensure: defined content as '{md5}780f3946cfc0f373c6d4146394650f6b' Notice: /Stage[main]/Main/Node[dbserver]/Mysql_grant[drupal@localhost/drupal.nodes]/ensure: created Notice: /Stage[main]/Main/Node[dbserver]/Mysql::Db[drupal]/Mysql_user[drupal@localhost]/ensure: created Notice: /Stage[main]/Main/Node[dbserver]/Mysql::Db[drupal]/Mysql_database[drupal]/ensure: created Info: /Stage[main]/Main/Node[dbserver]/Mysql::Db[drupal]/Mysql_database[drupal]: Scheduling refresh of Exec[drupal-import] Notice: /Stage[main]/Main/Node[dbserver]/Mysql::Db[drupal]/Mysql_grant[drupal@localhost/drupal.*]/ensure: created Notice: /Stage[main]/Main/Node[dbserver]/Mysql::Db[drupal]/Exec[drupal-import]: Triggered 'refresh' from 1 events Notice: Finished catalog run in 10.06 seconds
- Verify that the database and table have been created:
[root@dbserver ~]# mysql drupal Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 34 Server version: 5.1.73 Source distribution Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show tables; +------------------+ | Tables_in_drupal | +------------------+ | users | +------------------+ 1 row in set (0.00 sec)
- Now, verify that our default data has been loaded into the table:
mysql> select * from users; +----+-------------+-------------------------+ | id | title | body | +----+-------------+-------------------------+ | 1 | First Node | Contents of first Node | | 2 | Second Node | Contents of second Node | +----+-------------+-------------------------+ 2 rows in set (0.00 sec)
How it works...
We start with the definition of the new drupal database:
mysql::db { 'drupal':
host => 'localhost',
user => 'drupal',
password => 'Cookbook',
sql => '/root/drupal.sql',
require => File['/root/drupal.sql']
}
We specify that we'll connect from localhost (we could connect to the database from another server) using the drupal user. We give the password for the user and specify a SQL file that will be applied to the database after the database has been created. We require that this file already exist and define the file next:
file { '/root/drupal.sql':
ensure => present,
source => 'puppet:///modules/mysql/drupal.sql',
}
We then ensure that the user has the appropriate privileges with a mysql_grant
statement:
mysql_grant { 'drupal@localhost/drupal.nodes':
ensure => 'present',
options => ['GRANT'],
privileges => ['ALL'],
table => 'drupal.nodes',
user => 'drupal@localhost',
}
There's more...
Using the puppetlabs-MySQL and puppetlabs-Apache module, we can create an entire functioning web server. The puppetlabs-Apache module will install Apache, and we can include the PHP module and MySQL module as well. We can then use the puppetlabs-Mysql module to install the MySQL server, and then create the required drupal databases and seed the database with the data.
Deploying a new drupal installation would be as simple as including a class on a node.
definition of the new drupal database:
mysql::db { 'drupal':
host => 'localhost',
user => 'drupal',
password => 'Cookbook',
sql => '/root/drupal.sql',
require => File['/root/drupal.sql']
}
We specify that we'll connect from localhost (we could connect to the database from another server) using the drupal user. We give the password for the user and specify a SQL file that will be applied to the database after the database has been created. We require that this file already exist and define the file next:
file { '/root/drupal.sql':
ensure => present,
source => 'puppet:///modules/mysql/drupal.sql',
}
We then ensure that the user has the appropriate privileges with a mysql_grant
statement:
mysql_grant { 'drupal@localhost/drupal.nodes':
ensure => 'present',
options => ['GRANT'],
privileges => ['ALL'],
table => 'drupal.nodes',
user => 'drupal@localhost',
}
There's more...
Using the puppetlabs-MySQL and puppetlabs-Apache module, we can create an entire functioning web server. The puppetlabs-Apache module will install Apache, and we can include the PHP module and MySQL module as well. We can then use the puppetlabs-Mysql module to install the MySQL server, and then create the required drupal databases and seed the database with the data.
Deploying a new drupal installation would be as simple as including a class on a node.
and puppetlabs-Apache module, we can create an entire functioning web server. The puppetlabs-Apache module will install Apache, and we can include the PHP module and MySQL module as well. We can then use the puppetlabs-Mysql module to install the MySQL server, and then create the required drupal databases and seed the database with the data.
Deploying a new drupal installation would be as simple as including a class on a node.