avatarBhargav Bachina

Summary

The provided web content offers a comprehensive guide on managing Docker containers, including examples and best practices for running, inspecting, and interacting with containers.

Abstract

The article delves into the intricacies of Docker container management, emphasizing the importance of understanding Docker's architecture, including the Docker Engine, containerd, and runc. It provides detailed instructions on building Docker images, using Dockerfiles, and running containers with various options. The guide covers practical aspects such as listing containers, interacting with them using exec and attach commands, inspecting container details, and safely removing containers. It also discusses the usage of volumes for persistent data storage, accessing container logs, and adhering to best practices to ensure efficient and secure containerization.

Opinions

  • The author suggests that a graceful shutdown of containers is a best practice before removal.
  • It is recommended to use volumes for data persistence to avoid data loss when containers are removed.
  • The article advocates for the use of one application per container to maintain simplicity and avoid complexity.
  • The author emphasizes the importance of leveraging Docker's cache during image builds to speed up the process and reduce build times.
  • The article advises building the smallest possible images to minimize disk usage and improve container start times.
  • Multi-stage Dockerfiles are encouraged for creating small and secure production images.
  • Caution is urged when selecting public Docker images, as users have limited control over them.
  • The use of image tags is discussed, with a preference for digests due to their immutability.
  • The author recommends including only necessary tools in an image to reduce potential attack vectors.
  • When sharing data between containers using volumes, the article suggests ensuring that only one container writes to the volume to prevent data corruption.

Docker — Container Management With Examples

Exploring how to run and manage containers with different options.

Photo by frank mckenna on Unsplash

A container is the specific unit of software which packages code and all the necessary dependencies to work in a standardized way. It can run in any environment quickly and reliably and it’s a runnable instance of the image.

Docker images become containers at runtime when they run in the Docker engine. Docker Containers are standard, Lightweight and secure.

In this article, Let’s explore the different ways of creating, running, removing containers and best practices as well. Here are the topics we want to cover in this article.

  • Container Architecture
  • Understanding Docker Engine
  • Building Images
  • Understanding Dockerfile
  • Starting Containers
  • Understanding the docker run command
  • Listing containers
  • Interacting with Containers
  • Inspecting Containers
  • Removing containers
  • Container logs
  • Usage of volumes with containers
  • Best Practices

Container Architecture

Let’s look at the following diagram, we have three sections 1) Docker Engine provides rest API for the docker client and also has other functionality such as image builds and management, security, core networking, etc. 2) container runtime has two modules containerd and runc which are responsible for the container execution 3) Linux Operating System

Container Architecture

Understanding Docker Engine

Let’s understand the Docker Engine architecture before we dive into container management.

When Docker was first released, it has one monolithic binary which includes Docker client, Docker API, container runtime, images builds, etc. But, it was later modularized into separate components according to OCI(open container Initiative). OCI created two container-related specifications

  1. Image Spec
  2. Container runtime spec

As shown in the following figure, all the image specific and container-specific binaries were moved out of docker daemon and implemented as separate modules.

Docker Engine Architecture

Docker client takes the commands and sends these commands to daemon through API. Docker daemon is responsible for implementing those commands with the help of containerd and runc libs.

Here is another diagram which demonstrates each module purpose.

Docker Engine Architecture

Docker Client: This is mostly CLI where you send docker commands to docker daemon. If you install Docker on any OS like windows(cmd), mac(terminal) or Linux, This is where you actually interact with docker.

Docker daemon: This is the server which exposes Restful API to accept the docker commands from Docker client. This is responsible for executing those commands, image builds, authentication, security, networking, etc.

containerd: This sits between daemon and runc at the OCI layer and this is responsible for the container lifecycle operations. It started as a lightweight container management But, it has more functionality other than container execution.

runc: It creates containers. containerd uses a new instance of runc every time we create a container. runc process exits as soon as it creates a container so that we don’t have to create as many runc instances as containers.

Shim: Shim is nothing but a library which enables daemonless containers. The reason why it is daemonless is runc process exits as soon as it creates a container. Shim is responsible for the container from there. It reports the container’s exit status back to the daemon.

Building Images

Check out these articles about building images.

Understanding Dockerfile

Check out this article if you want to know Dockerfile in detail.

Starting Containers

A running instance of an image is called a container. We can start the container with any image we built or pulled from docker hub with the following command docker container run

For instance, Let’s start a container from the node image. If the image is cached locally it will it from the local, if not it will pull it from the official docker repo.

docker container run -it --name my-node node:latest /bin/bash

we don’t have local node image so, it is pulled from the docker hub and process we are running /bin/bash as soon as the container starts. We can interact with the bash shell immediately to check the versions of node like below. we can exit out with the exit command.

docker container run

We can start the container in a detached mode as well with -d flag. When we start the container like this, it just returns the id.

docker run with detached mode

We can also omit the container in the command like below

docker run

Understanding the docker run command

Let’s understand the following command in detail.

docker container run -it --name my-node node:latest /bin/bash
understanding docker run command

Listing containers

We can list the container in two ways docker container ls and docker ps. Both will result in the same output. Let’s see those in action

listing running containers

By default, it lists only running containers. What if we want to list all the containers such as exited, and running. we need to use flag -a

listing all containers

We can actually select particular fields to list with --format flag and Go like templates. add table for table-like format and \t for space.

