Highly Available Web Pages, Apache and PHP as an Example

Standard

Several weeks ago, I discussed how to have a highly available file storage and a highly available relational database.  With a robust supply of file system and database service, we can stack more services on top.  Today, I take Apache and PHP as an example how to have a highly available web server.  As usual, I use FreeBSD for the purpose.

Why Apache and PHP!?

Quite some people would argue Apache and PHP are outdated.  I am not going to make a comment on this.  I hope you will appreciate the shortness of this article because of this choice.  The concepts you acquire from this example can be applied with your favourite platform.

Active / Active Web Servers

The example today is active / active pair.  The two servers can serve webpages together without error.  In fact, one can have unlimited amount of hosts working together.  In contrast, in the previous examples of NFS, only one server is active and another server is passively receiving updates and standby to take over.

To make this happen, one needs to ensure intermediate state of executions of one servers are saved so they can be taken up by the other servers.  In this example, we will move these state to the highly available storage.  With role separation, those intermedia data will not be lost no matter what happens to the web servers.  Another benefit is that web servers can be easily added (handle more load), replaced (for system updates), or reduced (for economy) without affecting any user sessions.

Session Data

When running a cluster of web hosting servers, one needs to take care of the session data.  HTTP servers are stateless by itself.  Cookies (notes to Europeans) are saved on the client side so servers need not to worry about.  Sessions storage, provided by most web programming environments, expect the storage on the server is consistent and persistent for each user session.

Session data is useful storing information that should not be understood or modify the the web users.  This could be some intermediate game states, some login privileges, etc.  It is therefore important to make sure such data is not lost when a user encounters another web server in the cluster.

Packages

At the time of writing, Apache 2.4 and PHP 7.1 are the newest.

In order to run the PHP in the most lazy way, install “mod_php” packages.  In addition, install some common PHP modules.  There is a moderate collection called “php71-extensions”.  I would also install “php71-mysqli” and “php71-gd”.  They are for database connections and image processing respectively.

# pkg install mod_php71 php71-mysqli php71-gd

What about Apache?  It is installed automatically as an dependency when you request installing the “mod_php”.

Shared Directories

To build on top of the previous example, I am mounting the NFS prepared previously to /mnt/nfs.  On the shell, overtime you want to mount:

# mkdir /mnt/nfs
# mount 10.65.10.13:/nfs /mnt/nfs
#

Alternatively, in the “/etc/fstab”:

# mkdir /mnt/nfs
# cat >> /etc/fstab << EOF
10.65.10.13:/nfs.   /mnt/nfs    nfs    rw    0    0
EOF
# mount /mnt/nfs
#

Configuring the Apache

Modify Apache so that the web page and script locations are in the NFS share.  The file to be modified is “/usr/local/etc/apache24/httpd.conf”.  There are originally two directories for normal web pages and CGI pages in “/usr/local/www/apache24”.  We are moving it to “/mnt/nfs”.

# sed -ibak 's|/usr/local/www/apache24|/mnt/nfs|' \
  /usr/local/etc/apache24/httpd.conf
# cp -a /usr/local/www/apache24/cgi-bin /mnt/nfs/
# cp -a /usr/local/www/apache24/data /mnt/nfs/
# cat >> /etc/rc.conf << EOF
apache24_enable="YES"
EOF
#

Also, update the “/usr/local/etc/apache24/httpd.conf” so that it decodes PHP files.  Modify the following parts manually:

<IfModule dir_module>
    DirectoryIndex index.html index.php
</IfModule>

<FilesMatch "\.php$">
    SetHandler application/x-httpd-php
</FilesMatch>
<FilesMatch "\.phps$">
    SetHandler application/x-httpd-php-source
</FilesMatch>

Configuring the PHP

PHP requires only copying the default configuration, and then modifying the session path.  One can also modify the upload path similarly, but I do not think it is necessary.

# cd /usr/local/etc/
# cp php.ini-production php.ini
# vi php.ini

The line to be added is just as follows:

; http://php.net/session.save-path
;session.save_path = "/tmp"
session.save_path = "/mnt/nfs/tmp"

Example Code

To make effect, reboot Apache24.  Here is a simple code that prints a counter every time it is loaded.  Write a file “/mnt/nfs/data/counter.php”

<?php
session_start();
if (isset($_SESSION['counter']))
  $_SESSION['counter'] += 1;
else
  $_SESSION['counter'] = 1;
print($_SESSION['counter']);
session_write_close();
?>

Testing

In order to test the service, we first turn on the web servers.  Then, we alternatively point a domain name to either one of the IP addresses.  The counter code above is repetitively executed as the map changes.  If successful, the counter continues increasing.

The laziest way to change the domain name mapping is of course editing the host table where the web browser is run.  For BSD and BSD-like systems, edit “/etc/hosts”.  For Windows, edit “C:\Windows\System32\Drivers\etc\hosts”.

Upcoming Steps

Once there are multiple web servers ready, one can consider having a load balancer or a content distribution network.  A load balancer service can be found in some value-added cloud service providers.  (Or else, you can set up one yourself.)  A content distribution network can be found anywhere around the globe.  Put aside their fundamental difference, they both accept multiple web server addresses and route network traffic accordingly.

Troubleshoot

Getting a blank page of PHP:  If you follow my instructions, you have configured PHP in a production mode.  Errors messages are not displayed but saved to the Apache log, which is “/var/log/httpd-error.log” in our context.  Read it and you will get an idea.

Function “session_start” undefined:  Make sure you have installed the package “php71-session” and restarted Apache.  It should be installed as an dependency when you install the package “php71-extensions”.

Advertisements