Setting up a LAMP server on AWS EC2

Amazon Web Services have grown quite rapidly over the last few years. This is why they’re generally the most popular scalable hosting that large companies use to ensure their applications are up and running with no pitfalls.

Amazon provide something called ‘free tier’ so you can get started straight away and get stuck in. Go ahead, set up an account and have a play about. It’s free for the first year, either cancel your account after before that point or, if you’ve gotten far enough, it’s pretty cheap anyway.

Once you’re logged in via SSH, use the following commands to set up your LAMP server. Please read the code line by line to ensure you have copied and pasted every command in correctly, and that you understand what each one does (using the helpful comments).

This code will install a simple LAMP server on AWS installing PHP 5.5, MySQL, Intl, Git, APC and ElasticSearch.

# Install the web server (with php55, httpd24, mysql apc, intl and git)
sudo yum update -y
sudo yum install -y httpd24 php55 php55-mysql php55-pdo php55-gd php55-mysqlnd mysql-server php55-pecl-apc-3.1.15 php55-intl git

# Start the http service
sudo service httpd start
sudo chkconfig httpd on
sudo groupadd www
sudo usermod -a -G www ec2-user
exit

# Log back into the server (now that we are added to a new group)
sudo chown -R root:www /var/www
sudo chmod 2775 /var/www
find /var/www -type d -exec sudo chmod 2775 {} +
find /var/www -type f -exec sudo chmod 0664 {} +

# At this point we can add a phpinfo page to check installation has succeeded
#echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php
#rm /var/www/html/phpinfo.php

# Start mysqld and run a secure mysql installation
sudo service mysqld start
sudo mysql_secure_installation
sudo chkconfig mysqld on

# Create ssh key for server
ssh-keygen -t rsa -C "[email protected]"
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa
#cat ~/.ssh/id_rsa.pub

# Create the directory and install the source
mkdir -p /var/www/handle/production
cd /var/www/handle/production
git clone [email protected]/handle.git --recursive .

# Install composer dependances and generate autoloader
curl -sS https://getcomposer.org/installer | php
php composer.phar install --no-dev

# Change PHP configuration (allow short tags and add timezone = Europe/London)
sudo vi /etc/php.ini

# Set up Virtual Hosts
sudo vi /etc/httpd/conf.d/vhosts.conf

# @todo: Add EOF syntax

# <VirtualHost *:80>
# ServerAdmin [email protected]
# DocumentRoot /var/www/handle/production/public
# #SetEnv APPLICATION_ENV "production"
# AliasMatch ^/core/(.*)$ /var/www/handle/production/core/Company/public/$1
# ServerName handle.production.company.co.uk
# ErrorLog logs/handle.production.company.co.uk-error_log
# CustomLog logs/handle.production.company.co.uk-access_log common
# <Directory />
# Options FollowSymLinks
# AllowOverride None
# </Directory>
# <Directory /var/www/handle/production/public>
# Options Indexes FollowSymLinks MultiViews
# AllowOverride All
# Order allow,deny
# allow from all
# Require all granted
# </Directory>
# </VirtualHost>

# Create handlef.production.company.co.uk subdomain
# Eventually attach Route 53 API here

# Restart httpd
sudo service httpd restart

# Import Elasticsearch
sudo rpm --import https://packages.elasticsearch.org/GPG-KEY-elasticsearch

# Create custom repository
sudo vi /etc/yum.repos.d/elasticsearch.repo

# Paste in the following
[elasticsearch-1.5]
name=Elasticsearch repository for 1.5.x packages
baseurl=http://packages.elasticsearch.org/elasticsearch/1.5/centos
gpgcheck=1
gpgkey=http://packages.elasticsearch.org/GPG-KEY-elasticsearch
enabled=1

# Install Elasticsearch
sudo yum install -y elasticsearch

# Add Elasticsearch to startup routine
sudo chkconfig --add elasticsearch

# Create index
curl -XPUT 'http://localhost:9200/handle/'

# Edit the apcu configuration to allow caching on the command line
sudo vi /etc/php-5.5.d/apcu.ini

# Uncomment the following line and change to 1
apc.enable_cli=1

If you’re looking for specific packages to install, take a look at Amazon’s package list: http://aws.amazon.com/amazon-linux-ami/2013.09-packages/

 

Counting the most common PHP error logs

