[1/3] - Dockerising FreshRSS (PHP)


Covering how to use Docker with Symfony is useful, but in the wider scheme of things you likely use more than just Symfony in your stack. For the purposes of this demonstration we're going to use Docker with another PHP app, one completely unrelated to Symfony.

Recently I wanted to self-host an RSS feed reader. RSS readers are a little less popular today than they were back in ~2012-ish, but since the discontinuation of Google Reader I've never found a replacement service that met all my needs.

There are, however, a bunch of great self-hosted alternatives.

My requirements were fairly standard I feel:

  • Can be self-hosted
  • Ideally built with PHP
  • Is open source
  • Has recent updates to the code base

I found a great list of options. Many were written in other languages (Ruby, Python, Java), so I immediately focused on the PHP options. One in particular caught my eye - Tiny Tiny RSS - as there is a Docker image for the project.

I tried Tiny Tiny RSS but had trouble with adding a Tumblr feed. There seemed to be plugins available to enable Tumblr support, but as I had a bunch of alternatives from the earlier mentioned list of RSS feed readers, I decided I would check out the 'competition' before mucking around with adding plugins to the Docker image.

Next I tried Fresh RSS.

I couldn't find a demo of this that would allow me to test my Tumblr feed, but decided heck, let's just install it and see how we get on.

The installation instructions are ok, but scattered about a bit.

Here's the gist of what we are working with:

  1. Get FreshRSS with git or by downloading the archive
  2. Dump the application on your server (expose only the ./p/ folder)
  3. Add write access on ./data/ folder to the webserver user
  4. Access FreshRSS with your browser and follow the installation process
  5. Everything should be working :) If you encounter any problem, feel free contact us.
  6. Advanced configuration settings can be seen in config.default.php and modified in data/config.php.
  7. When using Apache, enable AllowEncodedSlashes for better compatibility with mobile clients.

And there's also a handy example of a full installation on Ubuntu or Debian flavoured Linux.

We're going to 'steal' all of this to make our Docker setup.

PHP Docker Image

We're going to use the PHP Docker image that we built earlier in this course.

There are some extras added to that PHP Docker image that are primarily in there for the benefit of Symfony.

What we could do, should we desire, would be to create an extra tier / new Docker image:

  • Base PHP image
  • Symfony customised PHP image

I'm not going to do this.

The extra bits added for the benefit of Symfony will cause no harm, and only slightly bloat the resulting base PHP image size. For the requirements I have, this is fine. If you want the slimmest possible file size then by all means break out your scalpel and start trimming.

We will create a new PHP image based on our base image.

Into this image we will do some things we have already covered - setting a working directory via an argument, setting the user to be www-data, and exposing the working directory as a volume.

We will also do some new things - cloning a git repo, checking out a specific branch, changing permissions on this directory. All of this comes from the Fresh RSS installation instructions detailed above.

Starting Point

As we've already covered the initial steps when making our Symfony-specific PHP Docker image, I'm going to skip over this step and show the starting point:

FROM docker.io/codereviewvideos/php-7:latest

ARG WORK_DIR
WORKDIR $WORK_DIR

USER www-data

VOLUME $WORK_DIR

If any of this is uncertain to you, please watch the earlier videos in this series.

You can use whatever setup you like, but I'm going to put this Dockerfile into a sub directory of my main project directory:

mkdir php
mv Dockerfile ./php

Now let's take a look at the 7 installation steps and see how many we can get through.

Installing Fresh RSS With Docker

We're going to use the 7 installation steps as high level guidance, and take the example of a full installation on Ubuntu or Debian flavoured Linux as our low level required command / reference for building our Docker image.

The first installation instruction is:

Get FreshRSS with git

Referencing the full example we can see:

# For FreshRSS itself (git is optional if you manually download the installation files)
cd /usr/share/
sudo apt-get install git
sudo git clone https://github.com/FreshRSS/FreshRSS.git
cd FreshRSS

Ok, sounds easy enough.

We are using Docker's WORKDIR concept to have already changed our directory to whatever dir we want to work with. Therefore we can skip the first cd command.

Our PHP base image contains git. This means we can run git commands, such as git clone, git checkout, etc. As such we don't need to apt-get install git either.

