Docker (1/2): containers, images and applications

Docker (1/2): containers, images and applications

Disclaimer: This post has been translated to English using a machine translation model. Please, let me know if you find any mistakes.

📚 **This entry is part of the _Docker Guide_ series**, divided into two chapters that are read in order:

> * 👉 **Part 1: Containers, images, and applications**

* Part 2: Docker Compose and advanced topics

Containerslink image 39

Hello worldlink image 40

Run the first Hello world type container with the command docker run hello-world

	
< > Input
Python
!docker run hello-world
Copied
>_ Output
			
Unable to find image 'hello-world:latest' locally
>_ Output
			
latest: Pulling from library/hello-world
85e32844: Pull complete 457kB/2.457kBBDigest: sha256:dcba6daec718f547568c562956fa47e1b03673dd010fe6ee58ca806767031d1c
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/

Since we do not have the container saved locally, Docker downloads it from Docker Hub. If we run the container again now, the first message will no longer appear, the one indicating that it is being downloaded.

	
< > Input
Python
!docker run hello-world
Copied
>_ Output
			
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/

To see the containers that are running, run docker ps

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

As we can see, there is no container running. However, if we run docker ps -a (all), we see that they do appear

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1efb51bbbf38 hello-world "/hello" 10 seconds ago Exited (0) 9 seconds ago strange_thompson
5f5705e7603e hello-world "/hello" 15 seconds ago Exited (0) 14 seconds ago laughing_jang

We see that two containers called hello-world appear, which are the two we ran before. Therefore, each time we run the run command, Docker creates a new container; it does not execute one that already exists

If we want to have more information about one of the two containers, we can run docker inspect <id>, where <id> corresponds to the container ID shown in the previous list

	
< > Input
Python
!docker inspect 1efb51bbbf38
Copied
>_ Output
			
[
{
"Id": "1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e",
"Created": "2023-09-04T03:59:17.795499354Z",
"Path": "/hello",
"Args": [],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-09-04T03:59:18.406663026Z",
"FinishedAt": "2023-09-04T03:59:18.406181184Z"
},
"Image": "sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d",
"ResolvConfPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hostname",
"HostsPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hosts",
"LogPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e-json.log",
"Name": "/strange_thompson",
...
}
}
}
]

Since remembering IDs is complicated for us, Docker assigns names to containers to make our lives easier. Thus, in the previous list, the last column corresponds to the name that Docker has assigned to each container, so if we now run docker inspect <name> we will get the same information as with the ID

I run docker ps -a again to see the list once more

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1efb51bbbf38 hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago strange_thompson
5f5705e7603e hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago laughing_jang

And now I run docker inspect <name> to see the container information

	
< > Input
Python
!docker inspect strange_thompson
Copied
>_ Output
			
[
{
"Id": "1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e",
"Created": "2023-09-04T03:59:17.795499354Z",
"Path": "/hello",
"Args": [],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-09-04T03:59:18.406663026Z",
"FinishedAt": "2023-09-04T03:59:18.406181184Z"
},
"Image": "sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d",
"ResolvConfPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hostname",
"HostsPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hosts",
"LogPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e-json.log",
"Name": "/strange_thompson",
...
}
}
}
]

But why with docker ps do we not see any container and with docker ps -a do we? This is because docker ps only shows the containers that are running, while docker ps -a shows all containers, both those that are running and those that are stopped.

We can create a container by assigning it a name ourselves using the command docker run --name <name> hello-world

	
< > Input
Python
!docker run --name hello_world hello-world
Copied
>_ Output
			
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/

This will be more convenient for us, since we will be able to control the container names ourselves

If we now want to create another container with the same name, we won't be able to, because Docker does not allow container names to be duplicated. So if we want to rename the container, we can use the command docker rename <old name> <new name>

	
< > Input
Python
!docker rename hello_world hello_world2
Copied

We now have a lot of identical containers. So if we want to delete one, we have to use the command docker rm <id> or docker rm <name>

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f432c9c2ca21 hello-world "/hello" 9 seconds ago Exited (0) 8 seconds ago hello_world2
1efb51bbbf38 hello-world "/hello" 4 minutes ago Exited (0) 4 minutes ago strange_thompson
5f5705e7603e hello-world "/hello" 4 minutes ago Exited (0) 4 minutes ago laughing_jang
	
< > Input
Python
!docker rm hello_world2
Copied
>_ Output
			
hello_world2

If we look at the list of containers again, the hello_world2 container will no longer be there

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1efb51bbbf38 hello-world "/hello" 5 minutes ago Exited (0) 5 minutes ago strange_thompson
5f5705e7603e hello-world "/hello" 5 minutes ago Exited (0) 5 minutes ago laughing_jang

If we want to delete all the containers, we can do it one by one, but since that is very cumbersome, we can delete them all using the docker container prune command. This command removes only the containers that are stopped

	
< > Input
Python
!docker container prune
Copied
>_ Output
			
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y

Docker asks if you’re sure, and if you say yes, it deletes them all. If you list all the containers now, none appears

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

The interactive modelink image 41

