Layered Architecture And Dockerfiles
What is a docker file?
A docker file is a document that contains scripts or instructions using which an image can be created.
Creating a docker file -
learning-ocean:~ gaurav$ vi Dockerfile
The docker build command by default builds an image by executing the instructions present in the file named as Dockerfile in the current working directory. So it is a best practice to always name your docker file as Dockerfile, if you name the file as something else then you need to specify the docker file name explicitly by using -f option along with the docker build command.
A docker file with the above content will build an image from the base docker image ubuntu:16.04.
FROM keyword is used to specify the base image and will create a layer using docker image ubuntu:16.04 as a base image.
Note: Best practices related to dockerfile can be found on docker docs.
learning-ocean:~ gaurav$ docker image build -t myubuntu:1 .
The above command will build an image from the dockerfile we created above with the image name as myubuntu and tag name 1. Dot(.) at the end specified the current working directory.
[email protected]:~/dockerfiles$ docker image build -t myubuntu:1 . Sending build context to Docker daemon 2.048kB Step 1/1 : FROM ubuntu:16.04 ---> 065cf14a189c Successfully built 065cf14a189c Successfully tagged myubuntu:1 [email protected]:~/dockerfiles$
On executing the build command, an image with name myubuntu will be created with tag 1 as shown in the above image. Also, you will notice that the image myubuntu:1 and ubuntu:16.04 have the same IMAGE_ID, this is because there were no file structure changes on top of the base image so the new image was tagged with the already existing image.
Now, let’s add some more instructions to our dockerfile.
FROM ubuntu:16.04 RUN apt-get update && apt-get install -y tree RUN touch /tmp/1.txt RUN touch /tmp/2.txt RUN touch /tmp/3.txt RUN touch /tmp/4.txt RUN touch /tmp/5.txt RUN touch /tmp/6.txt
The RUN instruction in the above image will install the tree command in our container image and also create some text files in the tmp folder.
RUN instruction is used to execute any command on top of the image and add a new layer on top of existing layers.
Docker daemon executes each instruction in dockerfile one by one and each instruction is executed independently so the previous instruction has no impact on the next one. Each instruction in the dockerfile created a new layer on top of the existing layers.
Before creating the new image let us check the images available currently in our file system.
So currently there is only 1 image available which ubuntu:14.04.
Now, creating a new image for the above dockerfile instructions.
[email protected]:~/dockerfiles$ docker image build -t myubuntu:1 . Sending build context to Docker daemon 2.048kB Step 1/8 : FROM ubuntu:14.04 14.04: Pulling from library/ubuntu 2e6e20c8e2e6: Pull complete 0551a797c01d: Pull complete 512123a864da: Pull complete Digest: sha256:43cb19408de1e0ecf3ba5b5372ec98978963d6d0be42d0ad825e77a3bd16b5f7 Status: Downloaded newer image for ubuntu:14.04 ---> 13b66b487594 Step 2/8 : RUN apt-get update && apt-get install -y tree ---> Running in e0cb9be128e5 Get:1 http://security.ubuntu.com trusty-security InRelease [65.9 kB] Get:2 http://security.ubuntu.com trusty-security/main amd64 Packages [1032 kB] Get:3 https://esm.ubuntu.com trusty-infra-security InRelease Get:4 https://esm.ubuntu.com trusty-infra-updates InRelease Get:5 http://security.ubuntu.com trusty-security/restricted amd64 Packages [18.1 kB] Get:6 https://esm.ubuntu.com trusty-infra-security/main amd64 Packages Get:7 http://security.ubuntu.com trusty-security/universe amd64 Packages [378 kB] Get:8 http://security.ubuntu.com trusty-security/multiverse amd64 Packages [4730 B] Get:9 https://esm.ubuntu.com trusty-infra-updates/main amd64 Packages Ign http://archive.ubuntu.com trusty InRelease Get:10 http://archive.ubuntu.com trusty-updates InRelease [65.9 kB] Get:11 http://archive.ubuntu.com trusty-backports InRelease [65.9 kB] Hit http://archive.ubuntu.com trusty Release.gpg Fetched 13.9 MB in 32s (430 kB/s) Reading package lists... Reading package lists... Building dependency tree... Reading state information... The following NEW packages will be installed: tree 0 upgraded, 1 newly installed, 0 to remove and 1 not upgraded. Need to get 37.8 kB of archives. After this operation, 109 kB of additional disk space will be used. Get:1 http://archive.ubuntu.com/ubuntu/ trusty/universe tree amd64 1.6.0-1 [37.8 kB] debconf: unable to initialize frontend: Dialog debconf: (TERM is not set, so the dialog frontend is not usable.) debconf: falling back to frontend: Readline debconf: unable to initialize frontend: Readline debconf: (This frontend requires a controlling tty.) debconf: falling back to frontend: Teletype dpkg-preconfigure: unable to re-open stdin: Fetched 37.8 kB in 0s (76.9 kB/s) Selecting previously unselected package tree. (Reading database ... 12097 files and directories currently installed.) Preparing to unpack .../tree_1.6.0-1_amd64.deb ... Unpacking tree (1.6.0-1) ... Setting up tree (1.6.0-1) ... Removing intermediate container e0cb9be128e5 ---> c0e9cf3cd8c7 Step 3/8 : RUN touch /tmp/1.txt ---> Running in 2a6392544c41 Removing intermediate container 2a6392544c41 ---> 40dc1afe860f Step 4/8 : RUN touch /tmp/2.txt ---> Running in 44657719002f Removing intermediate container 44657719002f ---> 04c9b46c6652 Step 5/8 : RUN touch /tmp/3.txt ---> Running in c188f4b2078d Removing intermediate container c188f4b2078d ---> 7174278df979 Step 6/8 : RUN touch /tmp/4.txt ---> Running in a250f272ac21 Removing intermediate container a250f272ac21 ---> a42d4bf321e7 Step 7/8 : RUN touch /tmp/5.txt ---> Running in 86f89db53e62 Removing intermediate container 86f89db53e62 ---> cf7a67141bef Step 8/8 : RUN touch /tmp/6.txt ---> Running in 89ca5baa345e Removing intermediate container 89ca5baa345e ---> 82e9ffb4b1fb Successfully built 82e9ffb4b1fb Successfully tagged myubuntu:1 [email protected]:~/dockerfiles$
The new image myubuntu:1 is created.
gaurav@learning-ocean:~$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE myubuntu 1 82e9ffb4b1fb 4 minutes ago 213MB ubuntu 16.04 065cf14a189c 4 weeks ago 135MB
Now, let’s create a container and check if the instructions that we wrote in the dockerfile were executed or not.
gaurav@learning-ocean:~$ docker container run -it myubuntu:1 root@cd35b098c9a7:/# cd /tmp root@cd35b098c9a7:/tmp# ls 1.txt 2.txt 3.txt 4.txt 5.txt 6.txt root@cd35b098c9a7:/tmp# tree . |-- 1.txt |-- 2.txt |-- 3.txt |-- 4.txt |-- 5.txt `-- 6.txt 0 directories, 6 files [email protected]:/tmp#
On running the container with our new image we can see that the text files that we intended to create using dockerfile have been created in the tmp folder.
Now let us see if any layers have been created for the instructions that were executed.
gaurav@learning-ocean:~$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE myubuntu 1 82e9ffb4b1fb 9 minutes ago 213MB gaurav@learning-ocean:~$ docker image ls -a REPOSITORY TAG IMAGE ID CREATED SIZE myubuntu 1 82e9ffb4b1fb 18 minutes ago 213MB <none> <none> cf7a67141bef 18 minutes ago 213MB <none> <none> a42d4bf321e7 18 minutes ago 213MB <none> <none> 7174278df979 18 minutes ago 213MB <none> <none> 04c9b46c6652 19 minutes ago 213MB <none> <none> 40dc1afe860f 19 minutes ago 213MB <none> <none> c0e9cf3cd8c7 19 minutes ago 213MB ubuntu 14.04 13b66b487594 3 months ago 197MB gaurav@learning-ocean:~$
In the above output we can see different images with unique Image ids listed. Starting with our base image which is ubuntu:14.04 with image id “13b66b487594” and the image that we created myubuntu:1 with id “82e9ffb4b1fb”. And the other images we see are created as a new layer on the execution of each step of the dockerfile.This can also be verified from the execution logs of the docker build command
Step 3/8 : RUN touch /tmp/1.txt ---> Running in 2a6392544c41 Removing intermediate container 2a6392544c41 ---> 40dc1afe860f
Step 3 is executed in an intermediated container with id 2a6392544c41 which is later removed and the output of this step is created as a new layer with image id 2a6392544c41 which can be seen in our list of images present in the picture above.
Note: It is not a best practice and an efficient way to use RUN multiple times in our dockerfile because it will create as many layers.
There is another point to note which is that the docker uses cache for executing commands which have already been executed for better performance.
So, if we modify an existing dockerfile and add a RUN instruction in the middle of the dockerfile instead of adding it to end, this will cause docker to execute each command again as a fresh command and hamper the performance.