Docker: Difference between revisions
(65 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
== Installation == | |||
As Docker development is too fast for the standard Debian/Ubuntu release cycles, there are only two realistic options for running an up to date version of Docker: | |||
# Use the Docker repositories. | |||
# Use the Docker snap. | |||
Both versions require any users who want to run Docker containers to be a member of the <code>docker</code> group. | |||
The upstream repositories usually only support LTS releases of Ubuntu. If you want to install Docker on a non-LTS release (e.g. Disco), you must use the latest LTS in <code>/etc/apt/sources.list</code> instead. | |||
== Containers == | == Containers == | ||
Line 7: | Line 18: | ||
* Lower resource utilisation, particularly RAM and CPU, means running a dozen containers is more realistic than the same number of VMs, especially on a developer's laptop. | * Lower resource utilisation, particularly RAM and CPU, means running a dozen containers is more realistic than the same number of VMs, especially on a developer's laptop. | ||
* Due to the sharing of resources, containers always run the same kernel as the host. | * Due to the sharing of resources, containers always run the same kernel as the host. | ||
* Containers are usually faster to start and stop than virtual machines. This helpful if you want to spin up a container, run a particular task, and then stop it immediately, e.g. for transaction processing. | |||
* Containers operate within their own namespaces, including processes. As a result, each container can have a process with PID 1, and these will not conflict with each other. | |||
Containers are ''instances'' of ''images''. Multiple containers can use the same base image, and only one copy of the image needs to be stored on disk. Any changes to files are stored within the container using copy-on-write. | |||
== Requirements == | == Requirements == | ||
Line 16: | Line 31: | ||
* The Docker daemon currently requires <code>root</code> privileges. As a result, all <code>docker</code> commands must be prefixed with <code>sudo</code>, or alternatively you can create a group called <code>docker</code> and add users to that. This does not provide any security benefits. | * The Docker daemon currently requires <code>root</code> privileges. As a result, all <code>docker</code> commands must be prefixed with <code>sudo</code>, or alternatively you can create a group called <code>docker</code> and add users to that. This does not provide any security benefits. | ||
* Avoid deploying containers which permit/require root access. | |||
== Basic running == | == Basic running == | ||
Line 24: | Line 40: | ||
<code>docker run -i -t debian /bin/bash</code> | <code>docker run -i -t debian /bin/bash</code> | ||
Creating a container without running it (i.e. created in 'stopped' state): | |||
<code>docker create debian</code> | |||
When creating a container, it may be useful to capture the container ID as an environment variable: | |||
<code>CONTAINER_ID=$(docker create debian)</code> | |||
Each container gets a long UUID (hex-encoded 1024 bit value). In most cases you can refer to the container using a short version of the UUID (usually 12 characters), or you can give it a name. Docker also generates names using a personal adjective followed by an underscore and the last name of a famous person, e.g. <code>serene_mahavira</code>. | |||
Tab-completion is available for images which you have previously run. | |||
<code>docker restart [name]</code> will restart a container. | |||
<code>docker logs</code> shows everything written to the stdout or stderr streams. By default it is never rotated or truncated, so is not a sensible option for long-lived processes. | |||
<code>docker stop</code> halts the process with PID 1 in the container. | |||
<code>docker exec [name] [command]</code> will run the command in the specified container. | |||
<code>docker pull [name]:[tag]</code> will download the tagged version of the container without running it. | |||
=== Environment variables === | |||
Multiple environment variables can be passed into the container using <code>-e</code>: | |||
docker run --detach --rm -e MYSQL_USER=my_user -e MYSQL_DATABASE=my_database -e MYSQL_PASSWORD=my_password -e MYSQL_RANDOM_ROOT_PASSWORD=true mysql:5.7 | |||
In Docker Compose, environment variables can be specified in two ways. First, within the <code>docker-compose.yml</code> file: | |||
environment: | |||
- MYSQL_DATABASE=docker | |||
- MYSQL_ROOT_PASSWORD=password | |||
Secondly, environment variables can be listed in a separate file: | |||
MYSQL_DATABASE=docker | |||
MYSQL_ROOT_PASSWORD=password | |||
This file can then be included using the <code>env_file</code> configuration option: | |||
env_file: .docker.env | |||
=== Running a command within a container === | |||
A combination of <code>docker run</code>, <code>--rm</code> and mounting a volume (<code>--volume</code>) can demonstrate a command being run within a container: | |||
docker run --rm --volume $(pwd):/app php:latest php /app/example.php | |||
=== Logging into an existing container === | |||
Attaching an interactive terminal to the named container: | |||
docker exec -it [container] bash | |||
This command assumes Bash is installed, if not then <code>sh</code> may work. | |||
=== Detached terminals === | |||
Passing the <code>--detach</code> option starts the container in the background and unattached to the terminal. This is a sensible option for server software such as Nginx. | |||
=== Port mapping === | |||
Port mapping from the container to the host can be accomplished using <code>-p</code> or <code>--publish</code>: | |||
docker run container:latest -p 80000:80 | |||
The above command will map port 80000 on the host to port 80 inside the container. | |||
=== Copying files from a running container === | |||
docker cp container_id:/path/to/container-file /path/to/local-file | |||
== Storage == | |||
The three most common types of storage are: | |||
# Bind mounts | |||
# In-memory | |||
# Docker volumes | |||
=== Bind mounts === | |||
Bind mounts attach a location on the host file system to a location in a container. For example, <code>/home/user/public_html</code> on the host might be bound to <code>/var/www</code> in the container. | |||
Issues with bind mounts: | |||
* Tie containers to file systems of specific hosts, i.e. reduces portability. | |||
* Opportunity for conflict with other containers. | |||
=== In-memory === | |||
In-memory mounts can be created using <code>--mount type=tmpfs,dst=/tmp</code>. Any files created under <code>/tmp</code> will be stored in memory instead of on disk. By default no files created will be interpreted as special devices or executable. | |||
=== Volumes === | |||
Volumes are named file system trees which are managed by Docker and stored on the host in a location under the control of the Docker engine. They are decoupled from the host file system because the location of the storage on the host is managed by Docker, and may vary between hosts. Docker can also clean up any files no longer used by a container when a volume is removed. | |||
Volumes can have human-readable labels assigned to them. | |||
Volumes persist over container restarts and can be removed with <code>docker volume prune</code> or <code>docker-compose down --volumes</code>. | |||
=== Read-only file systems === | |||
To start a container with a read-only file system, pass the <code>--read-only</code> flag to <code>docker run</code>: | |||
<code>docker run --read-only debian</code> | |||
== Container states == | |||
Containers will always be in one of four states: | |||
* Running | |||
* Paused | |||
* Exited (stopped) | |||
* Restarting | |||
By default, <code>docker ps</code> will only show running containers. | |||
== Configuration == | == Configuration == | ||
Build configuration is contained in a file named <code>Dockerfile</code>. | |||
All Docker configurations must start with a <code>FROM</code> instruction, which specifies the base image and optionally a tagged version, e.g. <code>debian:stretch</code>. | All Docker configurations must start with a <code>FROM</code> instruction, which specifies the base image and optionally a tagged version, e.g. <code>debian:stretch</code>. | ||
Arbitrary commands can be run using <code>RUN [command]</code>. | |||
<code>docker build .</code> will build an image in the current directory, using <code>./Dockerfile</code>. | |||
Docker Compose or an <code>.env</code> can be used to define how a container is started, e.g. environment variables. | |||
== Images == | |||
Images are made up of one or more ''layers''. | |||
<code>docker images</code> will list all images on the host. | |||
Although most images will come from a registry, it is possible to load an image from a file using <code>docker load</code>. | |||
An existing image can be saved to a file using <code>docker save</code>: | |||
docker pull busybox:latest | |||
docker save -o busybox.tar busybox:latest | |||
== Cleanup == | == Cleanup == | ||
Line 46: | Line 200: | ||
<code>docker rm -v $(docker ps -aq -f status=exited)</code> | <code>docker rm -v $(docker ps -aq -f status=exited)</code> | ||
All stopped containers, networks not used by at least one container and images not associated with a container can be removed with: | |||
<code>docker system prune</code> | |||
== Cross-platform == | |||
As containers require support from the kernel, Docker can run natively on Linux, but requires a virtual machine on macOS and Windows. | |||
== Registries == | |||
A registry contains images which can be downloaded using <code>docker pull</code>. Anyone can run a registry (private or public), but by default Docker will use [https://hub.docker.com Docker Hub] | |||
== Best practice == | |||
* Always use a specific version of a base image, to allow reproducible builds. For example, use <code>php:7.2</code> instead of <code>php:7</code>. | |||
* Do not run containers as root. | |||
== Links == | |||
* [https://dev.to/azure/improve-your-dockerfile-best-practices-5ll Improve your Dockerfile, best practices] | |||
* [https://cloudberry.engineering/article/dockerfile-security-best-practices/ Dockerfile Security Best Practices] | |||
* [https://pythonspeed.com/articles/dockerizing-python-is-hard/ Broken by default: why you should avoid most Dockerfile examples] | |||
* [https://zwischenzugs.com/2019/07/27/goodbye-docker-purging-is-such-sweet-sorrow/ Goodbye Docker: Purging is Such Sweet Sorrow] | |||
* [https://podman.io Podman] - A daemonless container engine for developing, managing, and running OCI Containers on your Linux System. Containers can either be run as root or in rootless mode. | |||
* [https://vulnerablecontainers.org/ VulnerableContainers.org] - list of Docker containers with open vulnerabilities. | |||
* [https://containrrr.dev/watchtower/ Watchtower] | |||
* [https://docs.traefik.io/ Traefix] | |||
* [https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker&ssr=false ms-azuretools.vscode-docker] - Useful extension for Visual Studio Code (Intellisense for Docker, amongst other things - syntax highlighting is built-in). | |||
[[Category:Docker]] |
Latest revision as of 08:57, 20 August 2021
Installation
As Docker development is too fast for the standard Debian/Ubuntu release cycles, there are only two realistic options for running an up to date version of Docker:
- Use the Docker repositories.
- Use the Docker snap.
Both versions require any users who want to run Docker containers to be a member of the docker
group.
The upstream repositories usually only support LTS releases of Ubuntu. If you want to install Docker on a non-LTS release (e.g. Disco), you must use the latest LTS in /etc/apt/sources.list
instead.
Containers
At a high level, containers are a lightweight form of virtual machines which encapsulate an application and its dependencies. However, there are some key differences between containers and virtual machines:
- Some resources are shared with the host operating system, which reduces the overhead involved in comparison with a VM. How much overhead is debatable, especially given that hardware support for virtualisation exists on most modern CPUs, and any machine operating as a server is likely to have this available and enabled.
- Portability of containers should make them easier to deploy and migrate across hardware.
- Lower resource utilisation, particularly RAM and CPU, means running a dozen containers is more realistic than the same number of VMs, especially on a developer's laptop.
- Due to the sharing of resources, containers always run the same kernel as the host.
- Containers are usually faster to start and stop than virtual machines. This helpful if you want to spin up a container, run a particular task, and then stop it immediately, e.g. for transaction processing.
- Containers operate within their own namespaces, including processes. As a result, each container can have a process with PID 1, and these will not conflict with each other.
Containers are instances of images. Multiple containers can use the same base image, and only one copy of the image needs to be stored on disk. Any changes to files are stored within the container using copy-on-write.
Requirements
- Modern kernel
- 64 bit Linux
Security
- The Docker daemon currently requires
root
privileges. As a result, alldocker
commands must be prefixed withsudo
, or alternatively you can create a group calleddocker
and add users to that. This does not provide any security benefits. - Avoid deploying containers which permit/require root access.
Basic running
docker run debian
Starting a container with an interactive shell:
docker run -i -t debian /bin/bash
Creating a container without running it (i.e. created in 'stopped' state):
docker create debian
When creating a container, it may be useful to capture the container ID as an environment variable:
CONTAINER_ID=$(docker create debian)
Each container gets a long UUID (hex-encoded 1024 bit value). In most cases you can refer to the container using a short version of the UUID (usually 12 characters), or you can give it a name. Docker also generates names using a personal adjective followed by an underscore and the last name of a famous person, e.g. serene_mahavira
.
Tab-completion is available for images which you have previously run.
docker restart [name]
will restart a container.
docker logs
shows everything written to the stdout or stderr streams. By default it is never rotated or truncated, so is not a sensible option for long-lived processes.
docker stop
halts the process with PID 1 in the container.
docker exec [name] [command]
will run the command in the specified container.
docker pull [name]:[tag]
will download the tagged version of the container without running it.
Environment variables
Multiple environment variables can be passed into the container using -e
:
docker run --detach --rm -e MYSQL_USER=my_user -e MYSQL_DATABASE=my_database -e MYSQL_PASSWORD=my_password -e MYSQL_RANDOM_ROOT_PASSWORD=true mysql:5.7
In Docker Compose, environment variables can be specified in two ways. First, within the docker-compose.yml
file:
environment: - MYSQL_DATABASE=docker - MYSQL_ROOT_PASSWORD=password
Secondly, environment variables can be listed in a separate file:
MYSQL_DATABASE=docker MYSQL_ROOT_PASSWORD=password
This file can then be included using the env_file
configuration option:
env_file: .docker.env
Running a command within a container
A combination of docker run
, --rm
and mounting a volume (--volume
) can demonstrate a command being run within a container:
docker run --rm --volume $(pwd):/app php:latest php /app/example.php
Logging into an existing container
Attaching an interactive terminal to the named container:
docker exec -it [container] bash
This command assumes Bash is installed, if not then sh
may work.
Detached terminals
Passing the --detach
option starts the container in the background and unattached to the terminal. This is a sensible option for server software such as Nginx.
Port mapping
Port mapping from the container to the host can be accomplished using -p
or --publish
:
docker run container:latest -p 80000:80
The above command will map port 80000 on the host to port 80 inside the container.
Copying files from a running container
docker cp container_id:/path/to/container-file /path/to/local-file
Storage
The three most common types of storage are:
- Bind mounts
- In-memory
- Docker volumes
Bind mounts
Bind mounts attach a location on the host file system to a location in a container. For example, /home/user/public_html
on the host might be bound to /var/www
in the container.
Issues with bind mounts:
- Tie containers to file systems of specific hosts, i.e. reduces portability.
- Opportunity for conflict with other containers.
In-memory
In-memory mounts can be created using --mount type=tmpfs,dst=/tmp
. Any files created under /tmp
will be stored in memory instead of on disk. By default no files created will be interpreted as special devices or executable.
Volumes
Volumes are named file system trees which are managed by Docker and stored on the host in a location under the control of the Docker engine. They are decoupled from the host file system because the location of the storage on the host is managed by Docker, and may vary between hosts. Docker can also clean up any files no longer used by a container when a volume is removed.
Volumes can have human-readable labels assigned to them.
Volumes persist over container restarts and can be removed with docker volume prune
or docker-compose down --volumes
.
Read-only file systems
To start a container with a read-only file system, pass the --read-only
flag to docker run
:
docker run --read-only debian
Container states
Containers will always be in one of four states:
- Running
- Paused
- Exited (stopped)
- Restarting
By default, docker ps
will only show running containers.
Configuration
Build configuration is contained in a file named Dockerfile
.
All Docker configurations must start with a FROM
instruction, which specifies the base image and optionally a tagged version, e.g. debian:stretch
.
Arbitrary commands can be run using RUN [command]
.
docker build .
will build an image in the current directory, using ./Dockerfile
.
Docker Compose or an .env
can be used to define how a container is started, e.g. environment variables.
Images
Images are made up of one or more layers.
docker images
will list all images on the host.
Although most images will come from a registry, it is possible to load an image from a file using docker load
.
An existing image can be saved to a file using docker save
:
docker pull busybox:latest docker save -o busybox.tar busybox:latest
Cleanup
Containers only run as long as their main process. However, exiting the main process will only stop the container, it will not remove it from disk. To do this you must run:
docker rm [container]
Passing --rm to docker run will automatically delete the container when the main process exits, e.g.
docker run --rm debian echo "Hello World"
docker ps -a
will show all containers, included those which have been stopped.
All stopped Docker containers can be removed with the following command:
docker rm -v $(docker ps -aq -f status=exited)
All stopped containers, networks not used by at least one container and images not associated with a container can be removed with:
docker system prune
Cross-platform
As containers require support from the kernel, Docker can run natively on Linux, but requires a virtual machine on macOS and Windows.
Registries
A registry contains images which can be downloaded using docker pull
. Anyone can run a registry (private or public), but by default Docker will use Docker Hub
Best practice
- Always use a specific version of a base image, to allow reproducible builds. For example, use
php:7.2
instead ofphp:7
. - Do not run containers as root.
Links
- Improve your Dockerfile, best practices
- Dockerfile Security Best Practices
- Broken by default: why you should avoid most Dockerfile examples
- Goodbye Docker: Purging is Such Sweet Sorrow
- Podman - A daemonless container engine for developing, managing, and running OCI Containers on your Linux System. Containers can either be run as root or in rootless mode.
- VulnerableContainers.org - list of Docker containers with open vulnerabilities.
- Watchtower
- Traefix
- ms-azuretools.vscode-docker - Useful extension for Visual Studio Code (Intellisense for Docker, amongst other things - syntax highlighting is built-in).