Recipe for Apache2 on Debian (Etch)
This recipe helps you manage sites and modules for apache2 in a default Debian (Etch) installation. It might work on other systems too! Be sure to edit the wiki if it works for your non-Debian setup.
Problem
When you run several loadbalanced webservers with a lot of websites, it can become quite difficult to maintain all the sites and modules that are needed. This recipe creates a few components that help you better maintain all this. It should work even better if you incorporate this with templates for the actual site configs.
Solution
Use these components in your webserver class:
$apache2_sites = "/etc/apache2/sites"
$apache2_mods = "/etc/apache2/mods"
class apache2 {
# Define an apache2 site. Place all site configs into
# /etc/apache2/sites-available and en-/disable them with this type.
#
# You can add a custom require (string) if the site depends on packages
# that aren't part of the default apache2 package. Because of the
# package dependencies, apache2 will automagically be included.
define site ( $ensure = 'present', $require = 'apache2' ) {
case $ensure {
'present' : {
exec { "/usr/sbin/a2ensite $name":
unless => "/bin/sh -c '[ -L ${apache2_sites}-enabled/$name ] \
&& [ ${apache2_sites}-enabled/$name -ef ${apache2_sites}-available/$name ]'",
notify => Exec["reload-apache2"],
require => Package[$require],
}
}
'absent' : {
exec { "/usr/sbin/a2dissite $name":
onlyif => "/bin/sh -c '[ -L ${apache2_sites}-enabled/$name ] \
&& [ ${apache2_sites}-enabled/$name -ef ${apache2_sites}-available/$name ]'",
notify => Exec["reload-apache2"],
require => Package["apache2"],
}
}
default: { err ( "Unknown ensure value: '$ensure'" ) }
}
}
# Define an apache2 module. Debian packages place the module config
# into /etc/apache2/mods-available.
#
# You can add a custom require (string) if the module depends on
# packages that aren't part of the default apache2 package. Because of
# the package dependencies, apache2 will automagically be included.
define module ( $ensure = 'present', $require = 'apache2' ) {
case $ensure {
'present' : {
exec { "/usr/sbin/a2enmod $name":
unless => "/bin/sh -c '[ -L ${apache2_mods}-enabled/${name}.load ] \
&& [ ${apache2_mods}-enabled/${name}.load -ef ${apache2_mods}-available/${name}.load ]'",
notify => Exec["force-reload-apache2"],
require => Package[$require],
}
}
'absent': {
exec { "/usr/sbin/a2dismod $name":
onlyif => "/bin/sh -c '[ -L ${apache2_mods}-enabled/${name}.load ] \
&& [ ${apache2_mods}-enabled/${name}.load -ef ${apache2_mods}-available/${name}.load ]'",
notify => Exec["force-reload-apache2"],
require => Package["apache2"],
}
}
default: { err ( "Unknown ensure value: '$ensure'" ) }
}
}
# Notify this when apache needs a reload. This is only needed when
# sites are added or removed, since a full restart then would be
# a waste of time. When the module-config changes, a force-reload is
# needed.
exec { "reload-apache2":
command => "/etc/init.d/apache2 reload",
refreshonly => true,
}
exec { "force-reload-apache2":
command => "/etc/init.d/apache2 force-reload",
refreshonly => true,
}
# We want to make sure that Apache2 is running.
service { "apache2":
ensure => running,
hasstatus => true,
hasrestart => true,
require => Package["apache2"],
}
}
Add your own recipes for actually including the stuff you want included:
# The packages that need to be installed on each webserver
$wantedpackages = ["apache2", "tomcat5", "sun-java5-jdk", "libapache2-mod-jk" ]
package { $wantedpackages: ensure => installed, }
$enabledsites = ["www.example.com", "blog.example.com"]
$disabledsites = ["nudes.example.com"]
apache2::site {
$enabledsites: ensure => 'present';
$disabledsites: ensure => 'absent';
}
# Tomcat-enabled site
apache2::module { "jk": require => "libapache2-mod-jk" }
apache2::site { "java.example.com": require => "libapache2-mod-jk" }
NOTE: Don't forget to add the config files!
Another Solution
Another solution based on the code above -- indeed, the one in production use at www.wesabe.com -- can be found at: http://wesabe.googlecode.com/svn/trunk/puppet/modules/apache2/trunk/
Discussion
using require in a define is generally bad as this happens:
warning: require is a metaparam; this value will inherit to all contained resources
I've used this example and made a few minor tweaks, mainly the following:
- change the define line to remove the require variable
- added package {"apache2": ensure => present} to the class apache2 (this way you do not need to define it on the node
- because of the above line it now requires you to include that class on the node
- Also this is a more important one the exec statements in the site and module define use:
[ ${apache2_mods}-enabled/${name}.load -ef ${apache2_mods}-available/${name}.load ]This is incorrect as it will always fail. the -ef checks if both files refer to the same inode....this will obviously fail as we use soft links here.
The cheapest way to get around this is by using:[ -L ${apache2_mods}-enabled/${name}.load ]This is also not perfect as it does not check where it points to just that its a link...anyone have any better ideas about this?