【Docker】学习笔记02:镜像、容器数据卷
Docker镜像
Docker 镜像是什么?
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码,运行时的库、环境变量和配置文件。所有应用直接打包,可以直接跑起来
如何获得镜像:
- 从远程仓库下载
- 朋友拷贝给你
- 自己制作一个镜像DockerFile
Docker镜像加载原理
UnionFs (联合文件系统)
UnionFs(联合文件系统):Union文件系统(UnionFs)是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下( unite several directories into a single virtual filesystem)。Union文件系统是 Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
Docker镜像加载原理
boots(boot file system)主要包含 bootloader和 Kernel, bootloader主要是引导加 kernel, Linux刚启动时会加bootfs文件系统,在 Docker镜像的最底层是 boots。这一层与我们典型的Linux/Unix系统是一样的,包含boot加載器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs转交给内核,此时系统也会卸载bootfs。
rootfs(root file system),在 bootfs之上。包含的就是典型 Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos等等。
平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M?
对于个精简的OS,rootfs可以很小,只需要包合最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的Linux发行版, boots基本是一致的, rootfs会有差別,因此不同的发行版可以公用bootfs.
虚拟机是分钟级别,容器是秒级!
分层理解
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层层的在下载
思考:为什么Docker镜像要采用这种分层的结构呢?
最大的好处,我觉得莫过于资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
查看镜像分层的方式可以通过docker image inspect 命令
[root@izuf6hlvm1mj8gdbdctw0rz ~]# docker inspect redis
[
{
"Id": "sha256:621ceef7494adfcbe0e523593639f6625795cc0dc91a750629367a8c7b3ccebb",
"RepoTags": [
"redis:latest"
],
"RepoDigests": [
"redis@sha256:0f97c1c9daf5b69b93390ccbe8d3e2971617ec4801fd0882c72bf7cad3a13494"
],
"Parent": "",
"Comment": "",
"Created": "2021-01-13T09:45:41.527587343Z",
"Container": "16535cfaf84a4049b6c02840219e8473787d5610e29409049df3a41bbf77a333",
"ContainerConfig": {
"Hostname": "16535cfaf84a",
"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.0.10",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.0.10.tar.gz",
"REDIS_DOWNLOAD_SHA=79bbb894f9dceb33ca699ee3ca4a4e1228be7fb5547aeb2f99d921e86c1285bd"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"redis-server\"]"
],
"Image": "sha256:222c0cecc006d8c73a04a58b5fa15ebae171a6e82a8ee8650ae616f6f1798ef4",
"Volumes": {
"/data": {}
},
"WorkingDir": "/data",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {}
},
"DockerVersion": "19.03.12",
"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.0.10",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.0.10.tar.gz",
"REDIS_DOWNLOAD_SHA=79bbb894f9dceb33ca699ee3ca4a4e1228be7fb5547aeb2f99d921e86c1285bd"
],
"Cmd": [
"redis-server"
],
"Image": "sha256:222c0cecc006d8c73a04a58b5fa15ebae171a6e82a8ee8650ae616f6f1798ef4",
"Volumes": {
"/data": {}
},
"WorkingDir": "/data",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": null
},
"Architecture": "amd64",
"Os": "linux",
"Size": 104285909,
"VirtualSize": 104285909,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/146713550a8e34dfe4a7d35d0ff328cec0a3191e5609ec1215094ab5934c7f02/diff:/var/lib/docker/overlay2/160d8915bc5fa3b0b3d5ec0af895798fd79208274043de4d8f17de71d9dc525b/diff:/var/lib/docker/overlay2/10558d2d50be59c966a77b2bc981336254be4561ac53d6d84b8006825ccc9ecd/diff:/var/lib/docker/overlay2/e0a1fabb7e859b0030f7d561776ea4b63f87e08ff07ad7a3784749c09234b4ff/diff:/var/lib/docker/overlay2/a1022c2288548dfd9cb8a8d2f2cf25bc091bfd42ebd3fbaac6e5a0c75399fb94/diff",
"MergedDir": "/var/lib/docker/overlay2/c9ca6ab9540156e58826153f045fe4b997ef5efba1be2449057d142b052011b9/merged",
"UpperDir": "/var/lib/docker/overlay2/c9ca6ab9540156e58826153f045fe4b997ef5efba1be2449057d142b052011b9/diff",
"WorkDir": "/var/lib/docker/overlay2/c9ca6ab9540156e58826153f045fe4b997ef5efba1be2449057d142b052011b9/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:cb42413394c4059335228c137fe884ff3ab8946a014014309676c25e3ac86864",
"sha256:8e14cb7841faede6e42ab797f915c329c22f3b39026f8338c4c75de26e5d4e82",
"sha256:1450b8f0019c829e638ab5c1f3c2674d117517669e41dd2d0409a668e0807e96",
"sha256:f927192cc30cb53065dc266f78ff12dc06651d6eb84088e82be2d98ac47d42a0",
"sha256:a24a292d018421783c491bc72f6601908cb844b17427bac92f0a22f5fd809665",
"sha256:3480f9cdd491225670e9899786128ffe47054b0a5d54c48f6b10623d2f340632"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]
理解:
所有的 Docker镜像都起始于一个基础镜像层,当进行修改或培加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于 Ubuntu Linux16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python包,
就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创健第三个镜像层该像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点.
特点
Docker 镜像都是只读的,当容器启动时,一个新的可写层加载到镜像的顶部!
这一层就是我们通常说的容器层,容器之下的都叫镜像层!
3)commit 镜像
docker commit #提交容器成为一个新的镜像
用法:docker commit -m=”提交的描述信息” -a=”作者” 容器id 目标镜像名:[TAG]
容器数据卷
什么是容器数据卷?
容器之间可以有一个数据共享的技术,将Docker容器中产生的数据同步到本地。
这就是卷技术:目录的挂载,将我们容器内的目录,挂载到linux上面,实现双向数据同步。
总结:容器的持久化操作。容器内也是可以数据共享的!
使用数据卷:
挂载的第一种方式:直接使用命令挂载 -v
docker run -it -v 主机目录:容器内目录 容器名 /bin/bash
docker run -it -v /home/ceshi:/home centos /bin/bash
将centos容器中/home目录下文件挂载到主机下/home/ceshi目录下
挂载成功检测(在主机下输入):docker inspect 容器id
分别在容器和主机对应目录下新建文件,发现可以同步;
将容器停止,修改主机上测试文件内容,再重新将容器启动,发现数据仍然同步。
具名和匿名挂载
docker volume ls #列出所有挂载
- 匿名挂载:-v 容器内目录
- 具名挂载:-v 卷名:容器内目录
- 指定路径挂载:-v /宿主机目录:容器内目录
- 通过docker inspect 容器id 查看具体挂载目录
“所有的docker容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volumes/自定义的卷名/_data下,如果指定了目录,docker volume ls 是查看不到的。”
拓展
通过 -v 容器内路径: ro rw 改变读写权限;默认rw可读可写
- ro #readonly 只读
- rw #readwrite 可读可写
$ docker run -d -P —name nginx05 -v juming:/etc/nginx:ro nginx
$ docker run -d -P —name nginx05 -v juming:/etc/nginx:rw nginx
一旦设定了ro,则容器的权限随之改变为只读,所以只能从宿主机操作。
ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作!
DockerFlie挂载
挂载的第二种方式:编写dockerfile脚本设置挂载目录
DockerFile 就是用来构建docker镜像的构建文件,就是命令脚本!
通过脚本可以生成镜像,镜像是一层一层的,所以脚本是一个一个的命令,每个命令就是一层!
编写dockerfile,设置挂载目录
创建一个dockerfile文件,名字可以随便 建议Dockerfile
dockerfile1 文件中的内容: 指令(大写) + 参数
$ vim dockerfile1
FROM centos # 当前这个镜像是以centos为基础的
VOLUME [“volume01”,”volume02”] # 挂载卷的卷目录列表(多个目录)
CMD echo “——-end——-“ # 输出一下用于测试
CMD /bin/bash # 默认走bash控制台
这里的每个命令,就是镜像的一层!
构建出这个镜像[root@atong ~]# docker build -f dockerfile1 -t atong/centos .
- -f dockerfile1 # f代表file,指这个当前文件的地址(这里是当前目录下的dockerfile1)
- -t atong/centos # t就代表target,指目标镜像名(注意镜像名前不能加斜杠‘/’)
- . # 表示生成在当前目录下 -t后一定要加个 . 要不然不成功
运行一个容器:docker run -it atong/centos /bin/bash
测试:在 atong/centos 镜像运行的容器内的 volume01 挂载目录下新建一个文件container.txt ,然后不退出在宿主机上通过命令:#docker inspect 容器id
查看 mounts 中的具体匿名挂载目录,进入该目录下查看是否 container.txt 文件已经同步,若同步则成功。
数据卷容器
两个或多个mysql同步数据:双向拷贝
实现容器间的数据共享:—volumes-from
用到的命令:
docker run -it —name docker01 atong/centos:latest #注意要加上版本号不然默认最新
docker run -it —name docekr02 —volumes-from docker01 84719eb2a4a5
docker attach 84719eb2a4a5
docker run -it —name docker03 —volumes-from docker01 atong/centos:latest
ctrl+p+q
docker run -d -p 3306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 —name mysql01 mysql:5.7
docker run -d -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 —name mysql02 —volumes-from mysql01 mysql:5.7 #将mysql02挂载到mysql01上
结论:容器之间的配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。
但是一旦你持久化到了本地,这个时候,本地的数据是不会删除的