Previously I shared how to get started with Docker and Docker Compose to start hosting your own projects, but what if the project you want to host is your own custom code? If you want to host your own code in a Docker Image that means you will need to create your own Docker Image for that. I will discuss how to do this with your own Drupal site, but for all other things you'd want to create a Docker Image for Docker has more than enough documentation out there to help you do it for whatever you'd need.
Into To Dockerfiles
What even is a Dockerfile? A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. It's a plain text file that on each line (called a layer in Docker terminology) is a different command Docker needs to run to build the image.
A very basic Dockerfile example is the following:
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py
Each instruction creates one layer:
FROM
creates a layer from theubuntu:18.04
Docker image.COPY
adds files from your Docker client’s current directory.RUN
builds your application withmake
.CMD
specifies what command to run within the container.
On the first line we have a FROM
this is important and very useful, you don't need to create everything from scratch but can start with a base and build your image on top of that. The colon 18.04 refers to what tag of Ubuntu we are using, which is a lot of times used to choose a version number or a flavor of the image. So we are starting with Ubuntu version 18.04. Then COPY
all files in the current path on our system into the /app
path in our image, this is used to take your code and put it into the image. After that we RUN
a make
command in the /app
directory which will build whatever is needed and defined in that makefile. Finally we run CMD
which is executing a bash command of python /app/app.py
which is starting up our code making it run.
That's a very basic sample but shows you everything you need to build upon making more complicated Dockerfiles. Whenever you're starting see if there is a good base to build upon, if you cannot find one Ubuntu server is probably a decent starting place.
Creating A Dockerfile
Luckily for us trying to build a custom image for Drupal there is a Drupal Docker image that already has everything we need installed to run Drupal. Much like the Ubuntu Image you can use the tag of what version of Drupal you need to run. The image itself doesn't have any code for Drupal in it, it's just a setup server with everything needed to run a Drupal site. Depending on your needs you can probably get away with doing a FROM
on Drupal COPY
in your files and then CMD
apache2-foreground
and be done. The Dockerfile I normally run does a little more than that as we found we like to modify the image a little bit more. Our finished Dockerfile is below (Also available as a Gist):
FROM drupal:8.7
RUN apt-get update && apt-get install -y libxml2-dev imagemagick mysql-client --no-install-recommends
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
RUN { \
echo 'memory_limit = 196M'; \
echo 'display_errors = Off'; \
echo 'post_max_size = 64M'; \
echo 'file_uploads = On'; \
echo 'upload_max_filesize = 64M'; \
echo 'max_file_uploads = 20'; \
} > /usr/local/etc/php/conf.d/codekoalas-settings.ini
RUN sed -i 's/\/var\/www\/html/\/var\/www\/html\/docroot/g' /etc/apache2/sites-available/000-default.conf
RUN rm -rf /var/www/html/*
COPY ./ /var/www/html
RUN apache2-foreground
What this Dockerfile does layer by layer
FROM
Drupal:8.7 to get a base server that has PHP and other dependencies needed to run a Drupal siteRUN
running an apt-get update and install of a couple extra packages we run into needing beyond what the base Drupal image gives us.RUN
that command tells Docker to install and enable mysqli, that's needed if you want to run any Drupal or Drush commands on your server ever, the command line needs to know how to talk to mysql.RUN
We are creating a php conf file with some settings we like over the default php settings. If you ever need larger file uploads or more memory you could edit this line right here.RUN
We are taking the default Apache site conf and changing the path from/var/www/html
to/var/www/html/docroot
as we build all of our sites in docroot.RUN
We are deleting any files currently in/var/www/html
as Apache normally gives you a plain index.html and we don't want that.COPY
Copying our Drupal site into/var/www/html
RUN
starting Apache so the site will be served.
Once you have that file created you should run the docker build command locally to test it and see what happens. The Docker Build command will be docker build -t user/project:optionaltag .
ran from inside your directory with the Dockerfile. That -t
part is you naming your image, like how we are calling drupal
in the from layer we need to name our image so we can reference it later when telling Docker to spin it up. After running that command if it doesn't fail you should be good to go!
From there you could move that build off to a pipeline by setting up some pipeline config with your repo or you could create a cron that pulls your repo and rebuilds every so often, whatever works for you. You can see the Dockerfile we run for our Drupal sites at Code Koalas on our Github repo Koality Drupal.
Spinning up your image
In my last post I showed you how to get started with Docker Compose to host your projects. Using that knowledge to get your new site setup using that image your new docker-compose.yaml
file will look like the following:
version: '2'
services:
nginx-proxy:
container_name: nginx-proxy
image: jwilder/nginx-proxy:alpine
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- /home/joshfabean/letsencrypt/certs:/etc/nginx/certs
- /etc/nginx/vhost.d
- /usr/share/nginx/html
- ./nginx-proxy/nginx.tmpl:/app/nginx.tmpl
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: letsencrypt
volumes_from:
- nginx-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /home/joshfabean/letsencrypt/certs:/etc/nginx/certs:rw
mydrupalsite:
container_name: mydrupalsite
image: fabean/mydrupalsite:latest
environment:
- VIRTUAL_HOST=mydrupalsite.joshfabean.com
- LETSENCRYPT_HOST=mydrupalsite.joshfabean.com
- DATABASE_NAME=default
- DATABASE_USERNAME=user
- DATABASE_PASSWORD=user
- DATABASE_HOST=db
restart: always
volumes:
- ./mydrupalsite:/var/www/html/docroot/sites/default/files
The main thing to notice is on the line for image
we put in what we typed in on the docker build
command in the -t
section. If you have an image with that name locally it will just pull that image, if not it will check other docker registries your are logged into or dockerhub for that image.
Wrapping Up
Building your own docker images isn't that hard and is super powerful. You are now no longer restricted to only hosting pre-built services in Docker but now you can host anything you can dream up in Docker. If you have questions or need more help Docker has great documentation or you can reach out to me on Twitter and hopefully I can point you in the right direction.