Repositories Images tags and Dockerfile

November 14, 2021 - Reading time: 8 minutes

Docker will pull images from Docker Hub by default, but we can specify the location of the image using: [REGISTRYHOST:PORT/][USERNAME/]NAME[:TAG]

Running your own repository could create a single point of failure!

Images can also be saved into a file (exported) and copied / distributed to other devices:

test@localhost: sudo docker pull busybox:latest
latest: Pulling from library/busybox
Digest: sha256:e7157b6d7ebbe2cce5eaa8cfe8aa4fa82d173999b9f90a9ec42e57323546c353
Status: Image is up to date for busybox:latest
docker.io/library/busybox:latest
##
test@localhost: sudo docker save -o /var/tmp/busy.img busybox:latest
##
test@localhost: sudo file /var/tmp/busy.img
/var/tmp/busy.img: POSIX tar archive

The resulting tar file contains:

test@localhost: sudo tar -tf /var/tmp/busy.img
7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14.json
c827bfcc430983f33f0807d0539883ecdaf786e41b20a77876e30f9cb2014e92/
c827bfcc430983f33f0807d0539883ecdaf786e41b20a77876e30f9cb2014e92/VERSION
c827bfcc430983f33f0807d0539883ecdaf786e41b20a77876e30f9cb2014e92/json
c827bfcc430983f33f0807d0539883ecdaf786e41b20a77876e30f9cb2014e92/layer.tar
manifest.json
repositories

We can now delete the image, and load it again from the saved file:

test@localhost: sudo docker rmi  busybox
Untagged: busybox:latest
Untagged: busybox@sha256:e7157b6d7ebbe2cce5eaa8cfe8aa4fa82d173999b9f90a9ec42e57323546c353
Deleted: sha256:7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14
Deleted: sha256:d94c78be13527d00673093f9677f9b43d7e3a02ae6fa0ec74d3d98243b5b40e4
##
test@localhost: sudo docker load -i /var/tmp/busy.img
d94c78be1352: Loading layer [==================================================>]  1.459MB/1.459MB
Loaded image: busybox:latest

An alternative way to build an image, is to specify the commands / base images using a Dockerfile:

test@localhost: cat test.sh
#!/bin/sh
echo "Hello this is ${HOSTNAME}"
test@localhost: chmod 777 test.sh
##
test@localhost: cat Dockerfile
FROM busybox:latest
ADD test.sh /var/tmp/
WORKDIR /var/tmp/
CMD ./test.sh
##
test@localhost: sudo docker build -f ./Dockerfile -t mytest:first ./
Sending build context to Docker daemon  321.5MB
Step 1/4 : FROM busybox:latest
 ---> 7138284460ff
Step 2/4 : ADD test.sh /var/tmp/
 ---> bba4f2a8d9bd
Step 3/4 : WORKDIR /var/tmp/
 ---> Running in 8ac9a959ddec
Removing intermediate container 8ac9a959ddec
 ---> 3ab3a1ddf92f
Step 4/4 : CMD ./test.sh
 ---> Running in f3ca9c11adf3
Removing intermediate container f3ca9c11adf3
 ---> 2de1580be977
Successfully built 2de1580be977
Successfully tagged mytest:first
##
test@localhost: sudo docker image ls mytest
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
mytest       first     2de1580be977   32 seconds ago   1.24MB
##
test@localhost: sudo docker run mytest:first
Hello this is 5d8087899746

A layer is set of files and file metadata that is packaged and distributed as an atomic unit.
Each layer is treated internally as an image. A layer can be "promoted" to an image by tagging it.
Most layers build up on existing layers by adding new files to the FS.

When pulling images, existing layers can be re-used. Layers are NOT modified, a new layer is created if a file needs to be modified. For example, here we're pulling two apps that rely on the same based layers. The layers are donwloaded only for the first image:

test@localhost: sudo docker pull dockerinaction/ch3_myapp
Using default tag: latest
latest: Pulling from dockerinaction/ch3_myapp
f5d23c7fed46: Pull complete 
eaa7ca9a16a1: Pull complete 
d7d34b884c95: Pull complete 
d0f024ff373b: Pull complete 
9384c9efb97d: Pull complete 
a7e74b426681: Pull complete 
6f1c51bc28c2: Pull complete 
ce0e70589db8: Pull complete 
df420ec9fa4c: Pull complete 
Digest: sha256:2e492fedd50d9d4ef5e8ea92c32795c3f53836199322cb85eafb93c2e139b3f1
Status: Downloaded newer image for dockerinaction/ch3_myapp:latest

test@localhost: sudo docker image ls dockerinaction/ch3_myapp
REPOSITORY                 TAG       IMAGE ID       CREATED       SIZE
dockerinaction/ch3_myapp   latest    0858f7736a46   2 years ago   401MB

