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:
- The PHP base Docker image is used for serving PHP web applications, and does not any command line tools to accomplish it’s mission.
- 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.
- 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:
And this is the
Dockerfile code for the Drush image:
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:
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:
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 … ?
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:
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:
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
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
./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:
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.
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:
Armed with a new database name, I can resume the installation process:
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 :
Back to the install. Let me give my site a self-descriptive name:
And here we go — our new Drupal site installed on a Docker container, running Alpine Linux all the way:
Regarding those missing styles, the Drupal Administration -> Reports page probably gives us a hint as to why:
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.