docker container ls -a --format '{{.ID}} {{.Image}} {{.Statue}}'
docker container ls -a --format 'table {{.ID}} {{.Image}} {{.Statue}}'
docker container ls -a --format 'table {{.ID}}\t{{.Image}}\t{{.Statue}}'
listing images with format flag

If we don’t want to type every time in the command line. There is a way to predefine this format so that every time we run container ls , it will result in a specific format. we need to edit the file under /Users/<username>/.docker folder.

defining format in config.json

after we edit the fileconfig.json and restart the docker, we can see the container ls command prints in that specific format

listing with format from config.json

we can print in a json format with the following command

docker container ls --format '{{json .}}'
listing containers in a json format

Interacting with Containers

We can interact with the running containers in two ways exec and attach, there is a slight difference between these two. Let’s see what that is.

we can directly interact with the container when we start a container by providing the process we want to run at the end.

for instance, if we are running node, we can provide bash at the end to interact with it

docker container run -it --name my-node node:latest /bin/bash

How to do you interact with already running container? Here are the options

attach: this is used to interact with the container with the same process that container is running. Let’s run the following commands.

// run the container in detached mode
docker container run -dit --name mynode1 node bash
// make sure container is up
docker container ls
// attach the running container
docker attach mynode
// exit out of the running process
exit
// check whether conatiner is running or not
docker ps

attach in action. if we exit with the attach command, it exits the present running process of the container. so, the container stops.

attach demonstration

exec: This command is used to interact with the container with a separate process. So that if we exit out of the process, the container is still running in another process.

// run the container in detached mode
docker container run -dit --name mynode1 node bash
// make sure container is up
docker container ls
// using exec, takes two arguments
docker exec -it mynode bash
// exit out of the running process
exit
// check whether conatiner is running or not
docker ps
docker exec in action

More about these commands, exec vs attach

Inspecting Containers

we can inspect containers with the following command. it doesn’t matter which state it is in as long as it is not removed.

docker container inspect <container name>

This command prints a lot of information in a json format such as networking details, storage, Name, etc.

If we want to know specific info, we can pipe this to grep like below

docker container inspect mynode | grep IPAddress
inspecting container for specific info

Removing containers

We can remove containers with this command docker container rm But, it’s always best practice to stop containers with docker container stop before removing.

// stop running containers
docker container stop <container id/name>
// remove containers
docker container rm <container id/name>
removing containers

The difference is that docker container stop sends SIGTERM to container process and docker container rm sends SIGKILL to container process. SIGKILL wait for 10 sec before it ends container’s life.

Container logs

We can access logs of the running container with the following command

// container logs
docker container logs mynode
// last 10 lines
docker container logs tail -10 mynode
// follow the logs
docker container logs --tail 10--follow mynode
container logs

There are so many logging drivers for the logs such as json-file, journald, none,gelf, fluentd, etc. docker container logs supports only json-file and journald.

we can specify the logging driver in the docker run command as below

docker container run -it --name mynode --log-driver none node bash

Usage of volumes with containers

Why use volumes?

we need to understand how containers work and save data before we dive into volumes.

All layers in the images are immutable and readonly. If we look at the following diagram, whenever we create a container from the image, it creates a writable layer on top of immutable image layers. whatever we create or save data inside the container, it will be saved into this writable layer.

writable layer in container

The downside of it is that if we remove the container, all the data will be lost with the container. One way to solve this problem is to use volumes. Volumes go beyond container lifecycle and persist data.

Run container With a volume

We can create volumes with this command docker volume create mydata

//create volumes
docker volume create mydata
//list volumes
docker volume ls
//inspect volumes
docker volume inspect mydata

We can actually see where exactly data in the volume is saved as a Mountpoint.

docker VM is hidden on macos, we have to use the following command to get into VM

// get into shell
screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty
// cd into Mountpoint location
cd /var/lib/docker/volumes
//list the volumes
ls -a
listing volumes in docker VM

Let’s run the container with the volume

// run container with volume
docker container run --name volumetest -it -v mydata:/data alpine sh
//cd into data
cd data
//create one file there
echo "This is some data" > somedata.txt

Since it is mounted to volume location, we can actually see the file in the volume location.

accessing volume on VM

Now we can remove the container, we still have this data in place and we can start another container with the same volume and access the file form volume like below.

accessing volume data

If you want more about volumes, check out this article

Best Practices

  • Always stop containers before removing. Graceful shutdown is always the best practice
  • Use volumes for persistent data. Make sure removing volumes if not necessary
  • use one application per container. For instance, don't use nodejs app and MongoDB in one container.
  • Use docker cache to our advantage while building images using dockerfiles
  • always build the smallest image possible so that container starts quickly and will be fewer disk usages.
  • make use of docker multi-stage dockerfile to build small and secure images.
  • when it comes to usage of public image, should be chosen carefully as we don’t have complete control over it.
  • tags are mutable so we should use these tags properly. Use digest since it is immutable.
  • Always build the image only with the necessary tools. Always reduce the usage of tools, it will have less surface for attacks.
  • whenever we are sharing data between containers with volumes, make sure only one container is writing into the volume to avoid concurrency.

Container management is very important to run efficient docker applications.

Docker
Containerization
Software Engineering
Web Development
DevOps
Recommended from ReadMedium