test@localhost: sudo docker pull dockerinaction/ch3_myotherapp
Using default tag: latest
latest: Pulling from dockerinaction/ch3_myotherapp
f5d23c7fed46: Already exists    ## <--- no need to download again 
eaa7ca9a16a1: Already exists 
d7d34b884c95: Already exists 
d0f024ff373b: Already exists      ## <--- no need to download again 
b739d2c7836e: Pull complete 
79f97461601b: Pull complete 
1c2b86e90a51: Pull complete 
57ebdb20c65a: Pull complete 
1558a979f442: Pull complete 
Digest: sha256:5ec2875ca4b24ad5df22b03b4cf45181ad544cdc8b22dc85d27960e28131433e
Status: Downloaded newer image for dockerinaction/ch3_myotherapp:latest
docker.io/dockerinaction/ch3_myotherapp:latest

Images are referenced by ID only, until they're tagged and published. In this example, the first layer is Debian, then there is an openjdk layer (3 layers) and the specific layers of each app.

Docker uses the MNT namespace to mount the filesystems specific to the image, and then limits the folder structure with chroot.

With the two applications running, we can see the mounts on the host os:

test@localhost: sudo mount  | tail -n4
overlay on /var/lib/docker/overlay2/d7a4bcb57c946ee97e153dbefe72582f2eba3edcbd7aa44609edaf2e404a607e/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/GBJ577KE4P36ABUP4E7AIJFO66:/var/lib/docker/overlay2/l/6URWUWCRMTSWR6RH72UZ2BWFWG:/var/lib/docker/overlay2/l/P4PI4JNJVXP3OGSOGHPHDY57HY:/var/lib/docker/overlay2/l/5KKCT7GQZFAN7DH3EJW3E6Y7FJ:/var/lib/docker/overlay2/l/OIEYT3DV5JPNTQG5BBNYOIV4PU:/var/lib/docker/overlay2/l/JSX77UNYPC3NILHOVBLSQ2WCQS:/var/lib/docker/overlay2/l/2U3BRBSWUJN3MOUTXXVEI3TFNL:/var/lib/docker/overlay2/l/TZ62SNSFUUKQ74ROE3RJ3I7R3H:/var/lib/docker/overlay2/l/DL65Y7L6TM2GMOOJHAAID6X5UL:/var/lib/docker/overlay2/l/ZJ4RTWLGDGBBNSYHSMUDSUP6LQ,upperdir=/var/lib/docker/overlay2/d7a4bcb57c946ee97e153dbefe72582f2eba3edcbd7aa44609edaf2e404a607e/diff,workdir=/var/lib/docker/overlay2/d7a4bcb57c946ee97e153dbefe72582f2eba3edcbd7aa44609edaf2e404a607e/work)
nsfs on /run/docker/netns/8f576aa794e3 type nsfs (rw)
overlay on /var/lib/docker/overlay2/8ee9f0042e38f75538eac9e765b31c3a178923346eb6ca907361ee5e7634e863/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/7W2ODADHDIMIZ2GWOSGXLU2LIY:/var/lib/docker/overlay2/l/7ILMY6OC6HYYMJZTZ23W6N2HAU:/var/lib/docker/overlay2/l/K433PZWEGYQ7GCBSKSNLGJ3V4V:/var/lib/docker/overlay2/l/VF55NXTGQXQV5JARHLCSEMTDNG:/var/lib/docker/overlay2/l/H5DPD3UM3R2JM4U7E4Z73NUUYN:/var/lib/docker/overlay2/l/TZCY2MCB3JYYWZIQDYYXEFDPJU:/var/lib/docker/overlay2/l/2U3BRBSWUJN3MOUTXXVEI3TFNL:/var/lib/docker/overlay2/l/TZ62SNSFUUKQ74ROE3RJ3I7R3H:/var/lib/docker/overlay2/l/DL65Y7L6TM2GMOOJHAAID6X5UL:/var/lib/docker/overlay2/l/ZJ4RTWLGDGBBNSYHSMUDSUP6LQ,upperdir=/var/lib/docker/overlay2/8ee9f0042e38f75538eac9e765b31c3a178923346eb6ca907361ee5e7634e863/diff,workdir=/var/lib/docker/overlay2/8ee9f0042e38f75538eac9e765b31c3a178923346eb6ca907361ee5e7634e863/work)
nsfs on /run/docker/netns/570a28936e82 type nsfs (rw)

Each storage driver has its pros and cons, and they can be replaced (like pluggins) Docker uses overlay2 by default:

test@localhost: sd info | grep Stora
 Storage Driver: overlay2

https://docs.docker.com/storage/storagedriver/select-storage-driver/