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:
- https://drupal.stackexchange.com/a/233753/24799
- https://stackoverflow.com/a/45507106/871793
- https://www.drupal.org/forum/support/post-installation/2011-01-14/drupal-7-access-denied-user1-after-correct-login#comment-11992952
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
- https://serversforhackers.com/c/installing-php-7-with-memcached
- https://www.digitalocean.com/community/tutorials/how-to-upgrade-to-php-7-on-centos-7
- https://httpd.apache.org/docs/2.4/logs.html
- https://www.drupal.org/docs/develop/using-composer/using-composer-to-manage-drupal-site-dependencies
- https://www.jeffgeerling.com/blog/2018/converting-non-composer-drupal-codebase-use-composer
- https://www.jeffgeerling.com/blog/2017/tips-managing-drupal-8-projects-composer
- https://stackoverflow.com/a/9189153/871793
- https://ubuntuforums.org/showthread.php?t=1475091
- https://www.disk91.com/2015/technology/systems/move-your-httpd-apache-files-on-centos-7/