Series Part II — Optimizing My Alpine Docker Image for Use With Drupal 8.x and PHP 7.3

Callback Insanity
8 min readJan 2, 2020

Today is January 1st, 2020 — and I welcome this new decade with the second part to my story about using Drupal with Alpine Linux and Docker. You can read the first part here.

To Recap: On the first part I created a minimal Alpine image which measured a mere 29.5 megabytes of disk space — but even though I followed Drupal.org’s list of required dependencies and installed them in my Alpine Docker image, I was unsure if that minimal set of dependencies was going to be enough to run the Drupal CMS.

In this story I am going to follow through and go through the process of testing the previously created Docker image to see if it’s got what it takes to run Drupal.

Docker Image Structure

In order to keep different aspects of my software stack logically divided, I am going to keep the core PHP image I built before alone, then build a new set of images on top of it just for command-line applications. These Command-Line Interface, or CLI aps are going to depend on the dependencies already installed on the core image. Here’s an hierarchical overview of my build plan:

#1 php7-fpm.core:alpine-3.11 -> Minimal PHP image
|
|- #2 php7-cli.core:alpine-3.11 -> CLI PHP image, adds Composer
|
| - #3 php7-cli.drush9:alpine-3.11 -> Adds Drush 9

Why structure things like that? Flexibility:

  1. The PHP base Docker image is used for serving PHP web applications, and does not any command line tools to accomplish it’s mission.
  2. The Composer Docker image can be used for installing a myriad of Composer projects not related to each other, but all using PHP and Composer — such as: Wordpress, Drupal, Symfony, Laravel, etc.
  3. The third Docker image includes the dependencies of the previous two and installs Drush version 9 using Composer. Drush is the command line tool used for installing and maintaining Drupal.

Building Composer and Drush

I’ll be building both Composer and Drush images at once, using a single command.

This is the Dockerfile code I’ll be using to build the Composer image:

php7-cli.core:alpine-3.11.Dockerfile Docker Image #2 → CLI PHP image, adds Composer

And this is the Dockerfile code for the Drush image:

php7-cli.drush9:alpine-3.11.Dockerfile: Docker Image #3 -> Adds Drush

If you’re wondering how I’m building the Docker images, I use a single command to build all the Dockerfiles avaiable at https://github.com/AlexanderAllen/localenv:

docker-compose -f ./build/nginx/docker-compose.yml build \
&& docker-compose -f ./build/mariadb-alpine/docker-compose.yml build \
&& docker-compose -f ./build/php-fpm/docker-compose.yml build \
&& docker-compose -f ./build/php-cli/docker-compose.yml build

After running this build command, the Composer Docker image (image #2) throws some complaints:

Composer: You Must Install Additional Pylons

I get two errors, both given by Composer: 1) Composer is saying that it it’s missing PHAR; 2) And, that either iconv or mbstring need to be installed.

After adding php7-mbstring and php7-phar to my Dockerfile , and running the build command for both Compose and Drush images, the build successfully finishes:

Composer and Drush images built

Having installed both PHAR and MBString, and I can move on to installing Drupal. I used Composer to install Drush. And will use Drush to install Drupal.

Do note that both the Composer and Drush 9.x images are coming in at 143MB and 224MB respectively, and as such they’ll be prime targets for getting Marie Kondo’d later on.

Before I continue to the next section, I’ll clarify a couple acronyms I’ve thrown around so far, just in case:

What is … ?

Drupal: It’s a Content Management System (CMS), written in PHP. It’s been around for a while, 20 years to be give or take a month or two!

Drush: A command line tool used to install Drupal. Composer is used to install Drush.

Composer: It’s the command line based tool written in PHP that the PHP world uses to install other PHP packages, some of them distributed using PHAR. Medium has a good list of Composer-related articles.

PHAR: Standing for PHP Archive, it’s a packaging format used for distributing PHP applications. You can read more about PHAR in this Medium story: https://medium.com/@tfidry/create-and-deploy-secure-phars-c5572f10b4dd.

MBString: The M and B characters in MBString stands for Multi-Byte and is used for handling internationalization, UTF-8, and it’s related formats.

Installing Drupal using Composer

Not too long ago, https://github.com/drupal-composer/drupal-project used to be the recommended way to install Drupal using Composer. As of Drupal 8.0.0, the recommended way to install Drupal using Composer is https://github.com/drupal/recommended-project. It looks like this change was made official on September, 2019 — so it’s pretty recent. The main difference I can tell is that drupal-composer/drupal-project is not “officially” maintained by Drupal.org and drupal/recommended-project is. I use quotes because it’s kinda all the same people from the same open-source community ^_^

