Container

Exercise - List container and images

In order to look up whether you have any containers, execute the following command:

docker container ls

As you have just started with the lab, the list of containers is empty.

$ docker container ls
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
Info

docker container ls and docker ps are identical commands

In order to check whether you already have any local images, execute the following command:

docker image ls

Again you will see an empty list:

$ docker image ls
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

Exercise - Your first container

Run your first container by executing:

docker run novatec/technologyconsulting-hello-container:v0.1

You will notice the following output:

$ docker run novatec/technologyconsulting-hello-container:v0.1
Unable to find image 'novatec/technologyconsulting-hello-container:v0.1' locally
v0.1: Pulling from novatec/technologyconsulting-hello-container
7c43afe89fe5: Already exists
2431b27052d8: Pull complete
95c210bc60c3: Downloading  3.271MB/7.607MB
e0df90639f4a: Verifying Checksum

What you can see above is the following scenario. Docker will try to run the image with the given tag novatec/technologyconsulting-hello-container:v0.1. As it can’t be found locally (as it has never been run or downloaded), the docker daemon will pull it from a remote registry (per default from Docker Hub , i.e. the “official” Container Image Registry as hardcoded in the standard Docker CLI tool) to download it:

sequenceDiagram
    participant U as User
    participant D as Docker Daemon
    participant R as Remote Registry
    U->>D: "docker run ..."
    Note over D: image present?
    D->>R: image pull
    R-->>D: image
    loop execute
        D->>D: run container in foreground until terminated
    end

Eventually the image will be there and the container is being run:

__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-12-01 08:52:34,627 INFO  [io.quarkus] (main) quarkus-hello 1.0.0-SNAPSHOT native (powered by Quarkus 3.1.3.Final) started in 0.052s. Listening on: http://0.0.0.0:8080
2023-12-01 08:52:34,630 INFO  [io.quarkus] (main) Profile prod activated.
2023-12-01 08:52:34,630 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy-reactive, smallrye-context-propagation, vertx]

Congratulations, you have just run your first container! Now what shall we do with it? At this point it is not particular exciting yet, because there is no interaction happening.

You will actually notice that your terminal does not take any commands from you any longer. Docker has run the container and put into the foreground. Hence your terminal is not accessible.

To get out of this scenario press Ctrl+C which will stop the container and give you the control over the terminal back.

Let’s recap what just happened. Execute the following command once again:

docker image ls

You will now see a first image in the list:

$ docker image ls
REPOSITORY                                     TAG       IMAGE ID       CREATED         SIZE
novatec/technologyconsulting-hello-container   v0.1      c4550e0a7743   19 hours ago    137MB

You never downloaded this image explicitly, but it was pulled implicitly through the run command.

Now re-run the command to list the containers:

docker container ls

The output looks familiar:

$ docker container ls
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

There is no active container running as it was stopped by pressing Ctrl+C

Modify the command slightly and call:

docker container ls -a

$ docker container ls -a
CONTAINER ID   IMAGE                                               COMMAND                  CREATED         STATUS                        PORTS     NAMES
28bed265baad   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   2 minutes ago   Exited (130) 54 seconds ago             friendly_elbakyan

This command shows all containers. Started ones and stopped ones. The status will show that this container has been exited and is therefore stopped.

Exercise - Run container with options - detached mode

In order to improve our experience with running containers let’s walk through a couple of options of the docker run call.

To run a container of the same image type, but switched to the background you need the so-called detached mode:

docker run -d novatec/technologyconsulting-hello-container:v0.1

Milestone: DOCKER/CONTAINER/RUN-OPTIONS-DETACHED

Observe output similar to this:

$ docker run -d novatec/technologyconsulting-hello-container:v0.1
05179501b5e00d9959b1b25554568566a4253491dcfcaa30a79400564d3ace1f

Three things are different compared to the first attempt.

  • There is no more image download. The exercise uses the image of the same type. It is stored locally after a download and will be used directly then.
  • There is no output log of the container visible. A long sequence of numbers and characters is displayed
  • You get back control over the terminal after the start of the container
