镜像是什么?
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需要的所有内容,包括代码,运行时(一个程序在运行或者在被执行的依赖)、库,环境变量和配置文件。
所有的应用直接打包成docker镜像,就可以直接跑起来!!!
如何获取镜像:
- 从远程仓库下载
- 向其他人获取
- 自己制作一个镜像DockerFile
镜像加载的原理
联合文件系统
UnionFS(联合文件系统):Union文件系统是一种分层、轻量级并且高性能的文件系统,它支持文件系统的修改作为一次提交来一层层的叠加,溶蚀可以将不同的目录挂在到同一个虚拟文件系统(Virtual File Systems(VFS))下。Union文件系统时Docker镜像的基础。镜像可以通过分层进行继承,基于基础的镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有的底层的文件和目录。
加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system)主要包含 bootloader 和 Kernel , bootloader 主要是引导加 kernel, Linux刚启动时会加载 bootfs 文件系统,在 Docker 镜像的最底层是 bootfs 。这一层与我们典型的 Linux/Unix系统是一样的,包含 boot 加载器和内核。当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs 。
rootfs(root file system),在 bootfs之上。包含的就是典型 Linux系统中 的 /dev,/proc,/bin,/etc 等标准目录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos 等等。
平时我们安装进虚拟机的CentOS系统好几个G,为什么Docker这里才231M?
[root@VM-0-16-centos /]# docker images
centos latest 5d0da3dc9764 5 weeks ago 231MB
对于一个精简的OS,rootfs可以很小,只需要包含最基础的命令,工具和程序就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同系统的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs。
分层的理解
分层的镜像
我们可以下载一个镜像,观察镜像的下载日志,可以看到镜像在一层层的下载
[root@VM-0-16-centos /]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
7d63c13d9b9b: Pull complete
a2c3b174c5ad: Pull complete
283a10257b0f: Pull complete
7a08c63a873a: Pull complete
0531663a7f55: Pull complete
9bf50efb265c: Pull complete
Digest: sha256:a89cb097693dd354de598d279c304a1c73ee550fbfff6d9ee515568e0c749cfe
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest
思考:为什么Docker镜像要使用分层结构呢?
最大的好处莫过于资源的共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需要在磁盘上存一份base镜像,同时内存里面也只需要加载一份base镜像,这样就可以给所有的容器服务了,而且镜像的每一层都是可以恭喜的。
查看镜像分层的方式可以使用:docker image inspect redis:latest
[root@VM-0-16-centos /]# docker image inspect redis:latest
[
{
"Id": "sha256:7faaec68323851b2265bddb239bd9476c7d4e4335e9fd88cbfcc1df374dded2f",
"RepoTags": [
"redis:latest"
],
"RepoDigests": [
"redis@sha256:a89cb097693dd354de598d279c304a1c73ee550fbfff6d9ee515568e0c749cfe"
],
"Parent": "",
"Comment": "",
"Created": "2021-10-12T09:42:28.403526293Z",
"Container": "10b9267d91b3a0b3d03e429697cf6edda84ecb6447d5c4a5d6054d8ed189c71a",
"ContainerConfig": {
"Hostname": "10b9267d91b3",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"6379/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.12",
"REDIS_VERSION=6.2.6",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.6.tar.gz",
"REDIS_DOWNLOAD_SHA=5b2b8b7a50111ef395bf1c1d5be11e6e167ac018125055daa8b5c2317ae131ab"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"redis-server\"]"
],
"Image": "sha256:77d41858d117f5fe9255dbff7861254660bb6789f3a74f69eebf0d432c214a52",
"Volumes": {
"/data": {}
},
"WorkingDir": "/data",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {}
},
"DockerVersion": "20.10.7",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"6379/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.12",
"REDIS_VERSION=6.2.6",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.6.tar.gz",
"REDIS_DOWNLOAD_SHA=5b2b8b7a50111ef395bf1c1d5be11e6e167ac018125055daa8b5c2317ae131ab"
],
"Cmd": [
"redis-server"
],
"Image": "sha256:77d41858d117f5fe9255dbff7861254660bb6789f3a74f69eebf0d432c214a52",
"Volumes": {
"/data": {}
},
"WorkingDir": "/data",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": null
},
"Architecture": "amd64",
"Os": "linux",
"Size": 112691012,
"VirtualSize": 112691012,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/14a6106e40c47f3459afa14f12e41df8f55ce17725279be266f3841a510b3995/diff:/var/lib/docker/overlay2/9f81a5c66de6db7bb7f16ae91967ebb0dec55207e372f52863b84a0f76df9c1b/diff:/var/lib/docker/overlay2/00eaaea0a3d09c805d76dbbafc72443a3c2fa8648e8e70acffb16efe9c890a3b/diff:/var/lib/docker/overlay2/300cce4365acff9e0913ec088b05159ef35ba669b62002bce2cf0f7cf6106bf0/diff:/var/lib/docker/overlay2/33875f5fef9dfa2f79d73a001b6b4bbdff35a0c6d8e8075ca9b949204ec1ec67/diff",
"MergedDir": "/var/lib/docker/overlay2/c748a98367cd2dd72d9eb24a3cd1098b465d37565241cf3e268b4f60a6e485be/merged",
"UpperDir": "/var/lib/docker/overlay2/c748a98367cd2dd72d9eb24a3cd1098b465d37565241cf3e268b4f60a6e485be/diff",
"WorkDir": "/var/lib/docker/overlay2/c748a98367cd2dd72d9eb24a3cd1098b465d37565241cf3e268b4f60a6e485be/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:e8b689711f21f9301c40bf2131ce1a1905c3aa09def1de5ec43cf0adf652576e",
"sha256:b43651130521eb89ffc3234909373dc42557557b3a6609b9fed183abaa0c4085",
"sha256:8b9770153666c1eef1bc685abfc407242d31e34f180ad0e36aff1a7feaeb3d9c",
"sha256:6b01cc47a390133785a4dd0d161de0cb333fe72e541d1618829353410c4facef",
"sha256:0bd13b42de4de0a0d0cc3f1f162cd0d4b8cb4ee20cbea7302164fdc6894955fd",
"sha256:146262eb38412d6eb44be1710bfe0f05d3493831f82b1c2be8dc8d9558c9f033"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]
这里就是镜像的分层信息
理解
所有的Docker镜像都起始于一个基础的镜像,当进行修改或新增的内容时,就会在当前的镜像创建新的镜像层。
举一个简单的例子:假如基于Ubuntu Linux 16.04创建一个新的镜像,这就是镜像的第一层。如果再该镜像添加Python包,就会再基础的镜像之上创建第二个新的镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含3个镜像层,如下图:
再添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图举一个例子:
每一个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件。
上图中的镜像层跟上面的图片中有差别,主要目的是便于展示文件。
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件这是因为文件7是文件5的一个更新版本。
这种情况下,上层镜像层长得文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新的镜像层添加到镜像中。
Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层的堆栈,并保证多镜像层对外展示为统一文件系统。
Linux上可用的存储引擎有AUFS、Overlay2、Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于Linux中对应的文件系统或者块设备技术,并且每种存储引擎都有其独特的性能特点。
特点
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!
这一层就是我们通常说的容器层,容器之下的都叫镜像层!
Commit镜像
如何提交一个新的容器镜像
docker commit 提交容器成为一个新的副本
# 命令和git原理类似
# docker commit -m="提交的描述信息" -a="作者" 容器ID 目标镜像名:[TAG]
实战测试:
# 1、启动一个tomcat,如果发现tomcat404可能是tomcat目录下webapps里面文件为空,解决方式参考:Docker常用命令>作业联系>Docker安装tomcat
[root@VM-0-16-centos /]# docker run -d -p 3344:8080 --name tomcat02 e81ee1ac2b97
736be05390b457d43b452f3facfd0855a6279c43888ff593599f267a66eed997
[root@VM-0-16-centos /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
736be05390b4 e81ee1ac2b97 "catalina.sh run" 8 seconds ago Up 8 seconds 0.0.0.0:3344->8080/tcp, :::3344->8080/tcp tomcat02
[root@VM-0-16-centos /]#
[root@VM-0-16-centos /]# ls
bin boot data dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@VM-0-16-centos /]# docker exec -it tomcat02 /bin/bash
root@736be05390b4:/usr/local/tomcat# ls
BUILDING.txt CONTRIBUTING.md LICENSE NOTICE README.md RELEASE-NOTES RUNNING.txt bin conf lib logs native-jni-lib temp webapps webapps.dist work
root@736be05390b4:/usr/local/tomcat#
root@736be05390b4:/usr/local/tomcat#
root@736be05390b4:/usr/local/tomcat# cd webapps
root@736be05390b4:/usr/local/tomcat/webapps# ll
bash: ll: command not found
root@736be05390b4:/usr/local/tomcat/webapps# ls
root@736be05390b4:/usr/local/tomcat/webapps#
root@736be05390b4:/usr/local/tomcat/webapps# cd ..
root@736be05390b4:/usr/local/tomcat#
root@736be05390b4:/usr/local/tomcat# cp -r webapps.dist/* webapps
root@736be05390b4:/usr/local/tomcat# cd webapps
root@736be05390b4:/usr/local/tomcat/webapps# ls
ROOT docs examples host-manager manager
# 2、当我们解决这个问题后,就相当于我们在原来的tomcat的镜像上面加了一层,我们可以重新打包镜像了
[root@VM-0-16-centos /]# docker commit -a="hecan" -m="测试" 736be05390b4 tomcat-test:1.0
sha256:4d9e4501ca5bd87dc36b48c86dbb20607bd6e60d867d14e8f63cdaee4946c8dc
[root@VM-0-16-centos /]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat-test 1.0 4d9e4501ca5b 11 seconds ago 685MB
tomcat 9.0 e81ee1ac2b97 11 days ago 680MB
tomcat latest 4ce9babdd885 11 days ago 680MB
redis latest 7faaec683238 12 days ago 113MB
nginx latest 87a94228f133 13 days ago 133MB
centos latest 5d0da3dc9764 5 weeks ago 231MB
portainer/portainer latest 580c0e4e98b0 7 months ago 79.1MB
elasticsearch 7.6.2 f29a1ee41030 19 months ago 791MB
以后我们启动tocmat-test就可以使用最新的tomcat了,不需要再去复制webapps.dist里面的文件到webapps了!!!
鸣谢
本文为狂神说Docker学习笔记
学习地址:https://www.bilibili.com/video/BV1og4y1q7M4
Docker系列
上一章:Docker系列(二)-Dcoker的常用命令