2021.05.14 - Docker... part1?

I finally break down and learn how to use Docker so I can run PHP 5.3

Intro

I've been very resistant to using Docker as it seems like most people just like to use it in place of just writing simple install directions and a dependancy list. I'm not a big fan of things just magicly working, I like to be in the middle. That said sometimes you just need stuff to work and don't want it messing with other code on your server. I ran into that situation when migrated an old CentOS 6 webserver to AlmaLinux 8 (and moving from php 5.3 to php 7.2).

Most of the code on the server was much happier under php 7, a couple of pages needed small updates, but I had three websites made by an old web developer long gone that refused to run without massive code changes. The right approch was probably to contact the site owners and say you have until this date to get your website upgraded, but that's not something I was to push on a bunch of small businesses, especially right now. But putting off the server upgraded isn't an option as it's already way later than it should be and running a server just for three websites isn't realistic.

Getting Started

So first, getting Docker installed and working. It's really pretty easy, I just followed the steps here: https://docs.docker.com/engine/install/centos/ The page is for CentOS 7/8 but should work fine on any Red Hat Enterprise Linux 8 like system. Basicly just add their repo, install Docker, start Docker and run a quick test. Also be sure you set Docker to start on system boot. One of the things this will add to your server is a new network interface to talk to all the Docker systems. It also adds a firewall rule to permit all traffic from the containers to reach your system. This works for me, but might be something you'd want to adjust.

I also found this guide by Pascal Landau to be a huge help in just understanding how Docker works. The guide is written for Windows 10, but once you have Docker installed it's basicly the same on all OSes.

Docker Commands

To interact with Docker you'll be using a bunch of CLI commands that all start with Docker. Here's a useful list:

docker ps -a # List all docker containers, running or stopped docker rm -f {container name} # Delete the container docker start {container name} # Start the container, replacing Start with Stop does what you think docker logs -f {container name} # View logs being generated by the container docker exec -it {container name} bash # Runs a command in the container. By running bash (or sh) # you can get a normal terminal to explore the container docker kill --signal="USR1" {container name} # I think it sends a kill signal to the forground proccess in the container # In this case the USR1 signal causes Apache to re-read it's config docker system prune # Handy if you've been playing with Docker and want to clean up # Can be dangerous, as it deletes anything not currently in use

Installing the PHP Container

I was very lucky to come across this GitHub project by Eugene Ware which was a setup of php 5.3 running with Apache on Debian jessie. Yes, jessie LTS support ended on June 2020, but I'm not sure I want to mess with trying to get this to run on something newer. To start with I downloaded all the files from his repo and set the scripts to executable:

wget https://raw.githubusercontent.com/eugeneware/docker-php-5.3-apache/master/apache2-foreground wget https://raw.githubusercontent.com/eugeneware/docker-php-5.3-apache/master/docker-php-ext-configure wget https://raw.githubusercontent.com/eugeneware/docker-php-5.3-apache/master/docker-php-ext-enable wget https://raw.githubusercontent.com/eugeneware/docker-php-5.3-apache/master/docker-php-ext-install wget https://raw.githubusercontent.com/eugeneware/docker-php-5.3-apache/master/apache2.conf wget https://raw.githubusercontent.com/eugeneware/docker-php-5.3-apache/master/Dockerfile chmod +x apache2-foreground docker-php-*

Then I was able to run the command docker build -t docker-php-image -f Dockerfile . to build the docker image. This command has to be run from the same directory the files are downloaded into. It reads the Dockerfile and builds a docker image named docker-php-image. It compiles a bunch of code so it does take a while to run.

Once the image is built it can be used in a container by running: docker run -di --name php5.3 -p 8080:80 docker-php-image This command names the container php5.3 and maps port 8080 on the host to port 80 on the container. When you are done playing with this delete the container with docker rm -f php5.3. I've still got more work to do before this is useful for me.