sequenceDiagram
    participant U as User
    participant D as Docker Daemon
    participant R as Remote Registry
    U->>D: "docker run ..."
    Note over D: image present?
    loop execute
        D->>D: run container in background until terminated
    end
    D-->>U: release terminal

To validate if it is actually working, re-run this command:

docker container ls -a

And observe the difference in the output.

$ docker container ls -a
CONTAINER ID   IMAGE                                               COMMAND                  CREATED          STATUS                            PORTS      NAMES
05179501b5e0   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   18 seconds ago   Up 15 seconds                     8080/tcp   charming_bhaskara
28bed265baad   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   3 minutes ago    Exited (130) About a minute ago              friendly_elbakyan

There are two containers listed. The original one, which is still stopped. And the new one which is up and running. For the running one you can also see the port information (8080/tcp).

This information is about the port on which the application listens internally. With our configuration it is not yet exposed to the outside of the container.

If we attempt to reach this port via:

curl localhost:8080/hello

There will be no server to respond

curl: (7) Failed to connect to localhost port 8080: Connection refused

To validate if the server is actually there, try and check the logs. The command to do so is:

docker logs <container_id>

Now what is this container id?

There are multiple options to use:

  • The container id as listed in the ls output. In my example case it would be: 05179501b5e0
  • The container name as listed in the ls output. This is generated by docker as well. In my case: charming_bhaskara
  • A subset of the container id. E.g. 051 in my case, which are the first three characters.

This will result to:

$ docker logs 051
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-12-01 08:55:38,627 INFO  [io.quarkus] (main) quarkus-hello 1.0.0-SNAPSHOT native (powered by Quarkus 3.1.3.Final) started in 0.052s. Listening on: http://0.0.0.0:8080
2023-12-01 08:55:38,630 INFO  [io.quarkus] (main) Profile prod activated.
2023-12-01 08:55:38,630 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy-reactive, smallrye-context-propagation, vertx]

So the container started well, we just can’t reach it yet.

Exercise - Run container with options - port mapping

The easiest way to get an exposed port is to run another container with different configuration:

docker run -d -p 8080:8080 novatec/technologyconsulting-hello-container:v0.1

The difference in the command is the specification of the -p 8080:8080 part. The execution behavior will be very similar to the previous run. An id will be returned.

Milestone: DOCKER/CONTAINER/RUN-OPTIONS-PORT

Re-run the listing of containers:

docker container ls -a

This will show the 3rd container:

$ docker container ls -a
CONTAINER ID   IMAGE                                               COMMAND                  CREATED          STATUS                       PORTS                                       NAMES
f5fe72e974ee   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   15 seconds ago   Up 14 seconds                0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   keen_payne
05179501b5e0   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   3 minutes ago    Up 3 minutes                 8080/tcp                                    charming_bhaskara
28bed265baad   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   6 minutes ago    Exited (130) 4 minutes ago                                               friendly_elbakyan

What is the significant difference between the two running containers?

Solution

In the newest container the port is mapped to the outside of the container in the other one it is not.

PORTS                                       NAMES
0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   keen_payne
8080/tcp                                    charming_bhaskara

Now try again to call the endpoint via curl:

curl localhost:8080/hello; echo

And get a response:

$ curl localhost:8080/hello; echo
Hello World (from f5fe72e974ee) to somebody

Suddenly things are much more fun:

sequenceDiagram
    participant U as User
    participant D as Docker Daemon
    participant C as Container
    U->>D: "docker run ..."
    Note over D: image present?
    D->>C: creates
    loop execute
        D->>D: run container with port mapping
    end
    D-->>U: release terminal
    U->>C: "curl ..."
    C-->>U: "Hello World to somebody"

Exercise - Run container with options - Environment variables

The parameter “somebody” in the Hello World output is an environment variable within the container. If this is set to something different the output will change accordingly. Docker provides the “-e” option to set environmental options in the container.

