Docker For Beginners — With a Python Example
I’ve only been using Docker for a short time, simply because I had trouble understanding what it was and what it could be used for. Indeed, even after reading various articles and the Docker documentation, I find that it’s not very clear for someone who knows nothing about containers.
I now use it excessively, because I’ve developed my own homelab and it makes it very easy to deploy applications on it. So I feel I can explain, in simple words, what Docker is, and how you can use it.
What is Docker?
In a nutshell, Docker is a tool that lets you deploy your applications in what are known as containers, i.e. isolated environments on a host machine. Containers allow you to isolate all the dependencies of an application and run it without generating conflicts with your system.
What is the difference between a container and a VM?
You may know what a VM (Virtual Machine) is. VMs enable you to run applications in a guest OS hosted on another machine. For example, if I want to use Windows on my Linux computer, I can create a Windows virtual machine that will allow me to use Windows in my virtual machine stored on my Linux computer.
But virtual machines are quite heavy and consume a lot of resources. That’s why containers were developed: to enable isolation similar in part to virtual machines without consuming as many resources.
But whereas a virtual machine is totally isolated from the host machine, containers are not, because they operate using the host machine’s kernel. In other words, a Linux machine can only contain Linux containers, whereas it could very well contain Windows VMs. Containers must therefore respect the host machine’s architecture.
Docker Engine
Docker Engine is simply the program that lets you run Docker containers. For those familiar with VMs, it’s a kind of hypervisor replacement.
To use Docker on a machine, you just need Docker Engine. That’s the power of Docker, no need for any dependencies for running your applications, just Docker Engine.
A simple example
I created a Discord bot in Python (see — Creating a Discord bot in Python). Obviously, I can’t deploy this bot on my computer, since it doesn’t run 24 hours a day. So I’d like to deploy it on my Raspberry Pi. The problem is that, for some reason, I don’t want to install Python on my Raspberry Pi. So I can’t deploy my bot?
Of course I can, as you can imagine. All I have to do is use a container that contains my application, Python, and all the dependencies I need: requests , discord.py , etc…
The only application I need to install is Docker Engine, which will allow me to run my container.
Getting started with Docker
See Install Docker Engine in Docker documentation. I won’t explain how to install it because it depends on your OS.
Once it is installed, you can check if Docker is working with this command:
docker run hello-world
[estebanthilliez@fedora ~]$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
719385e32844: Pull complete
Digest: sha256:4f53e2564790c8e7856ec08e384732aa38dc43c52f02952483e3f003afbf23db
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
I don’t even need to explain what happens when you run this command, because it does it for me!
What is a Docker image?
As you can see, a container needs an image to function. But what is an image?
It’s a simple file containing code that Docker Engine understands in order to create a container. It contains the various commands to be executed and the files to be installed in order to run your application correctly.
It’s the equivalent of a snapshot for a VM: a file that lets you recreate your entire VM.
Where can I find Docker images?
There are several ways to find Docker images. Firstly, you can find them in what are known as registries. Docker Hub is one such registry.
To get images from a registry, you just have to use docker pull
. For example, here is a Firefox image allowing you to use Firefox in a container. Here is how to pull it:
docker pull linuxserver/firefox
Another way to find images is to create them yourself.
Creating a Docker image
This is done using what’s known as a Dockerfile. This is a file containing instructions for building a Docker image.
For our example, let’s create a Docker image of a simple Python program. The advantage of Docker is that you don’t even need Python to create this image. Create the file hello.py :
print("Hello, World!")
Then create a file called “Dockerfile” in the same folder:
# FROM is used to specify the base image. In this case, we are using the official Python 3 image.
# We don't even need to have Python installed on our local machine to build the image thanks to this.
FROM python:3.11
# WORKDIR is used to set the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile.
WORKDIR /app
# COPY is used to copy files from the local filesystem into a specific location inside the container.
COPY ./hello.py /app
# CMD is used to specify the command to execute when a container is created from the image.
CMD ["python", "hello.py"]
Then, you can run the following command to build the image:
docker build -t hello-python .
It creates an image called hello-python
.
Running a container
Now that we’ve got our image, all we need to do is create a container from it. To do this, we use docker run
docker run hello-python
[estebanthilliez@fedora Playground]$ docker run hello-python
Hello, World!
It’s working!
Persisting Data
An important concept to understand with these containers is that they are ephemeral. When you use docker run
, the container is created, executed and then disappears without a trace when the execution of its command is complete. Even if you use the same image to run the container.
This is normal, as containers are designed to be isolated and leave no trace.
On the other hand, it is possible to provide containers with files and folders outside the isolation environment, enabling data to be persisted or files to be shared between the host machine and the container. Let’s test this with an example. Create the file counter.py :
import os
file_path = "/app/data/count.txt"
os.makedirs(os.path.dirname(file_path), exist_ok=True)
count = 0
if os.path.exists(file_path):
with open(file_path, "r") as file:
count = int(file.read())
count += 1
with open(file_path, "w") as file:
file.write(str(count))
print(f"This program has been run {count} times.")
This program counts the number of times it has been run. Then, update the Dockerfile:
FROM python:3.11
WORKDIR /app
# Create a directory to store data inside the container
RUN mkdir /app/data
COPY counter.py /app/
CMD ["python", "counter.py"]
And create the Docker image:
docker build -t counter-app .
For our first test, let’s run our container the way we’ve done it previously:
[estebanthilliez@fedora Playground]$ docker run counter-app
This program has been run 1 times.
If we want to run our app again without creating another container, we have to find the id or name of our container and use docker start
.
[estebanthilliez@fedora Playground]$ docker ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
064e0abc0dda counter-app "python counter.py" 5 seconds ago Exited (0) 4 seconds ago festive_jones
We use docker ps
with the --all
flag to list all our containers.
Now, we can just copy the id and start the container:
[estebanthilliez@fedora Playground]$ docker start 064e0abc0dda 064e0abc0dda
This time, we can’t see the output of our appliocation. It’s because the output of docker start
is not the output of our application. We have to use docker container logs
.
[estebanthilliez@fedora Playground]$ docker container logs 064e0abc0dda
This program has been run 1 times.
This program has been run 2 times.
Now, if we recreate the same container, you’ll see data is kept isolated into the containers because it will reset the counter, and not use the previous values:
[estebanthilliez@fedora Playground]$ docker run counter-app
This program has been run 1 times.
To persist data, we can use volume mappings. They allow to map a volume on the host machine to a location in the container. Here is an example:
[estebanthilliez@fedora Playground]$ docker run -v $(pwd)/data:/app/data counter-app
This program has been run 1 times.
I map a data directory in my working directory to the data directory in the container. If it worked correctly, the container should have created a file and eventually a directory in my working directory on my computer:
[estebanthilliez@fedora Playground]$ ls data
count.txt
[estebanthilliez@fedora Playground]$ cat data/count.txt
1
Now, let’s start our container again:
[estebanthilliez@fedora Playground]$ docker start e0aa18e806cd e0aa18e806cd
And if I check the file:
[estebanthilliez@fedora Playground]$ cat data/count.txt
2
It works, this file is no more isolated because the container can write into it. Now, let’s imagine I delete my container and recreate it:
[estebanthilliez@fedora Playground]$ docker rm e0aa18e806cd
e0aa18e806cd
[estebanthilliez@fedora Playground]$ docker run -v $(pwd)/data:/app/data counter-app
This program has been run 3 times.
It still works!
Final Note
Now you certainly know a bit more about Docker and can start containerizing your applications. There’s obviously a lot more to know, but I’ll talk about that in another article, and I’ll be talking about Docker Compose and Portainer in particular shortly.
In the meantime, you have the Docker documentation and tons of resources available on the Internet to go further (or you have docker — help
for the braver ones).
Thanks for reading!
Here are some links that may interest you:
- 💻 All my tech stories
- ❓ Know more about me and my articles!
- 🔔 Become an email subscriber!
- 🤝 Support me by subscribing with my referal link:
If you enjoyed this article, consider trying out the AI service I recommend. It provides the same performance and functions to ChatGPT Plus(GPT-4) but more cost-effective, at just $6/month (Special offer for $1/month). Click here to try ZAI.chat.