EDIT: So If you don't want port 8080 open to the world, or just don't want IPv6 Addresses spawning you can use -p 127.0.0.1:8080:80. To change this on an already setup docker image is annoying, but possible. First get the short name of the docker image/container docker ps, then use that to get the full id: docker inspect {short id} | jq | grep Id Then open the directory: cd cd /var/lib/docker/containers/{long id} Then you'll need to stop the docker docker stop {short id}, edit the hostconfig.json file, restart docker service docker restart and then restart the docker image docker start {short id}.

For example purposes I'll be setting up two websites, test.com and example.org. On the host server they are each setup as Virtual Hosts and the files for the site are each in their own home directory.

First I needed to add a php.ini file as the provided image doesn't include one. Since I already had a server working on php 5.3 I just copied the php.ini file from there and put it in the same folder as the other files I downloaded. The image uses /var/www/html as the webroot so I plan to create two folders in there, one for each site to mirror the real life folders in their home directories on the host. I also need to enable one apache mod, rewrite. So with that in mind I made the following additions to the end of the Dockerfile (just before the WORKDIR /var/www/html line).

RUN mkdir /var/www/html/test.com /var/www/html/example.org # Make the directories that we'll link to real folders on the hose RUN ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/rewrite.load # Enable the Apache rewrite module COPY php.ini /usr/local/lib/ # installed the php.ini file we copied from our current php server

Then I'll also be adding some code to the end of the apache2.conf file to enable the VirtualHosts on the container:

<VirtualHost _default_> ServerAdmin admin@test.com ServerName www.test.com DocumentRoot /var/www/html/test.com </VirtualHost> <VirtualHost _default_> ServerAdmin admin@test.com ServerName test.com DocumentRoot /var/www/html/test.com </VirtualHost> <VirtualHost _default_> ServerAdmin admin@example.org ServerName www.example.org DocumentRoot /var/www/html/example.org </VirtualHost> <VirtualHost _default_> ServerAdmin admin@example.org ServerName example.org DocumentRoot /var/www/html/example.org </VirtualHost>

Running the Container

If you haven't yet rebuild the Docker image after making the above changes, now is a great time: docker build -t docker-php-image -f Dockerfile . Once that's done then you actaully need to create the Docker Container. Mostly using the same command as we ran above for testing, but here we'll also be mapping some folders on the Host to folders on the Container. The folders will be synced, so if something changes on the Container that change will be reflecting in the host and vice versa. docker run -di --name php5.3 -p 8080:80 -v /home/test/public_html/test.com:/var/www/html/test.com -v /home/example/public_html/example.org:/var/www/html/example.org docker-php-image If all went well you should now be able to access the sites, albeit on port 8080 instead of the normal 80. I did find a couple of PHP files with Hard Coded paths that were upset becuase they were now running out of /var/www/ instead of /home/. I was able replace part of the path with the PHP Constant __DIR__ to let the script figure out where it's at automaticly.

Configuring Apache for Reverse-Proxy

There are some differant ways you could move on from here, but I'll be using Apache to proxy requests for the sites on the Host to the Docker Container. On the host it's easy enough to add the reverse proxy settings to the VirtualHost:

<VirtualHost _default_> ServerAdmin admin@test.com ServerName www.test.com DocumentRoot /home/test/public_html/test.com ProxyPreserveHost On ProxyPass "/" http://localhost:8080/ ProxyPassReverse "/" http://localhost:8080/ </VirtualHost>

Of course repeat the above as many times as needed for each ServerName. The DocumentRoot command doesn't actaully do anything as the Proxy command takes over, so you can remove it all together if you'd like. The ProxyPreserveHost is important as it passes the requested domain through, otherwise everything is way more complicated. I also found it's best to test in an incognito/InPrivate window, as if you make a mistake in the proxy config it's very hard to convice the webbrowser to forget and actaully try again, an incognito/InPrivate window you can just close and reopen.

-Nick