Hence the command to get this confirmation is:

docker run -d -p 8080:8080 -e PROPERTY=Stuttgart novatec/technologyconsulting-hello-container:v0.1

The execution of this command will fail. Why?

Solution

In previous container is still running and blocks the local port 8080. It cannot be used twice.

docker: Error response from daemon: driver failed programming external connectivity on endpoint musing_goldstine (39e564b597729006b9613ba2a29cd4e41cde46fcdbd88af5ede210b029c6ecad): Bind for 0.0.0.0:8080 failed: port is already allocated.

To resolve this stop the running container or just map to another port. The second option is what we will be doing now by invoking:

docker run -d -p 8081:8080 -e PROPERTY=Stuttgart novatec/technologyconsulting-hello-container:v0.1

Milestone: DOCKER/CONTAINER/RUN-OPTIONS-ENV

After the execution list your containers and validate:

docker ps

$ docker ps
CONTAINER ID   IMAGE                                               COMMAND                  CREATED         STATUS         PORTS                                       NAMES
4b480045a033   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   8 seconds ago   Up 5 seconds   0.0.0.0:8081->8080/tcp, :::8081->8080/tcp   wonderful_mcnulty
f5fe72e974ee   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   3 minutes ago   Up 3 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   keen_payne
05179501b5e0   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   6 minutes ago   Up 6 minutes   8080/tcp                                    charming_bhaskara

It is also possible to format the output to focus on particular tabs:

docker ps --format "{{.ID}} {{.Ports}}"

which will show only

4b480045a033 0.0.0.0:8081->8080/tcp, :::8081->8080/tcp
f5fe72e974ee 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp
05179501b5e0 8080/tcp

Validate if the set environment variable has worked as desired.

And get a response:

curl localhost:8080/hello; echo

Hello World (from f5fe72e974ee) to somebody

curl localhost:8081/hello; echo

Hello World (from 4b480045a033) to Stuttgart

This example also shows you how easy it is to run multiple different versions or configurations side by side:

$ curl localhost:8081/hello; echo
Hello World (from 4b480045a033) to Stuttgart

$ curl localhost:8080/hello; echo
Hello World (from f5fe72e974ee) to somebody
sequenceDiagram
    participant U as User
    participant D as Docker Daemon
    participant C1 as Container 1
    participant C2 as Container 2
    U->>D: "docker run ..."
    Note over D: image present?
    loop execute
        D->>D: run Container 1 with port 8080
    end
    D-->>U: release terminal
    U->>D: "docker run ..."
    Note over D: image present?
    loop execute
        D->>D: run Container 2 with port 8081
    end
    D-->>U: release terminal
    U->>C2: "curl ...:8081/..."
    C2-->>U: "Hello World to Stuttgart"
    U->>C1: "curl ...:8080/..."
    C1-->>U: "Hello World to somebody"

What a great time to be alive!

Exercise - Run container with options - Container name

You might have noticed that docker generates random (and arguably funny) names of container instances.

docker ps -a --format "Id: {{.ID}} Name: {{.Names}}"

which will result in:

$ docker ps -a --format "Id: {{.ID}} Name: {{.Names}}"
Id: 4b480045a033 Name: wonderful_mcnulty
Id: 1fcb204ef7c5 Name: musing_goldstine
Id: f5fe72e974ee Name: keen_payne
Id: 05179501b5e0 Name: charming_bhaskara
Id: 28bed265baad Name: friendly_elbakyan

You can specify a name when running a container, too:

docker run -d -p 8082:8080 -e PROPERTY=Stuttgart-Entwicklertag --name happy_container novatec/technologyconsulting-hello-container:v0.1

Milestone: DOCKER/CONTAINER/RUN-OPTIONS-NAME

To display only the recent container you can also use:

docker container ls -l

