Docker ====== **Docker: high-quality drop shipping** .. _Docker Website: https://docs.docker.com/get-started/overview/ Docker is a toolset that allows for the containerization of applications. Think of it as a virtual machine (e.g. Virtualbox, KVM, QEMU, VMWare, Hyper-V), but a lot slimmer, since there is no hardware switching happening between the host and the guest operating systems. The container replaces the userspace of the host operating system, isolating its dependencies from the host system, allowing for development without the interference of third-party tools that may spread dependencies around like crazy. Much more information about Docker can be found in their `Docker Website`_. Rootless Docker --------------- .. _Rootless Docker: https://docs.docker.com/engine/security/rootless/ While Docker is awesome, the default installation via `apt install docker` usually results in a Docker installation that is capable of escalating privileges and exposing the host operating system by requiring sudo to run. This can be really dangerous if proper security practices are not followed. For people who don't care, good like. For those that do, install the `Rootless Docker`_ version. First steps ----------- Really basic stuff regarding docker. Can also be used as a cheatsheet in case your memory is as bad as mine. Looking for a pre-built container image --------------------------------------- .. _Docker Hub: https://hub.docker.com/ One of the things that popularized Docker were the pre-built container images available at `Docker Hub`_. Some of which are just a few hundred megabytes in size, allowing for a single consumer-level machine to host and test a project in several linux distributions. Ubuntu, for example, has multiple containers, one for each release (``20.04``, ``22.04``, ``23.04``, ``23.10``), plus additional tags for ``latest`` and ``rolling``. .. image:: img/ubuntu-docker-hub-page.png :width: 100% To download one of the ubuntu images, ``docker image pull`` specifying the container image name and tag: .. sourcecode:: console $ docker image pull ubuntu:22.04 22.04: Pulling from library/ubuntu aece8493d397: Pull complete Digest: sha256:2b7412e6465c3c7fc5bb21d3e6f1917c167358449fecac8176c6e496e5c1f05f Status: Downloaded newer image for ubuntu:22.04 docker.io/library/ubuntu:22.04 You can check the container image was properly downloaded by running ``docker image ls``: .. sourcecode:: console $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu 22.04 e4c58958181a 7 weeks ago 77.8MB Container images by themselves, however, don't do anything. We need to create a container first. Creating the first container ---------------------------- We need to attach it to a container, that will use it as its base disk drive (e.g. ``/``). To do that, we use ``docker container create`` command: .. sourcecode:: console $ docker container create --name mycontainer ubuntu:22.04 d60703e8847a1845e9e7566aa8bd2a0273d9cacdd9ebff725e6ed600e3c98dcf That container will be created but will not be running all by itself. We can double check this is the case with ``docker container ls``, which shows running containers, and ``docker container ls -a``, which shows all containers. .. sourcecode:: console $ docker container ls $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d60703e8847a ubuntu:22.04 "/bin/bash" 14 seconds ago Created mycontainer Note that we passed a name for the container when creating it. That is for ease of use. If you don't pass it, you will need to refer to the container via its Container ID (the gigantic hexadecimal string you saw printed before, or its shorthand shown above). To remove a container, use ``docker container rm containername``. .. sourcecode:: console $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d60703e8847a ubuntu:22.04 "/bin/bash" 14 seconds ago Created mycontainer $ docker container rm mycontainer mycontainer $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES $ docker container create --name mycontainer ubuntu:22.04 306b5af9ab3bbae1e0049a456ca4e420e571d9a288a53c60a2a596525f0c15af $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 306b5af9ab3b ubuntu:22.04 "/bin/bash" 14 seconds ago Created mycontainer Now we will have a container that can be run. We can run interactively, or non-interactively. Below we see each case separately. Running non-interactively ------------------------- In this mode, we expect to run a bunch of services to run in the background as daemons. The container is supposed to start them right-away, and they will continue to run until the container is stopped. We can use ``docker start container`` to start an existing container. .. sourcecode:: console $ docker start mycontainer mycontainer This means our container was successfully started. To check if it is running, we can use ``docker container ls -a``. .. sourcecode:: console $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 306b5af9ab3b ubuntu:22.04 "/bin/bash" 23 minutes ago Exited (0) 12 seconds ago mycontainer As we can see, it was executed, but started right away because the container image ``entry-point`` is set to the interactive ``bash`` command, which won't receive any commands. To exemplify how a container image with a background service would behave, we can use a different container image using a brand new command: ``docker run -d --name container_name container_image:tag`` to fetch the container image, create a new container and start it detached in the background (``-d``). .. sourcecode:: console $ docker run -d --name nginx nginx:latest Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx 1f7ce2fa46ab: Pull complete 9b16c94bb686: Pull complete 9a59d19f9c5b: Pull complete 9ea27b074f71: Pull complete c6edf33e2524: Pull complete 84b1ff10387b: Pull complete 517357831967: Pull complete Digest: sha256:10d1f5b58f74683ad34eb29287e07dab1e90f10af243f151bb50aa5dbb4d62ee Status: Downloaded newer image for nginx:latest d2f93616ff56bc84b1f057f44c3756fea909ae58ee9a3d736daa054bcf870ca8 $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d2f93616ff56 nginx:latest "/docker-entrypoint.…" 15 seconds ago Up 15 seconds 80/tcp nginx 306b5af9ab3b ubuntu:22.04 "/bin/bash" 27 minutes ago Exited (0) 4 minutes ago mycontainer As we can see, the nginx container is up and running right away. And will continue like that until stopped. To hop into the terminal of the container and interact with the server container, we use ``docker exec -it container command``. .. sourcecode:: console $ docker exec -it nginx bash root@d2f93616ff56:/# apt update Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB] Get:2 http://deb.debian.org/debian bookworm-updates InRelease [52.1 kB] Get:3 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB] Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8780 kB] Get:5 http://deb.debian.org/debian bookworm-updates/main amd64 Packages [6668 B] Get:6 http://deb.debian.org/debian-security bookworm-security/main amd64 Packages [105 kB] Fetched 9143 kB in 5s (1909 kB/s) Reading package lists... Done Building dependency tree... Done Reading state information... Done 1 package can be upgraded. Run 'apt list --upgradable' to see it. root@d2f93616ff56:/# apt install top Reading package lists... Done Building dependency tree... Done Reading state information... Done The following additional packages will be installed: libgpm2 libncursesw6 libnl-3-200 libnl-genl-3-200 Suggested packages: lm-sensors lsof strace gpm The following NEW packages will be installed: htop libgpm2 libncursesw6 libnl-3-200 libnl-genl-3-200 0 upgraded, 5 newly installed, 0 to remove and 1 not upgraded. Need to get 385 kB of archives. After this operation, 1101 kB of additional disk space will be used. Do you want to continue? [Y/n] y Get:1 http://deb.debian.org/debian bookworm/main amd64 libncursesw6 amd64 6.4-4 [134 kB] Get:2 http://deb.debian.org/debian bookworm/main amd64 libnl-3-200 amd64 3.7.0-0.2+b1 [63.1 kB] Get:3 http://deb.debian.org/debian bookworm/main amd64 libnl-genl-3-200 amd64 3.7.0-0.2+b1 [21.6 kB] Get:4 http://deb.debian.org/debian bookworm/main amd64 htop amd64 3.2.2-2 [152 kB] Get:5 http://deb.debian.org/debian bookworm/main amd64 libgpm2 amd64 1.20.7-10+b1 [14.2 kB] Fetched 385 kB in 0s (793 kB/s) debconf: delaying package configuration, since apt-utils is not installed Selecting previously unselected package libncursesw6:amd64. (Reading database ... 7590 files and directories currently installed.) Preparing to unpack .../libncursesw6_6.4-4_amd64.deb ... Progress: [ 0%] [.....................................................................] Unpacking libncursesw6:amd64 (6.4-4) ..................................................] Selecting previously unselected package libnl-3-200:amd64..............................] Preparing to unpack .../libnl-3-200_3.7.0-0.2+b1_amd64.deb ... Unpacking libnl-3-200:amd64 (3.7.0-0.2+b1) ............................................] Selecting previously unselected package libnl-genl-3-200:amd64.........................] Preparing to unpack .../libnl-genl-3-200_3.7.0-0.2+b1_amd64.deb ... Unpacking libnl-genl-3-200:amd64 (3.7.0-0.2+b1) ...###############.....................] Selecting previously unselected package htop.#######################...................] Preparing to unpack .../htop_3.2.2-2_amd64.deb ... Unpacking htop (3.2.2-2) ...#########################################..................] Selecting previously unselected package libgpm2:amd64.################.................] Preparing to unpack .../libgpm2_1.20.7-10+b1_amd64.deb ... Unpacking libgpm2:amd64 (1.20.7-10+b1) ...#############################................] Setting up libgpm2:amd64 (1.20.7-10+b1) ...#############################...............] Progress: [ 52%] [######################################################...............] Setting up libncursesw6:amd64 (6.4-4) ...################################..............] Progress: [ 62%] [########################################################.............] Setting up libnl-3-200:amd64 (3.7.0-0.2+b1) ...############################............] Progress: [ 71%] [##########################################################...........] Setting up libnl-genl-3-200:amd64 (3.7.0-0.2+b1) ...##########################.........] Progress: [ 81%] [#############################################################........] Setting up htop (3.2.2-2) ...###################################################.......] Progress: [ 90%] [###############################################################......] Processing triggers for libc-bin (2.36-9+deb12u3) ...#############################.....] root@d2f93616ff56:/# htop After installing ``htop`` and running it, we get the list of running processes within the container. .. image:: img/nginx-container-htop.png :width: 100% After finishing our work, we can exit the container with a simple ``exit`` command. And then, we can stop our container with ``docker container stop containername``. .. sourcecode:: console root@d2f93616ff56:/# exit $ docker container stop nginx nginx $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d2f93616ff56 nginx:latest "/docker-entrypoint.…" 13 minutes ago Exited (0) 9 seconds ago nginx 306b5af9ab3b ubuntu:22.04 "/bin/bash" 40 minutes ago Exited (0) 17 minutes ago mycontainer Running interactively --------------------- In this mode, we jump straight to the container command line. Super useful for testing, not so much for deployment. Since it is typical to throw away test containers right away, it is not really necessary to give them a name. The command for it is ``docker run -it image_name:tag`` .. sourcecode:: console $ docker run -it ubuntu:23.04 Unable to find image 'ubuntu:23.04' locally 23.04: Pulling from library/ubuntu f93f952dad40: Pull complete Digest: sha256:51e70689b125fcc2e800f5efb7ba465dee85ede9da9c268ff5599053c7e52b77 Status: Downloaded newer image for ubuntu:23.04 root@bf6ecf1cecd0:/# exit $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bf6ecf1cecd0 ubuntu:23.04 "/bin/bash" 50 seconds ago Exited (0) 7 seconds ago festive_mclean 306b5af9ab3b ubuntu:22.04 "/bin/bash" 4 minutes ago Created mycontainer d2f93616ff56 nginx:latest "/docker-entrypoint.…" 22 minutes ago Exited (0) 9 minutes ago nginx As we can see above, we now have a new container with a randomly assigned name (``festive_mclean``) with the ``ubuntu:23.04`` docker image. Cleaning up containers and images --------------------------------- All of these containers are stopped, and their images won't be removed from your system until containers using them are removed. To remove all stopped containers, use ``docker container prune``. .. sourcecode:: console $ docker container prune WARNING! This will remove all stopped containers. Are you sure you want to continue? [y/N] y Deleted Containers: bf6ecf1cecd0cb540054dbcea3b4dc14492b321d33ea82c3a649c17226230bd8 306b5af9ab3bbae1e0049a456ca4e420e571d9a288a53c60a2a596525f0c15af d2f93616ff56bc84b1f057f44c3756fea909ae58ee9a3d736daa054bcf870ca8 Total reclaimed space: 20.63MB Then, we can remove the docker container images with ``docker image prune -a``: .. sourcecode:: console $ docker image prune -a WARNING! This will remove all images without at least one container associated to them. Are you sure you want to continue? [y/N] y Deleted Images: untagged: ubuntu:23.04 untagged: ubuntu@sha256:51e70689b125fcc2e800f5efb7ba465dee85ede9da9c268ff5599053c7e52b77 deleted: sha256:639282825872ec6978281e00795f8f02e3b752112dfa01a5f55a19a0f6cf47dc deleted: sha256:6896079fa03f4ec6953883a0645397ecf3d805a9a692b231b48188bfa5d489bf untagged: ubuntu:22.04 untagged: ubuntu@sha256:2b7412e6465c3c7fc5bb21d3e6f1917c167358449fecac8176c6e496e5c1f05f deleted: sha256:e4c58958181a5925816faa528ce959e487632f4cfd192f8132f71b32df2744b4 deleted: sha256:256d88da41857db513b95b50ba9a9b28491b58c954e25477d5dad8abb465430b untagged: nginx:latest untagged: nginx@sha256:10d1f5b58f74683ad34eb29287e07dab1e90f10af243f151bb50aa5dbb4d62ee deleted: sha256:a6bd71f48f6839d9faae1f29d3babef831e76bc213107682c5cc80f0cbb30866 deleted: sha256:fe7723b2df19ccf75328cb1f39c90c2332679144231501f3d4d00f51b16c2867 deleted: sha256:c0f3f17b019abbcfeb0c162054786ea8087ca792a2191a79f03040a8cd98f41d deleted: sha256:32cfe66e62a5f36abf128703007285e1a3b9078f5b33a367df1534399065cc70 deleted: sha256:8713bfa322a66040e882b7822dc0c110a68cfafd3bb37332fdbb9426171d7ec9 deleted: sha256:253c039db964b57be02d9bb0f3d6916b7948687b4f6f4fc681644a419a47979d deleted: sha256:2c235ef4cca1fbb74e3a7aa47e654fe943bc0c2becbdd4d6af7cbf9e375fe08a deleted: sha256:92770f546e065c4942829b1f0d7d1f02c2eb1e6acf0d1bc08ef0bf6be4972839 Total reclaimed space: 334.9MB We can check if all containers and images were correctly removed with ``docker container ls -a`` and ``docker image ls -a``. .. sourcecode:: console $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES $ docker image ls -a REPOSITORY TAG IMAGE ID CREATED SIZE Next, we check how to expose services hosted inside the container to the external network from which the host is part of. Exposing ports to the container ------------------------------- Since we can containerize a service inside a container, we need to expose its services via network ports. This is done by specifying a port mapping with ``docker container create -p external_port:internal_port ...`` or ``docker run -it -p external_port:internal_port ...``. Let's try with the following Python script running within the container. .. sourcecode:: console $ docker run -it -p 80:1234 ubuntu:22.04 Unable to find image 'ubuntu:22.04' locally 22.04: Pulling from library/ubuntu aece8493d397: Pull complete Digest: sha256:2b7412e6465c3c7fc5bb21d3e6f1917c167358449fecac8176c6e496e5c1f05f Status: Downloaded newer image for ubuntu:22.04 docker: Error response from daemon: driver failed programming external connectivity on endpoint distracted_ramanujan (7967363fec04d2d4ac9899ba0987fa7831d9fcd26509dc200ce48ac648809840): Error starting userland proxy: error while calling PortManager.AddPort(): cannot expose privileged port 80, you can add 'net.ipv4.ip_unprivileged_port_start=80' to /etc/sysctl.conf (currently 1024), or set CAP_NET_BIND_SERVICE on rootlesskit binary, or choose a larger port number (>= 1024): listen tcp4 0.0.0.0:80: bind: permission denied. ERRO[0008] error waiting for container: Since I am running with Rootless Docker, Linux forbids ports lower than 1024. Additional permissions can be explicitly granted that would allow for that, but that is a topic for later. Let's try a different port number. .. sourcecode:: console $ docker run -it -p 1234:8000 ubuntu:22.04 root@44dc2e6581eb:/# apt update && apt install -y python3 ... root@44dc2e6581eb:/# python3 -m http.server Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ... With our server running inside the container, we can then try to access it from our host machine from a different terminal. .. sourcecode:: console $ wget http://127.0.0.1:1234 --2023-11-28 11:48:46-- http://127.0.0.1:1234/ Connecting to 127.0.0.1:1234... connected. HTTP request sent, awaiting response... 200 OK Length: 1063 (1,0K) [text/html] Saving to: ‘index.html’ index.html 100%[==============================] 1,04K --.-KB/s in 0s 2023-11-28 11:48:46 (55,5 MB/s) - ‘index.html’ saved [1063/1063] $ cat index.html