Docker – part 2 – containers
Rather than diving straight into getting a a large system like WordPress up and running, It’s useful to play with containers on their own first. It shows us why they’re useful and why we would want to split up our WordPress installation into multiple containers instead of one monolithic container running the full stack.
Why
As mentioned in part 1, splitting into component containers allows us to easily swap in and out parts of our stack. Be it a new PHP version, maybe a new web server and PHP version just to see how it runs. Maybe you need a specific version of MariaDB because that’s all the host offers.
Starting off simple
To show this in action if that also shows more than just a “hello world” on the command line I thought instead we could show it in the browser.
It’s quite common these days to split up separate parts of your web apps into smaller services, so that’s what we’ll do. We will have:
- A simple API written in PHP that responds to
/api/greetingand randomly returns a “Hello, World” in some language - A small react app that consumes that API in the browser
- To show off container reuse, we’ll also have a go cli app that will consume the api and echo out the output.
Doing it like this means that we can deploy our little react app to Vercel, or Netlify or a digital ocean droplet. We keep our API and app separate from each other. This also means that tomorrow, the boss saw a fireship video and decides that Vue is where it’s now at, you only rebuild part of your infrastructure.
PHP API
Check out the following repository: PHP API
There’s not much happening here, a router to route requests and a greetings endpoint.
The Dockerfile is the recipe card to run our API in an isolated container. Lets break it down:
FROM php:8.2-alpine
This tells docker that we want to base our new image from the PHP alpine image using PHP 8.2.
Side tangent: Alpine images
It’s quite common when looking on docker hub to see an image with an alpine variant. Alpine variants are smaller images with a lot of things stripped out in order to both reduce the overall image size as well as keeping things more secure due to having less software installed on them.
As a point of size comparison here’s the sizes of the various php images at the time of writing this post:
- php:8.2-fpm – 490MB
- php:8.2 – 525MB
- php:8.2-fpm-alpine – 81MB
- php:8.2-alpine – 99.8MB
Be aware that Alpine uses musl libc instead of glibc so you may get some compatibility issues depending on what you’re trying to run. For most web dev activities you should be fine. If you’re working with a language like C or Go then you might run into issues.
Back to the Dockerfile
WORKDIR /usr/local/bin
This essentially changes directory into /usr/local/bin because that will exist on just about every Linux system
COPY . .
We then take all our PHP files and copy them into the container.
EXPOSE 8080
We tell the container to open port 8080 so that things will be able to connect to it
CMD ["php", "-S", "0.0.0.0:8080", "router.php"]
Finally, we tell the container that when its created and started, run the php built in server with 0.0.0.0 so that it will be accessible from anywhere in the network. We’ll put it on port 8080 and have it run the router.php file.
Now we can build our image with this Dockerfile, we do this by running the build command and passing a flag called -t (for tag) to name our image and telling it where our Docker file is, the current directory . :docker build -t php-simple-api .
This should take a minute and finish. Once it does we can then run it with:docker run -p8080:8080 --name php-api php-simple-api
This might look a little much, so lets go through it:
docker runis the docker command to run a container-p8080:8080tells docker to map a port FROM the host : TO the container.--name php-apigives our container a friendly same that we can identify it when usingdocker psto see whats runningphp-imple-apiis our image name that we want to run the container with.
Now that the container is running you can stop it by using its name (php-api)docker stop php-api
If you called it something different you’ll need to use whatever you called it. If you didn’t set a name, or can’t remember what you called it, you can check the output of docker ps