This workshop teaches you the absolute basics of docker within 60 minutes. Its main purpose is to motivate you to use docker in your own projects.
This readme complements the slides provided in the slides/
directory (PDF link).
We will barely scratch the surface of docker's capabilities and focus on selected parts from the get started guide in docker's documentation. More details and information about advanced functionality can be found on the official docker website.
You can install docker on Linux, Mac and Windows. Have a look at the official install guides for more information.
This step should be done before the workshop starts, especially since the Windows installation with WSL 2 can take a while.
We will start exploring docker basics with a simple hello world example.
-
Run the
hello-world
docker imagedocker run hello-world
What happens when you execute this command? Read the response that is printed in your terminal. The newly created container will directly exit.
-
Verify that you now have a local copy of the
hello-world
image...docker image ls
-
..and a corresponding container with a random name that has already exited.
docker container ls --all
-
We won't need these anymore, so let's delete them.
docker container prune docker image rm hello-world
If you are very fast and there is time left, you can follow the output of hello-world and interactively run bash on an ubuntu container with -it
docker run --name ubuntu-container -it ubuntu bash
You can execute additional commands in a running container with docker exec. Open additional multiple terminals and run
docker exec -it ubuntu-container bash
to start bash on the running container. You can now touch
a file in one shell and verify the changed state with ls
on another one. The file will still be there if you stop (docker stop container_name
) and then start (docker start container_name
) the container. But it will be gone forever if you remove the container.
This repository creates everything you need to set up a simple web server: a Dockerfile
and some content.
-
Have a look at the Python code in the
/app
directory. Theapp.py
module contains a minimalist web application that uses theflask
library. -
Have a look at the
Dockerfile
and understand what it does. -
Use the
Dockerfile
to build the image and give it the namekwt22-image
docker build -t kwt22-image .
Note that
.
refers to the current directory, i.e. call this from the root directory of the repository. -
Congratulations, you built your first image. Now create and run the container with
docker run --rm -it --name kwt22-container -p 8080:5000 kwt22-image --host=0.0.0.0 --port=5000
The arguments after
kwt22-image
are passed to the entrypoint of your container. This will let the python web application listen on port 5000, which docker then maps to port 8080 (http://127.0.0.1:8080) on your system. With--rm
, we tell docker to remove the container when it exits. Wit-it
, we run the web application as an interactive process.Note that
--host=0.0.0.0 --port=5000
is already provided by default viaCMD ["--host", "0.0.0.0", "--port", "5000"]
in the
Dockerfile
. You actually don't need it and can just calldocker run --rm -it --name kwt22-container -p 8080:5000 kwt22-image
-
You can investigate the status of the container with
docker container ls
in another terminal.
-
Optional: to stop the container, you can abort the current process in the terminal that is running the web server with
control+C
or executedocker container stop kwt22-container
.
Given that all steps are executed as described above, is there another way to access the website?
Hint: Investigate the debug output of Flask or execute
docker inspect kwt22-container
Next, we will use so-called mount binds
to mount our code in a docker container. This is a simple way to develop with docker.
All our code is still on the local machine, but it is executed within docker.
The current Dockerfile
copies everything in /app
to the docker image. We don't want that anymore, so just comment out the line that says
COPY ./app ./
To mount your local app directory, rebuild the image and run the container with
docker run --rm -it --name kwt22-container -v "/$(pwd)/app":"/app" -p 8080:5000 kwt22-image
Note that this creates the app/__pycache__
directory and places the compiled Python byte code for your app.py
module there. To this date, the management of file permissions with docker is still a bit tedious. If you are running Linux, the files in app/__pycache__
will be owned by root:root
on the host system, as they are created by root in the container. Although the root user in the docker container is not the root user on the host system, you have to be root on the host to delete the files (or change permissions).
Your task is to edit app.py
on your host system however you like. First try small modifications like changing the returned string. You will find further inspiration in the flask documentation. The flask development server will automatically reload the file when you modify it.
The flask development server is pretty slow. While it makes development easy, we want to deploy our web app with a more sophisticated Web Server Gateway Interface (WSGI) server.
Create a simple multi-stage Dockerfile
that allows to build development and production images. We will use waitress in the hints and the solution, but you could also use different WSGI servers.
To use the waitress server, you first have to download it, e.g. with pip
. You can then use the waitress-serve
command like
waitress-serve --host=0.0.0.0 --port=5000 app:app
How does this map to your previous entrypoint and run command? How do you have to change them?
The following commands assume that your dockerfile is structured like this:
# development image
FROM python:3.9-slim-bullseye as dev
# ... do some stuff
ENTRYPOINT ...
CMD ...
# production image
FROM dev as prod
# ... do some stuff
ENTRYPOINT ...
CMD ...
This allows you to build development and production images like
# development
docker build -t kwt22-image:dev --target dev .
docker run --rm -it --name kwt22-container-dev -v "/$(pwd)/app":"/app" -p 8080:5000 kwt22-image:dev
# production
docker build -t kwt22-image:prod --target prod .
docker run --rm -it --name kwt22-container-prod -p 8080:5000 kwt22-image:prod
Congratulations, you are done! You now know..
- Dockerfile basics and how to build images
- How to run containers, container management basics
- A simple way to develop with docker
- How to create production images with multi-stage Dockerfiles
- Extra: how to set up a simple Python web app
Time to transfer this knowledge to your own projects!
You wrote your code and executed it with the ancient Python version 3.9. Does it also work with Python 3.10? Have a look at https://hub.docker.com/_/python and modify the base image to run your web application with Python 3.10. You can also try a later release candidate if you feel adventurous. All by changing a single line.
While it is possible to do everything by hand, managing your development containers manually is a bit tedious. Many IDEs include extensions for remote development, including development with Docker containers.
The .devcontainer/ directory contains everything you need to set up this project with VS Code.