[3/3] - Dockerising FreshRSS (docker-compose)
We've got our PHP and Web Dockerfiles created. Let's now tie them together using docker-compose
.
First, let's create the docker-compose.yml
file. I'm going to do this in the project root.
cd {project root}
touch docker-compose.yml
Whilst here, I'm also going to create a Makefile
.
The purpose of the Makefile
is to simplify the creation of both the running environment, and also the building of our Docker images.
touch Makefile
Before adding content to the Makefile
though, let's see the Docker build commands individually.
Building the PHP Image
First, to build our PHP image:
docker build \
--build-arg WORK_DIR=/var/www/freshrss \
-t freshrss_php ./php
I'm splitting the command across multiple lines using the \
. You do not need to do this. I'm doing this for readability.
We've covered this command already, but for clarity:
docker build
is the core command to trigger a Docker build process to build an image from a given Dockerfile
.
--build-arg WORK_DIR=/var/www/freshrss
sets the expected build argument as defined in our Dockerfile. In this case our
Dockerfileexpects a
WORK_DIR` argument to be passed in. You can change this as desired, but remember to update the hardcoded value in the nginx config from the previous video.
-t freshrss_php ./php
This is actually two parts.
First, we tag the image (-t freshrss_php
). This means when the image is built, it will be tagged locally as freshrss_php
. When building the web image we will tag as freshrss_web
. Of course, feel free to name your tags however you like.
Lastly we tell the docker build
command where our Dockerfile
lives.
Typically we've run this command as -t whatever .
.
The use of the period (.
) here means look in the current directory for the Dockerfile
.
In this case, however, we say look in the ./php
sub directory for the Dockerfile
.
Building the Web Image
Next to build the web image:
docker build \
-t freshrss_web ./web
Much simpler this time.
We have no build arguments, just a tag and point the docker build
command at the Dockerfile
in the ./web
sub directory.
After running these two commands we have the expected images available locally.
Let's add these two to the Makefile
:
build_php:
@docker build \
--build-arg WORK_DIR=/var/www/freshrss \
-t freshrss_php ./php
build_web:
@docker build \
-t freshrss_web ./web
Note: Tabs are very important in a Makefile
.
Composing With Docker
We have our two images. We also need a third - our database image - but fortunately, we don't need to build this ourselves.
Inside the docker-compose.yml
file let's add:
version: '3'
services:
php:
image: freshrss_php:latest
container_name: php
web:
image: freshrss_web:latest
container_name: web
depends_on:
- "php"
db:
image: mysql:5.7.20
container_name: db
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=db
- MYSQL_USER=dbuser
- MYSQL_PASSWORD=dbpassword
Ok, hopefully nothing new to you here.
Our php
service expects to use the local image of freshrss_php
. We use the :latest
tag to indicate that yup, we want to use whatever is the most recent built image.
We specify the container_name
because as mentioned in the previous video, the nginx upstream.conf
file depends on being able to access this container by hostname. This is against typical best practice for nginx, which suggests we use an IP instead. In Docker, however, hostnames are much easier to use, and we can pretty much guarantee our setup as per this very file.
The web
service is similar to the php
service.
The key difference being that we don't want the web
service to be brought online before the php
container is up. To ensure this never happens, we make our web
service depends_on
the php
service. This isn't strictly necessary, but can stop real world situations where people are hammering nginx before php is available, leading to pointless errors.
Finally the db
service.
We rely on a specific tag here: mysql:5.7.20
. At the time of recording, 5.7.20
is the latest MySQL version available. You may find a newer version, just check Docker Hub.
The container name is important. When setting up Fresh RSS we will need to provide the name of the database server. We can simply pass in db
here thanks to the container_name
property.
We must also set some mandatory environment
variables, or our db
container won't start properly.
I'm setting my typical values here for a development environment. Yes, they aren't secure. No, I'm not going to use these same values in production :)
Is this enough to get us up and running?
Not quite.
There's two potential issues we need to fix.
Exposing Yourself
It's easy to miss, but in order to access our Dockerised services we need to expose certain ports.
It may be enough to expose the web
service alone here.
Or you may also wish to expose the db
, so you can poke around using your favourite client, or if you're a true geek, the CLI.
Let's add these in:
version: '3'
services:
php:
image: freshrss_php:latest
container_name: php
web:
image: freshrss_web:latest
container_name: web
depends_on:
- "php"
ports:
- "82:80"
db:
image: mysql:5.7.20
container_name: db
ports:
- "33061:3306"
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=db
- MYSQL_USER=dbuser
- MYSQL_PASSWORD=dbpassword
You don't need to use different public ports.
I do because I have tons of Docker stacks and often have multiple stacks running concurrently.
I set web
to expose port 82
, but really that passes through to port 80
internally.
Again, we've covered this so I'm not going to dive into it again.
What this means is I can access 0.0.0.0:82
in my browser to hit this stack.
Likewise, my db
is exposed on port 33061
, which means I can use e.g. SequelPro to hit 0.0.0.0:33061
and the credentials from the environment
section to meddle with my database.
Keeping Your Data Around
Typically in this course we've used bind mounts over volumes.
Moving forwards I would suggest you use volumes. It's the best practice way suggested by Docker. It's also easier in many ways, if a little more confusing initially.
Let's add this config in now:
version: '3'
services:
php:
image: freshrss_php:latest
container_name: php
volumes:
- my_freshrss_php:/var/www/freshrss
web:
image: freshrss_web:latest
container_name: web
depends_on:
- "php"
ports:
- "82:80"
volumes:
- my_freshrss_php:/var/www/freshrss
db:
image: mysql:5.7.20
container_name: db
ports:
- "33061:3306"
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=db
- MYSQL_USER=dbuser
- MYSQL_PASSWORD=dbpassword
volumes:
- my_freshrss_db:/var/lib/mysql
volumes:
my_freshrss_php:
my_freshrss_db:
The volumes syntax admittedly looks weird.
As a heads up, you don't need to add a volume to your db
container.
If you don't, your data will persist for the lifetime of the db
container. This means if you shut your db
container down, then restart it a second, a day, a week, month, or year later, it will still be there.
It won't be there, however, if you delete and recreate your db
container for any reason.
If you want the data to hang around then you need to externalise it - ideally in a volume.
The php
service does need to use a volume, as the web
service needs access to the php
services data, in order to properly function.
Again, we have covered this already in this series.
What we've added here are named volumes.
You can, of course, switch back to version 2 syntax with bind mounts. It's really your call.
At this point you can bring up your stack with docker-compose up -d
, and browse to 0.0.0.0:82
and start using Fresh RSS. You will be guided through the installation process, and if at all unsure, see the video for a demo.
Remember to shut down after finishing with docker-compose down
.
Updating the Makefile
We can add this command into the Makefile
:
dev:
@docker-compose down && \
docker-compose build --pull --no-cache && \
docker-compose \
-f docker-compose.yml \
up -d --remove-orphans
This is simply a copy / paste from what I add to most every Makefile
I use with Docker projects.
What this allows me to do is to run make dev
from the terminal whenever I want to bring this project up. This makes every project I work with follow a set standard.
And with that, we are done.