We are going to run Ubuntu using the command docker run ubuntu

	
< > Input
Python
!docker run ubuntu
Copied
>_ Output
			
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
Digest: sha256:20fa2d7bb4de7723f542be5923b06c4d704370f0390e4ae9e1c833c8785644c1[1A
Status: Downloaded newer image for ubuntu:latest

As we can see, it has now taken longer to download. If we list the containers using the docker ps command, we see that the container we just created does not appear, that is, it is not running.

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

We now list all containers

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
da16b3a85178 ubuntu "bash" 4 seconds ago Exited (0) 3 seconds ago hardcore_kare

We see that the container status is Exited (0)

If we look at the container command, it shows bash and along with the Exited (0) status it indicates that Ubuntu has started, has executed its *bash*, has finished execution, and has returned a 0. This happens because Ubuntu's Bash has not been told to do anything. To solve this, now we are going to run the container using the command docker run -it ubuntu, with it what we are indicating is that we want to run it in interactive mode

	
< > Input
Python
!docker run -it ubuntu
Copied
>_ Output
			
root@5b633e9d838f:/#

Now we can see that we are inside the Ubuntu bash shell. If we run the command cat /etc/lsb-release we can see the Ubuntu distribution

	
< > Input
Python
!root@5b633e9d838f:/# cat /etc/lsb-release
Copied
>_ Output
			
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.1 LTS"

If we open another terminal and see the list of containers, the container running Ubuntu will now appear.

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b633e9d838f ubuntu "bash" 3 minutes ago Up 3 minutes funny_mirzakhani

We see the container with Ubuntu and in its status we can see UP

If we now look at the list of all containers, we will see that the two containers with Ubuntu appear, the first one stopped and the second one running

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b633e9d838f ubuntu "bash" 3 minutes ago Up 3 minutes funny_mirzakhani
da16b3a85178 ubuntu "bash" 3 minutes ago Exited (0) 3 minutes ago hardcore_kare

If we return to the terminal where we had Ubuntu running inside Docker, if we type exit we will exit Ubuntu.

	
< > Input
Python
!root@5b633e9d838f:/# exit
Copied
>_ Output
			
exit

If we run docker ps, the container no longer appears

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

But if I run docker ps -a it does appear. This means that the container stopped

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b633e9d838f ubuntu "bash" 4 minutes ago Exited (0) 27 seconds ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 4 minutes ago Exited (0) 4 minutes ago hardcore_kare

This happens because when we type exit, we are actually typing it in the Ubuntu bash console, which means we are terminating the Ubuntu bash process.

Container lifecyclelink image 42

In Docker, when the main process of a container ends, the container stops. Several processes can run inside a container, but the container only stops when the main process ends.

Therefore, if we want to run a container that does not stop when a process finishes, we must ensure that its main process does not end. In this case, do not let bash finish.

If we want to run a container with Ubuntu, but have it not exit when the Bash process ends, we can do it as follows

	
< > Input
Python
!docker run --name alwaysup -d ubuntu tail -f /dev/null
Copied
>_ Output
			
ce4d60427dcd4b326d15aa832b816c209761d6b4e067a016bb75bf9366c37054

What we do is first give it the name alwaysup, secondly pass the -d option (detach) so that the container runs in the background and finally we tell it the main process we want to run in the container, which in this case is tail -f /dev/null which is equivalent to a nop command

This will return the container ID to us, but we will not be inside Ubuntu as before

If we now look at the list of running containers, the container we just created appears.

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 18 seconds ago Up 17 seconds alwaysup

As we already have a container running all the time, we can connect to it using the exec command. We tell it the name or ID of the container and pass the process we want to be executed. We also pass the -it option to indicate that it should be interactive

	
< > Input
Python
!docker exec -it alwaysup bash
Copied
>_ Output
			
root@ce4d60427dcd:/#

Now we are back inside Ubuntu. If we run the command ps -aux we can see a list of the processes that are running inside Ubuntu.

	
< > Input
Python
!ps -aux
Copied
>_ Output
			
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 2820 1048 ? Ss 13:04 0:00 tail -f /dev/null
root 7 0.0 0.0 4628 3796 pts/0 Ss 13:04 0:00 bash
root 15 0.0 0.0 7060 1556 pts/0 R+ 13:05 0:00 ps -aux

We only see three processes, the ps -aux, the bash and the tail -f /dev/null

This container will remain on as long as the tail -f /dev/null process keeps running

If we exit the container with the command exit and run the command docker ps we can see that the container is still running

	
< > Input
Python
!exit
Copied
>_ Output
			
exit
	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 2 minutes ago Up 2 minutes alwaysup

To be able to finish the process and shut down the container, we must use the command docker stop <name>

	
< > Input
Python
!docker stop alwaysup
Copied
>_ Output
			
alwaysup

If we now list the running containers again, the container with Ubuntu no longer appears

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

And if we list all the containers, the container with Ubuntu appears, and its status Exited

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 14 minutes ago Exited (137) About a minute ago alwaysup
5b633e9d838f ubuntu "bash" 19 minutes ago Exited (0) 15 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 20 minutes ago Exited (0) 20 minutes ago hardcore_kare

We can also pause a container using the command docker pause <name>

	
< > Input
Python
!docker run --name alwaysup -d ubuntu tail -f /dev/null
Copied
>_ Output
			
8282eaf9dc3604fa94df206b2062287409cc92cbcd203f1a018742b5c171c9e4

Now we pause it

	
< > Input
Python
!docker pause alwaysup
Copied
>_ Output
			
alwaysup

If we look at all the containers again, we can see that the container with Ubuntu is paused

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8282eaf9dc36 ubuntu "tail -f /dev/null" 41 seconds ago Up 41 seconds (Paused) alwaysup
5b633e9d838f ubuntu "bash" 19 minutes ago Exited (0) 15 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 20 minutes ago Exited (0) 20 minutes ago hardcore_kare

Single-use containerslink image 43

If, when running a container, we use the --rm option, that container will be deleted when it finishes executing.

	
< > Input
Python
!docker run --rm --name autoremove ubuntu:latest
Copied

If we now see which containers we have

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

We see that the container we just created is not there

Expose containers to the outside worldlink image 44

We are going to create a new container with a server

	
< > Input
Python
!docker run -d --name proxy nginx
Copied
>_ Output
			
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
f1ad4ce1: Pulling fs layer
b079d0f8: Pulling fs layer
5fbbebc6: Pulling fs layer
ffdd25f4: Pulling fs layer
32c8fba2: Pulling fs layer
24b8ba39: Pull complete 393kB/1.393kBB[5ADigest: sha256:2888a97f7c7d498bbcc47ede1ad0f6ced07d72dfd181071dde051863f1f79d7b
Status: Downloaded newer image for nginx:latest
1a530e04f14be082811b72ea8b6ea5a95dad3037301ee8a1351a0108ff8d3b30

This creates a server; let's list the containers that are running again.

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a530e04f14b nginx "/docker-entrypoint.…" 1 second ago Up Less than a second 80/tcp proxy

Now a new column appears with the port, and it tells us that the server we just created is on port 80 under the tcp protocol.

If we open a browser and try to connect to the server using http://localhost:80 we are unable to connect. This is because each container has its own network interface. In other words, the server is listening on port 80 of the container, but we are trying to connect to port 80 of the host

We stop the container to relaunch it in a different way

	
< > Input
Python
!docker stop proxy
Copied
>_ Output
			
proxy

If we list the containers, it doesn’t appear to be running

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Let's delete it to create it again

	
< > Input
Python
!docker rm proxy
Copied
>_ Output
			
proxy

If we list all the containers, they are no longer there

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 19 minutes ago Exited (137) 5 minutes ago alwaysup
5b633e9d838f ubuntu "bash" 24 minutes ago Exited (0) 20 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 24 minutes ago Exited (0) 24 minutes ago hardcore_kare

To recreate the container with the server and be able to view it from the host, we need to use the -p option (publish), indicating first the port on which we want to view it on the host and then the container port, that is, -p <host ip>:<container ip>

	
< > Input
Python
!docker run -d --name proxy -p 8080:80 nginx
Copied
>_ Output
			
c199235e42f76a30266f6e1af972e0a59811806eb3d3a9afdd873f6fa1785eae

We list the containers

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c199235e42f7 nginx "/docker-entrypoint.…" 22 seconds ago Up 21 seconds 0.0.0.0:8080-&gt;80/tcp, :::8080-&gt;80/tcp proxy

We see that the container port is 0.0.0.0:8080->80/tcp. If we now open a browser and enter 0.0.0.0:8080, we will be able to access the container’s server

When listing the containers, in the PORTS column it shows 0.0.0.0:8080->80/tcp, which helps us see the port mapping.

To view the container logs, using the command docker logs <name> I can see the container records

	
< > Input
Python
!docker logs proxy
Copied
>_ Output
			
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/09/13 13:24:06 [notice] 1#1: using the "epoll" event method
2022/09/13 13:24:06 [notice] 1#1: nginx/1.23.1
2022/09/13 13:24:06 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2022/09/13 13:24:06 [notice] 1#1: OS: Linux 5.15.0-46-generic
2022/09/13 13:24:06 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2022/09/13 13:24:06 [notice] 1#1: start worker processes
2022/09/13 13:24:06 [notice] 1#1: start worker process 31
2022/09/13 13:24:06 [notice] 1#1: start worker process 32
2022/09/13 13:24:06 [notice] 1#1: start worker process 33
2022/09/13 13:24:06 [notice] 1#1: start worker process 34
2022/09/13 13:24:06 [notice] 1#1: start worker process 35
2022/09/13 13:24:06 [notice] 1#1: start worker process 36
2022/09/13 13:24:06 [notice] 1#1: start worker process 37
2022/09/13 13:24:06 [notice] 1#1: start worker process 38
2022/09/13 13:24:06 [notice] 1#1: start worker process 39
2022/09/13 13:24:06 [notice] 1#1: start worker process 40
2022/09/13 13:24:06 [notice] 1#1: start worker process 41
...
172.17.0.1 - - [13/Sep/2022:13:24:40 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://0.0.0.0:8080/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "üâV$Zqi'×ü[€ïºåÇè÷&amp;3nSëÉì‘ÂØÑ‰ž¾ Ç?áúaΐ˜uã/ØRfOHì’+“\»±¿Òm°9 úúÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "ü)šbCÙmñ†ëd"ÏÄE‡#~LÁ„µ‘k˜«lî[0 ÐÒ`…Æ‹…R‹‡êq{Pòû⨝IôtH™~Ê1-|Ž êêÀ+À/À,À0̨̩ÀÀœ/5“" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:26:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"

Now I can see all the requests that have been made to the server. But if I want to see the logs in real time, using docker logs -f <name> I can do it

	
< > Input
Python
!docker logs -f proxy
Copied

Now I can see the logs in real time. To exit, enter CTRL+C

How can there come a time when there are many logs, if you only want the latest logs, using the option --tail <num> you can view the last <num> logs. If I add the option -f we will always be seeing the last <num> logs

	
< > Input
Python
!docker logs --tail 10 proxy
Copied
>_ Output
			
2022/09/13 13:24:06 [notice] 1#1: start worker process 41
2022/09/13 13:24:06 [notice] 1#1: start worker process 42
172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "üE޶ EgóɚœÊì§y#3’•ÜQïê$¿# ƒ÷-,s!rê|®ß¡LZª4y³t«ÀÎ_¸çÿ'φ êêÀ+À/À,À0̨̩ÀÀœ/5“ŠŠ localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "ü}©Dr{Œ;z‚­¼ŠzÂxßšæl?§àDoK‘'g»µ %»ýق?ۀ³TöcJ÷åÂÒ¼¢£ë½=R¼ƒ‰… ÊÊÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:24:39 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
2022/09/13 13:24:40 [error] 34#34: *3 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "0.0.0.0:8080", referrer: "http://0.0.0.0:8080/"
172.17.0.1 - - [13/Sep/2022:13:24:40 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://0.0.0.0:8080/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "üâV$Zqi'×ü[€ïºåÇè÷&amp;3nSëÉì‘ÂØÑ‰ž¾ Ç?áúaΐ˜uã/ØRfOHì’+“\»±¿Òm°9 úúÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "ü)šbCÙmñ†ëd"ÏÄE‡#~LÁ„µ‘k˜«lî[0 ÐÒ`…Æ‹…R‹‡êq{Pòû⨝IôtH™~Ê1-|Ž êêÀ+À/À,À0̨̩ÀÀœ/5“" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:26:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"

If we also add the -t option, we can see the date and time of each log, so that if we have had a problem, we can know at what moment it occurred

	
< > Input
Python
!docker logs --tail -t 10 proxy
Copied
>_ Output
			
2022-09-13T13:24:06.573362728Z 2022/09/13 13:24:06 [notice] 1#1: start worker process 41
2022-09-13T13:24:06.651127107Z 2022/09/13 13:24:06 [notice] 1#1: start worker process 42
2022-09-13T13:24:16.651160189Z 172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "üE޶ EgóɚœÊì§y#3’•ÜQïê$¿# ƒ÷-,s!rê|®ß¡LZª4y³t«ÀÎ_¸çÿ'φ êêÀ+À/À,À0̨̩ÀÀœ/5“ŠŠ localhostÿ" 400 157 "-" "-" "-"
2022-09-13T13:24:16.116817914Z 172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "ü}©Dr{Œ;z‚­¼ŠzÂxßšæl?§àDoK‘'g»µ %»ýق?ۀ³TöcJ÷åÂÒ¼¢£ë½=R¼ƒ‰… ÊÊÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
2022-09-13T13:24:39.117398081Z 172.17.0.1 - - [13/Sep/2022:13:24:39 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
2022-09-13T13:24:39.117412408Z 2022/09/13 13:24:40 [error] 34#34: *3 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "0.0.0.0:8080", referrer: "http://0.0.0.0:8080/"
2022-09-13T13:24:40.117419389Z 172.17.0.1 - - [13/Sep/2022:13:24:40 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://0.0.0.0:8080/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
2022-09-13T13:25:00.117434249Z 172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "üâV$Zqi'×ü[€ïºåÇè÷&amp;3nSëÉì‘ÂØÑ‰ž¾ Ç?áúaΐ˜uã/ØRfOHì’+“\»±¿Òm°9 úúÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
2022-09-13T13:25:00.223560881Z 172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "ü)šbCÙmñ†ëd"ÏÄE‡#~LÁ„µ‘k˜«lî[0 ÐÒ`…Æ‹…R‹‡êq{Pòû⨝IôtH™~Ê1-|Ž êêÀ+À/À,À0̨̩ÀÀœ/5“" 400 157 "-" "-" "-"
2022-09-13T13:26:25.223596738Z 172.17.0.1 - - [13/Sep/2022:13:26:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"

We stop and delete the container

	
< > Input
Python
!docker rm -f proxy
Copied
>_ Output
			
proxy
	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 26 minutes ago Exited (137) 13 minutes ago alwaysup
5b633e9d838f ubuntu "bash" 31 minutes ago Exited (0) 27 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 32 minutes ago Exited (0) 32 minutes ago hardcore_kare

Data in Dockerlink image 45

Bind mountslink image 46

Let's see the containers we have stopped

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 26 minutes ago Exited (137) 13 minutes ago alwaysup
5b633e9d838f ubuntu "bash" 31 minutes ago Exited (0) 28 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 32 minutes ago Exited (0) 32 minutes ago hardcore_kare

Let's delete the two Ubuntu instances whose main command is Bash, and we'll keep the one we left as non-operational.

	
< > Input
Python
!docker rm funny_mirzakhani
Copied
>_ Output
			
funny_mirzakhani
	
< > Input
Python
!docker rm hardcore_kare
Copied
>_ Output
			
hardcore_kare
	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 27 minutes ago Exited (137) 14 minutes ago alwaysup

Let's run the Ubuntu container we left behind again; this is done using the start command

	
< > Input
Python
!docker start alwaysup
Copied
>_ Output
			
alwaysup

We go back inside the

	
< > Input
Python
!docker exec -it alwaysup bash
Copied
>_ Output
			
root@ce4d60427dcd:/#

Inside the container, I can create a new folder called dockerfolder

	
< > Input
Python
!mkdir dockerfolder
Copied

If we list the files, the new folder will appear

	
< > Input
Python
!ls
Copied
>_ Output
			
bin boot dev dockerfolder etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var

If we leave the container

	
< > Input
Python
!exit
Copied
>_ Output
			
exit

And we delete it

	
< > Input
Python
!docker rm -f alwaysup
Copied
>_ Output
			
alwaysup

If we list all the containers, the last one we created no longer appears

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Let's do everything again, but first let's create a folder on the host in which we will share the data with the container

	
< > Input
Python
!mkdir dockerHostFolder
Copied

We see that there is nothing inside the folder

	
< > Input
Python
!ls dockerHostFolder
Copied

Now we obtain our absolute path

	
< > Input
Python
!pwd
Copied
>_ Output
			
/home/wallabot/Documentos/web/portafolio/posts

We create the container again but adding the -v option (bind mount). Next, the absolute path of the host folder and the absolute path of the folder in the container are added, -v <host path>:<container path>

	
< > Input
Python
!docker run -d --name alwaysup -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/null
Copied
>_ Output
			
4ede4512c293bdcc155e9c8e874dfb4a28e5163f4d5c7ddda24ad2863f28921b

We enter the container, list the files, and the folder we had created already appears

	
< > Input
Python
!docker exec -it alwaysup bash
Copied
>_ Output
			
root@4ede4512c293:/#
	
< > Input
Python
root@4ede4512c293:/# ls
Copied
>_ Output
			
bin dev etc lib lib64 media opt root sbin sys usr
boot dockerContainerFolder home lib32 libx32 mnt proc run srv tmp var

Let's go to the shared container directory, create a file, and exit the container

	
< > Input
Python
root@4ede4512c293:/# cd dockerContainerFolder
Copied
	
< > Input
Python
root@4ede4512c293:/dockerContainerFolder# touch bindFile.txt
Copied
	
< > Input
Python
root@4ede4512c293:/dockerContainerFolder# exit
Copied
>_ Output
			
exit

Let's see what’s inside the shared folder

	
< > Input
Python
!ls dockerHostFolder
Copied
>_ Output
			
bindFile.txt

But even more, if we delete the container, the file is still there

	
< > Input
Python
!docker rm -f alwaysup
Copied
>_ Output
			
alwaysup
	
< > Input
Python
!ls dockerHostFolder
Copied
>_ Output
			
bindFile.txt

If I create a container again sharing the folders, all the files will be in the container

	
< > Input
Python
!docker run -d --name alwaysup -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/null
Copied
>_ Output
			
6c021d37ea29d8b23fe5cd4968baa446085ae1756682f65340288b4c851c362d
	
< > Input
Python
!docker exec -it alwaysup bash
Copied
>_ Output
			
root@6c021d37ea29:/#
	
< > Input
Python
!root@6c021d37ea29:/# ls dockerContainerFolder/
Copied
>_ Output
			
bindFile.txt:/#

We remove the container

	
< > Input
Python
!docker rm -f alwaysup
Copied
>_ Output
			
alwaysup
	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Volumeslink image 47

Volumes were created as an evolution of bind mounts to provide more security. We can list all Docker volumes using docker volume ls

	
< > Input
Python
!docker volume ls
Copied
>_ Output
			
DRIVER VOLUME NAME

Let's create a new volume for the Ubuntu container; to do this, we use the command docker volume create <volume name>

	
< > Input
Python
!docker volume create ubuntuVolume
Copied
>_ Output
			
ubuntuVolume

If we list the volumes again, the one we just created will appear

	
< > Input
Python
!docker volume ls
Copied
>_ Output
			
DRIVER VOLUME NAME
local ubuntuVolume

However, it does not appear as a folder in the host file system. With ls -d */ we list all folders

	
< > Input
Python
!ls -d */
Copied
>_ Output
			
dockerHostFolder/ __pycache__/

We are going to create a container again, but now we will create it with the volume we just created using the --mount option, indicating the source volume with src=<volume name> (if the volume did not exist, Docker would create it), then the destination separated by a ,, dst=<container path>, that is, --mount src=<volume name>,dst=<container path>

	
< > Input
Python
!docker run -d --name alwaysup --mount src=ubuntuVolume,dst=/dockerVolumeFolder ubuntu tail -f /dev/null
Copied
>_ Output
			
42cdcddf4e46dc298a87b0570115e0b2fc900cb4c6db5eea22a61409b8cb271d

Once created, we can see the container volumes using the inspect command and filtering by '{{.Mounts}}'

$ docker inspect --format '{{.Mounts}}' alwaysup
[
{
volume ubuntuVolume /var/lib/docker/volumes/ubuntuVolume/_data /dockerVolumeFolder local z true
}
]

We see that the volume is called ubuntuVolume and we can also see the path where it is stored, in this case in /var/lib/docker/volumes/ubuntuVolume/_data. We do the same as before, we enter the container, create a file in the volume path, exit, and check on the host whether it has been created

$ docker exec -it alwaysup bash
root@42cdcddf4e46:/# touch dockerVolumeFolder/volumeFile.txt
root@42cdcddf4e46:/# exit
$ sudo ls /var/lib/docker/volumes/ubuntuVolume/_data
volumeFile.txt

The file has been created

Insert and extract files from a containerlink image 48

First, let’s create a file that we want to copy into a container

	
< > Input
Python
!touch dockerHostFolder/text.txt
Copied

We entered the container

$ docker exec -it alwaysup bash
root@42cdcddf4e46:/#

We create a new folder where we are going to copy the file and exit

root@42cdcddf4e46:/# mkdir folderToCopy
root@42cdcddf4e46:/# ls
bin boot dev dockerVolumeFolder etc folderToCopy home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr varroot@42cdcddf4e46:/# exit
exit

We copy the file inside the container using the cp command, indicating the file that **I want** to copy, the container where we want to copy it, and the path inside the container, docker cp <file> <container>:<container path>

	
< > Input
Python
!docker cp dockerHostFolder/text.txt alwaysup:/folderToCopy
Copied

We re-enter the container and check that the file is there

$ docker exec -it alwaysup bash
root@42cdcddf4e46:/# ls folderToCopy/text.txt

We exit the container

/# exit
exit

Now we are going to extract the file from the container and save it on the host with another name. To do this, we use the cp command again, but now indicating the container, the file path in the container, and the path and name we want the file to have on the host, docker cp <container>:<docker file path> <host file path>

	
< > Input
Python
!docker cp alwaysup:/folderToCopy/text.txt dockerHostFolder/fileExtract.txt
Copied

We see that it is on the host

	
< > Input
Python
!ls dockerHostFolder
Copied
>_ Output
			
bindFile.txt fileExtract.txt text.txt

Although the container is stopped, files can also be copied.

Finally, we remove the container

	
< > Input
Python
!docker rm -f alwaysup
Copied
>_ Output
			
alwaysup

Imageslink image 49

Fundamental conceptslink image 50

Images are the files ("templates") with all the configuration needed to create a container. Every time we create a container, it is created from an image. When we created new containers, the first time a message appeared saying that we did not have the image and that it was going to download it. On Docker Hub there are many images with all kinds of machines, but for a very specific development environment we can create our own template to pass it to someone so they can work in a container with the same configuration as ours

We can see all the images that we have saved on our computer using the command docker image ls

	
< > Input
Python
!docker image ls
Copied
>_ Output
			
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

We can see the sizes, and we can see how the nginx one takes up a lot of space, which is why it took longer to download than the rest

Another column we can see is the TAG column, this indicates the version of the image. They all show latest, which means it is the latest one. In other words, at the moment we downloaded it, we have downloaded the latest version available on Docker Hub. This is not optimal in a development environment, because we can download an Ubuntu image, and if we do not specify a version, the latest one is pulled, for example 20.04. But after some time someone may want to develop with you and download that image, but by not specifying the version, they will download the latest one again, which in their case could be 22.04. This can lead to problems and to things working for one person but not for the other

We can view all the images on Docker Hub by going to https://hub.docker.com/. There you can search for the image that best fits the project you want to build. If we navigate to the Ubuntu image, for example, we can see the versions (tags) of the images.

Let's download, **but not run**, an image. To do this we use the command docker pull <hub> <image name>:<tag>. If we do not specify the hub, it will download it from Docker Hub by default, but we can specify another one, for example a private one from our organization. Also, if we do not specify the tag, it will download the latest version by default

	
< > Input
Python
!docker pull ubuntu:20.04
Copied
>_ Output
			
20.04: Pulling from library/ubuntu
Digest: sha256:35ab2bf57814e9ff49e365efd5a5935b6915eede5c7f8581e9e1b85e0eecbe16[1A
Status: Downloaded newer image for ubuntu:20.04
docker.io/library/ubuntu:20.04

If we list the images again, we see that we now have two Ubuntu images, one with the tag 20.04 and another with the tag latest

	
< > Input
Python
!docker image ls
Copied
>_ Output
			
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Create images using Dockerfilelink image 51

We create a directory on the host called dockerImages to work in it

	
< > Input
Python
!mkdir dockerImages
Copied

We create a Dockerfile with which we will create an image

	
< > Input
Python
!touch dockerImages/Dockerfile
Copied

We open the file created with our preferred editor and write the following:

FROM ubuntu:latest

This tells Docker to create the image based on the latest Ubuntu image.

Next, we write a command that will be executed at compile time

RUN touch /test.txt

This means that when the Dockerfile is built, that command will be executed, but not when the image container is run

In the end, the Dockerfile looks like this:

FROM ubuntu:latest
RUN touch /test.txt

We build the Dockerfile using the build command; with the -t option we can give it a tag. Finally, we must indicate the path of the build context; we will explain this later

	
< > Input
Python
!docker build -t ubuntu:test ./dockerImages
Copied
>_ Output
			
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM ubuntu:latest
---&gt; 2dc39ba059dc
Step 2/2 : RUN touch /test.txt
---&gt; Using cache
---&gt; a78cf3ea16d8
Successfully built a78cf3ea16d8
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

As we can see, it is compiled in 2 steps, each one has an id, each of those ids are layers of the image, we will also see this later

We look at the images we have saved on our computer again, and the one we just created appears.

	
< > Input
Python
!docker image ls
Copied
>_ Output
			
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu test a78cf3ea16d8 8 minutes ago 77.8MB
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

We run the container from the image we just created

$ docker run -it ubuntu:test
root@b57b9d4eedeb:/#

We enter the container's bash. As we said, the RUN command is executed at image build time, so the file we asked to be created should be in our container

root@b57b9d4eedeb:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys test.txt tmp usr var

It is important to understand that that file was created when the image was built, that is, the container image already has that file. It is not created when the container is launched

We exit the container

root@b57b9d4eedeb:/# exit
exit

Since we already have an image, we could upload it to Docker Hub, but let's list the images again before doing that.

	
< > Input
Python
!docker image ls
Copied
>_ Output
			
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu test a78cf3ea16d8 20 minutes ago 77.8MB
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

If we look, it’s telling us that the image we just created belongs to the Ubuntu repository, but we do not have access to the Ubuntu repository, so on Docker Hub we need to create an account in order to upload the image to our repository. In my case, my repository is called maximofn, so I change the image repository using the tag command, telling it the image whose repository we want to change and the new repository. In the new repository, it is usually specified as the repository name followed by the image type and the tag; in my case maximofn/ubuntu:test

	
< > Input
Python
!docker tag ubuntu:test maximofn/ubuntu:test
Copied

If we now list the images again

	
< > Input
Python
!docker image ls
Copied
>_ Output
			
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu test a78cf3ea16d8 24 minutes ago 77.8MB
maximofn/ubuntu test a78cf3ea16d8 24 minutes ago 77.8MB
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Now we need to log in to Docker Hub so we can push the image; to do this, we use the login command

$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you do not have a Docker ID, head over to https://hub.docker.com to create one.
Username: maximofn
Password:

Login Succeeded

Now we can upload the image using the push command

	
< > Input
Python
!docker push maximofn/ubuntu:test
Copied
>_ Output
			
The push refers to repository [docker.io/maximofn/ubuntu]
06994357: Preparing
06994357: Pushed from library/ubuntu test: digest: sha256:318d83fc3c35ff930d695b0dc1c5ad1b0ea54e1ec6e3478b8ca85c05fd793c4e size: 735

Only the first layer has been uploaded; the second one, since I used it based on the Ubuntu image, what it does is place a pointer to that image so that I don’t have layers uploaded more than once

It should be noted that this repository is public, so you should not upload images with sensitive data. Also, if an image is not used for 6 months, it will be deleted

The layer systemlink image 52

Using the history command, we can see the layers of an image. If we look at the layers of the image we just created, we use docker history ubuntu:test

	
< > Input
Python
!docker history ubuntu:test
Copied
>_ Output
			
IMAGE CREATED CREATED BY SIZE COMMENT
a78cf3ea16d8 3 minutes ago /bin/sh -c touch /test.txt 0B
2dc39ba059dc 12 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
&lt;missing&gt; 12 days ago /bin/sh -c #(nop) ADD file:a7268f82a86219801… 77.8MB

We see that the first layer has the command that we introduced in the Dockerfile; it also says that it was created 3 minutes ago. However, the rest of the layers were created 12 days ago, and they are the layers of the Ubuntu image we based ourselves on.

To the Dockerfile we created earlier, we add the line

RUN rm /test.txt

In the end, the Dockerfile looks like this:

FROM ubuntu:latest
RUN touch /test.txtRUN rm /test.txt

If we recompile, we’ll see what happens

	
< > Input
Python
!docker build -t ubuntu:test ./dockerImages
Copied
>_ Output
			
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:latest
---&gt; 2dc39ba059dc
Step 2/3 : RUN touch /test.txt
---&gt; Using cache
---&gt; a78cf3ea16d8
Step 3/3 : RUN rm /test.txt
---&gt; Running in c2e6887f2025
Removing intermediate container c2e6887f2025
---&gt; 313243a9b573
Successfully built 313243a9b573
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

As we can see, there is one more layer with the new line that we have added. If we look again at the image layers with history

	
< > Input
Python
!docker history ubuntu:test
Copied
>_ Output
			
IMAGE CREATED CREATED BY SIZE COMMENT
313243a9b573 About a minute ago /bin/sh -c rm /test.txt 0B
a78cf3ea16d8 3 minutes ago /bin/sh -c touch /test.txt 0B
2dc39ba059dc 12 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
&lt;missing&gt; 12 days ago /bin/sh -c #(nop) ADD file:a7268f82a86219801… 77.8MB

We see that the first layers are the same as before and a new layer has been added with the new command

There is no need to go to the Docker Hub page to look for images; it can be done from the terminal. To do this, we use the command docker search <image name>

	
< > Input
Python
!docker search ubuntu
Copied
>_ Output
			
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Ubuntu is a Debian-based Linux operating sys… 16425 [OK]
websphere-liberty WebSphere Liberty multi-architecture images … 297 [OK]
open-liberty Open Liberty multi-architecture images based… 62 [OK]
neurodebian NeuroDebian provides neuroscience research s… 104 [OK]
ubuntu-debootstrap DEPRECATED; use "ubuntu" instead 52 [OK]
ubuntu-upstart DEPRECATED, as is Upstart (find other proces… 115 [OK]
ubuntu/nginx Nginx, a high-performance reverse proxy &amp; we… 98
ubuntu/squid Squid is a caching proxy for the Web. Long-t… 66
ubuntu/cortex Cortex provides storage for Prometheus. Long… 4
ubuntu/apache2 Apache, a secure &amp; extensible open-source HT… 60
ubuntu/kafka Apache Kafka, a distributed event streaming … 35
ubuntu/mysql MySQL open source fast, stable, multi-thread… 53
ubuntu/bind9 BIND 9 is a very flexible, full-featured DNS… 62
ubuntu/prometheus Prometheus is a systems and service monitori… 51
ubuntu/zookeeper ZooKeeper maintains configuration informatio… 12
ubuntu/postgres PostgreSQL is an open source object-relation… 31
ubuntu/redis Redis, an open source key-value store. Long-… 19
ubuntu/grafana Grafana, a feature rich metrics dashboard &amp; … 9
ubuntu/memcached Memcached, in-memory keyvalue store for smal… 5
ubuntu/dotnet-aspnet Chiselled Ubuntu runtime image for ASP.NET a… 11
ubuntu/dotnet-deps Chiselled Ubuntu for self-contained .NET &amp; A… 11
ubuntu/prometheus-alertmanager Alertmanager handles client alerts from Prom… 9
ubuntu/dotnet-runtime Chiselled Ubuntu runtime image for .NET apps… 10
ubuntu/cassandra Cassandra, an open source NoSQL distributed … 2
ubuntu/telegraf Telegraf collects, processes, aggregates &amp; w… 4

Using Docker to create applicationslink image 54

Port exposurelink image 55

Before, we saw how we could bind a port from a container to a port on the computer (-p 8080:80). But for that to be possible, when creating the image you have to expose the port; this is done by adding the line EXPOSE <port> to the Dockerfile, in the previous case

EXPOSE 80

Or use images as a base that already have exposed ports

Reusing layer cache when compilinglink image 56

When we build an image, if any of the layers we have defined have already been built before, Docker detects them and uses them; it does not rebuild them. If we build again the image we defined in the Dockerfile, it will now take very little time, because all the layers are already built and Docker does not rebuild them.

	
< > Input
Python
!docker build -t ubuntu:test ./dockerImages
Copied
>_ Output
			
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:latest
---&gt; 2dc39ba059dc
Step 2/3 : RUN touch /test.txt
---&gt; Using cache
---&gt; a78cf3ea16d8
Step 3/3 : RUN rm /test.txt
---&gt; Using cache
---&gt; 313243a9b573
Successfully built 313243a9b573
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

In the second and third layers, the text Using cache appears

Since this is a Jupyter notebook, when running the cells it gives you information about how long they take to execute. The previous time I built the image it took 1.4 seconds, whereas now it has taken 0.5 seconds

But if I now change the Dockerfile, and in the first line, where it said that we were based on the latest version of Ubuntu, and we change it to version 20.04

FROM ubuntu:20.04

In the end, the Dockerfile looks like this:

FROM ubuntu:20.04
RUN touch /test.txtRUN rm /test.txt

If we recompile it, it will take much longer

	
< > Input
Python
!docker build -t ubuntu:test ./dockerImages
Copied
>_ Output
			
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:20.04
---&gt; a0ce5a295b63
Step 2/3 : RUN touch /test.txt
---&gt; Running in a40fe8df2c0d
Removing intermediate container a40fe8df2c0d
---&gt; 0bb9b452c11f
Step 3/3 : RUN rm /test.txt
---&gt; Running in 2e14919f3685
Removing intermediate container 2e14919f3685
---&gt; fdc248fa833b
Successfully built fdc248fa833b
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

It took 1.9 seconds and the text Using cache no longer appears

When changing the first layer, Docker rebuilds all the layers. This can be a problem because, when developing code, the following situation may occur

  • We develop the code on our computer
  • When building the image, we copy all the code from our computer into the container
  • Then we ask the image to install the necessary libraries

This can cause, when changing any part of the code, that when the image has to be rebuilt, the layer in which the libraries are installed has to be rebuilt as well, since a previous layer has changed

To solve this, the idea would be that, when creating the image, we first ask for the libraries to be installed and then for the code to be copied from our computer into the container. That way, each time we change the code and rebuild the image, only the layer where the code is copied will be rebuilt, so the build will be faster

You may think that it is better to share a folder between the host and the container (bind mount) where we will keep the code, so there is no need to rebuild the image every time we change the code. And the answer is that this is true; I only used this example because it is very easy to understand, but it is meant to illustrate that when creating images, you need to think carefully so that if it does need to be rebuilt, you rebuild the minimum number of layers.

Write a Dockerfile correctlylink image 57

As we have seen, Docker does not rebuild layers of a Dockerfile if it has already built them before, so it loads them from cache. Let’s see what the correct way to write a Dockerfile is in order to take advantage of this.

We are going to start from this Dockerfile to discuss possible corrections.

FROM ubuntu
      COPY ./sourceCode /sourceCode
      RUN apt-get update
      RUN apt-get install -y python3 sshCMD ["python3", "/sourceCode/sourceApp/app.py"]

As can be seen, we start from an Ubuntu image, copy the folder with the code, update the repositories, install Python, install ssh as well, and run the application

Copy the code before executionlink image 58

As we said before, if we first copy the code and then install Python, every time we make a change in the code and build the image it will rebuild everything, but if we copy the code after installing Python, every time we change the code and build the image, it will only build from the code copy and won’t reinstall Python, so the Dockerfile should now look like this

FROM ubuntu
      RUN apt-get update
      RUN apt-get install -y python3 sshCOPY ./sourceCode /sourceCode
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Copy only the necessary codelink image 59

We are copying the folder with all the code, but maybe inside we have code that we do not need, so we need to copy only the code that we really need for the application. In this way, the image will take up less memory. So the Dockerfile would look like this:

FROM ubuntu
      RUN apt-get update
      RUN apt-get install -y python3 sshCOPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Update repositories and install Python on the same linelink image 60

We are updating the repositories in one line and installing python3 in another.

FROM ubuntu
      RUN apt-get update && apt-get install -y python3 ssh
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Do not install sshlink image 61

We had installed ssh in the image so we could debug it if needed, but that makes the image use more memory. If debugging is needed, we should enter the container, install ssh, and then debug. So we removed the ssh installation

FROM ubuntu
      RUN apt-get update && apt-get install -y python3
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Use --no-install-recommendslink image 62

When we install something on Ubuntu, it installs recommended packages, but we do not need them, so the image takes up more space. So, to avoid that, we add --no-install-recommends to the installation.

FROM ubuntu
      RUN apt-get update && apt-get install -y python3 --no-install-recommends
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Delete updated repositories listlink image 63

We have updated the repository list and installed Python, but once that is done we no longer need the updated repository list, because all it will do is make the image larger, so we remove them after installing Python and on the same line.

FROM ubuntu
      RUN apt-get update && apt-get install -y python3 --no-install-recommends && rm -rf /var/lib/apt/lists/*
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Use a Python imagelink image 64

Everything we have done to update the package list and install Python is unnecessary, since there are already Python images based on Ubuntu, which have likely also followed best practices, may have done it even better than us, and have been scanned for vulnerabilities by Docker Hub. So we remove all that and start from a Python image

FROM python
      COPY ./sourceCode/sourceApp /sourceCode/sourceAppCMD ["python3", "/sourceCode/sourceApp/app.py"]

Specify the Python imagelink image 65

Since the Python image is not specified, the latest one is being downloaded, but depending on when you build the container, one or another version may be pulled, so you need to add the tag with the Python version you want.

FROM python:3.9.18
      COPY ./sourceCode/sourceApp /sourceCode/sourceAppCMD ["python3", "/sourceCode/sourceApp/app.py"]

Choose a small taglink image 66

We have chosen the tag 3.9.18, but that Python version has a lot of libraries that we may not need, so we can use the 3.9.18-slim versions, which have many fewer libraries installed, or the 3.9.18-alpine version, which is a Python version based on Alpine rather than Ubuntu. Alpine is a very lightweight Linux distribution that has very few packages installed and is commonly used a lot in Docker containers so they take up very little space

The Python 3.9.18 image takes up 997 MB, the 3.9.18-slim takes up 126 MB, and the 3.9.18-alpine takes up 47.8 MB

FROM python:3.9.18-alpine
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Indicate the workspacelink image 67

Instead of specifying the image path /sourceCode/sourceApp, we set that path to be the image workspace. This way, when we copy the code or run the application, there is no need to specify the path.

FROM python:3.9.18-alpine
      WORKDIR /sourceCode/sourceApp
      COPY ./sourceCode/sourceApp .
      CMD ["python3", "app.py"]

Indicate the workspacelink image 68

Instead of specifying the image path /sourceCode/sourceApp, we set that path to be the image workspace. That way, when we copy the code or run the application, there is no need to specify the path.

FROM python:3.9.18-alpine
      WORKDIR /sourceCode/sourceApp
      COPY ./sourceCode/sourceApp .
      CMD ["python3", "app.py"]

Code shared in a bind mount folderlink image 69

We had created a folder called dockerHostFolder in which we had shared files between the host and a container. Inside, there should also be three files

	
< > Input
Python
!ls dockerHostFolder
Copied
>_ Output
			
bindFile.txt fileExtract.txt text.txt

Let's use the text.txt file to see that. Let's see what's inside text.txt

	
< > Input
Python
!cat dockerHostFolder/text.txt
Copied

There is no output, the file is empty. Let's create a new Ubuntu container sharing the dockerHostFolder directory

	
< > Input
Python
!docker run --name alwaysup -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/null
Copied
>_ Output
			
24adbded61f507cdf7f192eb5e246e43ee3ffafc9944b7c57918eb2d547dff19

We can see that the container is running

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
24adbded61f5 ubuntu "tail -f /dev/null" 16 seconds ago Up 15 seconds alwaysup

We enter the container, see that there is a text.txt file, and that it is empty

$ docker exec -it alwaysup bash
root@24adbded61f5:/# ls dockerContainerFolder/
bindFile.txt fileExtract.txt text.txt
root@24adbded61f5:/# cat dockerContainerFolder/text.txt
root@24adbded61f5:/#

Now we open the text.txt file on the host with the text editor we want, write Hola mundo, and save it. If we now look at what is inside the file in the container, we will see the same text

root@24adbded61f5:/# cat dockerContainerFolder/text.txt
Hello world

Now we edit the file in the container and exit the container

root@24adbded61f5:/# echo hola contenedor > dockerContainerFolder/text.txt
root@24adbded61f5:/# cat dockerContainerFolder/text.txt
hello container
root@24adbded61f5:/# exit
exit

If we look at the file on the host, we will see the text we wrote in the container

	
< > Input
Python
!cat dockerHostFolder/text.txt
Copied
>_ Output
			
hola contenedor

We delete the container

	
< > Input
Python
!docker rm -f alwaysup
Copied
>_ Output
			
alwaysup

Connect containers through networklink image 70

In case we want to have several containers running and want them to communicate, we can make them communicate over a network. Docker gives us the possibility to do this through its virtual networks

Let's see what networks Docker has using the command docker network ls

	
< > Input
Python
!docker network ls
Copied
>_ Output
			
NETWORK ID NAME DRIVER SCOPE
de6e8b7b737e bridge bridge local
da1f5f6fccc0 host host local
d3b0d93993c0 none null local

We see that by default Docker has three networks

  • bridge: It is there for backward compatibility with earlier versions, but we should no longer use it
  • host: It is the host network
  • none: This is the option we should use if we want a container to have no Internet access

We can create new networks to which other containers can connect. To do this, we use the command docker network create <name>. For other containers to be able to connect, we must also add the --attachable option.

	
< > Input
Python
!docker network create --attachable myNetwork
Copied
>_ Output
			
2f6f3ddbfa8642e9f6819aa0965c16339e9e910be7bcf56ebb718fcac324cc27

We can inspect it using the command docker network inspect <name>

	
< > Input
Python
!docker network inspect myNetwork
Copied
>_ Output
			
[
{
"Name": "myNetwork",
"Id": "2f6f3ddbfa8642e9f6819aa0965c16339e9e910be7bcf56ebb718fcac324cc27",
"Created": "2022-09-14T15:20:08.539830161+02:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

Now we need to create two containers so that they can communicate.

Let's create a new container, which we'll call container1, with a shared folder that will be called folder1 inside it

	
< > Input
Python
!docker run --name container1 -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/folder1 ubuntu tail -f /dev/null
Copied
>_ Output
			
a5fca8ba1e4ff0a67002f8f1b8cc3cd43185373c2a7e295546f774059ad8dd1a

Now we create another container, called container2, with another shared folder, but named folder2

	
< > Input
Python
!docker run --name container2 -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/folder2 ubuntu tail -f /dev/null
Copied
>_ Output
			
6c8dc18315488ef686f7548516c19b3d716728dd8a173cdb889ec0dd082232f9

We see the containers running and we see that both are there

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6c8dc1831548 ubuntu "tail -f /dev/null" 3 seconds ago Up 2 seconds container2
a5fca8ba1e4f ubuntu "tail -f /dev/null" 4 seconds ago Up 3 seconds container1

Now we have to connect the containers to the network; to do this, we use the command docker network connect <network name> <container name>

	
< > Input
Python
!docker network connect myNetwork container1
Copied
	
< > Input
Python
!docker network connect myNetwork container2
Copied

To verify that they have connected correctly, we can inspect the network, but filtering by the connected containers.

$ docker network inspect --format '{{.Containers}}' myNetwork
map
[
6c8dc18315488ef686f7548516c19b3d716728dd8a173cdb889ec0dd082232f9:
{
container2
f828d211e894f7a5a992ce41a2a0def8e2424e9737fb4e1485fc09cc2d607b69
02:42:ac:12:00:03
172.18.0.3/16
}
a5fca8ba1e4ff0a67002f8f1b8cc3cd43185373c2a7e295546f774059ad8dd1a:
{
container1
cff762e6286ebc169804b2a675bbff904102de796751d367c18d4b490c994c45
02:42:ac:12:00:02
172.18.0.2/16
}
]

As we can see, the container1 container has the IP 172.18.0.2 and the container2 container has the IP 172.18.0.3

We enter the container1 container and install ping

$ docker exec -it container1 bash
root@a5fca8ba1e4f:/# apt update
...
root@a5fca8ba1e4f:/# apt install iputils-ping
...
root@a5fca8ba1e4f:/#

We enter the container2 container and install ping

$ docker exec -it container2 bash
root@a5fca8ba1e4f:/# apt update
...
root@a5fca8ba1e4f:/# apt install iputils-ping
...
root@a5fca8ba1e4f:/#

Now from the container1 container we make a ping to the IP 172.18.0.3, which belongs to the container2 container

root@a5fca8ba1e4f:/# ping 172.18.0.3
PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data.
64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.115 ms
64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.049 ms
64 bytes from 172.18.0.3: icmp_seq=3 ttl=64 time=0.056 ms
64 bytes from 172.18.0.3: icmp_seq=4 ttl=64 time=0.060 ms
^C
--- 172.18.0.3 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3068ms
rtt min/avg/max/mdev = 0.049/0.070/0.115/0.026 ms

And from the container2 container, we perform a ping to the IP 172.18.0.2, which belongs to the container1 container

root@6c8dc1831548:/# ping 172.18.0.2
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.076 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.045 ms
64 bytes from 172.18.0.2: icmp_seq=3 ttl=64 time=0.049 ms
64 bytes from 172.18.0.2: icmp_seq=4 ttl=64 time=0.051 ms
^C
--- 172.18.0.2 ping statistics ---
4 paquetes enviados, 4 recibidos, 0% de pérdida de paquetes, tiempo 3074ms
rtt min/avg/max/mdev = 0.045/0.055/0.076/0.012 ms

But there is one better thing that Docker allows us to do: if I don’t know the IP of the container I want to connect to, instead of writing its IP I can write its name

Now from the container1 container we ping the IP of container2

root@a5fca8ba1e4f:/# ping container2
PING container2 (172.18.0.3) 56(84) bytes of data.
64 bytes from container2.myNetwork (172.18.0.3): icmp_seq=1 ttl=64 time=0.048 ms
64 bytes from container2.myNetwork (172.18.0.3): icmp_seq=2 ttl=64 time=0.050 ms
64 bytes from container2.myNetwork (172.18.0.3): icmp_seq=3 ttl=64 time=0.052 ms
64 bytes from container2.myNetwork (172.18.0.3): icmp_seq=4 ttl=64 time=0.053 ms
^C
--- container2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3071ms
rtt min/avg/max/mdev = 0.048/0.050/0.053/0.002 ms

As we can see, Docker knows that the IP of the container2 container is 172.18.0.3

And from the container2 container we ping the IP of container1

root@6c8dc1831548:/# ping container1
PING container1 (172.18.0.2) 56(84) bytes of data.
64 bytes from container1.myNetwork (172.18.0.2): icmp_seq=1 ttl=64 time=0.051 ms
64 bytes from container1.myNetwork (172.18.0.2): icmp_seq=2 ttl=64 time=0.058 ms
64 bytes from container1.myNetwork (172.18.0.2): icmp_seq=3 ttl=64 time=0.052 ms
64 bytes from container1.myNetwork (172.18.0.2): icmp_seq=4 ttl=64 time=0.056 ms
^C
--- container1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3057ms
rtt min/avg/max/mdev = 0.051/0.054/0.058/0.003 ms

As we can see, Docker knows that the IP of the container1 container is 172.18.0.2

We exit the containers and delete them

	
< > Input
Python
!docker rm -f container1 container2
Copied
>_ Output
			
container1
container2

We also delete the network we created

	
< > Input
Python
!docker network rm myNetwork
Copied
>_ Output
			
myNetwork

GPU Usagelink image 71

To be able to use the host GPUs inside Docker containers, it is necessary to follow the steps described on the Nvidia container toolkit installation page

Set up the repository and the GPG keylink image 72

We need to configure the nvidia container toolkit repository and the GPG key, for this we run the following command in the console

distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
&& curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

Installation of nvidia container toolkitlink image 73

Once we have updated the repository and the key, we update the repositories using the command

sudo apt update

And we installed nvidia container toolkit

sudo apt install -y nvidia-docker2

Docker restartlink image 74

Once we have finished, we need to restart the Docker daemon by

sudo systemctl restart docker

GPU Usagelink image 75

Now that we have configured Docker to be able to use the host GPUs inside the containers, we can test it using the --gpus all option. If you have more than one GPU and only want to use one, you would need to specify it, but for now here we only explain how to use all of them

We create a container that will not run in the background, but instead will execute the nvidia-smi command so we can see whether it has access to the GPUs

	
< > Input
Python
!docker run --name container_gpus --gpus all ubuntu nvidia-smi
Copied
>_ Output
			
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
6a12be2b: Pull complete .54MB/29.54MBBDigest: sha256:aabed3296a3d45cede1dc866a24476c4d7e093aa806263c27ddaadbdce3c1054
Status: Downloaded newer image for ubuntu:latest
Mon Sep 4 07:10:36 2023
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 510.39.01 Driver Version: 510.39.01 CUDA Version: 11.6 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 Quadro T1000 On | 00000000:01:00.0 Off | N/A |
| N/A 44C P0 15W / N/A | 9MiB / 4096MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| 0 N/A N/A 2545 G 4MiB |
| 0 N/A N/A 3421 G 4MiB |
+-----------------------------------------------------------------------------+

We delete the container

	
< > Input
Python
!doker rm container_gpus
Copied

---

➡️ **Continue in Part 2: Docker Compose and advanced topics**, where you will orchestrate multiple containers at once and go deeper into Docker.

Full series

Continue reading

Last posts -->

Have you seen these projects?

Gymnasia

Gymnasia Gymnasia
React Native
Expo
TypeScript
FastAPI
Next.js
OpenAI
Anthropic

Mobile personal training app with AI assistant, exercise library, workout tracking, diet and body measurements

Horeca chatbot

Horeca chatbot Horeca chatbot
Python
LangChain
PostgreSQL
PGVector
React
Kubernetes
Docker
GitHub Actions

Chatbot conversational for cooks of hotels and restaurants. A cook, kitchen manager or room service of a hotel or restaurant can talk to the chatbot to get information about recipes and menus. But it also implements agents, with which it can edit or create new recipes or menus

View all projects -->
>_ Available for projects

Do you have an AI project?

Let's talk.

maximofn@gmail.com

Machine Learning and AI specialist. I develop solutions with generative AI, intelligent agents and custom models.

Do you want to watch any talk?

Last talks -->

Do you want to improve with these tips?

Last tips -->

Use this locally

Hugging Face spaces allow us to run models with very simple demos, but what if the demo breaks? Or if the user deletes it? That's why I've created docker containers with some interesting spaces, to be able to use them locally, whatever happens. In fact, if you click on any project view button, it may take you to a space that doesn't work.

Flow edit

Flow edit Flow edit

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
View all containers -->
>_ Available for projects

Do you have an AI project?

Let's talk.

maximofn@gmail.com

Machine Learning and AI specialist. I develop solutions with generative AI, intelligent agents and custom models.

Do you want to train your model with these datasets?

short-jokes-dataset

HuggingFace

Dataset with jokes in English

Use: Fine-tuning text generation models for humor

231K rows 2 columns 45 MB
View on HuggingFace →

opus100

HuggingFace

Dataset with translations from English to Spanish

Use: Training English-Spanish translation models

1M rows 2 columns 210 MB
View on HuggingFace →

netflix_titles

HuggingFace

Dataset with Netflix movies and series

Use: Netflix catalog analysis and recommendation systems

8.8K rows 12 columns 3.5 MB
View on HuggingFace →
View more datasets -->