前言:

推荐免费Docker基础讲解视频:【狂神说Java】Docker最新超详细版教程通俗易懂_哔哩哔哩_bilibili

镜像联合文件系统

镜像是什么

镜像是一种轻量级,可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,包含运行某个软件所需的全部内容,包括代码、库、环境变量、配置文件等。

如何得到镜像:

  • 远程仓库下载镜像;
  • 自己制作镜像;

Docker镜像加载原理

UnionFS(联合文件系统)

UnionFS(联合文件系统):Union文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行集成,基于基础镜像,可以制作各种具体的应用镜像。

特性:一次同时加载多个文件系统,但从外表来看,只能看到一个文件系统,联合加载会把各层文件系统叠加,最终的文件系统会包含所有底层的文件和目录。

Docker镜像加载原理

Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS;

bootfs(boot file system)主要包含bootloaderkernel,Linux刚启动会加载bootfs文件系统,在Docker镜像的最底层是bootfs,这一层与我们典型的Linux系统一样,包含boot加载器和内核,当boot内核加载完成之后整个内核就都在内存中了,此时内存使用权由bootfs转交给内核,系统也会卸载bootfs。

rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,例如Centos等。

image-20220923104612247

一般我们安装虚拟机都是好多个G大小,为什么Docker才200多M;

对于一个精简的OS,rootfs非常小,只需要包含基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以。由此可见对于不同的Linux发行版,bootfs基本是一致的,rootfs会有差别,不同的发行版可以共用bootfs;

镜像分层理解

分层的镜像

我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是分层下载。

思考:为什么Docker镜像要采用这种分层结构?

最大的好处莫过于资源共享!比如多个镜像都是从相同的Base镜像构建而来,那么宿主机只需要在磁盘保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务,而且镜像的每一层都可以被共享。

查看镜像分层 docker image inspect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 查看nginx的镜像分层
[root@jokerdig ~]# docker image inspect nginx
[
{
"Id": "sha256:2d389e545974d4a93ebdef09b650753a55f72d1ab4518d17a30c0e1b3e297444",
"RepoTags": [
"nginx:latest"
],
"RepoDigests": [
"nginx@sha256:0b970013351304af46f322da1263516b188318682b2ab1091862497591189ff1"
],
"Parent": "",
"Comment": "",
"Created": "2022-09-13T06:29:32.251894602Z",
"Container": "d221caed08a42c6f86cf8bf787da9114acd72f3e27f0a21a0a12f7f38cfa652e",
"ContainerConfig": {
"Hostname": "d221caed08a4",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.23.1",
"NJS_VERSION=0.7.6",
"PKG_RELEASE=1~bullseye"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"nginx\" \"-g\" \"daemon off;\"]"
],

# ...(省略)

分层理解

所有的 Docker镜像都起始于一个基础镜像层,当进行修改或添加新的内容时,就会在当前镜像层之上,创建新的镜像层。

举一个简单的例子,假如基于 Ubuntu Linux16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创健第三个镜像层该像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。

image-20220923111909209

在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件。

image-20220923112335464

上图中的镜像层跟之前图中的略有区別,主要目的是便于展示文件

下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版;

image-20220923112418137

文种情況下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中;

Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统;

Linux上可用的存储引撃有AUFS、 Overlay2、 Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于 Linux中对应的件系统或者块设备技术,井且每种存储引擎都有其独有的性能特点。

Docker在 Windows上仅支持 windowsfilter 一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW。

下图展示了与系统显示相同的三层镜像。所有镜像层堆并合井,对外提供统一的视图
image-20220923112552680

特点

Docker镜像都是只读,当容器启动时,一个新的可写层被加载到镜像的顶部!

这一层就是我们通常说的容器层,容器之下叫镜像层。

Commit镜像

提交命令

1
2
docker commit # 提交容器称为一个新的副本
docker commit -m="提交描述" -a="作者" 容器ID 目标镜像名字:[TAG]

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 启动tomcat
docker run -it -p 8080:8080 tomcat
# 复制一个新的会话
[root@jokerdig ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cd31c1bb98e4 tomcat "catalina.sh run" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp stupefied_kilby
[root@jokerdig ~]# docker exec -it cd31c1bb98e4 /bin/bashroot@cd31c1bb98e4:/usr/local/tomcat# cd webapps
root@cd31c1bb98e4:/usr/local/tomcat/webapps# ls
root@cd31c1bb98e4:/usr/local/tomcat/webapps# cd ..

# 复制ebapps.dist到webapps
root@cd31c1bb98e4:/usr/local/tomcat# cp -r webapps.dist/* webapps
root@cd31c1bb98e4:/usr/local/tomcat# cd webapps
root@cd31c1bb98e4:/usr/local/tomcat/webapps# ls
docs examples host-manager manager ROOT

# Ctrl+P+Q 退出Tomcat 提交tomcat
[root@jokerdig ~]# docker commit -a="jokerdig" -m="add webapps" cd31c1bb98e4 tomcat02:1.0
sha256:297bac3f1d17d4114212c4e9a98479c92f699fced8466d31cecf44deab80c2cf
[root@jokerdig ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
# 我们刚提交的tomcat02,TAG为1.0
tomcat02 1.0 297bac3f1d17 About a mi

容器数据卷使用

什么是容器数据卷

将应用环境打包成一个镜像!

如果数据都在容器中,那么我们容器删除,数据就会丢失!

需求:数据可以持久化;

容器之间可以有一个数据共享的技术!docker容器中产生的数据,同步到本地

这就是卷技术,目录的挂载,将容器内的目录挂载在Linux上面。

image-20220923123535879

容器的持久化和同步操作,容器间可以数据共享

使用数据卷

使用命令来挂载 -v

1
2
# 数据挂载
docker run -it -v 主机目录:容器目录

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 将容器的/home挂载到主机/home/test1
[root@jokerdig ~]# docker run -it -v /home/test1:/home centos /bin/bash
[root@1c38b1781266 /]#

# 复制一个会话,查看/home
[root@jokerdig ~]# cd /home
[root@jokerdig home]# ls
# 这个test1和容器内的home的文件是互相关联的
test1

# 查看容器信息
[root@jokerdig home]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c38b1781266 centos "/bin/bash" 4 minutes ago Up 4 minutes kind_edison
[root@jokerdig home]# docker inspect 1c38b1781266
"Mounts": [
{
"Type": "bind",
"Source": "/home/test1", # 主机内地址
"Destination": "/home", # docker容器内地址
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],

测试挂载是否生效

image-20220923125838521

MySQL同步数据

MySQL数据持久化问题

  1. MySQL下载

    1
    docker pull mysql:8.0
  2. 运行MySQL容器

    1
    2
    3
    # 启动MySQL及相关配置
    # -v 挂载 -e MYSQL_ROOT_PASSWORD 配置密码
    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:8.0

    运行

    1
    2
    3
    4
    5
    6
    [root@jokerdig home]# 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:8.0
    146e27589f8999fff3319b2d57da51f49b5ff15ff78f7430443891d78aed4e24
    # 查看进程
    [root@jokerdig home]# docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    146e27589f89 mysql:8.0 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql01
  3. 尝试连接到服务器MySQL

    image-20220923131640126

  4. 测试数据映射

    在本地创建数据库,就会自动映射到服务器;

    本地新建数据库

    image-20220923132814209

    数据库自动映射到服务器

    image-20220923132724272

即使容器被删除,MySQL的数据仍然是存在的,实现了容器数据的持久化。