WordPress 0 to 60 in 5 minutes flat

by lesliebuna on January 1, 2012 | Leave your comment

So Steve and I agreed that our first sprint was to build a WordPress server, that’ll be a snap to do (or so I thought!)

In order to create a WordPress server I need 4 main components:

A Server with an Operating System installed
A MySQL database
A HTTP Web Daemon and
The WordPress application itself.
So like a good boy scout I set off about installing a WordPress on a centos box by hand and I found some most excellent instructions here by Andrew at Adlibre.

I hit my first problem, the WordPress package from EPEL for RHEL/CentOS 5 is quite old, it’s 2.8 while the version of WordPress with all the new hotness is 3.0.

Hmm ”old busted” or “new hotness” (not to say the 2.8 is either old or busted but you get what I mean). I decided I’d roll with the new hotness and then I hit another dilemma I couldn’t find a RPM for RHEL/CentOS 5, so should I just roll out the tar ball or create a package for it?

This is currently a hot topic in the Devops community and when I went to Devops Hamburg back in October one of the more popular open space sessions was “packaging vs. non-packaging, when and what?” and the main output was it “depends”.

However I wanted a zero touch and fully automated deployment so I investigated both methods and weighted them up:

Scenario 1 – Installation of Tar ball

Download WordPress tar ball to the server file system
Untar the package and install it to the correct location
Track the installation and ensure that it is valid (correctly installed, all checksums in place (Thanks AndrewH!)) also be able to cleanly handle upgrades.

Scenario 2 – Installation RPM

Package WordPress RPM
Yum install WordPress

I quickly came to the conclusion that the packaging case won over when I imagined the amount of puppet code I’d have to churn out to do scenario #1 and make it robust versus just using what I believed was the right tool for the job i.e. RPM and keeping it as simple as possible.

Name: wordpress
Version: 3.0.1
Release: 1
Group: System/Servers
Summary: Personal publishing platform.
URL: http://wordpress.org/
Source0: http://wordpress.org/%{name}-%{version}.tar.gz
Vendor: WordPress
License: GPLv2+
Packager: Martin Jackson
BuildArch: noarch
BuildRoot: %{_tmppath}/%{name}-%{version}-root
Requires: php
Requires: php-mysql

%description
WordPress is an open source Content Management System (CMS), often used as a
blog publishing application, powered by PHP and MySQL. It has many features
including a plug-in architecture and a template system.

%prep
%setup -q -n %{name}

# fix dir perms
find . -type d | xargs chmod 755

# fix file perms
find . -type f | xargs chmod 644

# disable wordpress update option
sed -i -e "s/add_action/#add_action/g" wp-includes/update.php

%install
rm -rf %{buildroot}

install -d %{buildroot}%{_sysconfdir}/httpd/conf.d
install -d %{buildroot}%{_sysconfdir}/%{name}
install -d %{buildroot}/var/www/%{name}

cp -aRf * %{buildroot}/var/www/%{name}/

cat > %{buildroot}%{_sysconfdir}/httpd/conf.d/%{name}.conf << EOF

Alias /%{name} /var/www/%{name}

AllowOverride None
Allow from All

EOF


# cleanup
rm -f %{buildroot}/var/www/%{name}/license.txt

%clean
rm -rf %{buildroot}

%files
%defattr(-,root,root)
/var/www/%{name}
%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/httpd/conf.d/%{name}.conf

%changelog
* Wed Nov 17 2010 Martin Jackson 3.0.1.1
- initial Red Hat Enterprise package

OK now I have my newly packaged WordPress 3.0 rpm and life is good, everything else I need is already packaged and can be easily installed using yum, a few minutes later I have a fully functioning WordPress server.

Now for the fun part, how do a fully automate the creation of a WordPress server end to end.

Firstly I created a custom kickstart profile on my cobbler server to install a minimal Just Enough Operating System (JeOS) base build (faster to install and patch with the minimum security attack surface) with some additional post install stuff to bootstrap puppet.

install
url --url http://rasengan/cblr/links/Centos-5.5-x86_64
key --skip
lang en_US.UTF-8
keyboard uk
skipx
network --device eth0 --bootproto dhcp --hostname testvm-wordpress.uncommonsense.local

rootpw --iscrypted $MD5PASSWORD
firewall --enabled --http --port=22:tcp
authconfig --useshadow --enablemd5
selinux --permissive
timezone Europe/London
bootloader --location=mbr --driveorder=vda
clearpart --all --initlabel
part /boot --fstype ext3 --size=100
part pv.2 --size=0 --grow --ondisk=vda
volgroup VolGroup00 --pesize=32768 pv.2
logvol swap --fstype swap --name=LogVol01 --vgname=VolGroup00 --size=512 --grow --maxsize=1024
logvol / --fstype ext3 --name=LogVol00 --vgname=VolGroup00 --size=1024 --grow
repo --name=source-1 --baseurl=http://rasengan/cobbler/ks_mirror/Centos-5.5-x86_64/
$yum_repo_stanza
reboot

