Getting started with Docker part 1: "Hello, world!"

Getting started with Docker part 1: "Hello, world!"

9 Mar 2022 docker mysqli php

Docker is the answer to the question, "what's the best way to ship an app including not just its content but also its environment (runtime)?"

In this four-part guide I'll introduce you to Docker in the context of PHP + MySQL - though you can use Docker with almost stack. Don't worry if you don't know PHP; this is a Docker tutorial, not a PHP one, and we'll keep the PHP side of things very simple.

Here's the structure we'll be following:

  • Part 1: Docker basics - building a PHP "Hello, world!" app
  • Part 2: Persisting storage - building a simple to-do app
  • Part 3: Multi-container apps - adding MySQL to our to-do app
  • Part 4: Docker Compose - the smart way to handle multi-container apps

What is Docker? 🔗

Docker is an open-source platform that allows you to develop, package and run applications inside containers.

A container is a self-sufficient environment that runs in isolation and contains everything an app needs to run - not only its content, but also its runtime (programming languages, web service, DB platform, config etc.).

Containers are "virtualised"; they exist in a virtual filesystem. They are easily created and destroyed, without wider infrastructural ramifications. Once destroyed, nothing of them remains - they do not persist data (at least by default.)

This used to be much harder. Before containerisation, to build a PHP+MySQL app we had to locally install PHP and MySQL, plus a web server like Apache. Any co-developers working on the app had to do the same. Then the server had to match our setup. The potential for bugs was huge.

With Docker, this problem goes away.

Getting set up 🔗

The first thing to do is install Docker. The easiest way here is to install Docker Desktop, which contains Docker's main CLI services plus the Docker Desktop user GUI. You'll also need your favourite terminal.

Docker is principally a CLI-based platform; the Desktop GUI provides a graphical interface for some common tasks, but it doesn't replace the need to use a terminal.

Next, set up a new directory called "learn-docker" with the following structure:

learn-docker/ |-- docker/ | |-- index.php |-- app/

In docker/index.php put the following:

<?= "Hello, world!" ?>

Docker Images 🔗

Along with containers, images are the other key concept in Docker. Images provide an immutable blueprint for how a container should be built. As such, containers are always built from, i.e. based on, an image. Images are:

  • Blueprints for building and running containers
  • Immutable
  • Built from files known as Dockerfiles
  • Multi-layered (often), meaning they can inherit from parent images

Let's create an image for our app! As noted above, images are built from Dockerfiles - so let's create one.

Create a file in the docker directory called Dockerfile - no file extension, and in the case shown. In it, put:

FROM php:8.1-apache COPY index.php /var/www/html RUN docker-php-ext-install mysqli

A Dockerfile is a list of instructions for Docker to build our image. There's many commands it can include, but our image is simple and contains just three:

  • FROM says our image should inherit from another image, named "php"; Docker will first look for such an image on our local machine and, failing to find it, will then search Docker Hub (or whatever is configured as our default Docker registry). The upshot is it will use the official PHP image - specifically, the version 8.1 + Apache tag of that image).
  • COPY bundles our PHP script so any container based on our image will include it (in the Apache web root)
  • RUN is a build-step for our image that instructs Docker to also run a shell command which installs PHP's MySQLi extension

Now you can see what's meant by an image being multi-layered; our image inherits from (i.e. includes) the PHP image, which in turn inherits from one of the base Linux images, and so on.

Image inheritance is at the heart of Docker; rather than build our own, custom PHP image, which would involve all sorts of steps e.g. installing the binaries, we just inherit from the official PHP image and hey presto, we have a PHP environment!

Although we're not using it here, a special mention also to the CMD instruction; this runs a command within the container once it's built. It's common for Node.js images, for example, to include CMD yarn install, to install dependencies.

Finally, we need to build our image, via the docker build command. Let's terminal into our docker directory and build:

cd c:\...\learn-docker\docker docker build -t php-app .

That tells Docker to look for a Dockerfile in the current directory (.) and build an image from it, tagging it with php-app. Once done, open Docker Desktop and under Images, or run the following, and you should see our built image!

docker image list

Running containers 🔗

Now we have an image built, let's run a container off it!

Containers are created and run via the docker run command. (It's also possible to just create, i.e. not run, containers via the docker create command.) We'll name ours "php-container". In your terminal, run the following:

docker run ` --name php-container ` -d ` -p 80:80 ` php-app

` is a way of delimiting line-breaks in multi-line terminal commands. If you're not using Powershell, though, you may need to replace ` with \.

That tells Docker to run a container named "php-container" based on our image (which we tagged php-app.) It should run in "detatched" mode (\d), and map container port 80 to local system port 80 (80 being the default HTTP port).

--name is optional - in any case Docker will assign a random ID and slug to the container - but providing an explicit name helps reference it in some later operations. As for port-mapping, we're not going to worry too much about that in this tutorial, but you can read more here.

Detatched mode means our container runs in the background, without hogging the terminal (so we can run more commands while the container is still running).

Look in Docker Desktop again, this time under Containers, and you should see our container! Visit http://localhost:80 and you'll see your PHP script's output! (Remember we bundled the script with our image.)

What we have now is a shippable app! If we shared our image, for example by publishing it to Docker Hub, anyone could build a container off it and see our script output without needing to worry about installing PHP or other setup.

Summary 🔗

Docker is a platform allowing for the development and deployment of "containerised" apps, which run in a virtual space with their own runtime, filesystem, resources etc.

Containers are built from images, while images describe how a container should be built. Images themselves can be multi-layered and inherit from parent images.

Docker Desktop provides a GUI for common CLI functions, but mostly Docker is a CLI-based platform.

This is all well and good, but how do we develop in a container? We don't want to keep rebuilding images and creating containers for each change we make.

We'll cover that in part two. See you there!

Did I help you? Feel free to be amazing and buy me a coffee on Ko-fi!