Setting up Drupal 8 Dev Instance on EC2

  • henok_mikre

    Henok Mikre

In our previous post ([Creating EC2 Instances Using AWS CLI]({{ "/blog/2018/06/aws-cli" | prepend: site.baseurl }})), we discussed how to setup a CentOS EC2 instance using the aws CLI. In this post, we will discuss setting up a basic Drupal 8 dev environment on such an instance.

There are many posts on the web that discuss this same subject. In fact, there are far easier ways of running Drupal on AWS. What is different in this post is that we discuss each step in detail so that we have a better understanding of what happens under the hood. For most enterprise deployments, we would use Ansible, Puppet, Cloud Formations, or some other provisioning tool to create instances.

These steps are not to be used to setup a production environment.

Get Instance

Let's assume we now have a running instance on which we plan to setup Drupal 8. The command below will output a table of instances for the account:

aws ec2 describe-instances --query 'Reservations[*].Instances[*].[Placement.AvailabilityZone, State.Name, InstanceId, Architecture, ImageId, InstanceType, PublicIpAddress]'

What we want is the InstanceId of the machine on which we want to install Drupal 8. Let's take an example out of the AWS documentation and save a couple of pertinent information as shell variables:

# Save instance id.
instance_id='<id-of-new-instance>'

When we created the instance, we had provided the name of the key with which we plan to access the instance. We can add that key to the ssh agent (check with ssh-add -L). All that is left for us to do is SSH into the instance. Here is a one-liner that will fetch the public IP by InstanceId and SSH into it as the user centos, which is the default for the image we are using:

ssh -A centos@$(aws ec2 describe-instances --instance-ids $instance_id --query 'Reservations[*].Instances[*].[PublicIpAddress]' --output json | python -c "import sys, json; print json.load(sys.stdin)[0][0][0]")

Setup

Now we can install the appropriate tools on the environment:

Install basic tools

# Update packages
sudo yum -y update

# Install tools
sudo yum install -y vim git wget patch unzip

# Bash completion
sudo yum -y install bash-completion bash-completion-extras

Setup ViM

# Download favorite .vimrc file
cd ~ && curl -LO https://raw.githubusercontent.com/henokmikre/dotfiles/master/.vimrc

# Backup system vimrc
sudo cp /etc/vimrc /etc/vimrc.bak

# Turn off vim autocmd that opens at last position (sucks for git commit)
# You can also verify those lines first: sed -n 20,24p /etc/vimrc
sudo sed -i '20,24s/^/"/' /etc/vimrc

# Turn-off vim syntax highlighting last term on opening file
# Verify: sed -n 49,52p /etc/vimrc
sudo sed -i '49,52s/^/"/' /etc/vimrc

# Also copy vimrc to root home so files still look great in sudo mode
sudo cp ~/.vimrc /root/

Install Apache

# Install apache server.
sudo yum install -y httpd

# Enable start on boot.
sudo systemctl enable httpd.service

# Start
sudo apachectl start
sudo apachectl status

# Create vhost dir.
sudo mkdir /var/www/vhosts
sudo chgrp apache /var/www/vhosts
sudo chmod g+ws /var/www/vhosts/

Verify that Apache is running by going to the FQDN (see shortcut above).

Install PHP 7.2

cd ~ && curl 'https://setup.ius.io/' -o setup-ius.sh
sudo bash setup-ius.sh
rm setup-ius.sh
sudo yum install -y mod_php72u php72u-cli php72u-mysqlnd php72u-opcache php72u-pecl-memcached php72u-mbstring php72u-dom php72u-gd

# Backup php.ini
sudo cp /etc/php.ini /etc/php.ini.bak

# Increase max file size.
sudo sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 50M/g' /etc/php.ini

# Change max size of POST data PHP will accept.
sudo sed -i 's/post_max_size = 8M/post_max_size = 50M/g' /etc/php.ini

# Restart apache.
sudo apachectl restart

Setup MySQL

# Install
sudo rpm -Uvh http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm
sudo yum install -y mysql-server
sudo systemctl enable mysqld.service
sudo systemctl start mysqld.service

# Configure credentails and secure instance.
mysql_secure_installation

# Backup config
sudo cp /etc/my.cnf /etc/my.cnf.bak

# Add max_allowed_packet so we can load large SQL dump files.
sudo sed -i '/\[mysqld_safe\]/imax_allowed_packet = 10000M' /etc/my.cnf

# Restart MySQL.
sudo systemctl restart mysqld.service

Install Composer

cd ~
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
sudo ln -s /usr/local/bin/composer /usr/bin/composer

Install Drush Launcher

Drush is already provided by the drupal installation, so we don't need to install it separately. What we do need is the Drush launcher.

curl -OL https://github.com/drush-ops/drush-launcher/releases/download/0.6.0/drush.phar
chmod +x drush.phar
sudo mv drush.phar /usr/local/bin/drush

Setup Drupal Console Launcher

Just like Drush, Drupal Console will also be part of our Drupal repo. So all we need is the launcher.

curl -L https://drupalconsole.com/installer > drupal.phar
chmod +x drupal.phar
sudo mv drupal.phar /usr/local/bin/drupal

Create a Drupal Vhost Dir

# Add apache to user group
# But for security all artifacts should be owned by app user and apache should have write access to files dir.
sudo usermod -a -G $(whoami) apache

# Add current user to apache group so it can write any files that are owned by apache.
# Reminder, this is a dev environment.
sudo usermod -a -G apache $(whoami)

# Logout and log back in to update group membership
exit
ssh -A centos@$(aws ec2 describe-instances --instance-ids $instance_id --query 'Reservations[*].Instances[*].[PublicIpAddress]' --output json | python -c "import sys, json; print json.load(sys.stdin)[0][0][0]")

