Docker is a tool which allows developers to make deployment of their software easier.
Imagine you have a web service. Then you usually not only have your own software, but you built it with other libraries. You run it on a web server. The server needs some configuration, exposes some ports, reads and writes data. Being a good developer, you identified different components which can be built (and scaled) independently (aka microservices). They have a way to talk with each other and they need to run in order to make the service work. As you are the developer, you know what you need. But you might not be the one who deploys the software. Hence you have to write documentation about it.
And you can create a (Docker) container.
Glossary
- Image
- An image is an immutable file which contains the application code, the
operating system, the required software. They are created with
docker build
. Images can be stored in a registry such as hub.docker.com. An image is defined using aDockerfile
. Typically, an image is built on another image.
You can list images on your machine withdocker images
. - Container
- A virtual operating system. It is an instance of an image. A container
is created with
docker run
. A methaphor used by paislee is that an image is a class and a container is an object. They encapsulate the environment in which the application runs.
docker ps -a
lists all containers - running and stopped. - Docker Compose
- A tool for defining and running multi-container applications.
- Docker Swarm
- A tool to deploy containers in a cluster. Similar to Kubernetes.
- Kubernetes
- A tool to deploy containers in a cluster. Similar to Docker Swarm.
Context
Containers vs Virtual Machines
- Resource sharing / isolation: Docker containers share resources. A docker container is similar to a host process, hence the memory / CPU scheduling will be handled by the host for Docker. Docker is less isolated than a VM. Docker containers use less disk space and less memory.
- Startup time: A VM takes minutes to start, Docker takes seconds to start
- Kernel: Docker containers share the same kernel (the host kernel) whereas each virtual machine has its own kernel.
Alternatives
Although Docker seems to be by far the most common container tool, there are alternatives:
And there are VMs:
Using Docker
First, install Docker.
Dockerfile
A Dockerfile
defines an image. It is a simple text file which could look
as follows:
FROM ubuntu:latest
LABEL maintainer="[email protected]"
# Settings for the local user
ENV APP_USER docker
ENV APP_USER_UID 9999
ENV APP_USER_GROUP docker
ENV APP_USER_GROUP_GID 4711
# Install and update software
RUN apt-get update -y
RUN apt-get install -y python-pip python-dev build-essential
RUN apt-get upgrade -y
# Copy projects code
COPY . /opt/app
WORKDIR /opt/app
RUN pip install -r requirements.txt --no-cache-dir
# Create user
RUN groupadd --gid ${APP_USER_GROUP_GID} ${APP_USER_GROUP}
RUN useradd --uid ${APP_USER_UID} --create-home -g ${APP_USER_GROUP} ${APP_USER}
RUN chown -R $APP_USER:$APP_USER_GROUP /opt/app
# Start app
USER docker
RUN mkdir -p /opt/app/uploads
ENTRYPOINT ["python"]
CMD ["app.py"]
Let's go through it:
FROM
defines on which base image we built our image. We add another layer to that one.LABEL
: Add metadata in akey=value
based fashion to the image.RUN <command>
: Execute this.COPY <src> <dest>
: Copy files fromsrc
on the host todest
in the container.WORKDIR /abs/path/to/workdir
: Work directory for andRUN
,CMD
,ENTRYPOINT
instruction that followsENTRYPOINT ["executable", "param1", "param2"]
: Run this when the Docker container is started.CMD ["executable","param1","param2"]
: The command is to run via the entrypoint.
You might see ENV DEBIAN_FRONTEND noninteractive
in a couple of Dockerfiles,
but there is a reason against it.
See also: What is pip's --no-cache-dir
good for?.
Using a Dockerfile
Build the image:
# docker build -t a_tag_you_like .
Run the image in a container:
# docker run a_tag_you_like
Commands
Docker needs to be run in super-user mode. I indicate that with #
. You can
run sudo su
in order to run all subsequent commands as a superuser.
List all images:
# docker image ls
List all running containers:
# docker container ls
Go into an interactive (-i
) bash in a pseudo-tty (-t
) in a container with ID 4f7d3f0763ad
:
# docker exec -it 4f7d3f0763ad bash
Volumes
If you copy the code directly into the image, you need to rebuild the image every time you make a change to the code. In order to prevent this, you can use volumes. There are two types of volumes:
- Persist and share data between containers
- Share folders between host and containers
List volumes:
# docker volume ls
Remove volumes:
# docker volume rm <volume name>
docker-compose.yml
The docker-compose.yml
is a YAML file
which defines multi-container applications. It looks like this:
version: '3' # Version number of the docker-compose file format
services:
web:
build: .
command: app.py
ports:
- "8082:5000"
volumes:
- .:/opt/app # enables hot code reloading
- uploads:/opt/app/uploads # for data persistance (<volume name>:<abs path in container>)
links:
- db
hostname: myappserver
environment:
- MYSQL_ROOT_PASSWORD=p@ssw0rd123
- MYSQL_DATABASE=flask_db
- MYSQL_HOST=db
- MYSQL_PORT=3306
depends_on:
- db
db:
image: mysql
ports:
- "3307:3306"
environment:
- MYSQL_ROOT_PASSWORD=p@ssw0rd123
- MYSQL_DATABASE=flask_db
- MYSQL_HOST=mysqlserver
volumes:
- ./flask_db.sql:/docker-entrypoint-initdb.d/flask_db.sql
volumes:
uploads:
driver: local
Start it with docker-compose up
.
The host name of each service is the name of the service itself.
You can find the data persistance volume with:
# docker volumes ls
DRIVER VOLUME NAME
local db6caef4eae28832f35af0951e95ae77d6c29189a793300f5aa9523cd25af362
local flaskmysqldockerized_uploads
And you can find the path on the host with
# docker volume inspect flaskmysqldockerized_uploads
[
{
"Driver": "local",
"Labels": {
"com.docker.compose.project": "flaskmysqldockerized",
"com.docker.compose.volume": "uploads"
},
"Mountpoint": "/var/lib/docker/volumes/flaskmysqldockerized_uploads/_data",
"Name": "flaskmysqldockerized_uploads",
"Options": {},
"Scope": "local"
}
]
Cleanup
Kill all running containers:
# docker kill $(docker ps -q)
Delete all stopped containers
# docker rm $(docker ps -a -q)
Delete all images
# docker rmi $(docker images -q)
Remove unused data
# docker system prune
See also
- Docker.com
- StackOverflow (docker tag)
- Docker Clustering Tools Compared: Kubernetes vs Docker Swarm
- github.com/MartinThoma/flask_mysql_dockerized: A minimal example how to use a database and a webserver with
docker-compose
. - Itamar Turner-Trauring: The best Docker base image for your Python application, August 2019.