Managing Docker Containers
Throughout your container journey, you will be pulling, starting, stopping, and removing containers from your local environment quite frequently. Prior to deploying a container in a production environment, it is critical to run the container locally to understand how it should work. This includes starting, stopping and restarting containers, getting details about how the container is running, and, of course accessing verbose logs to view critical details about the applications running inside the container. The basic commands we are going to discuss are the following:
- docker start: This command starts a container instance that is no longer in a running state.
- docker stop: This command stops a running container instance.
- docker restart: This command restarts a running container.
- docker pull: This command downloads a container image to the local cache.
- docker attach: This command allows users to gain access (or attach) to the primary process of a running Docker container instance.
- docker exec: This command executes a command inside a running container.
- docker rm: This command deletes a stopped container.
- docker rmi: This command deletes a container image.
- docker inspect: This command shows verbose details about the state of a container.
Container life cycle management is an essential component of managing containers in production environments. Knowing how to investigate running containers is a critical skill that will help you evaluate the health of your containerized infrastructure.
In the following exercise, we are going to manage a container using these commands.
Managing Container Life Cycles
When managing containers in any kind of environment, it is important to understand the status of container instances. Most of the time we use base container images that contain a specific baseline configuration on top of which the applications are deployed. Ubuntu is one of the most commonly used base images that are used for packaging applications.
Unlike a full operating system image, the Ubuntu base container image is quite slim and intentionally leaves out a lot of packages. Most of the base images do have a package system that allows us to install any missing package.
Keep in mind, though, that you want to keep the base images as slim as possible, only installing the packages you really need. This ensures that container images can quickly be pulled and started by Docker hosts.
In this exercise, we will work with the official Ubuntu base container image. This image will be used to start container instances on which we will use various container life cycle management commands.
In a new terminal or PowerShell window, execute the docker pull command to download Ubuntu 18.04 container image. Remember, we can use tags to specify the image version. If you don't provide a tag, the latest will automatically be pulled.
docker pull ubuntu:18.04
You should see the following output:
18.04: Pulling from library/ubuntu
7c457f213c76: Pull complete
Digest: sha256:152dc042452c496007f07ca9127571cb9c29697f42acbfad72324b2bb2e43c98
Status: Downloaded newer image for ubuntu:18.04
docker.io/library/ubuntu:18.04
Use the docker pull command to download the Ubuntu 19.04 base image:
docker pull ubuntu:19.04
You should see the following output:
19.04: Pulling from library/ubuntu
4dc9c2fff018: Pull complete
0a4ccbb24215: Pull complete
c0f243bc6706: Pull complete
5ff1eaecba77: Pull complete
Digest: sha256:2adeae829bf27a3399a0e7db8ae38d5adb89bcaf1bbef378240bc0e6724e8344
Status: Downloaded newer image for ubuntu:19.04
docker.io/library/ubuntu:19.04
Use the docker images command to confirm that the container images are downloaded to the local container cache:
docker images
The contents of the local container cache will display the Ubuntu 18.04 and Ubuntu 19.04 base images, as well as any other images you have in your local cache:
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 18.04 f9a80a55f492 12 months ago 63.2MB
hello-world latest d2c94e258dcb 13 months ago 13.3kB
ubuntu 19.04 c88ac1f841b7 4 years ago 70MB
... ... ... ... ...
Before running these images, use the docker inspect command to get verbose output about the images. In your terminal, run the docker inspect command and use the IMAGE ID value if the Ubuntu 18.04 container image as an argument:
docker inspect f9a80a55f492
The inspect output will contain a large list of attributes that define the container. For example, you can see what environment variables are configured within the container, whether the container has a hostname set when the image was last updated, and a breakdown of all of the layers that define that container.
"Id": "sha256:f9a80a55f492e823bf5d51f1bd5f87ea3eed1cb31788686aa99a2fb61a27af6a",
"RepoTags": [
"ubuntu:18.04"
],
"RepoDigests": [ "ubuntu@sha256:152dc042452c496007f07ca9127571cb9c29697f42acbfad72324b2bb2e43c98"
],
"Parent": "",
"Comment": "",
"Created": "2023-05-30T09:32:09.432301537Z",
"Container":"00da56b63e7a5e6508d4ff7a380a7fb2b4e7ffcb5dcf799d41cb75bf20f12132",
Inspecting the Ubuntu 19.04 container, you can see that this parameter is different. Run the docker inspect command in the Ubuntu 19.04 container image ID:
docker inspect c88ac1f841b7
In the displayed output, you will see that this container image was created on a different date to the 18.04 container image:
"Id": "sha256:c88ac1f841b72add46f5a8b0e77c2ad6864d47e5603686ea64375acd55e27906",
"RepoTags": [
"ubuntu:19.04"
],
"RepoDigests": [ "ubuntu@sha256:2adeae829bf27a3399a0e7db8ae38d5adb89bcaf1bbef378240bc0e6724e8344"
],
"Parent": "",
"Comment": "",
"Created": "2020-01-16T01:20:46.938732934Z",
"Container": "1d952a25729fba44399443aa7cb60e2452250fc4535b7135db02424006e304d5"
This can be useful if, for example, you know that a security vulnerability might be present in an Ubuntu base image.
After inspecting both the container images, it will be clear that our best choice in this scenario is to stick with the LTS 18.04 release. The preceding outputs show that the 18.04 release is more up to date than the 19.04. This is something to be expected, as Ubuntu generally will provide more stable updates for the LTS releases.
Write the docker run command with the -d flag on your terminal of choice to start up an instance of the Ubuntu 18.04 container:
docker run -d ubuntu:18.04
This time we are using the -d flag. This tells Docker to run the container in daemon mode (or in the background). If we omit the -d flag, the container will take over our current terminal session until the primary process inside the container terminates.
Check the status of the container using the docker ps -a command:
docker ps -a
This will reveal a similar output to the following:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f05ee6d22795 ubuntu:18.04 "/bin/bash" 2 minutes ago Exited (0) 2 minutes ago condescending_thompson
The container is stopped and exited. This is because the primary process inside this Ubuntu container is /bin/bash, which is a shell. The Bash shell cannot run without being executed in an interactive mode since it expects text input from the user.
Run the docker run command again, passing in the -i flag to make the session interactive, and the -t flag to allocate a pseudo-tty handler to the container. A pseudo-tty handler will essentially link the user's terminal to the interactive Bash sell running inside the container.
This will allow Bash to run properly since it will instruct the container to run in an interactive mode, expecting user input. You can also give the container a human-readable name by passing in the --name flag. Use the following command in your terminal:
docker run -i -t -d --name ubuntu18 ubuntu:18.04
You should now see the new instance running, as well as the instance that failed to start previously:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0b4b857747a5 ubuntu:18.04 "/bin/bash" 21 seconds ago Up 20 seconds ubuntu18
f05ee6d22795 ubuntu:18.04
We now have an Ubuntu container up and running. We can run commands inside the container using the docker exec command. We can use the exec command to access a Bash shell, which will allow us to run commands inside the container. Similar to docker run, pass in the -i and -t flags to make it an interactive session. Also pass in the name or ID of the container, so that Docker knows which container you are targeting. The final argument of docker exec is always the command you wish to execute. In this case, it will be /bin/bash to start a Bash shell inside the container instance:
docker exec -it ubuntu18 /bin/bash
You should immediately see your terminal to change to a root shell. This indicates that you have successfully launched a shell inside your Ubuntu container. The hostname of the container, (in my case 0b4b857747a5) is taken from the first twelve characters of the container ID. This allows the user to know for certain which container they are accessing
root@0b4b857747a5:/#
Run the echo command inside the ubuntu18 container instance to write a hello world message:
echo "Hello world from ubuntu18" > helloworld.txt
Run the exit command to exit from the Bash shell of the ubuntu18 container. You should return to your normal terminal shell:
root@0b4b857747a5:/# exit
Now, lets create a second container named ubuntu19 that will also run in the Docker environment using the Ubuntu 19.04 image:
docker run -itd --name ubuntu19 ubuntu:19.04
Again, run the docker exec to access a shell of this second container. Remember to use the name or container ID of the new container you created. Likewise, access a Bash shell inside this container, so the final argument will be /bin/bash:
docker exec -it ubuntu19 /bin/bash
You should see that the prompt once again changed to a Bash root shell, similar to how it did for the Ubuntu 18.04 container image:
root@b073985d739a:/#
Run the echo command inside the ubuntu19 container instance to write a hello world message:
echo "Hello world from ubuntu19" > helloworld.txt
Currently, you should have two Ubuntu container instances running in your Docker environment, with two separate hello-world files in the home directory. If you run the docker ps command, you should see your containers
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b073985d739a ubuntu:19.04 "/bin/bash" 11 minutes ago Up 11 minutes ubuntu19
0b4b857747a5 ubuntu:18.04 "/bin/bash" 3 hours ago Up 3 hours ubuntu18
Instead of using docker exec to access a shell inside our containers, we are going to use it to display the output of the helloworld.txt files, by executing the cat command inside the containers
docker exec -it ubuntu18 cat helloworld.txt
The output will display the helloworld message we added to the container in previous steps. Notice that as soon as the cat command was completed and the output displayed, the user was moved back to the context of the main terminal. This is because the docker exec session will only exist for as long the command provided by the user is running.
In the previous example of the Bash shell, Bash will only exit if the user terminates it by using the exit command. In this example, only the Hello world output is displayed, because the cat command displayed the output and exited, and thus, terminating the docker exec session.
Hello world from ubuntu 18
Run the same cat command in the ubuntu2 container instance:
Hello world from ubuntu 19
As you can see, Docker was able to allocate an interactive session on both the containers, execute the command, and return the output directly in our running container instances.
In a similar manner to that we used to execute commands inside our running containers, we can also stop, start and restart them. Stop one of your container instances using the docker stop command. In your terminal session, execute the docker stop command, followed by the name or container ID of the ubuntu19 container:
docker stop ubuntu19
This command should return the name of the container.
We can use the docker ps command to view all running container instances:
docker ps
The output will display the ubuntu1 container up and running:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c8c0010f65fb ubuntu:18.04 "/bin/bash" 5 days ago Up 4 seconds ubuntu18
Execute the docker ps -a command to view all container instances, regardless of whether they are running. This will show us the stopped container:
docker ps -a
The output should display something similar to the following:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
df6354e844f9 ubuntu:19.04 "/bin/bash" 5 days ago Exited (0) 5 days ago ubuntu19
c8c0010f65fb ubuntu:18.04 "/bin/bash" 5 days ago Up 2 minutes ubuntu18
From this state we can experiment with starting, stopping, or executing commands inside the containers.
When container instances are in a stopped state, we can use the docker rm command to delete the container instances altogether. Use the docker rm followed by the name or id of the container to delete the ubuntu19 instance:
docker rm ubuntu19
The output should be the name of the container
To completely reset the state of your docker environment, delete the base images you downloaded during this post. Use the docker images command to view the cached based images:
docker images
A list of Docker images and associated metadata in the local cache should be displayed:
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 18.04 f9a80a55f492 12 months ago 63.2MB
hello-world latest d2c94e258dcb 13 months ago 13.3kB
ubuntu 19.04 c88ac1f841b7 4 years ago 70MB
Use the docker rmi command followed by an image ID to delete the first image:
docker rmi f9a80a55f492
Similar to docker pull, the rmi command will delete each image and all associated layers
Untagged: ubuntu:18.04
Untagged: ubuntu@sha256:152dc042452c496007f07ca9127571cb9c29697f42acbfad72324b2bb2e43c98
Deleted: sha256:f9a80a55f492e823bf5d51f1bd5f87ea3eed1cb31788686aa99a2fb61a27af6a
Deleted: sha256:548a79621a426b4eb077c926eabac5a8620c454fb230640253e1b44dc7dd7562
It is important to periodically clean up your Docker environment, as building and running containers can cause large amounts of disk usage over time. To streamline the cleaning up of your environment, Docker provides a prune command that will automatically remove old containers and base images
docker system prune -fa
Executing this command will remove any container images that are not tied to an existing running container, along with any other resources in your Docker environment.
Summary
Using commands such as docker run
, docker start
, docker exec
,
docker ps
, and docker stop
, we have explored the basics of container life cycle management through the Docker CLI. Through the various steps in this post, we launched container instances from the same base image, configured them using docker exec
, and cleaned up the deployments using other basic container life cycle commands such as docker rm
and docker rmi
.
Top comments (1)
How does using the
docker inspect
command provide insight into the security of a container image? Your detailed breakdown of these basic commands is really helpful!