# Create new vhost dir
mkdir /var/www/vhosts/drupal.dev

# Create web and log directores
mkdir -p /var/www/vhosts/drupal.dev/log

# Create placeholder dir for drupal installation
mkdir -p /var/www/vhosts/drupal.dev/drupal/web

Prepare Vhost

Since this is a dev environment, we should setup basic HTTP authentication account. The command below will create an htpasswd file with the user 'xdrupal' the password 'ydrupalx':

sudo htpasswd -cb /etc/httpd/.htpasswd xdrupal ydrupalx

Let's now create a Drupal vhost conf file:

sudo tee -a /etc/httpd/conf.d/drupal.dev.conf &>/dev/null << EOF
<VirtualHost *:80>
  ServerName drupal.dev.example.com
  ServerAdmin root@localhost
  DocumentRoot "/var/www/vhosts/drupal.dev/drupal/web"
  ErrorLog "/var/www/vhosts/drupal.dev/log/error.log"
  CustomLog "/var/www/vhosts/drupal.dev/log/access.log" combined
  LogLevel warn
  <Directory "/var/www/vhosts/drupal.dev/drupal/web">
    # Add HTTP Authentication.
    AuthType Basic
    AuthName "Authentication Required"
    AuthUserFile "/etc/httpd/.htpasswd"
    Require valid-user

    # Add access control.
    Require ip 127.0.0.1

    # Allow access to .htaccess.
    AllowOverride All
  </Directory>
</VirtualHost>
EOF

Change security context of custom log dir:

sudo chcon -Rv --type=httpd_log_t /var/www/vhosts/drupal.dev/log

Test the configuration:

apachectl configtest

We can now disable the default vhost config:

sudo mv /etc/httpd/conf.d/welcome.conf /etc/httpd/conf.d/welcome.conf.bak

Restart apache so new conf files are loaded:

sudo apachectl restart

Create database

Let's assume we're using the root MySQL user with the password dkeoshD3826283#1. And the user drupal with password drupal for the new database.

mysql -u root -p'dkeoshD3826283#1' -e "CREATE DATABASE drupal"
mysql -u root -p'dkeoshD3826283#1' -e "CREATE USER 'drupal'@'localhost' IDENTIFIED BY 'drupal'"
mysql -u root -p'dkeoshD3826283#1' -e "CREATE USER 'drupal'@'%' IDENTIFIED BY 'drupal'"
mysql -u root -p'dkeoshD3826283#1' -e "GRANT USAGE ON *.* TO 'drupal'@'localhost'"
mysql -u root -p'dkeoshD3826283#1' -e "GRANT ALL PRIVILEGES ON drupal.* TO 'drupal'@'localhost'"

Download Drupal using Composer

cd /var/www/vhosts/drupal.dev/

# Remove placeholder dir
rm -rf drupal

# Install
composer create-project drupal-composer/drupal-project:8.x-dev drupal --stability dev --no-interaction

Configure SELinux Context for Directories

sudo chcon -R --reference=/var/www/html /var/www/vhosts/drupal.dev/drupal/web/
sudo chcon -R -t httpd_sys_rw_content_t /var/www/vhosts/drupal.dev/drupal/web/sites/default/files/

# Create config dir and allow write.
mkdir /var/www/vhosts/drupal.dev/drupal/config
sudo chcon -R -t httpd_sys_rw_content_t /var/www/vhosts/drupal.dev/drupal/config/

# Restart apache
sudo apachectl restart

Prepare Drupal for Installation

# Create settings.php file.
cd /var/www/vhosts/drupal.dev/drupal/web
sudo chcon -R -t httpd_sys_rw_content_t /var/www/vhosts/drupal.dev/drupal/web/sites/default/settings.php

# Create services.yml file.
cp sites/default/default.services.yml sites/default/services.yml

# Set cookie domain to prevent access denied errors on login.
sed -i "s/# cookie_domain: '.example.com'/cookie_domain: '<the-fully-qualified-domain-of-instance>'/g" sites/default/services.yml

Install Drupal

Here is a one-liner that will output the fully qualified domain name of our instance:

host $(aws ec2 describe-instances --instance-ids $instance_id --query 'Reservations[*].Instances[*].[PublicIpAddress]' --output json | python -c "import sys, json; print json.load(sys.stdin)[0][0][0]") | awk -F' ' '{ print $5 }' | rev | cut -c 2- | rev

You should see the Drupal 8 installation page. Provide the database name, user and password we created previously and proceed with installation.

Note: During installation, you might experience mysterious issues. Try a different browser. If it works in a different browser, then it might be a session/cookie issue. This may only evident in Chrome and not in other browsers. If this is the case, go to the Chrome settings and delete any cookie for that domain.

See: - https://www.drupal.org/node/2613796

Once the installation is complete, we should move the config sync dir outside of the docroot:

# Move sync dir to custom config dir
mv sites/default/files/config_*/sync ../config/

# Remove deprecated dir
rm -rf sites/default/files/config_*

# Change the sync dir in settings.php
sed -i "s#\$config_directories\['sync'\] = 'sites/default/files/.*#\$config_directories['sync'] = '..\/config\/sync';#" sites/default/settings.php

You should now have a basic Drupal dev environment. Again, if you see access denied errors when trying to login, try a different browser. If it works in a different browser, then it might be a session/cookie issue. See the following issues:

Stop or start the instance

The following commands can be used to start, stop, or terminate the instance.

# Stop instance
aws ec2 stop-instances --instance-ids $instance_id

# Start instance
aws ec2 start-instances --instance-ids $instance_id

# Terminate
aws ec2 terminate-instances --instance-ids $instance_id

Resources

Let's work together to deliver a success story.