第6章 数据持久化
在容器层的 UnionFS(联合文件系统)中对文件/目录的任何修改,无论是手工修改还是容器在运行过程中的修改,在该容器丢失或被删除后这些修改将全部丢失。即这些修改是无法保存下来的。若要保存下来这些修改,通常有两种方式:
-
定制镜像持久化:将这个修改过的容器生成一个新的镜像,让这些修改变为只读的镜像
-
数据卷持久化:将这些修改通过数据卷同步到宿主机
6.1 定制镜像持久化
6.1.1 需求
我们这里要实现的功能是,为 tomcat:10.0 镜像修改其 webapps 目录。原本该目录内容是空的,用户访问 tomcat 页面会报 404,而真正的内容是在 webapps.dist 中。现要将原webapps 目录删除,然后重命名 webapps.dist 目录为 webapps,以使用户可以看到 tomcat页面。
首先先下载tomcat10.0
docker pull tomcat:10.0
6.1.2 实现
-
运行 tomcat 容器
首先以分离模式启动 tomcat 容器。
docker run --name tomcat10 -dp 8081:8080 tomcat:10.0
此时通过浏览器是无法访问到 tomcat 页面的。
-
进入容器后删除目录
通过 docker exec 命令可以进入到容器命令行。可以看到 webapps 目录是空的,而真正需要的内容在 webapps.dist 目录中。
docker exec -it tomcat10 /bin/bash
删除 webapps 目录,然后将 webapps.dist 目录重命名为 webapps。
rm -rf webapps mv webapps.dist webapps
再次刷新访问地址 就可以看见tomcat首页了
-
容器生成镜像
执行 docker commit 命令,以当前运行的容器为范本生成镜像。
# -a 指定该镜像的作者 # tomcat10:cusdefine 为新镜像的<repository>:<tag> docker commit -m="modify webapps for tomcat10" -a="Jerry" tomcat10 tomcat10:cusdefine
-
测试新镜像
docker run --name tomcat10cusdefine -it -dp 9000:8080 tomcat10:cusdefine
访问路径可以直接访问,无需进行修改
6.2 数据卷持久化
Docker 提供了三种实时同步(宿主机与容器 FS 间数据的同步)方式:
-
数据卷(生产常用)
-
Bind mounts(绑定挂载)
-
tmpfs(临时文件系统)
6.2.1 数据卷概述
-
数据卷简介
数据卷是宿主机中的一个特殊的文件/目录,这个文件/目录与容器中的另一个文件/目录进行了直接关联,在任何一端对文件/目录的写操作,在另一端都会同时发生相应变化。
在宿主中的这个文件/目录就称为数据卷,而容器中的这个关联文件/目录则称为该数据卷在该容器中的挂载点。
数据卷的设计目的就是为了实现数据持久化,其完全独立于容器的生命周期,属于宿主机文件系统,但不属于 UnionFS。因此,容器被删除时,不会删除其挂载的数据卷
-
数据卷的特性
数据卷具有如下明显特性
-
数据卷在容器启动时初始化,如果容器启动后容器本身已经包含了数据,那么,这些数据会在容器启动后直接出现在数据卷中,反之亦然
-
可以对数据卷或挂载点中的内容直接修改,修改后对方立即可看到
-
数据卷会一直存在,即使挂载数据卷的容器已经被删除
-
数据卷可以在容器之间共享和重用
-
6.2.2 创建读写数据卷 run -v
读写数据卷指的是容器对挂载点具有读写权限。
-
命令
数据卷是在使用 docker run 启动容器时指定的,其语法格式为:
docker run –it –v /宿主机目录绝对路径:/容器内目录绝对路径 镜像
注:无论是宿主机中的数据卷还是容器中的挂载点,如果指定的目录不存在,那么 docker引擎都会自动创建。即使是多级目录不存在。
-
需求
在宿主机中的/root/host_mount 目录与 ubuntu 容器的/opt/uc_mount 目录间建立关联,即宿主机中的/root/host_mount 目录作为数据卷,而 ubuntu 容器的/opt/uc_mount 目录作为挂载点。
-
创建数据卷
以交互方式启动一个 ubuntu 容器,同时指定在启动容器时创建数据卷。容器启动完毕后在容器中查看/opt 目录,可以看到新建了一个 uc_mount 子目录。这个就是容器中的挂载点目录。
docker run --name myubuntu -it -v /root/host_mount:/opt/uc_mount ubuntu:latest /bin/bash
在容器数据卷目录中生成一个文件,则在宿主机对应/host_mount目录中也生成了对应的文件
-
停止容器后的操作
即使容器停止了,在宿主机中只要修改了数据卷目录内容,在重新启动容器后,该修改过的数据仍会出现在容器中。因为容器是一个 UnionFS,是一个存在于宿主机中的文件系统,无论容器是否运行,该 FS 都是存在的。通过 exit 退出并停止容器。
修改宿主机文件内容
重启容器,观察文件已经发生了变化
docker start myubuntu docker exec -it myubuntu /bin/bash
-
查看数据卷详情
通过 docker inspect [容器] 命令可以查看到当前容器中挂载点与数据卷的绑定关系。
# RW标识读写权限 docker inspect myubuntu
这里给出了数据卷 Source 与挂载点 Destination 的绑定关系,且容器对挂载点的默认操作权限是 RW 读写的。
6.2.3 创建只读数据卷 ro
只读数据卷,指的是容器对挂载点的操作权限是只读的。宿主机对数据卷的操作权限始终是读写的。有些情况下,为了防止容器在运行过程中对文件产生修改,就需要创建只读数据卷。
-
命令
该命令仅比之前的命令仅多了
:ro
【readonly】,具体语法如下:docker run –it –v /宿主机目录绝对路径:/容器内目录绝对路径:ro 镜像
-
创建数据卷
以交互方式启动一个 ubuntu 容器,同时指定在启动容器时创建只读数据卷
docker run --name myubuntu01 -it -v /root/host_mount:/opt/uc_mount:ro ubuntu:latest /bin/bash
-
数据卷/挂载点互操作
上面图中在容器中无法在挂载点进行新增文件,提示没有权限,在宿主机修改aaa.xxx文件,在容器中可以发现文件已经被修改
-
查看数据绑定详情
# 通过 docker inspect [容器] 命令可以查看到该数据卷的只读属性。 docker inspect myubuntu01
6.2.4 数据卷共享 volumes-from
当一个容器与另一个容器使用相同的数据卷时,就称这两个容器实现了“数据卷共享”。
-
数据卷容器
数据卷容器是实现数据卷共享的一种非常有效的方案。当一个容器 C 启动运行时创建并挂载了数据卷,若其它容器也需要共享该容器 C 挂载的数据卷,这些容器只需在 docker run 启动时通过
--volumes-from [容器 C]
选项即可实现数据卷共享。此时容器 C 就称为数据卷容器。注意:如果 --volumes-from 后面的容器挂掉,其余引用此容器卷的容器也会受到影响,无法使用共享数据卷
-
需求
myubuntu2 容器要共享前面的 myubuntu 容器的数据卷,即宿主机中/root/host_mount为数据卷目录,而这两个容器的挂载点目录都是/opt/uc_mount。
-
进入 myubuntu 容器
通过 docker exec 命令进入 myubuntu 容器后,进入挂载点目录/opt/uc_mount
docker exec -it myubuntu
-
创建并运行 myubuntu2 容器
这里在创建并运行 myubuntu2 容器时,使用–volumes-from 指定该容器要共享 myubuntu的数据卷,即指定 myubuntu 容器为数据卷容器。此时可以发现,myubuntu2 容器中也同样出现了挂载点目录/opt/uc_mount。
docker run --name myubuntu2 --volumes-from myubuntu -it ubuntu:latest /bin/bash
-
数据卷共享操作
在myubuntu2中修改文件内容,在myubuntu中查看也进行了修改
6.3 Dockerfile 持久化
Dockerfile 持久化,其实就是通过使用 Dockerfile 的 VOLUME 指令指定数据卷方式实现的持久化。
6.3.1 VOLUME 指令
VOLUME 指令可以在容器中创建可以挂载数据卷的挂载点。其参数可以是字符串数组,也可以是使用空格隔开的多个纯字符串。
例如,VOLUME ["/var/www", "/etc/apache"]
或 VOLUME /var/www /etc/apache
6.3.2 持久化实现
-
创建 Dockerfile
在/root 目录中 mkdir 一个目录,例如 vols,然后在其中新建 Dockerfile,内容如下。这里指定/opt/xxx 与/opt/ooo 为容器端的挂载点。
FROM centos:7 # xxx以及ooo都是容器的挂载点 VOLUME /opt/xxx /opt/ooo CMD /bin/bash
-
构建镜像 build
# 使用 Dockerfile 构建镜像 volscon:latest docker build -t volscon:latest .
-
运行新建镜像
# 以交互方式运行新建镜像 docker run --name myvols -it volscon
-
查看宿主机端目录
# Ctrl + P + Q 退出容器后,使用 docker inspect 查看当前容器中的挂载点目录到底与宿主机中的哪个数据卷对应 docker inspect myvols
-
验证
在容器端首先进入/opt/xxx 目录,并在其中新建一个文件 hello.log。
docker exec -it myvols /bin/bash cd /opt/xxx echo "hello world" > hello.log
在宿主机端,从 docker inspcet 命令中找到/opt/xxx 目录对应的数据卷 Source 目录,首先进入到该目录,然后就可以查看到对应的 hello.log 文件了。说明数据卷设置成功,可以实现从容器到数据卷的持久化了。
在宿主机端修改文件内容,在容器端同样也是可以看到的。