The previous Blog described in detail how to quickly follow several middleware or servers and simply test and use them. As of the last article, two blogs were used to complete the form and bottom things, that is, how to play Docker. This Blog will talk about some principle level content, which is convenient for further use. There is a theoretical basis to support it. The learning process is similar to this. First use and then learn the theory to guide, and then use it in depth, layer by layer.
Docker image loading principle
What is mirroring? Image is a lightweight and executable independent software package, which is used to package the software running environment and the software developed based on the running environment. It contains all the contents required to run a software, including code, runtime (the dependency of a program running or being executed), library, environment variables and configuration files. In fact, it can also be understood as a file directory.
UnionFS - federated file system
Docker's image is actually composed of a layer by layer file system. This layer of file system is the UnionFS federated file system. The UnionFS file system (UnionFS) is a layered, lightweight and high-performance file system. It supports the modification of the file system as a layer by layer superposition, At the same time, you can mount different directories into a single virtual file system. The Union file system is the foundation of docker image. Images can be inherited through layering. Based on the basic image, various specific application images can be made
Features: multiple file systems can be loaded at the same time, but from the outside, only one file system can be seen. Joint loading will overlay all layers of file systems, so that the final file system will contain all underlying files and directories
A typical Linux file system consists of bootfs and rootfs:
- bootfs(boot file system) mainly includes bootloader and kernel. Bootloader is mainly used to boot and load the kernel. When the kernel is loaded into memory, bootfs is umount ed, that is, unloaded
- When loading bootfs, traditional Linux will first set rootfs to read-only, then change rootfs from read-only to read-write after system self-test, and then we can write and read on rootfs.
- After the self-test is completed, it will not be changed to docs read-boot after the self-test is completed. Instead, one or more read only rootfs are loaded onto the previous read only rootfs layer by using union mount (a mounting mechanism of UnionFS). After loading so many layers of rootfs, it still looks like a file system. In the Docker system, these read-only rootfs of union mount are called Docker images. However, at this time, each layer of rootfs is read only, and we cannot operate it at this time. When we create a container, that is, instantiate the Docker image, the system will allocate a layer of empty read-write rootfs over one or more layers of read-only rootfs
- rootfs (root file system) contains standard directories and files such as / dev, / proc, / bin, / etc in a typical linux system. For a simplified OS, rootfs can be very small and only need to include the most basic commands, tools and program libraries. Because the underlying layer directly uses the Host kernel, it only needs to provide rootfs itself. It can be seen that for different linux distributions, bootfs are the same, and rootfs will be different. Therefore, different distributions can share bootfs
Remember my previous description in the first Blog [Docker learning notes I] basic concepts and theoretical basis of Docker , the applications in Docker container run directly in the kernel of the host machine. The container has neither its own kernel nor virtual hardware. It is a kernel level virtualization technology.
Mirror layering mechanism
In the design of Docker image, the concept of layer is introduced, that is, every step of user's image making operation will generate a layer, that is, an incremental Directory:
The benefits of layering are obvious: resource sharing. If multiple images are built from the same base image, the host only needs to save a base image on disk and load a base image in memory to serve all containers. And every layer of the image can be shared.
The unified file system technology can integrate different layers into a file system, providing a unified perspective for these layers, which hides the existence of multiple layers. From the perspective of users, there is only one file system.
For example, if you install a tomcat, you will find that the basic image layer does not need to be downloaded repeatedly if it already exists
[root@192 ~]# docker pull tomcat Using default tag: latest latest: Pulling from library/tomcat 0e29546d541c: Already exists 9b829c73b52b: Already exists cb5b7ae36172: Already exists 6494e4811622: Already exists 668f6fcc5fa5: Already exists dc120c3e0290: Already exists 8f7c0eebb7b1: Already exists 77b694f83996: Already exists 0f611256ec3a: Already exists 4f25def12f23: Already exists Digest: sha256:9dee185c3b161cdfede1f5e35e8b56ebc9de88ed3a79526939701f3537a52324 Status: Downloaded newer image for tomcat:latest docker.io/library/tomcat:latest
You can view the layered loaded image through the following command:
[root@192 ~]# docker image inspect tomcat [ { "Id": "sha256:fb5657adc892ed15910445588404c798b57f741e9921ff3c1f1abe01dbb56906", "RepoTags": [ "tomcat:latest" ], "RepoDigests": [ "tomcat@sha256:9dee185c3b161cdfede1f5e35e8b56ebc9de88ed3a79526939701f3537a52324" ], "Parent": "", "Comment": "", "Created": "2021-12-22T17:07:13.333084424Z", "Container": "de0900b3a6caf902ccdaa1c7871d244e29978119ad8a1cce799cf47f1717b679", "ContainerConfig": { "Hostname": "de0900b3a6ca", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "8080/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/tomcat/bin:/usr/local/openjdk-11/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "JAVA_HOME=/usr/local/openjdk-11", "LANG=C.UTF-8", "JAVA_VERSION=11.0.13", "CATALINA_HOME=/usr/local/tomcat", "TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib", "LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib", "GPG_KEYS=A9C5DF4D22E99998D9875A5110C01C5A2F6059E7", "TOMCAT_MAJOR=10", "TOMCAT_VERSION=10.0.14", "TOMCAT_SHA512=c2d2ad5ed17f7284e3aac5415774a8ef35434f14dbd9a87bc7230d8bfdbe9aa1258b97a59fa5c4030e4c973e4d93d29d20e40b6254347dbb66fae269ff4a61a5" ], "Cmd": [ "/bin/sh", "-c", "#(nop) ", "CMD [\"catalina.sh\" \"run\"]" ], "Image": "sha256:6e2683bf6f13f0050833b6807871b4980142835747139a2c2ae91b274787e399", "Volumes": null, "WorkingDir": "/usr/local/tomcat", "Entrypoint": null, "OnBuild": null, "Labels": {} }, "DockerVersion": "20.10.7", "Author": "", "Config": { "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "8080/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/tomcat/bin:/usr/local/openjdk-11/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "JAVA_HOME=/usr/local/openjdk-11", "LANG=C.UTF-8", "JAVA_VERSION=11.0.13", "CATALINA_HOME=/usr/local/tomcat", "TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib", "LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib", "GPG_KEYS=A9C5DF4D22E99998D9875A5110C01C5A2F6059E7", "TOMCAT_MAJOR=10", "TOMCAT_VERSION=10.0.14", "TOMCAT_SHA512=c2d2ad5ed17f7284e3aac5415774a8ef35434f14dbd9a87bc7230d8bfdbe9aa1258b97a59fa5c4030e4c973e4d93d29d20e40b6254347dbb66fae269ff4a61a5" ], "Cmd": [ "catalina.sh", "run" ], "Image": "sha256:6e2683bf6f13f0050833b6807871b4980142835747139a2c2ae91b274787e399", "Volumes": null, "WorkingDir": "/usr/local/tomcat", "Entrypoint": null, "OnBuild": null, "Labels": null }, "Architecture": "amd64", "Os": "linux", "Size": 679618222, "VirtualSize": 679618222, "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/2fa9b45db352ce43e33bc21cbcbac9570ed998ba2d0e6f2ea1bd849848aa378e/diff:/var/lib/docker/overlay2/4ed31015b0a7955642669dfa51bcc51d41050d08fe3b27a103d088e366a83c85/diff:/var/lib/docker/overlay2/6ec7f2f6d2420d08adcaa7f8f4c63050bd4621bc7c9a39f1042c0d3c1ecaef03/diff:/var/lib/docker/overlay2/24a7055f343fe15c5fe4957e0266a7cdccaf5e09debe2a13224afd16eb4dacb6/diff:/var/lib/docker/overlay2/3a8a1619c0ca510532d8062be2b40e09f59bbb91ea48704da894006c34680d29/diff:/var/lib/docker/overlay2/c2a07a3ad966e9e6bb504c23e3007a491e82f5561c5538ed1d73278612fe2ca3/diff:/var/lib/docker/overlay2/3cfbce5aac5aa34a20a12b86a0d76a2b757eb39f23e2d27c2ce937e041129f6c/diff:/var/lib/docker/overlay2/cc83174f85ca6519fd5f5b439acb26ea36d45bcff37704e1e4e19ef0d747499e/diff:/var/lib/docker/overlay2/e38e7ef258495ff25d8c928367274a6097f2b950527f03a941f4746debb77215/diff", "MergedDir": "/var/lib/docker/overlay2/7f19eb1e3fdaed283c2e2c1a2eb865150de7f8ee50ce64e9ebdf6e595695ad75/merged", "UpperDir": "/var/lib/docker/overlay2/7f19eb1e3fdaed283c2e2c1a2eb865150de7f8ee50ce64e9ebdf6e595695ad75/diff", "WorkDir": "/var/lib/docker/overlay2/7f19eb1e3fdaed283c2e2c1a2eb865150de7f8ee50ce64e9ebdf6e595695ad75/work" }, "Name": "overlay2" }, "RootFS": { "Type": "layers", "Layers": [ "sha256:11936051f93baf5a4fb090a8fa0999309b8173556f7826598e235e8a82127bce", "sha256:31892cc314cb1993ba1b8eb5f3002c4e9f099a9237af0d03d1893c6fcc559aab", "sha256:8bf42db0de72f74f4ef0c1d1743f5d54efc3491ee38f4af6d914a6032148b78e", "sha256:26a504e63be4c63395f216d70b1b8af52263a5289908df8e96a0e7c840813adc", "sha256:f9e18e59a5651609a1503ac17dcfc05856b5bea21e41595828471f02ad56a225", "sha256:832e177bb5008934e2f5ed723247c04e1dd220d59a90ce32000b7c22bd9d9b54", "sha256:3bb5258f46d2a511ddca2a4ec8f9091d676a116830a7f336815f02c4b34dbb23", "sha256:59c516e5b6fafa2e6b63d76492702371ca008ade6e37d931089fe368385041a0", "sha256:bd2befca2f7ef51f03b757caab549cc040a36143f3b7e3dab94fb308322f2953", "sha256:3e2ed6847c7a081bd90ab8805efcb39a2933a807627eb7a4016728f881430f5f" ] }, "Metadata": { "LastTagTime": "0001-01-01T00:00:00Z" } } ] [root@192 ~]#
The loading process is as follows:
"RootFS": { "Type": "layers", "Layers": [ "sha256:11936051f93baf5a4fb090a8fa0999309b8173556f7826598e235e8a82127bce", "sha256:31892cc314cb1993ba1b8eb5f3002c4e9f099a9237af0d03d1893c6fcc559aab", "sha256:8bf42db0de72f74f4ef0c1d1743f5d54efc3491ee38f4af6d914a6032148b78e", "sha256:26a504e63be4c63395f216d70b1b8af52263a5289908df8e96a0e7c840813adc", "sha256:f9e18e59a5651609a1503ac17dcfc05856b5bea21e41595828471f02ad56a225", "sha256:832e177bb5008934e2f5ed723247c04e1dd220d59a90ce32000b7c22bd9d9b54", "sha256:3bb5258f46d2a511ddca2a4ec8f9091d676a116830a7f336815f02c4b34dbb23", "sha256:59c516e5b6fafa2e6b63d76492702371ca008ade6e37d931089fe368385041a0", "sha256:bd2befca2f7ef51f03b757caab549cc040a36143f3b7e3dab94fb308322f2953", "sha256:3e2ed6847c7a081bd90ab8805efcb39a2933a807627eb7a4016728f881430f5f" ] },
Let's understand the following contradictions:
Why does tomcat need 15M for normal installation and 680M for docker installation
This is a tomcat on my machine
This is tomcat installed by Docker
This is because the docker image principle is layered construction. For example, Tomcat is the top layer of Tomcat. Because Tomcat depends on jdk, the next layer is jdk, the basic image is rootfs (Ubuntu, centos), and the lowest layer is bootfs. Because the Tomcat image construction needs to rely on other files, the docker needs 680MB to install Tomcat. You can see the image content of Tomcat:
Why is a centos image in Docker only 200M, while the iso file of a centos operating system needs several G?
Docker image is built in layers, and each layer can be reused. The bootfs of Linux system are basically the same. Therefore, installing docker image in linux series system will reuse the kernel at the bottom of host Linux. Only rootfs and other image layers need to be downloaded, so it is relatively small.
Container startup process
Docker images are read-only. When the container starts, a new writable layer is loaded to the top of the image. This layer is usually called the container layer, and those below the container layer are called the image layer. If users want to modify an image, they can build an image through a read-write container.
Here, the operation of the Container uses the technology used by all drivers - write time replication (CoW). CoW is copy on write, which means to copy only when writing is needed. This is a modification scenario for existing files. For example, if you start multiple containers based on one image, if you allocate a file system with the same image to each Container, it will occupy a lot of disk space. CoW technology allows all containers to share the file system of the image, and all data is read from the image. Only when the file is to be written, the file to be written is copied from the image to its own file system for modification. Therefore, no matter how many containers share the same image, the write operation is performed on the replica copied from the image to their own file system, and the source file of the image will not be modified. If multiple containers operate on the same file, a replica will be generated in the file system of each Container. What each Container modifies is its own replica, which is isolated from each other, They don't affect each other. Using CoW can effectively improve disk utilization
Commit an image
As mentioned above, when starting the container, we can divide the structure into two layers. Read only is used as the image layer and read write is used as the container layer. If we want to make a new image based on the current image, we actually submit read write as a new layer.
use docker commit The command submission container becomes a new version docker commit -m=""Submitted description" -a="author" container id Target image name:[TAG]
Because the original downloaded tomcat cannot be started, we put the tomcat made yesterday tomcat-tml Publish as a new image:
[root@192 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 78664208a245 portainer/portainer "/portainer" 24 hours ago Up 2 hours 0.0.0.0:8088->9000/tcp, :::8088->9000/tcp thirsty_gauss 2f59536a92da tomcat "catalina.sh run" 26 hours ago Up 23 seconds 0.0.0.0:3335->8080/tcp, :::3335->8080/tcp tomcat-tml [root@192 ~]# docker commit -m="add webapps" -a="Ethan" 2f59536a92da mytomcat:1.0 sha256:251e3ac9aff1efe0651c0dfb0e60ce097ab0e56e7b813b31291300988b6e4668 [root@192 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE mytomcat 1.0 251e3ac9aff1 5 seconds ago 684MB nginx latest 605c77e624dd 7 weeks ago 141MB tomcat latest fb5657adc892 8 weeks ago 680MB mysql latest 3218b38490ce 2 months ago 516MB hello-world latest feb5d9fea6a5 4 months ago 13.3kB centos latest 5d0da3dc9764 5 months ago 231MB portainer/portainer latest 580c0e4e98b0 11 months ago 79.1MB elasticsearch 7.6.2 f29a1ee41030 23 months ago 791MB elasticsearch latest 5acf0e8da90b 3 years ago 486MB [root@192 ~]#
You can also see from the panel that the image is finished:
To sum up
Image is a lightweight and executable independent software package, which is used to package the software running environment and the software developed based on the running environment. It contains all the contents required to run a software, including code, runtime (the dependency of a program running or being executed), library, environment variables and configuration files. In fact, it can also be understood as a file directory. This explains why image-based containers can operate independently and in isolation. The file system organization mode, image layering and container write time replication characteristics of UnionFS can also explain in principle why the container can only occupy a small amount of memory when running and why the startup speed can reach the second level.