How to use docker debug command to debug containers and images

DockerDockerBeginner
Practice Now

Introduction

In this lab, we will explore how to effectively debug Docker containers and images using the docker debug command and other related techniques. We will begin by tackling the challenge of debugging slim containers that lack a shell, demonstrating how to execute commands directly within their environment.

Following this, we will learn how to debug a slim image directly before it's even run as a container. We will also cover how to modify files within a running container for debugging purposes and how to manage the debug toolbox using the install and uninstall commands. Finally, we will gain a deeper understanding of container entry points by utilizing the entrypoint command.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL docker(("Docker")) -.-> docker/ContainerOperationsGroup(["Container Operations"]) docker(("Docker")) -.-> docker/VolumeOperationsGroup(["Volume Operations"]) docker(("Docker")) -.-> docker/DockerfileGroup(["Dockerfile"]) docker/ContainerOperationsGroup -.-> docker/run("Run a Container") docker/ContainerOperationsGroup -.-> docker/rm("Remove Container") docker/ContainerOperationsGroup -.-> docker/exec("Execute Command in Container") docker/VolumeOperationsGroup -.-> docker/cp("Copy Data Between Host and Container") docker/DockerfileGroup -.-> docker/build("Build Image from Dockerfile") subgraph Lab Skills docker/run -.-> lab-555138{{"How to use docker debug command to debug containers and images"}} docker/rm -.-> lab-555138{{"How to use docker debug command to debug containers and images"}} docker/exec -.-> lab-555138{{"How to use docker debug command to debug containers and images"}} docker/cp -.-> lab-555138{{"How to use docker debug command to debug containers and images"}} docker/build -.-> lab-555138{{"How to use docker debug command to debug containers and images"}} end

Debugging a slim container with no shell

In this step, we will learn how to debug a slim container that does not have a shell installed. Slim containers are often used to reduce the image size and attack surface, but they can be challenging to debug when something goes wrong.

First, let's run a simple slim container that prints a message and then exits. We will use the alpine image, which is a very small Linux distribution.

docker run alpine echo "Hello from Alpine!"

You should see the output Hello from Alpine! in your terminal. This confirms that the container ran successfully.

Now, let's try to run a command inside this container using docker exec. We will try to run /bin/sh, which is a common shell.

docker run -d --name slim-container alpine sleep 3600
docker exec -it slim-container /bin/sh

You will likely see an error message indicating that /bin/sh is not found. This is because the alpine image, in its default configuration, does not include a shell like bash or sh. This is a common characteristic of slim containers.

To debug a container without a shell, we can use the docker exec command to run a specific command within the container's environment. However, since there's no shell, we need to execute the command directly.

Let's try to list the files in the root directory of the container. We know that the ls command is usually available even in minimal environments.

docker exec -it slim-container ls /

You should see a list of directories and files in the root of the container's filesystem, such as bin, etc, lib, etc. This demonstrates that we can still execute commands directly within the container even without a shell.

Finally, let's clean up the container we created.

docker stop slim-container
docker rm slim-container

This step showed us how to interact with a slim container that lacks a shell by executing commands directly using docker exec. In the next steps, we will explore other debugging techniques.

Debugging a slim image directly

In the previous step, we learned how to interact with a running slim container. However, sometimes you might need to inspect the contents of a slim image before running it as a container, or if the container fails to start. In this step, we will explore how to debug a slim image directly.

Docker provides the docker run command with the ability to override the default entrypoint and command of an image. This allows us to run a different command within a temporary container created from the image, giving us a way to inspect its contents.

Let's use the alpine image again. We know it's a slim image and doesn't have a shell by default. We can use docker run to execute a command like ls / directly on the image.

docker run --rm alpine ls /

The --rm flag ensures that the temporary container is removed automatically after the command finishes. You should see the same output as when we executed ls / on the running container in the previous step. This confirms that we can inspect the image's filesystem directly.

Now, let's try to see if a specific command exists within the image. For example, let's check for the presence of the ping command.

docker run --rm alpine which ping

You will likely see an error message like which: not found. This indicates that the ping command is not present in the default alpine image. This is another characteristic of slim images โ€“ they often only include the absolute minimum required tools.

Let's try a command that we know exists, like cat.

docker run --rm alpine which cat

This time, you should see the output /bin/cat, confirming that the cat command is available in the image.

This technique of using docker run --rm <image> <command> is very useful for quickly checking the contents of an image, verifying the presence of specific files or commands, and understanding the image's structure without needing to run a long-lived container or build a new image.

Modifying files in a running container

In this step, we will learn how to modify files within a running container. This can be useful for debugging purposes, such as changing configuration files or adding temporary scripts to a container that is already running.

We will start by running a simple container based on the ubuntu image, which is more feature-rich than alpine and includes a shell and common utilities.

docker run -d --name my-ubuntu ubuntu sleep 3600

This command runs an Ubuntu container in detached mode (-d) and keeps it running for an hour using the sleep 3600 command. We've named the container my-ubuntu for easy reference.

Now, let's use docker exec to get a shell inside the running container.

docker exec -it my-ubuntu /bin/bash

You should now be inside the bash shell of the my-ubuntu container. The prompt will change to reflect that you are inside the container.

Inside the container, let's create a new file in the /tmp directory.

echo "This is a test file." > /tmp/test_file.txt

Now, let's verify that the file was created and contains the correct content.

cat /tmp/test_file.txt

You should see the output This is a test file.. This confirms that we were able to create and write to a file inside the running container.

To exit the container's shell, simply type exit.

exit

You are now back in your LabEx VM terminal.

