Today I will use vagrant and Puppet for all the setup.

I will setup:

  • 1 Load balancer with HAProxy.
  • 2 Webservers with Apache and PHP FPM.
  • 1 NFS Server to keep webserver files in sync.
  • 1 MySQL Server.

I will use puppetlabs Ubuntu 16.04 vagrant box with puppet preinstalled.

Create directory structure:

mkdir ha
cd ha
mkdir -p environment/vagrant/manifests
mkdir environment/vagrant/modules

Create a Vagrantfile file with this content: Adapt IPs, memory and cores dependening on your needs.

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(2) do |config|
  config.vm.box = "puppetlabs/ubuntu-16.04-64-puppet"

  config.vm.provision "puppet" do |puppet|
    puppet.environment_path = "environment"
    puppet.environment = "vagrant"
  end

  config.vm.define "webserver1" do |webserver1|
    webserver1.vm.hostname = "webserver1"
    webserver1.vm.network :private_network, ip: "192.168.33.11"

    webserver1.vm.provider "virtualbox" do |v|
      v.memory = 600
      v.cpus = 1
    end
  end

  config.vm.define "webserver2" do |webserver2|
    webserver2.vm.hostname = "webserver2"
    webserver2.vm.network :private_network, ip: "192.168.33.12"

    webserver2.vm.provider "virtualbox" do |v|
      v.memory = 600
      v.cpus = 1
    end
  end

  config.vm.define "balancer" do |balancer|
    balancer.vm.hostname = "balancer"
    balancer.vm.network :private_network, ip: "192.168.33.10"

    balancer.vm.provider "virtualbox" do |v|
      v.memory = 256
      v.cpus = 2
      v.customize ["modifyvm", :id, "--ioapic", "on"]
    end
  end

  config.vm.define "nfs" do |nfs|
    nfs.vm.hostname = "nfs"
    nfs.vm.network :private_network, ip: "192.168.33.20"

    nfs.vm.provider "virtualbox" do |v|
      v.memory = 256
      v.cpus = 1
    end
  end

  config.vm.define "db" do |db|
    db.vm.hostname = "db"
    db.vm.network :private_network, ip: "192.168.33.25"

    db.vm.provider "virtualbox" do |v|
      v.memory = 512
      v.cpus = 1
    end
  end

end

Add a Puppetfile in environment/vagrant/Puppetfile with this content:

#!/usr/bin/env ruby
#^syntax detection

forge "https://forgeapi.puppetlabs.com"

mod 'puppetlabs-apache'
mod 'puppetlabs-haproxy'
mod 'puppetlabs-mysql'
mod 'derdanne-nfs'
mod 'puppet-php',
  :git => 'git://github.com/voxpupuli/puppet-php.git'

Create an empty default.pp in environment/vagrant/manifests/default.pp

Start the balancer instance:

vagrant up balancer

This could have errors on startup, don’t worry and connect to it:

vagrant ssh balancer

Inside this machine install dependences to load required puppet modules:

sudo apt-get install ruby git
sudo gem install librarian-puppet

And download modules:

cd /vagrant/environment/vagrant
rmdir modules
librarian-puppet install

This will detect your Puppetfile and will download required modules. After the execution we should have our modules directory back with the modules installed:

ls modules
apache          archive         haproxy         mysql           php             staging         yum
apt             concat          inifile         nfs             puppi           stdlib          zypprepo

Now logout from balancer and fill the default.pp file that we created before: Remembder to change IPs acording to your needs if you changed them in Vagrantfile.

node 'balancer' {

  include ::haproxy

  haproxy::listen { 'dev.ntplu.com':
    collect_exported => false,
    ipaddress        => '0.0.0.0',
    ports            => '80',
  }

  haproxy::balancermember { 'webserver1':
    listening_service => 'dev.ntplu.com',
    server_names      => 'webserver1',
    ipaddresses       => '192.168.33.11',
    ports             => '80',
    options           => 'check',
  }

  haproxy::balancermember { 'webserver2':
    listening_service => 'dev.ntplu.com',
    server_names      => 'webserver2',
    ipaddresses       => '192.168.33.12',
    ports             => '80',
    options           => 'check',
  }

  haproxy::listen { 'stats':
    ipaddress => '0.0.0.0',
    ports     => '9090',
    options   => {
      'mode'  => 'http',
      'stats' => [
        'uri /',
        'auth puppet:puppet'
      ],
    },
  }

}

node 'nfs' {
  
  class { '::nfs':
    server_enabled => true,
    nfs_v4_idmap_domain => 'nfs.ntplu.com',
  }

  nfs::server::export{ '/srv/nfs/':
    ensure  => 'mounted',
    clients => '192.168.33.0/24(rw,insecure,async,no_root_squash) localhost(rw)',
  }
}

node 'db' {
  
  class { '::mysql::server':
    override_options => {
      'mysqld' => {
        'bind-address' => '0.0.0.0',
      }
    }
  }

  mysql::db { 'wordpress':
    user     => 'wordpress',
    password => 'tJJyXb40JF',
    host     => '%',
    grant    => ['ALL'],
  }
}


node 'webserver1', 'webserver2', 'webserver3' {

  include '::apt'
  
  class { '::nfs':
    server_enabled => false,
    client_enabled => true,
    nfs_v4_client => true,
    nfs_v4_idmap_domain => 'nfs.ntplu.com',
  }

  nfs::client::mount { '/var/www/':
    server => '192.168.33.20',
    share => 'srv/nfs/',
  }

  class { 'apache':
    default_vhost => false,
  }
  class { 'apache::mod::proxy': }
  class { 'apache::mod::proxy_fcgi': }
  class { '::php':
    extensions => {
      mysql => { },
      curl => { },
    },
  }

  php::apache_vhost { 'dev.ntplu.com':
    vhost => 'dev.ntplu.com',
    docroot => '/var/www/html/',
    default_vhost  => false,
    fastcgi_socket => 'fcgi://127.0.0.1:9000/var/www/html/$1'
  }

}

You can test your puppet + vagrant setup by provisioning the balancer:

vagrant provision balancer.

If everything goes well provision the other machines. Start first the NFS otherwise webservers will fail:

vagrant up nfs
vagrant up

Now that all servers are up an running we can check the HAProxy status page in our browser http://192.168.33.10:9090. And see the Ubuntu test page in dev.ntplu.com, adding an entry to your hosts file pointing the domain to 192.168.33.10. It won’t work without the domain name because in the default.pp I didn’t create a default host and only configured a virtualhost with this domain name. You can choose the name that you want modifiing the default.pp.

After this test you can install a wordpress by login to one of the webservers:

vagrant ssh webserver1
sudo su -
cd /var/www/html
rm index.html
wget https://wordpress.org/latest.zip
unzip latest.zip
rm latest.zip
mv wordpress/* .
mv wordpress/.* .
rmdir wordpress
chown -R www-data:www-data /var/www/html

And follow wordpress installation instructions. Remember to use the db server IP that you defined in your vagrant file, and use the database name, user and password defined in default.pp

Nexts steps necessary for performance reasons:

  • Enable PHP opcache to depend less on NFS for PHP files.
  • Enable NFS Cache on NFS mounts.
  • Install W3 Total Cache and enable page cache.