Exercise 1: Hello from Alpine
The goal of this first exercise is to launch containers based on the alpine image.
-
Launch a container based on alpine and provide it the command echo hello
-
What are the steps performed by the docker daemon?
-
Launch a container based on alpine without specifying a command. What do you observe?
Solution
- The command to run is:
docker container run alpine echo hello
- The result of the previous command shows the different steps that are performed:
Unable to find image 'alpine:latest' locally
latest: Pulling from library/alpine
88286f41530e: Pull complete
Digest: sha256:1072e499f3f655a032e88542330cf75b02e7bdf673278f701d7ba61629ee3ebe
Status: Downloaded newer image for alpine:latest
hello
Since the alpine image isn’t available locally, it is downloaded from Docker Hub. A container is then launched from this image with the specified command (echo “hello”). The result of this command is displayed on standard output. Once the command is finished, the container is stopped but still exists.
- The command to run is “docker container run alpine”
Since the alpine image is already present locally (downloaded in the previous step), it is reused. The default command executed when launching a container based on alpine is “/bin/sh”. This command produces no output on standard output. Once the command is finished, the container is stopped but still exists.
Exercise 2: Interactive Shell
The goal of this exercise is to launch containers in interactive mode.
-
Launch a container based on alpine in interactive mode without specifying a command
-
What happened?
-
What is the default command of a container based on alpine?
-
Navigate through the file system
-
Use alpine’s package manager (apk) to add a package
$ apk update
$ apk add curl
Solution
- The command to launch a container in “interactive” mode is:
docker container run -ti alpine
-
We get a sh shell in the container.
-
By default, the default command used in the alpine image is /bin/sh
Note: we’ll come back to this later, but it’s interesting to see that this information is present in the Dockerfile used to create the image (https://github.com/gliderlabs/docker-alpine/blob/2127169e2d9dcbb7ae8c7eca599affd2d61b49a7/versions/library-3.6/x86_64/Dockerfile)
-
We can navigate through the file system the same way we do in other more “traditional” Linux distributions using commands like cd, ls, pwd, cat, less, …
-
The package manager for an alpine distribution is apk
To update the package list, we can use the command apk update.
To install a package, like curl, we use the following command apk add curl
Exercise 3: Foreground / Background
The goal of this exercise is to create containers in foreground and background.
-
Launch a container based on alpine by specifying the command
ping 8.8.8.8
-
Stop the container with CTRL-C
Is the container still running?
Note: you can use the docker ps
command which lists the containers running on the machine.
-
Launch a container in interactive mode by specifying the command
ping 8.8.8.8
-
Stop the container with CTRL-P CTRL-Q
Is the container still running?
- Launch a container in background, still specifying the command
ping 8.8.8.8
Solution
- The container can be launched with the following command:
docker container run alpine ping 8.8.8.8
-
The docker ps command doesn’t list the container because the process was stopped by the CTRL-C command
-
The following command launches the container in “interactive” mode:
docker container run -ti alpine ping 8.8.8.8
- The CTRL-P CTRL-Q command detaches from the pseudo terminal (allocated with options -t -i, or -ti).
The container continues to run. It is listed with the docker ps
command
- The following command launches the container in background:
docker container run -d alpine ping 8.8.8.8
The docker ps command shows that the container is running, it’s simply not attached to the terminal from which it was launched.
Exercise 4: Port Publishing
The goal of this exercise is to create a container by exposing a port on the host machine
-
Launch a container based on nginx and publish container port 80 to host port 8080
-
Verify from your browser that nginx’s default page is served on http://localhost:8080
-
Launch a second container publishing the same port
What do you observe?
Solution
- The following command launches the container based on nginx and exposes port 80 of this container on port 8080 of the host machine
docker container run -d -p 8080:80 nginx
- The default index.html served by nginx is accessible on port 8080
- If we launch another container using the same host port, we get an error because this port cannot be used for both containers.
$ docker container run -d -p 8080:80 nginx
c9dc92128bd8871d1c75678fd41dc09c5afcf02857c7c64bd89f560cb2b6aec7
docker: Error response from daemon: driver failed programming external connectivity on endpoint objective_leakey (25ea6fe30398e51b5ad3dbd55a56cded518370c81a41d4013d58ca399647718a): Bind for 0.0.0.0:8080 failed: port is already allocated.
Exercise 5: Container List
The goal of this exercise is to show the different options for listing containers on the system
- List running containers
Are all the containers you created listed?
-
Use the -a option to also see containers that have been stopped
-
Use the -q option to list only container IDs (running or stopped)
Solution
- To list running containers on the system, the following two commands are equivalent:
docker container ls
docker ps
Note: the second command is historical and dates from when the Docker platform was primarily used for container and image management. Subsequently, other primitives that we’ll see in the next chapters were added (volume, network, node, …), which led to a refactoring of the available command-line API.
Only running containers are listed. Containers that have been created and then stopped are not visible in this command’s output.
- The following commands list both running containers and those that are stopped:
docker container ls -a and docker ps -a
- The following commands list the identifiers of all containers running on the host machine, both those running and those that have been stopped:
docker container ls -aq
docker ps -aq
Note: we’ll see that these commands are very useful, especially when we want to perform an action on a set of containers.
Exercise 6: Container Inspection
The goal of this exercise is container inspection
- Launch, in background, a new container based on nginx:1.20 publishing container port 80 to host port 8000.
Note the container ID returned by the previous command.
-
Inspect the container using its ID
-
Using Go template format, retrieve the container’s name and IP
-
Manipulate Go templates to retrieve other information
Solution
1. The command to launch the container in question is:docker container run -d -p 8000:80 nginx:1.20
- Container inspection is done using the docker inspect CONTAINER_ID command
Note: you can use just the first characters of the identifier, or the container name if it was specified with the –name option during creation.
docker inspect 6ee
- The “Go template” formatting allows you to retrieve only the information you need from this imposing JSON structure.
The command used to retrieve the container name:
$ docker inspect --format "{{ .Name }}" efc940
/elated_mclean
The command to retrieve the container’s IP address:
$ docker inspect -f '{{ .NetworkSettings.IPAddress }}' efc940
172.17.0.5
- Go templating is very rich, you can find additional examples at: Go template
Exercise 7: Exec in a Container
The goal of this exercise is to show how to launch a process in an existing container
-
Launch a container in background, based on the alpine image. Specify the command
ping 8.8.8.8
and the name ping with the –name option -
Observe the container logs using the ID returned by the previous command or the container name
Quit the logs command with CTRL-C
-
Launch an interactive sh shell in the previous container
-
List the container’s processes
What do you observe regarding process IDs?
Solution
- The following command launches the container in question:
$ docker container run -d --name ping alpine ping 8.8.8.8
172c401915f56e3fb10391259fac77bc2d3c194a1b27fa5072335e04656e57bb
- The following command allows continuous log following (option -f) using the identifier:
$ docker container logs -f 172
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=37 time=20.713 ms
64 bytes from 8.8.8.8: seq=1 ttl=37 time=19.747 ms
64 bytes from 8.8.8.8: seq=2 ttl=37 time=19.730 ms
64 bytes from 8.8.8.8: seq=3 ttl=37 time=20.345 ms
64 bytes from 8.8.8.8: seq=4 ttl=37 time=24.476 ms
The container name can also be used:
$ docker container logs -f ping
...
64 bytes from 8.8.8.8: seq=162 ttl=37 time=20.364 ms
64 bytes from 8.8.8.8: seq=163 ttl=37 time=20.822 ms
64 bytes from 8.8.8.8: seq=164 ttl=37 time=22.478 ms
64 bytes from 8.8.8.8: seq=165 ttl=37 time=19.772 ms
64 bytes from 8.8.8.8: seq=166 ttl=37 time=19.630 ms
CTRL-C quits the log output but doesn’t stop the container as it’s not sent to the process running inside it.
- The following command launches a sh shell in the container:
$ docker exec -ti ping sh
/ #
- The following command lists the processes running in the container:
/ # ps aux
PID USER TIME COMMAND
1 root 0:00 ping 8.8.8.8
7 root 0:00 sh
13 root 0:00 ps aux
We can see that the command with which the container was launched (ping 8.8.8.8) has PID 1 (process identifier). The sh command we then launched in the container has PID 7 in the process tree. We also see the ps aux command which got PID 13; this command is no longer active and if we run it again, we’ll get a new PID:
/ # ps aux
PID USER TIME COMMAND
1 root 0:00 ping 8.8.8.8
7 root 0:00 sh
14 root 0:00 ps aux
We can exit our shell with a simple exit
, the container will continue running as long as PID 1 process is active.
Exercise 8: Cleanup
The goal of this exercise is to stop and remove existing containers
-
List all containers (active and inactive)
-
Stop all still-active containers by providing the list of IDs to the stop command
-
Verify there are no more active containers
-
List stopped containers
-
Remove all containers
-
Verify there are no more containers
Solution
- To list both running containers and those that have been stopped, use the -a option; the following commands are equivalent:
docker ps -a
docker container ls -a
- To stop all running containers in a single command line, we can give the list of identifiers to the stop command. We use the -q option when listing containers for this:
docker container stop $(docker container ls -q)
- The following command should no longer return any containers:
docker container ls
Note: this command is equivalent to docker ps
- If we add the -a option, we get the stopped containers:
docker container ls -a
Note: this command is equivalent to docker ps -a
- To remove stopped containers, we proceed as in question 2, giving the list of identifiers to the rm command:
docker container rm $(docker container ls -aq)
- The following command should no longer list any containers:
docker container ls -a