“A Docker container is a Linux container that has been instantiated from a Docker image. A specific container can only exist once; however, you can easily create multiple containers from the same image.”
—Sean P. Kane & Karl Matthias, Docker Up & Running, 2nd Edition
Docker is a fully-featured container environment that automates the creation of containers, and allows the monitoring and management of multiple containers, even across multiple hosts.
In this studio, you will:
piuser to the
cgroupsand namespaces similarly to the simple container environment in previous exercises.
Please complete the required exercises below. We encourage you to please work in groups of 2 or 3 people on each studio (and the groups are allowed to change from studio to studio) though if you would prefer to complete any studio by yourself that is allowed.
As you work through these exercises, please record your answers, and when finished upload them along with the relevant source code to the appropriate spot on Canvas.
Make sure that the name of each person who worked on these exercises is listed in the first answer, and make sure you number each of your responses so it is easy to match your responses with each exercise.
As the answer to the first exercise, please list the names of the people who worked together on this studio.
To begin, you will need to install Docker on your Raspberry Pi.
The Docker client and server are bundled together in the APT package
Install it by running the command:
sudo apt-get update
sudo apt-get install docker.io
The Docker server, as installed, should run automatically when your Raspberry Pi boots.
To make sure of this, though, you will manually add it as a startup service to
sudo systemctl enable docker
sudo systemctl start docker
To verify that Docker installed correctly, and that the server is now running, issue the following two commands:
sudo docker version
ps aux | grep docker
As the answer to this exercise, please say (1) the Client version that was installed,
(2) the version for the Engine, containerd, runc, and docker-init
server components that were installed, and (3) the location of the Docker server daemon executable,
as reported by
docker version without
You should observe that only the Client version is reported;
by default, non-root users are restricted from access to the Docker server.
Docker establishes a group, aptly named
to which its members are conferred additional privileges for launching and managing Docker containers.
pi user to this group with the following command:
sudo usermod -a -G docker pi
Now, verify that the user has been added to the group by issuing the following command:
getent group docker
In Linux, a user can view their own group membershp with the command
Run this command. You might not see the
docker group listed,
in which case you will need to log out and back into your user session to have the session's group membership updated.
After doing so, run the
groups command again to verify that
docker is listed.
As the answer to this exercise, please list the output of the
groups commands that you ran.
Now, you are ready to start using Docker!
We'll begin with launching a container from a simple image.
Alpine Linux is a minimal Linux distribution that
uses the lightweight
musl c library instead of
and has a minimal set of shell utilities and mounted files and directories to enable
interaction via the host Linux kernel.
Alpine Linux is available as an image in the Docker Hub, which is Docker's public registry for distributing container images.
Run an Alpine Linux container by issuing the following command:
docker run --rm -ti alpine:latest /bin/sh
For interactive processes, e.g. those which provide a shell,
you must use the
-ti flags to allocate a tty for the container process.
-t flag allocates a pseudo-tty, and the
-i flag keeps
stdin open for the tty.
/bin/sh specifies which command should be run in the container when it is launched.
This launches the
sh shell, a lighter shell environment than
As the answer to this exercise, please provide the following:
Explain what the
--rm flag, passed to the
docker run command, accomplishes.
HINT: refer to the Docker run reference
Inside of your container, run the following commands, and show their output:
docker ps command allows you to view Docker containers that are running on a system.
In a separate terminal window (i.e., outside of the container), run the command, and show its output.
df -a from both inside and outside the container, and show its output in both places.
Explain what this tells you about where the container's root filesystem resides from the perspective of the global system namespace.
In the container, run
ls -l /
Outside of the container, navigate to the directory that holds the container's root filesystem,
ls -l from there
(you may need to launch a root shell to do so).
Show the output of both commands.
Once you are done, exit the container.
A Docker image is intended to encapsulate an application, including all dependencies it requires to run on the Linux kernel. Docker images are constructed in layers, defined in a Dockerfile, and a layer can refer to a base image from which it inherits. To create an image that encapsulates a program, you will create a Dockerfile that inherits, as its first layer, from the Alpine Linux container image you used in the previous exercise.
The next layer could consist of a compiled binary of a program; however, images are intended to be portable across achitectures. Inserting a binary executable compiled on the Raspberry Pi would mean that the image could not be used to run containers on, e.g., Intel platforms.
Images allow you to add layers that specify a command that runs in the container as it starts.
So, in an image specification (the Dockerfile),
you can specify one or more layers that compile the programs
encapsulated by the container.
Unfortunately, Alpine Linux does not come with a built-in C compiler.
Luckily, though, it does come with a package manager,
which can be used to install
For this exercise, you will create a new Docker image,
that will inherit from the Alpine Linux image as a base layer,
then a second layer will install
Create a new directory on your Raspberry Pi,
in which you will keep your Docker image specifications and files.
In that directory, create a directory called
create a new file, simply called
That file should have the following lines:
(This establishes a layer that inherits the Alpine Linux image.)
RUN apk add gcc musl-dev
gcc and the
musl-dev C library.)
Now, still in the
build the image using the following command:
docker build -t alpine-gcc:v0 .
You can replace
v0 with any release tag that you would like to use.
Now, launch a container from your new image with the following command:
docker run --rm -ti alpine-gcc:v0 /bin/sh
Verify that the container has
gcc installed by running
Of course, with no program to provide as input, it should report an error and terminate compilation;
gcc should still be found.
As the answer to this exercise, please show (1) the output of the
command, and (2) the output of the
gcc command from inside your container.
When you are done, exit the container.
Now that you have an image of Alpine Linux with
you are ready to package an application!
Create a new directory for this image,
and navigate into that directory.
First, create a subdirectory called
This will hold your application's code.
In that directory, create a simple program
(for our example, we will call it
(1) prints a simple message to the terminal, then
(2) spins indefinitely in a loop.
Compile and run your program, just to verify that it works as expected. After running it, delete the compiled binary. This will be compiled by the container.
Outside of the
(but still in the directory you created for this image),
create a new Dockerfile.
The first line should inherit from the
image you created in the previous exercise.
Use the following for the remaining lines:
COPY app /app
(This copies the app directory into the /app root directory of the container)
RUN gcc /app/hello-world.c -o /app/hello-world
(This instructs the container to run
gcc to compile the program)
(This specifies a command to run when the container launches)
Now, build your container:
docker build -t mycontainer:v0 .
mycontainer with the name you would like to use for your container)
Since your image already specifies a command, you can run it with:
docker run --rm -ti mycontainer:v0
With your container running, open another terminal window.
ps aux command to retrieve the PID of the process
running your application in the container.
As the answer to this exercise, please show:
(1) the output of the
docker ps command
(2) the contents of the
/proc/PID/cgroup file (where
PID is the PID you retrieved with
and (3) the output of the commands
ls -li /proc/self/ns
ls -li /proc/PID/ns (you may need to run the second one from a root shell).
What does this tell you about the
cgroup and namespace membership of the container?
Keep your container running for the next exercise.
For the final exercise of today's studio, you will export and inspect the files from your running container.
To export your container,
use the container ID as reported by the
docker ps command.
It should be a 12-character hexadecimal string.
Now, run the command:
docker export fedcba987654 -o mycontainer.tar
fedcba987654 with the container ID.
Now, look at the contents of the file with the following command:
tar -tvf mycontainer.tar | less
tar -tvf allows you to inspect the contents of a tar archive,
and piping the output into
less provides a scrollable view that fits in your terminal window.
Take a look at the files, and get some sense of what's in there.
As the answer to this exercise, please (1) report how many files are in the container
(e.g., by piping
tar -tvf mycontainer.tar into the
wc wordcount utility),
then (2) report the size of the container (i.e., the size of the exported tar file).
Please also (3) say how that compares to the size of the original program you wrote.
Page updated Tuesday, March 1, 2022, by Marion Sudvarg and Chris Gill.