Having identified the current recommended way to install Drupal (using Composer), I’ll fire up the brand spanking new Drush container I just created:

Docker Compose command:

docker-compose: sudo make me a drush sandwhich https://xkcd.com/149/

Screenshot:

Ready to install Drupal using Drush …

This is the command according to Drupal.org to install Drupal:

composer create-project drupal/recommended-project my_site_name_dir --no-interaction

Or rather, the command “template” to use. This is the actual command I’m running in my Alpine Docker container for Drush (I named my example project localenv-2020):

composer create-project drupal/recommended-project localenv-2020 --no-interaction

And this is the actual, full installation in action, in three screenshots:

The Result:

No warnings or errors showed up in the console, which is excellent!

But the Drupal installation is not complete, yet.

The Real Test — Running the Drupal Installation Wizard, Using My Alpine Docker Image

The Composer composer create-project drupal/recommended-project [project name] command downloaded the Drupal project to the current shell directory I was in. In this case the location where all my project source code is located. In order to finish the Drupal CMS install I need to point my Nginx container to my new Drupal project location.

My Nginx container is expecting files to be served from the ./app directory of my Docker project. My Docker project’s ./app directory doesn’t have anything important in it except for a test page, so delete the app directory and move the Drupal project there:

3bd1402354e7# rm -rf localenv/app

Then move Drupal into the app directory:

3bd1402354e7# mv localenv-2020 localenv/app

Now Nginx is expecting the actual document root to be in ./app/docroot , and Drupal by default has it’s document root in the web directory, so a symbolic link will link these two:

3bd1402354e7# ln -s web docroot
3bd1402354e7# ls -lad docroot web
lrwxrwxrwx 1 root root 3 Jan 2 00:34 docroot -> web
drwxrwxrwx 2 root root 4096 Jan 2 00:34 web

Now the ./app/docroot where Nginx expects to find it’s contents points to the ./app/web directory where Drupal has it’s document root and we should be able to serve the Drupal installation page:

SUCCESS: Behold The Drupal Installation Page !

Presenting the Drupal installation page with no major errors so far is … well, a major victory!

But now I have to run through the actual Drupal installation wizard to finalize my test.

Unami? Sounds fancy delicious !
Some things to address later …
Drupal telling us our Docker container’s specs. Everything checks out out-of-the-box.
Ahh … I need a database!

I need a database, so I can create one using the existing MySQL container that I created as part of my Docker stack. My Docker Compose exposes MySQL port 3306 to my local machine so that any MySQL client can access the database without having to open a Docker container to log in. So from my local machine I run:

mysql -u root --host=127.0.0.1

And voila, I can create the Drupal database:

My new Drupal database name is happy2k20 :)

Armed with a new database name, I can resume the installation process:

Downloading translations took a minute, I’d be nice to skip it.

If you want to see a display of real-time resource usage statistics, you can use docker stats to show the current Docker containers’ statistics:

There you can see that I’ve assigned almost gigabytes of memory to my Docker for Windows instance, and that MySQL is consuming 53.07% of the available CPU resources during installation, and PHP 9.99%. Memcached has not been configured in the Drupal settings so it’s sitting there pretty doing nothing. Memory usage is very low across the board, which is awesome.

To view current running processes, you can use docker-compose top :

docker-compose top

Back to the install. Let me give my site a self-descriptive name:

I hope Medina Station gets the Memo Klaes Ashford sent them
Final Step ?

And here we go — our new Drupal site installed on a Docker container, running Alpine Linux all the way:

The styles seem a bit off for a fancy demo … but OK, I’ll take an order of that Quiche.

Regarding those missing styles, the Drupal Administration -> Reports page probably gives us a hint as to why:

Failed to create style directory

Probably something related to Window 10’s WSL permissions and Docker for Windows to investigate down the line, and an opportunity to write another story…

~ That wraps up today’s story about Docker, Alpine, PHP, and Drupal ~

Next up on this Dockerland journey I want to write about:

  • Putting the Composer and Drush containers on a diet too. Will probably involve creating a core or base version of these with minimal dependencies, production environment-like, and another one for development with debugging capabilities. Right now Drush container has everything in it kitchen sink included.
  • Building a Docker image with Drupal already installed in it. This will involve installing Drupal programmatically through the command line using Drush — which is how most production Drupal installations happen anyways.
  • And so much more ! But so much to cover and so little time !!

Happy New Year 2020 and remember to clap if you liked my story.

--

--

Callback Insanity

Organic, fair-sourced DevOps and Full-Stack things. This is a BYOB Establishment — Bring Your Own hipster Beard.