The web is full of Docker examples and tutorials and repos.
There are many like it, but this one is mine.
I like learning from first principles. Docker masks a surprising amount of complexity, and most tutorials try to gloss over them to show 'the cool shiny things' before you can even understand what's going on.
I'd rather start really simple, and build from there until I fully understand what's going on. Therefore the examples in this repo build on each other until we get to some actual 'this could do something useful' kinds of infrastructure.
- Install Docker for Mac.
- Start Docker.app
- Open Terminal, make sure it's running with
docker --version
.
To kick the tires and make sure things are working, run:
docker run hello-world
This command is doing the following:
docker
- The main Docker command.run
- Run a container.hello-world
- The name of the Docker Hub repository to pull from. In this case, we'll get the latesthello-world
Docker image. If you don't specify a version, this is interpreted ashello-world:latest
.
If things are working correctly, you should see some output, then the container will exit.
Docker's tutorial provides a simple example of running an Nginx webserver on localhost with the command:
docker run -d -p 80:80 --name webserver nginx
This command is doing the following:
docker
- The main Docker command.run
- Run a container.-d
- Run a container detached; when the process (nginx, in this case) exits, the container will exit.-p 80:80
- Publish or expose a port ([host-port]:[container-port]
, so in this case bind the container's port80
to the host's port80
).--name webserver
- Assign a name to a container.nginx
- The name of the Docker Hub repository to pull from. In this case, we'll get the latestnginx
Docker image. If you don't specify a version, this is interpreted asnginx:latest
.
The first time you run this command, it will download the Nginx Docker image (it actually downloads a few 'layers' which build up the official image), then run a container based on the image.
Run the command, then access http://localhost:80/
in a web browser. You should see the 'Welcome to nginx!' page.
- Run
docker ps
to see a list of running containers; you should see the Nginx container you just started in the list. - Run
docker stop webserver
to stop the named container (you can also use the 'container ID' if you want). - Run
docker ps -a
to see a list of all containers on the system, including stopped containers. - Run
docker start webserver
to start the named container again. - Run
docker rm webserver
to delete the container entirely (you can also pass--rm
to therun
command if you want the container deleted after it exits).
Note: Starting and stopping a container is usually quicker than building it from scratch with
docker run
, so if possible, it's best to generate the container withrun
once and usestart
/stop
until you need to rebuild the container.
Docker has another tutorial that digs a little deeper into Docker CLI usage, but for our purposes, we'll just run the main command, and this time allow Docker to map an ephemeral port (any available high port number on our host) to the port configured in the container's configuration:
docker run -d -P training/webapp python app.py
Besides the obvious, this command is doing a couple new things:
-P
- Publish all ports that areEXPOSE
d by the docker container to ephemeral ports on the host (unlike-p
, which requires specification of each port mapping).training/webapp
- The name of the Docker Hub repository to pull from. In this case we'll get the latesttraining/webapp
Docker image.python app.py
- This is the command that will be run inside the container when it's launched. Until theapp.py
exits, or youdocker stop
ordocker kill
the container, it will keep running.
Once the container is started, run docker ps
to see what host port the container is bound to, then visit that port in your browser, e.g. http://localhost:32768/
. You should see the text "Hello world!" in your browser.
Since we didn't specify a --name
when we ran this docker run
command, Docker assigned a random name to the container (in my case, romantic_bell
), so to stop
, rm
, or otherwise interact with the container, you have to use the generated name or the container ID.
At this point, you should be somewhat familiar with the main Docker CLI. Some other commands that come in handy at this point are:
docker images
: Show a list of all images you have downloaded locally.docker rmi [image-name]
: Remove a particular image (save some disk space!).docker logs [container-name]
: Tail the logs (stdout) of a container (try this on the Flask app while refreshing the page!).
Other examples warrant their own dedicated directories, with example code and individual detailed README's explaining how they work. Included examples:
/flask
- Python Flask and MySQL.- Introduces
docker-compose
.
- Introduces
/php
- PHP-FPM and Nginx.- Introduces extra package installation.
- Introduces
HEALTHCHECK
.
/symfony
- Symfony and SQLite.- TODO.
/traefik
- Traefik proxy.- Introduces proxying of traffic for multiple hostnames on one port.
- Introduces the
.env
file.
This project is licensed under the MIT open source license.
Jeff Geerling is the author of Ansible for DevOps and manages tons of infrastructure, as well as open source projects like Drupal VM.