We can also copy files into and out of a running container using the docker cp command. Let's create a file on our LabEx VM and copy it into the container.

First, create a file named local_file.txt in your ~/project directory.

echo "This file is from the host." > ~/project/local_file.txt

Now, copy this file into the /tmp directory of the my-ubuntu container.

docker cp ~/project/local_file.txt my-ubuntu:/tmp/

The format for docker cp is docker cp <source_path> <container_name>:<destination_path> or docker cp <container_name>:<source_path> <destination_path>.

Let's verify that the file was copied into the container. Get back into the container's shell.

docker exec -it my-ubuntu /bin/bash

Inside the container, check for the presence of local_file.txt in /tmp.

ls /tmp/

You should see local_file.txt listed along with test_file.txt.

Now, let's view the content of local_file.txt inside the container.

cat /tmp/local_file.txt

You should see the output This file is from the host..

Exit the container's shell again.

exit

Finally, let's clean up the container.

docker stop my-ubuntu
docker rm my-ubuntu

This step demonstrated how to modify files within a running container using docker exec to get a shell and standard Linux commands, and how to copy files between the host and the container using docker cp.

Managing the debug toolbox with install and uninstall

In this step, we will explore how to add and remove debugging tools within a running container. While it's generally recommended to keep production container images minimal, sometimes you need to install temporary tools for debugging purposes.

We will use the ubuntu image again, as it has a package manager (apt) that makes installing software easy.

First, let's run a new Ubuntu container in detached mode.

docker run -d --name debug-ubuntu ubuntu sleep 3600

Now, let's get a shell inside the container.

docker exec -it debug-ubuntu /bin/bash

Inside the container, let's try to use a command that is not installed by default, like ping.

ping google.com

You will likely see a "command not found" error.

To install ping and other network utilities, we can use the apt package manager. First, it's a good practice to update the package list.

apt update

This command fetches the latest information about available packages from the repositories.

Now, let's install the iputils-ping package, which provides the ping command.

apt install -y iputils-ping

The -y flag automatically confirms the installation without prompting.

After the installation is complete, you should be able to use the ping command.

ping -c 4 google.com

You should see the output of the ping command, indicating that it is now available and working inside the container.

Once you are finished debugging, it's a good idea to remove any tools you installed to keep the container clean and reduce its size if you were to commit it as a new image (though modifying running containers for production is generally discouraged).

To remove the iputils-ping package, use apt remove.

apt remove -y iputils-ping

You can verify that ping is no longer available.

ping google.com

You should see the "command not found" error again.

Exit the container's shell.

exit

Finally, clean up the container.

docker stop debug-ubuntu
docker rm debug-ubuntu

This step demonstrated how to temporarily install and uninstall debugging tools within a running container using its package manager. This is a powerful technique for troubleshooting issues in containers where the necessary tools are not included in the base image.

Understanding container entry points with the entrypoint command

In this step, we will learn about the ENTRYPOINT instruction in a Dockerfile and how it affects how a container runs. The ENTRYPOINT defines the command that will be executed when a container starts. It's often used to set the main executable for the container.

Let's create a simple Dockerfile that uses ENTRYPOINT. In your ~/project directory, create a file named Dockerfile with the following content:

FROM alpine
ENTRYPOINT ["echo", "Hello from the entrypoint!"]
CMD ["default", "command"]

This Dockerfile uses the alpine image as the base. The ENTRYPOINT is set to ["echo", "Hello from the entrypoint!"]. The CMD is set to ["default", "command"]. When both ENTRYPOINT and CMD are specified, the CMD arguments are passed as arguments to the ENTRYPOINT command.

Now, let's build an image from this Dockerfile.

docker build -t my-entrypoint-image ~/project

This command builds an image named my-entrypoint-image from the Dockerfile in the ~/project directory.

Now, let's run a container from this image without providing any additional command.

docker run my-entrypoint-image

You should see the output Hello from the entrypoint! default command. This shows that the ENTRYPOINT command (echo) was executed, and the CMD arguments (default command) were passed to it.

Now, let's run the container and provide a different command on the docker run line. When you provide a command on the docker run line, it overrides the CMD instruction in the Dockerfile, but the ENTRYPOINT is still executed with the provided command as arguments.

docker run my-entrypoint-image "override command"

You should see the output Hello from the entrypoint! override command. This demonstrates that the ENTRYPOINT was still executed, but the arguments from the docker run command (override command) replaced the CMD arguments.

What if you want to completely ignore the ENTRYPOINT and run a different command? You can use the --entrypoint flag with docker run.

docker run --entrypoint /bin/echo my-entrypoint-image "Running a different command"

You should see the output Running a different command. In this case, the --entrypoint flag overrode the ENTRYPOINT specified in the Dockerfile, and the provided command (/bin/echo) was executed with the arguments (Running a different command).

Understanding ENTRYPOINT and CMD is crucial for building and debugging Docker images. ENTRYPOINT defines the core executable, while CMD provides default arguments to that executable or a default command if no ENTRYPOINT is set.

Summary

In this lab, we learned how to debug Docker containers and images using the docker debug command and related techniques. We began by exploring how to debug a slim container that lacks a shell, demonstrating that we can still execute commands directly within the container's environment using docker exec even without a traditional shell like bash or sh. This is a crucial technique for troubleshooting minimal container images.

While the provided content only covers the first step, the lab's overall structure indicates that subsequent steps would delve into debugging slim images directly, modifying files within running containers, managing the debug toolbox with install and uninstall commands, and understanding container entry points using the entrypoint command. These steps would further enhance our ability to diagnose and resolve issues within Dockerized applications and images.