Keeping an eye on your error logs is something that most developers neglect. Server administrators, on the other hand, tend to look out for these minor details, as they’ll keep your website secure and ensure there are no warnings, notices or even fatal errors that you may have missed during development.

You can check to where your error log files are piping their information by checking a phpinfo() file (just a php file with the following code inside (be sure to start the file with a php open tag):

phpinfo();

Or you can check via the command line using this command:

php -i | grep error_log

which simply shows the php information and configuration, piping with grep and finding the string ‘error_log’. It will then return where your error log files are. This will either be at an absolute path, starting with a forward slash (/var/log/php5-fpm.log), or, it will be a relative file such as ‘error_log’. This means the error log will be at the relative path of the executed script that produced the error.

Once you have found the path for your error log, you can run a simple command (it’s simple once we break it down) that will count your errors so you can go ahead and remove the most common errors that are clogging up your php error log:

cat /var/log/php-errors.log | sort | uniq -c | sort -rn | head

Let’s break that down. The cat command simply outputs the content of the file you have defined. You’ll notice everything after that uses a pipe (|) to layer on other commands to that output. The sort command – yes, you guessed it – sorts the data numerically and alphabetically. The uniq command removes any duplicates, line by line (makes each line unique) and the -c flag puts a count at the beginning of each line showing how many matches it found. The sort command sorts the data again (after identifying the duplicates) with the -r (which reverses the order, you’ll see why in the last command) and -n flag. Finally, the head command returns the last 10 lines (10 lines by default – you can change this number using the -n, just check the manual).

If your errors (like mine) are relative, you can create a global file using this command:

cat /home/*/public_html/error_log > /var/log/php-errors.log

After starting a new job, I had to take over a few servers which kept going down. After finding out the disk space was full almost every week which caused the server to go ka-boom, I found the PHP FPM error log was over 15GB is size! Amazing, huh? About 40 sites had been developed with no debugging turned on locally so all of these errors had been adding up. Since these sites had quite a lot of traffic, we found ourselves having to truncate the error log almost ever week to free up disk space until I could get round to fixing the most common errors.

Delete a file from the command line that’s still in use

I found myself taking over a few servers which had around 30 sites on them, all with hundreds of their own errors. I had a look in the /var/log directory and found the php5-fpm.log file was over 10GB in size! There are ways of easily truncating this but I found a particular issue which could help some of you. Deleting a large file that’s currently open on the server. A large file which is being used by the system can still remain in your memory. So when you delete your large file and expect your disk space to be free from that file, you’ll be disappointed when you run:

df -h

This command shows the server disk usage. Turns out, there is a process still running in the background which needs to be stopped until this can free up your memory.

To show the list of open files currently running on your server, you can use a simple command called lsof:

lsof -nP | grep '(deleted)'

We’re using the n and P flags and then just piping grep to show the deleted files. The flags used are as follows (taken from the man file):

-P inhibits the conversion of port numbers to port names for network files. Inhibiting the conversion may make lsof run a little faster. It is also useful when port name lookup is not working properly.

-n inhibits the conversion of network numbers to host names for network files. Inhibiting conversion may make lsof run faster.  It is also useful when host name lookup is not working properly.

This will return the files currently deleted that are still in use. Take note of the key (you can check this if you run lsof -nP at the top). The second column shows the process ID. To remove this, simply kill it using:

kill 1234

Where 1234 will be your process ID.

After a quick check using:

df -h

You’ll see that your disk space is now free!

Installing WordPress via the command line

I found myself having to create more and more WordPress websites after clients would specifically ask for ‘a WordPress website’. Although WordPress is built as a blogging tool, it can be used as a very simple content management system (without too much customisation in the back end).

After installing WordPress manually for the seventh time, I thought it would be an idea to make a quick shell script that I would be able to use over SSH, as servers are able to download files via wget much quicker than I can downloading to my local machine and then uploading via FTP or copying it over via scp or rsync. Some hosting providers have automated scripts already installed within Plesk, cPanel and other web host management systems that allow you to install WordPress with a click of a button. It will install the latest version, set up the database and you’re set. The only issue here is when you’re working on dedicated or cloud servers where these tools are not readily available.

I’ve always been the one to start something from scratch so I know the exact the process, how long it should take and what to expect, as well as the ability to easily customise so it can meet my needs.

This simple shell script will:

  • download and unzip the latest version of WordPress
  • remove the sample configuration file
  • generate random salts (taken directly from the WordPress website): https://api.wordpress.org/secret-key/1.1/salt/
  • create a custom configuration file
  • define the SITE_URL and HOME_URL variables as per your handle name
  • copy over any predefined themes and plugins
  • change permissions and ownership of the installed files (otherwise, if you’re running this as root, you’ll end up with internal server errors)

I’ve added a load of comments against each part of the code so it should be easy enough to follow. Anyway, here it is:

#!/bin/bash
#
# Install WordPress
#
# Installation: Copy this script into /usr/local/bin/wordpress
# Useage: [email protected] [~]# wordpress
#

# Get the handle name
echo 'Please enter the handle name'
read HANDLE

# Get the group and username
group=$(ls -la | head -3 | awk 'END {print $3}')
user=$(ls -la | head -3 | awk 'END {print $4}')

# Install the latest version of WordPress
wget http://wordpress.org/latest.tar.gz
tar xfz latest.tar.gz
mv ./wordpress/* ./
rmdir ./wordpress/
rm -f latest.tar.gz

# Remove the sample configuration file
rm -f ./wp-config-sample.php

# Get random salts from the WordPress website
salts=$(wget -O- -q https://api.wordpress.org/secret-key/1.1/salt/)

# Create the configuration file
cat <./wp-config.php
 /**
* The base configurations of the WordPress.
*
* This file has the following configurations: MySQL settings, Table Prefix,
* Secret Keys, WordPress Language, and ABSPATH. You can find more information
* by visiting {@link http://codex.wordpress.org/Editing_wp-config.php Editing
* wp-config.php} Codex page. You can get the MySQL settings from your web host.
*
* This file is used by the wp-config.php creation script during the
* installation. You don't have to use the web site, you can just copy this file
* to "wp-config.php" and fill in the values.
*
* @package WordPress
*/

define('SITE_URL', 'http://$HANDLE.local');
define('HOME_URL', 'http://$HANDLE.local');

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', '$HANDLE_wordpress');

/** MySQL database username */
define('DB_USER', 'dev');

/** MySQL database password */
define('DB_PASSWORD', 'dev');

/** MySQL hostname */
define('DB_HOST', 'localhost');

/** Database Charset to use in creating database tables. */
define('DB_CHARSET', 'utf8');

/** The Database Collate type. Don't change this if in doubt. */
define('DB_COLLATE', '');

/**#@+
* Authentication Unique Keys and Salts.
*
* Change these to different unique phrases!
* You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
* You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
*
* @since 2.6.0
*/
$salts

/**#@-*/

/**
* WordPress Database Table prefix.
*
* You can have multiple installations in one database if you give each a unique
* prefix. Only numbers, letters, and underscores please!
*/
\$table_prefix = 'wp_';

/**
* WordPress Localized Language, defaults to English.
*
* Change this to localize WordPress. A corresponding MO file for the chosen
* language must be installed to wp-content/languages. For example, install
* de_DE.mo to wp-content/languages and set WPLANG to 'de_DE' to enable German
* language support.
*/
define('WPLANG', '');

/**
* For developers: WordPress debugging mode.
*
* Change this to true to enable the display of notices during development.
* It is strongly recommended that plugin and theme developers use WP_DEBUG
* in their development environments.
*/
define('WP_DEBUG', false);

/* That's all, stop editing! Happy blogging. */

/** Absolute path to the WordPress directory. */
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');

/** Sets up WordPress vars and included files. */
require_once(ABSPATH . 'wp-settings.php');
EOF

# Insert custom blank theme (comment out the unfavored copying process)
mkdir ./wp-content/themes/$HANDLE
#cp -r /path/to/my/custom-theme/* ./wp-content/themes/$HANDLE
scp -r [email protected]:/path/to/my/custom-theme/* ./wp-content/themes/$HANDLE

# Replace placeholder content (this was to replace the comments within the custom theme but I have yet to get it working, maybe something to fix in the near future)
#perl -pi -w -e 's/Company Name/$HANDLE/g;' ./wp-content/themes/$HANDLE/*.php
#perl -pi -w -e 's/since 2014/since $(date +%Y)/g;' ./wp-content/themes/$HANDLE/*.php

# Install plugins (comment out the unfavored copying process)
#cp -r /path/to/my/plugins/* ./wp-content/plugins/
scp -r [email protected]:/path/to/my/plugins/* ./wp-content/plugins/

# Change file permissions and ownership
sudo chown -R $group:$user ./
find ./ -type d -exec chmod 755 {} \;
find ./ -type f -exec chmod 655 {} \;

I think the only bits I am missing, that I will eventually get round to doing is:

  • Install a new database for the WordPress website
  • Set up new database credentials and insert them into the configuration file
  • Add an environment variable (so it will use scp instead of cp for copying over files if you’re not installing locally, change the predefined URL names and other useful bits)

I hope this script helps someone installing WordPress on their server or local machine (or at least makes the process a little quicker). If there’s anything that I have missed that might be useful to you, please mention them in the comments and I will update the installation script.

This code is also available on GitHub so feel free to download or even contribute: https://github.com/olivertappin/wordpress/blob/master/wordpress.sh

Watermarking images using PHP

My girlfriend recently started a photography business. I found her uploading her photos to Facebook or her website with her logo in the bottom right of the image. She would use Photoshop to do this manually but the work really started when there were quite a few photos to do this to.

I know there are batch processes in Photoshop but this can be quite slow depending on what you’re doing. Photoshop seems to show you the running batch process which uses up a lot of CPU rending the changes on screen. As she’s lucky enough to have me for a boyfriend, I thought I would help out by writing a little script she could use to do this for her.

The class takes a predefined logo and sticks it on the image, saves it and moves on to the next one. There are two versions of the logo, one to be used on a light background, the other to be used on a dark background:

View the class on GitHub: https://github.com/olivertappin/watermark/blob/master/watermark.php

To use the code:

// Create a new instance of the Watermark class
$watermark = new Watermark();

// Set the input directory (where the images are located)
$watermark->setInputDirectory('input');

// Set the output directory (where the files will be saved)
$watermark->setOutputDirectory('output');

// Run the script
$watermark->run();

Creating HTML email templates

There are plenty of resources out there from various email marketing websites, it’s just a case of putting them all together and taking note of the main rules:

  • Code like it’s 1999
  • Tables, tables, tables
  • Nest your tables like crazy
  • Test your email template
  • Avoid using margin
  • Style your <td> tags (nest your tables if needed)

Let us treat each email client like a different browser. You’re going to run into old versions which do not support newer technologies. As you design and create your email templates, you will run into problematic issues in different email clients. If you stick to tables and basic syntax from the start, the chances of you running into these issues will be minimal.

Most email clients do not support <style> tags so we’re much better off using inline styling. To make this easier, we’ll let PHP handle this using an HTML and CSS parser. You may be working with a framework that already takes care of this or you could need to implement this yourself. There are plenty of resources available such as:

Useful links and resources:

Transactional email API services:

How to use Ack – Beyond grep

I was first introduced to ack when working for a small web agency in the south by a guy called Stuart. I remember him explaining it as “it’s much faster than grep, but that’s because it cheats”. This article will show you how to use ack as well as the useful flags available to speed up your back-end web development.

If you’re on Mac OS X using Homebrew, installation is as easy as

brew install ack

Otherwise, installation is just as easy across other packages, just visit http://beyondgrep.com/install/

Using ack is pretty simple, and super fast. It’s rather useful as it shows the line numbers within the code and you can change how many lines you want to show before and after using the -c flag like so:

ack '\.container' -C 9

The backslash stops the dot from being used as a regular expression, so we’re essentially searching for just ‘.container’. The results are as follows:

➜ resource git:(master) ✗ ack '\.wrapper' -C 9
sass/style.scss
27- height: 26px;
28- padding-left: 162px;
29- box-sizing: border-box;
30- background-repeat: no-repeat;
31- background-image: url('../img/logo.svg');
32-}
33-.content {
34- padding: 55px 0 20px 0;
35-}
36:.wrapper {
37- margin: 0 auto;
38- padding: 0 20px;
39- max-width: 1000px;
40- position: relative;
41-}
42-
43-
44-/* --- Page --------------------- */
45-

If you’re a new guy taking over someone else’s work and have no idea where anything is, without having anyone to ask, ack will end up being your best friend. Looking for that horrible problematic inline styling on that div tag? Just use ack:

ack '\<div style\=\"margin-top: -27px;\">'

And there it is. No more nasty !important hacks. I know this has helped me on a number of projects on so many occasions, especially during handovers and the person of interest is off.

If you’re ever looking for something, don’t forget you always have man files. I always forget we have these to look at, it’s just a case of using them and searching for the right keyword to find what you’re looking for. Just use:

man ack