Running Docker Containers
Overview
Images are build-time constructs, whereas containers are run-time constructs.
An image is a stack of layers. The container is the last layer at the top, where you can manipulate it, adding commands and packages on top of that base image.
With the docker commit
command or a Dockerfile, you can save this new image that contains the base image, plus all the additions.
Starting Containers
Use docker container run
and docker service create
commands to start one or more containers from a single image. For example:
docker container run <image-name>
Setting Limits on Containers
Memory
You can limit how much memory the container can use with the -m
flag. For example:
docker container run -m 512m ubuntu
CPU
By default, each container’s access to the host machine’s CPU cycles is unlimited. To assign CPUs to a container, use the following command:
docker run -it cpuset-cpus="1,3" ubuntu
The example above specifies that processes in the container can be executed on CPU 1 and CPU 3.
Controlling Capabilities
Docker Engine includes a default list of capabilities for newly created containers. By using the --cap-drop
option for docker run
, you can exclude additional capabilities. This is good for limiting the attack surface of the container.
All privileges can be dropped with the --user
option.
Likewise, additional capabilities can be granted with the --cap-add
. Using --cap-add=ALL
is highly discouraged!
Removing All Limits
By adding the --privileged
flag, you give all capabilities to the container and also lift any limitations enforced by the device cgroup controller. In other words, the container can do almost everything the host can do. This is generally a bad idea!
It’s better to add specific capabilities with --cap-add
or specific devices with the --device
flag. For example:
docker run --device=/dv/snd:/dev/snd
Managing and Updating Containers
The docker update
command dynamically updates the configuration on running or stopped containers. You can use this command to prevent containers from consuming too many resources from their Docker host. To specify more than one container, provide a space-separated list of container names or IDs.
To see the process list across all containers, use: docker ps
.
To see the process list in a specific container, use: docker container top <container-name>
.
For live stats across all containers, use: docker container stats
.
For live stats on a specific container, use: docker container stats <container-name>
.
To see everything that’s been executed in the container, use:
docker logs <container-name>
To stop the container:
docker container stop <container-name>
You can also pause and unpause a container from another terminal:
docker container pause <container-name>
docker container unpause <container-name>
To restart the container:
docker container start <container-name>
If you want to restart the container in interactive mode, use: docker container start -ai <container-name>
. The a
stands for attach and the i
for interactive.
To remove the container:
docker container rm <container-name>
To check the container is removed, run:
docker container ls -a
The -a
flag tells Docker to list all containers, even those in stopped state.
To delete a running container with a single command, use:
docker container rm <container-name> -f
But it’s best practice to take the two-step approach of stopping then removing the container.
To delete all containers, use:
docker container -rm -f $(docker container ls -aq)
You cannot delete an image until the last container using it has been stopped and destroyed.
Containers run until the app they are executing exits. For example, a Linux container exits when the Bash shell exits.
Running a Container in Interactive Mode
To run a container in interactive mode, add the -it
flag:
docker container run -it ubuntu:latest /bin/bash
Press Ctrl-PQ
to exit the container without terminating it.
It should still be visible through this command:
docker container ls
You can attach your shell to the terminal of a running container with the docker container exec
command:
docker container exec -it <container-name> bash
This exec command runs a new process inside a running container. This means the container won’t stop when you exit it.
You can also use:
docker container attach <container-name>
attach
doesn’t start a new process.
Running Self-Healing Containers with Restart Policies
Restart policies can be configured:
- imperatively on the command line as part of run commands
- declaratively in YAML files for use with Docker Swarm, Docker Compose, and Kubernetes
There are currently three types of restart policy:
- always
- unless-stopped
- on-failure
always
This policy always restarts a stopped container unless it has been explicitly stopped, such as via a docker container stop
command.
For example, if you exit from a container’s shell, it kills the container. However, if you’ve set the --restart always
policy, it’ll restart automatically.
If you explicitly stop a container with docker container stop
and restart the Docker daemon, the container will be automatically restarted.
unless-stopped
Unlike containers with the --restart always
policy, those with an unless-stopped
policy won’t be restarted when the daemon restarts.
on-failure
The on-failure
policy restarts a container if it exits with a non-zero exit code. It also restarts containers when the Docker daemon restarts, even containers that were in the stopped state.
Checking Container Health
Docker monitors the health of your app at a basic level every time you run a container. Docker checks the process is still running. If it stops, the container goes into the exited state.
This checks the process is running, but not whether the app is actually healthy. For example, it could be returning a 503 error to every request.
You can add a HEALTHCHECK
instruction to the Dockerfile. This tells the runtime exactly how to check whether the app in the container is still healthy.
The HEALTHCHECK
instruction specifies a command for Docker to run inside the container, which returns a status code. For example:
HEALTHCHECK CMD curl --fail http://localhost/health
The health check makes an HTTP call to the /health
endpoint, which tests whether the app is healthy. Using the --fail
parameter means the curl
command passes the status code on to Docker. If the request succeeds, it returns 0
.
Docker runs that command in the container at a timed interval. If the status code says everything is OK, the container is healthy. If the status code denotes failure several times in a row, the container is marked as unhealthy.
Docker can’t be sure that taking action to fix the unhealthy container won’t make the situation worse, so it broadcasts that the container is unhealthy but leaves it running. The health check continues, too. If the failure is temporary and the next check passes, the container status flips to healthy again.
Inspecting Changes to Containers
To inspect changes to files or directories on a container’s filesystem, use the following command:
docker diff <container-name>
Three different types of change are tracked:
- A - a file or directory was added
- D - a file or directory was deleted
- C - a file or directory was changed
Backing Up Containers
The docker export
command exports a container’s filesystem as a tar archive.
The docker export
command does not export the contents of volumes associated with the container. If a volume is mounted on top of an existing directory in the container, docker export
will export the contents of the underlying directory, not the contents of the volume.