Let's add this in:

FROM docker.io/codereviewvideos/php-7:latest

ARG WORK_DIR
WORKDIR $WORK_DIR

RUN git clone https://github.com/FreshRSS/FreshRSS.git $WORK_DIR

USER www-data

VOLUME $WORK_DIR

This is no different to how you would clone a project from GitHub (or similar) to your local machine.

We're cloning the entire repository into the Docker image under whatever working directory we pass in at build time.

This also means that we don't need to run the last command: cd FreshRSS, as we are already in the directory we just git clone'd in too.

Next:

Dump the application on your server (expose only the ./p/ folder)

Well, we kinda covered this in step one. We cloned the entire application, including the /p/ directory.

We will expose the /p/ directory via our web server config, which we haven't got to just yet.

Additional Step

Not covered in the high level instructions, but shown in the full installation example is:

# If you want to use the development version of FreshRSS
sudo git checkout -b dev origin/dev

We're hardcore devs who love living on the bleeding edge, amirite?

New and shiny all the way!

Let's add this in:

FROM docker.io/codereviewvideos/php-7:latest

ARG WORK_DIR
WORKDIR $WORK_DIR

RUN git clone https://github.com/FreshRSS/FreshRSS.git $WORK_DIR
RUN git checkout -b dev origin/dev

USER www-data

VOLUME $WORK_DIR

There's an optimisation we can make here regarding the RUN instruction, but for the moment we are going to skip that. We will address this later.

Permissions

You can't go far in software dev without running into permissions.

Add write access on ./data/ folder to the webserver user

We know our webserver user will be www-data.

The automated instructions detail exactly what permissions are required:

# Set the rights so that your Web server can access the files
sudo chown -R :www-data . && sudo chmod -R g+r . && sudo chmod -R g+w ./data/
# If you would like to allow updates from the Web interface
sudo chmod -R g+w .

Let's add these in to our Docker image:

RUN chown -R www-data:www-data $WORK_DIR
RUN chmod -R g+r $WORK_DIR
RUN chmod -R g+w ./data/

Ok, let's break this down so we understand what's happening.

chown -R www-data:www-data $WORK_DIR

This changes ownership of our working directory to the user and group of www-data.

The -R flag means recursive, so any sub directories also get their owning user and group changed.

This is slightly different to the automated installation instructions, which only changed the owning group. In our Docker image this makes no difference.

chmod -R g+r .

This gives the owning group (www-data) the permission to read the current directory (.) and any sub directories (-R).

chmod -R g+w ./data/

This gives the owning group (www-data) the permission to write to the /data directory, and any sub directories of the /data directory (-R).

There is an extra step here that we skip:

# If you would like to allow updates from the Web interface
sudo chmod -R g+w .

To update our code we will need to build a new Docker image - otherwise we would need to worry about volumes, and / or changes being only saved to the existing container. Better to 'extract' this part, in my opinion.

Optimising the RUN Instruction

As it stands, we have five RUN instructions.

Each instruction means a new layer in our Docker image.

This used to be a bigger problem in the earlier days of Docker, and certainly on larger builds (such as our base PHP image) this is a bigger concern.

Even so, it's a good idea (in my opinion) to streamline instructions where possible. This helps with caching, which can help with build times. It's a more advanced concept that we're not going into here, but you can find out more here if at all interested.

Combining instructions is easy. It's exactly the same as combining commands on the command line of your OS. We just need to use the && operator.

Also, we will split onto new lines for readability. To do this, we will use the \ slash.

Here's our revised image:

FROM docker.io/codereviewvideos/php-7:latest

ARG WORK_DIR
WORKDIR $WORK_DIR

RUN git clone https://github.com/FreshRSS/FreshRSS.git $WORK_DIR && \
    git checkout -b dev origin/dev && \
    chown -R www-data:www-data $WORK_DIR && \
    chmod -R g+r . && \
    chmod -R g+w ./data/

USER www-data

VOLUME $WORK_DIR

Next Up

To continue we need to start working with our web server. In order to do that we need another image - a web server image.

We'll get to that in the next video.

Code For This Course

Get the code for this course.

Episodes