CONTAINER ID   IMAGE                                               COMMAND                  CREATED          STATUS         PORTS                                       NAMES
6f4fc2fe956d   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   11 seconds ago   Up 9 seconds   0.0.0.0:8082->8080/tcp, :::8082->8080/tcp   happy_container

Done!

Exercise - Clean up

While walking through those exercises a lovely little container collection has been established on your machine.

Re-run the following command to show all the work already completed:

docker ps -a

Result should look somewhat like this:

$ docker ps -a
CONTAINER ID   IMAGE                                               COMMAND                  CREATED          STATUS                        PORTS                                       NAMES
6f4fc2fe956d   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   43 seconds ago   Up 41 seconds                 0.0.0.0:8082->8080/tcp, :::8082->8080/tcp   happy_container
4b480045a033   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   4 minutes ago    Up 3 minutes                  0.0.0.0:8081->8080/tcp, :::8081->8080/tcp   wonderful_mcnulty
1fcb204ef7c5   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   4 minutes ago    Created                                                                   musing_goldstine
f5fe72e974ee   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   7 minutes ago    Up 7 minutes                  0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   keen_payne
05179501b5e0   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   10 minutes ago   Up 10 minutes                 8080/tcp                                    charming_bhaskara
28bed265baad   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   13 minutes ago   Exited (130) 12 minutes ago                                               friendly_elbakyan

You may notice that it also created a container for the unsuccessful attempt to run two instances on the same port. A container instance exists even though it never got started at all.

To remove a container let’s start with the first one we built. In my example (28bed265baad/friendly_elbakyan). Just as before when logging, please do not use the names and IDs from the script, but from your real environment.

Invoke:

docker rm 28b (Replace “28b” with the first 3 characters of your container id!)

List all the containers again to validate.

If you try to remove the last running container you have built (top one in the list by now), will this work?

docker rm happy_container (Do not replace “happy_container” as you should have this container, too!)

Solution

No, it won’t. You cannot remove a running container. Stop it first.

Error response from daemon: You cannot remove a running container 6f4fc2fe956d395c115f54c3381e49a7129ce6cb1fd70c61057cf5eb0e2ee63f. Stop the container before attempting removal or force remove

So, let’s stop this container:

docker stop happy_container

and try again:

docker rm happy_container

This time things look better.

As you realize that can become a bit of a task if you have to clean up a certain container mess. You can use shell tooling and scripting to improve, but be careful when doing this in production.

The “-q” option returns only the IDs, which can be handy for that:

docker ps -q

4b480045a033
f5fe72e974ee
05179501b5e0

To stop all running containers execute:

docker ps -q | xargs docker stop

This will call “docker stop” with all running container ids.

Milestone: DOCKER/CONTAINER/CLEANUP-STOP

Running docker ps -a again should show no containers with status “Up”.

$ docker ps -a
CONTAINER ID   IMAGE                                               COMMAND                  CREATED          STATUS                        PORTS     NAMES
4b480045a033   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   7 minutes ago    Exited (143) 14 seconds ago             wonderful_mcnulty
1fcb204ef7c5   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   8 minutes ago    Created                                 musing_goldstine
f5fe72e974ee   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   11 minutes ago   Exited (143) 14 seconds ago             keen_payne
05179501b5e0   novatec/technologyconsulting-hello-container:v0.1   "./application -Dqua…"   14 minutes ago   Exited (143) 14 seconds ago             charming_bhaskara

To remove all containers run:

docker ps -a -q | xargs docker rm

This will call “docker rm” with all container ids.

Warning

ONCE AGAIN: BE CAREFUL DOING THIS AT HOME!

Milestone: DOCKER/CONTAINER/CLEANUP-RM

No more containers should be there:

$ docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Do not remove the image yet as it is needed at a later step in the exercise.

$ docker images
REPOSITORY                                     TAG       IMAGE ID       CREATED         SIZE
novatec/technologyconsulting-hello-container   v0.1      c4550e0a7743   19 hours ago    137MB

The first part of the exercise concludes with this. Congratulations! You’ve done the first step into a larger container world!