1. Overview
When we work with Docker, sometimes we need to check configuration or log files inside a container.
In this quick tutorial, we'll see how to inspect the filesystem of a Docker container to help us address such situations.
2. Interactive Exploring
We can explore the filesystem interactively for most containers if we get shell access to them.
2.1. Running a Container With Shell Access
Let's start a container directly with shell access using the docker run command with the -it option:
$ docker run -it alpine /# ls -all ... -rwxr-xr-x 1 root root 0 Mar 5 13:21 .dockerenv drwxr-xr-x 1 root root 850 Jan 16 21:52 bin drwxr-xr-x 5 root root 360 Mar 5 13:21 dev drwxr-xr-x 1 root root 508 Mar 5 13:21 etc drwxr-xr-x 1 root root 0 Jan 16 21:52 home ....
Here, we launched the Alpine Linux container in interactive mode and connected to its shell.
But what happens if we want to explore something that's not directly a Linux distribution?
$ docker run -it cassandra ... INFO [MigrationStage:1] 2020-03-05 13:44:36,734 - Initializing system_auth.resource_role_permissons_index INFO [MigrationStage:1] 2020-03-05 13:44:36,739 - Initializing system_auth.role_members INFO [MigrationStage:1] 2020-03-05 13:44:36,743 - Initializing system_auth.role_permissions INFO [MigrationStage:1] 2020-03-05 13:44:36,747 - Initializing system_auth.roles INFO [main] 2020-03-05 13:44:36,764 - Waiting for gossip to settle... ...
The Cassandra docker container comes with a default startup command, which runs Cassandra. As a result, we're no longer connected to a shell.
Instead, we just see the standard output populated with log messages of the application.
However, we can bypass the default startup command.
Let's pass the /bin/bash additional argument to the docker run command:
$ docker run -it cassandra /bin/bash root@a71f71e98598:/# ls -all total 4 ... -rwxr-xr-x 1 root root 0 Mar 5 13:30 .dockerenv drwxr-xr-x 1 root root 920 Aug 14 2019 bin drwxr-xr-x 1 root root 0 Mar 28 2019 boot drwxr-xr-x 5 root root 360 Mar 5 13:30 dev lrwxrwxrwx 1 root root 34 Aug 14 2019 docker-entrypoint.sh -> usr/local/bin/docker-entrypoint.sh drwxr-xr-x 1 root root 1690 Mar 5 13:30 etc ...
Unfortunately, this has a nasty side-effect. The actual Cassandra application is no longer started, and we have to do this from the shell manually.
When we use this approach, we assume that we can control the startup of the container. In a production environment, this might not be possible.
2.2. Spawning a Shell in a Running Container
Fortunately, we can use the docker exec command, which allows us to connect to running containers.
Let's first start the container we want to explore:
$ docker run cassandra ... INFO [MigrationStage:1] 2020-03-05 13:44:36,734 - Initializing system_auth.resource_role_permissons_index INFO [MigrationStage:1] 2020-03-05 13:44:36,739 - Initializing system_auth.role_members INFO [MigrationStage:1] 2020-03-05 13:44:36,743 - Initializing system_auth.role_permissions INFO [MigrationStage:1] 2020-03-05 13:44:36,747 - Initializing system_auth.roles INFO [main] 2020-03-05 13:44:36,764 - Waiting for gossip to settle... ...
Next, we identify the container id with docker ps:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED 00622c0645fb cassandra "docker-entrypoint.s…" 2 minutes ago
Then, we pass /bin/bash as the argument with -it option to docker exec:
$ docker exec -it 00622c0645fb /bin/bash root@00622c0645fb:/# ls -all ... -rwxr-xr-x 1 root root 0 Mar 5 13:44 .dockerenv drwxr-xr-x 1 root root 920 Aug 14 2019 bin drwxr-xr-x 1 root root 0 Mar 28 2019 boot drwxr-xr-x 5 root root 340 Mar 5 13:44 dev lrwxrwxrwx 1 root root 34 Aug 14 2019 docker-entrypoint.sh -> usr/local/bin/docker-entrypoint.sh drwxr-xr-x 1 root root 1690 Mar 5 13:44 etc ...
Here, we used Bash as our shell of choice. This can vary depending on which Linux distribution the container is based on.
By contrast, our first example uses Alpine Linux, which comes with the Bourne Shell by default:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED 8408c85b3c57 alpine "/bin/sh" 3 seconds ago
Since Bash isn't available, we pass /bin/sh as the argument to docker exec:
$ docker exec -it 8408c85b3c57 /bin/sh / # ls -all ... -rwxr-xr-x 1 root root 0 Mar 5 14:19 .dockerenv drwxr-xr-x 1 root root 850 Jan 16 21:52 bin drwxr-xr-x 5 root root 340 Mar 5 14:19 dev drwxr-xr-x 1 root root 508 Mar 5 14:19 etc drwxr-xr-x 1 root root 0 Jan 16 21:52 home ...
3. Non-Interactive Exploring
Sometimes, the container is stopped, and we can't run it interactively, or it simply does not have a shell.
For example, the hello-world is a minimal container that starts from scratch. As a result, shell access is not possible.
Lucky for us, in both situations, we can dump the filesystem to our host machine for further exploration.
Let's see how we can do this.
3.1. Exporting the Filesystem
We can export the filesystem of a container into a tar file by using the docker export command.
Let's first run the hello-world container:
$ docker run hello-world Hello from Docker! This message shows that your installation appears to be working correctly. ....
Similarly, we first obtain the container id of a stopped container by passing the -a flag to docker ps:
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED a0af60c72d93 hello-world "/hello" 3 minutes ago ...
Then we dump the filesystem into the hello.tar file using the -o option of docker export:
$ docker export -o hello.tar a0af60c72d93
Finally, we print the contents of the archive using the tar utility with the -tvf flags:
$ tar -tvf hello.tar -rwxr-xr-x root/0 0 2020-03-05 16:55 .dockerenv .... drwxr-xr-x root/0 0 2020-03-05 16:55 dev/pts/ drwxr-xr-x root/0 0 2020-03-05 16:55 dev/shm/ .... -rwxr-xr-x root/0 0 2020-03-05 16:55 etc/resolv.conf -rwxrwxr-x root/0 1840 2019-01-01 03:27 hello ...
Alternatively, we can use any archive explorer to see what's inside.
3.2. Copying the Filesystem
We can also copy the entire filesystem using the docker cp command.
Let's try this as well.
First, we copy the complete filesystem starting from the root (/) from our container to the test directory:
$ docker cp a0af60c72d93:/ ./test
Next, let's print the contents of the test directory:
$ ls -all test/ total 28 .. drwxr-xr-x 4 baeldung users 4096 Mar 5 16:55 dev -rwxr-xr-x 1 baeldung users 0 Mar 5 16:55 .dockerenv drwxr-xr-x 2 baeldung users 4096 Mar 5 16:55 etc -rwxrwxr-x 1 baeldung users 1840 Jan 1 2019 hello
4. Conclusion
In this quick tutorial, we discussed how to explore the filesystem of a Docker container.
We can start most containers with shell access directly with the docker run command. In addition, we can spawn a shell for running containers with the help of docker exec.
When it comes to stopped containers or minimal containers, we can simply export or even copy the entire filesystem locally.