参考文档:

S4:数据共享(Docker Volume)

1. Docker 持久化数据

Docker 对于数据持久化,提供了 Docker Volumn 和 Docker Bind 两种方式。

  • volume 默认位于 /var/lib/docker/volumes 目录中。

在 Docker 中,数据的持久化,需要将数据从宿主机挂载到容器中。

2. Docker 文件系统结构

2.1 文件系统结构

Docker 会将所有的文件系统源合并在一起呈现给 Docker 容器,而 Docker 容器能看到只是一个普普通通的块存储设备,Docker 称这样的系统为 union filesystem,下图是 union filesystem 的一个基本组成结构:

Docker 容器的文件系统结构

  • 最底下的是镜像层文件系统,文件来源是(或者说是镜像)Dockerfile 中生成、复制或者其
    他操作产生的文件,这些文件是只读的,但是因为使用了copy-on-write 技术的原因,这些
    文件某种意义上其实也是可以变更的
  • Docker Volume 是 Docker 提供的用于容器与容器之间共享数据的文件系统。来自 Docker 生成。
  • Docker Bind 是 Docker 提供的挂载型文件系统,用于交互主机(甚至是云文件系统)与容器的文件系统,文件来源于挂载源
  • 可写层是每个容器都独有的文件系统,这些文件仅在容器存在时存在,文件来源于容器本身

2.2 镜像层文件系统和可写层文件系统

镜像层文件系统顾名思义来自于镜像,镜像是用来分享的,且必须保持一致,所以镜像层文件系统是只读的。但是实际应用中,我们发现镜像中原本就存在的文件其实是可以修改的,这是因为在镜像层文件系统上还有一层可写层文件系统。

可写层文件系统由 Docker 在容器运行时特地为容器创建,在容器移除时移除。可写层不仅仅只应用于新添加的文件,其实也应用于修改镜像层存在的文件,在我们尝试修改镜像层存在的文件时,Docker 其实会把该文件复制一份到可写层上,你所有的修改操作实际上全部都操作在可写层的文件上,所以对镜像层文件不会有任何的影响。这种技术称之为copy-on-write。

应用领域
镜像层文件系统应用于镜像分享,因为它的只读属性这保证了镜像的一致性。
可写层文件系统应用于短期存储,比如缓存一些数据之类的。

3. Docker Volume

3.1 概念

Docker Volume 就相当于一个 U 盘,只不过这个 U 盘由 Docker 创建,且服务于容器,Docker Volume是一个独立的组件,和容器的生命周期互不相关,就像你电脑关机了,但是 U 盘还在一样。以容器的视角来看 Docker Volume,它其实就只是一个普通的目录,像平常一样操作这个目录就可以了。就相当于我们把 U 盘挂载到了此目录下,容器中此目录下的文件,存储在 Volume 中。

容器删除后 V

3.2 使用方式

3.2.1 Volume 的基本操作

# 创建 volume
docker volume create volumeName

# 查看所有容器卷
docker volume ls 

# 将 volume 挂载到容器目录
docker run -it -v volumeName:/mydata 镜像名

# 查看 volume
docker volume inspect volumeName

# 可见 volume 位于 /var/lib/docker/volumes/volumeName/_data

# 删除自定义数据卷
docker volume rm volumeName

mac 机器上若使用 docker desktop,则 volume 其实是在 docker 虚拟机中的,不在主机真实目录下 参考 Mac 机找不到容器卷的目录/var/lib/docker/volumes

3.2.2 在 Dockerfile 中指定

VOLUME /data

在 Dockerfile 中添加 Volume 指令,告诉 Docker 在创建这个镜像的容器时,创建一个 Docker Volume 并挂载在 /data 目录下,需要注意的是,这样运行的每个容器的 Docker Volume 都是不同的(即使是同一个镜像)

3.2.3 容器间共享 volume

参考 docker 宿主机与容器里文件共享 -v和 VOLUME

命名的容器挂载数据卷,其它容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为数据卷容器。

# 新建一个容器
docker run -it --name dc01 rainsheep/centos
# 容器 dc02 的数据卷和 dc01 共享
docker run -it --name dc02 --volumes-from dc01 rainsheep/centos 
# 容器 dc03 的数据卷和 dc01 共享
docker run -it --name dc03 --volumes-from dc01 rainsheep/centos 
# 删除容器 dc01
docker rm dc01
# 发现 dc02 和 dc03 数据还能共享,就像加入了群聊一样

3.2.3 手动创建 volume

docker volume create myvolume
docker run --name <containerName> --volume myvolume:'/data' <imageName>

3.2.4 总结

  • 与 bind mount 不同的是,如果 volume 是空的而 container 中的目录有内容,那么 docker 会将 container 目录中的内容拷贝到 volume 中,但是如果 volume 中已经有内容,则会将 container 中的目录覆盖。

  • Docker Volume 可以同时挂载给多个容器,但同时你得考虑到这么一个问题,那就是多个容器同时对一个目录下的资源进行操作,你的应用可能工作得并不如你得预期(因为你要的数据可能正好被另一个容器中的应用修改了)

  • Docker Volume 中 Volume 的优先级以命令中指定的 Volume 为最优先,如果同时 Dockerfile 也指定了 Volume,则该指令无效。

  • 作为镜像创作者,你应该在 Dockerfile 中指定 Volume,以避免用户不指定 Volume 而造成的各种问题。作为用户,你应该避免使用镜像制作者为你提供的默认Volume。

4. Docker Bind

docker run -it -v /宿主机绝对路径目录:/容器内目录 镜像名
  -v # 指定数据卷,容器和宿主机数据共享,没有的话会自动新建文件夹

# 查看数据卷是否挂载成功
docker inspect 容器 ID

# 容器内目录只读
docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
  • 宿主机目录路径必须为全路径(准确的说需要以/~/开始的路径),不然 docker 会将其当做 volume 处理
  • 宿主机上的目录不存在时,docker 会自动创建该目录
  • 容器中的目录不存在时,docker 会自动创建该目录
  • 宿主机内容会将容器内容覆盖(宿主机为空时,容器文件不会被 copy 到宿主机)
  • 容器内目录 一致的情况下,Docker Bind 会覆盖 Docker Volume