赞
踩
上一篇Blog详细介绍了如何快速按照几个中间件或服务器,并简单的测试使用,那么截止上篇文章,用了两篇Blog来完成形而下的东西,也就是怎么玩儿Docker,本篇Blog就来唠唠一些原理层面的内容,方便再更深度使用的时候有理论基础来支撑。学习过程就类似这样,先用、再学理论去引导、再深度的去用,一层一层的。
什么是镜像?镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需要的所有内容,包括代码,运行时(一个程序在运行或者在被执行的依赖)、库,环境变量和配置文件。其实也可以理解为一个文件目录。
Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统是UnionFS联合文件系统, Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承, 基于基础镜像, 可以制作各种具体的应用镜像
特性: 一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
典型的Linux文件系统由bootfs和rootfs两部分组成:
/dev,/proc,/bin,/etc
等标准目录和文件,对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供 rootfs 就行了。由此可见对于不同的linux发行版, bootfs是一致的, rootfs会有差别, 因此不同的发行版可以公用bootfs还记得我之前在第一篇Blog中的描述【Docker学习笔记 一】Docker基本概念及理论基础,Docker容器内的应用直接运行在宿主机的内核,容器是没有自己的内核的,也没有虚拟硬件,是内核级别的虚拟化技术。
Docker镜像的设计中,引入了层(layer)的概念,也就是说,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量目录:
分层的好处显而易见:资源共享,假如有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
统一文件系统(Union File System)技术能够将不同的层整合成一个文件系统,为这些层提供了统一的视角,这样就隐藏了多层的存在在用户的角度来看,只存在一个文件系统。
例如安装一个tomcat,会发现基础镜像层如果已经存在就不需要重复下载
[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
可以通过如下命令查看分层加载的镜像:
[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 ~]#
其中的加载过程如下:
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:11936051f93baf5a4fb090a8fa0999309b8173556f7826598e235e8a82127bce",
"sha256:31892cc314cb1993ba1b8eb5f3002c4e9f099a9237af0d03d1893c6fcc559aab",
"sha256:8bf42db0de72f74f4ef0c1d1743f5d54efc3491ee38f4af6d914a6032148b78e",
"sha256:26a504e63be4c63395f216d70b1b8af52263a5289908df8e96a0e7c840813adc",
"sha256:f9e18e59a5651609a1503ac17dcfc05856b5bea21e41595828471f02ad56a225",
"sha256:832e177bb5008934e2f5ed723247c04e1dd220d59a90ce32000b7c22bd9d9b54",
"sha256:3bb5258f46d2a511ddca2a4ec8f9091d676a116830a7f336815f02c4b34dbb23",
"sha256:59c516e5b6fafa2e6b63d76492702371ca008ade6e37d931089fe368385041a0",
"sha256:bd2befca2f7ef51f03b757caab549cc040a36143f3b7e3dab94fb308322f2953",
"sha256:3e2ed6847c7a081bd90ab8805efcb39a2933a807627eb7a4016728f881430f5f"
]
},
这样我们来理解一下几个矛盾点:
为什么tomcat普通安装需要15M,通过docker安装确需要680M
这是我本机的一个tomcat
这是Docker安装的tomcat
这是因为docker镜像原理是分层构建,比如tomcat最上边一层是tomcat。由于tomcat依赖于jdk所以下边一层是jdk,基础镜像是rootfs(Ubuntu、centos),最下边一层是bootfs。由于tomcat镜像构建需要依赖其他文件导致docker安装Tomcat要680MB,可以看下tomcat的镜像内容:
Docker中一个centos镜像为什么只有200M,而一个centos操作系统的iso文件要几个G?
Docker镜像是分层构建的,每一层可以复用,Linux 系统底层(bootfs)基本一致,所以linux系列系统中安装docker镜像会复用宿主机Linux底层的内核,只有rootfs和其他镜像层需要下载,所以比较小。
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作容器层,容器层之下的都叫镜像层如果用户想修改一个镜像的话,可以通过可读写的容器构建一个镜像。
这里容器的运行使用了所有驱动都用到的技术——写时复制(CoW)。CoW就是copy-on-write,表示只在需要写时才去复制,这个是针对已有文件的修改场景。比如基于一个image启动多个Container,如果为每个Container都去分配一个image一样的文件系统,那么将会占用大量的磁盘空间。而CoW技术可以让所有的容器共享image的文件系统,所有数据都从image中读取,只有当要对文件进行写操作时,才从image里把要写的文件复制到自己的文件系统进行修改。所以无论有多少个容器共享同一个image,所做的写操作都是对从image中复制到自己的文件系统中的复本上进行,并不会修改image的源文件,且多个容器操作同一个文件,会在每个容器的文件系统里生成一个复本,每个容器修改的都是自己的复本,相互隔离,相互不影响。使用CoW可以有效的提高磁盘的利用率
如上所述,当启动容器时我们可以将结构分为两层,read-only被当做镜像层,read-write被当做容器层,如果我们想要基于当前镜像再制作一个新的镜像,实际上就是把read-write提交为一个新的layer层。
使用docker commit 命令提交容器成为一个新的版本
docker commit -m=“提交的描述信息” -a="作者" 容器id 目标镜像名:[TAG]
因为原始下载的tomcat无法启动,所以我们把昨天制作好的tomcattomcat-tml发布为一个新镜像:
[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 ~]#
从面板也能看到镜像制作完成:
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需要的所有内容,包括代码,运行时(一个程序在运行或者在被执行的依赖)、库,环境变量和配置文件。其实也可以理解为一个文件目录。这也就解释了为什么基于镜像的容器可以独立的隔离的运行了。UnionFS的文件系统组织模式、镜像分层、容器写时复制特性也能从原理上说明容器运行时为什么可以只占用不多的内存、启动速度为什么可以做到秒级。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。