%packages --nobase --excludedocs
coreutils
dhclient
yum
rpm
ruby
libselinux-ruby
subversion
e2fsprogs
lvm2
grub
sysstat
ntp
openssh-server
openssh-clients
xorg-x11-xauth
screen
net-snmp
net-snmp-utils
system-config-securitylevel
xterm
mutt
sudo
yum-downloadonly
yum-protectbase
-dhcpv6-client
-iptables-ipv6
-system-config-securitylevel-tui
-wireless-tools
-rhpl
-mdadm

%post
exec < /dev/tty3 > /dev/tty3
chvt 3
echo
echo ##############################
echo # Running Post Configuration #
echo ##############################
echo
rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm
echo "192.168.1.5 puppet rasengan rasengan.uncommonsense.local" >> /etc/hosts
yum -y install puppet rdoc
chkconfig puppet on
cat > /etc/sysconfig/puppet << EOF # The puppetmaster server PUPPET_SERVER=rasengan # If you wish to specify the port to connect to do so here #PUPPET_PORT=8140 # Where to log to. Specify syslog to send log messages to the system log. #PUPPET_LOG=/var/log/puppet/puppet.log # You may specify other parameters to the puppet client here #PUPPET_EXTRA_OPTS=--waitforcert=500 EOF cat > /etc/puppet/puppet.conf << EOF [main] # The Puppet log directory. # The default value is '$vardir/log'. logdir = /var/log/puppet # Where Puppet PID files are kept. # The default value is '$vardir/run'. rundir = /var/run/puppet # Where SSL certificates are kept. # The default value is '$confdir/ssl'. ssldir = $vardir/ssl libdir = /var/lib/puppet/lib [puppetd] # The file in which puppetd stores a list of the classes # associated with the retrieved configuratiion. Can be loaded in # the separate ``puppet`` executable using the ``--loadclasses`` # option. # The default value is '$confdir/classes.txt'. classfile = $vardir/classes.txt # Where puppetd caches the local configuration. An # extension indicating the cache format is added automatically. # The default value is '$confdir/localconfig'. localconfig = $vardir/localconfig pluginsync = true plugindest = /var/lib/puppet/lib EOF cat >> /etc/rc.local < installed
}
}

class apache::config {
File{
require => Class["apache::install"],
notify => Class["apache::service"],
owner => "root",
group => "root",
mode => 644
}
}

class apache::service {
service{"httpd":
ensure => running,
enable => true,
require => Class["apache::config"],
}
}

class apache {
include apache::install,
apache::config,
apache::service
}

class apache::disable {
include apache::install,
apache::config
}

For the MySQL installation and configuration I wanted something a little more robust since I believed I could make a lot of use out of a good MySQL puppet module. I found it quite ironic that I stumbled across a problem that I had previously answered on server fault that being how do you find a top notch MySQL module:

There were quite a few out there and after a fair bit of back and forth I settled on the Camp to Camp boys’ MySQL puppet module, it is very functional and quite compact in the amount of supporting modules that are needed to make it function:

There were quite a few out there and after a fair bit of back and forth I settled on the Camp to Camp boys’ MySQL puppet module, it is very functional and quite compact in the amount of supporting modules that are needed to make it function:

puppet-mysql
puppet-augeas
puppet-common

You’ll need to have the plugin sync enabled in your puppet.conf for this to work because it copies some custom facts and plugins to the client in order to create the databases.

1 [main]
2 libdir = /var/lib/puppet/lib
3
4 [puppetd]
5 pluginsync = true
6 plugindest = /var/lib/puppet/lib

Onto the final stretch I need to install WordPress so I whipped up the following puppet module to install WordPress.

?
# Class: wordpress
#
# This class manages the wordpress blogging application
#
# Parameters:
# None
#
#
# Actions:
# Install the wordpress blogging application
#
# Requires:
# - Package["apache","mysqlclient"]
#
# Sample Usage:
#

class wordpress::install {
$packagelist = ["php","php-mysql","wordpress"]
package{ $packagelist:
ensure => latest,
require => Class["repository::uncommonsense"],
}
}

class wordpress::config {
File{
require => Class["wordpress::install"],
notify => Class["apache::service"],
owner => "root",
group => "root",
mode => 644
}
}

class wordpress {
include wordpress::install,
wordpress::config
}

class wordpress::disable {
include wordpress::install
}

I used the following entry in my nodes.pp file to pull it all together

node "testvm-wordpress.uncommonsense.local" {
include repository::uncommonsense
include apache
include augeas
include wordpress
$mysql_password = "foo"
include mysql::server::small

mysql::rights {"Set rights for wordpress database":
user => "username",
password => "password",
database => "wordpress",
}

mysql::database {"wordpress":
ensure => present
}
}

Ok so I now have a fully functional WordPress Server in about 5 minutes. This is just a basic setup but it demonstrates what can be done without too much effort and if I wanted to take this further in the future, I would create an erb template for the wp-config.php so that the database settings are populated by puppet using the existing username, password and database name variables.

Leave your comment

Required.

Required. Not published.

If you have one.