抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Docker 数据管理

数据卷概述

数据卷实际上就是宿主机上的目录或者是文件,可以被直接mount 到容器当中使用 。

​ 实际生成 环境中,需要针对不同类型的服务、不同类型的数据存储要求做相应的规划, 最终保证服务的可扩展性、稳定性以及数据的安全性。

​ 为docker run 命令使用-v 选项即可使用volume,其实也就是将宿主机的文件挂载到容器中,默认挂载为rw的权限。

数据卷的特点

  1. 数据卷是目录或者文件,并且可以在多个容器之间共同使用 。

  2. 对数据卷更改数据容器里面,会立即更新 。

  3. 数据卷的数据可以持久保存,即使删除使用使用该容器卷的容器也不 影响 。

  4. 在容器里面的写入数据,不会影响到镜像本身

数据卷使用场景

  1. 日志输出

  2. 静态web页面

  3. 用配置文件

  4. 多容器间目录或文件共享

存储卷用法选项

  • -v选项:将存储卷挂载到哪个目录下
  • –volume-from选项:从哪个容器主机进行复制

Docker挂载方式

在Docker中,要想实现数据的持久化(所谓Docker的数据持久化即数据不随着Container的结束而结束),需要将数据从宿主机挂载到容器中。

目前Docker提供了三种不同的方式将数据从宿主机挂载到容器中

volumes

Docker管理宿主机文件系统的一部分,默认位于 /var/lib/docker/volumes 目录中;(最常用的方式

​ 由上图可以知道,目前所有Container的数据都保存在了这个目录下边,由于没有在创建时指定卷,所以Docker帮我们默认创建许多匿名(就上面这一堆很长ID的名字)卷。

bind mounts

意为着可以存储在宿主机系统的任意位置;(比较常用的方式

​ 但是,bind mount在不同的宿主机系统时不可移植的,比如Windows和Linux的目录结构是不一样的,bind mount所指向的host目录也不能一样,这也是为什么bind mount不能出现在Dockerfile中的原因,因为这样Dockerfile就不可移植了。

tmpfs

挂载存储在宿主机系统的内存中,而不会写入宿主机的文件系统;(一般都不会用的方式

三者区别

三种方式的示意图如下所示:

img

命令格式

docker run -v 路径:容器内部路径:权限 镜像id

示例
1
2
3
4
docker run --name $CONTAINER_NAME -it \
-v $PWD/$CONTAINER_NAME/app:/app:rw \
-v $PWD/$CONTAINER_NAME/data:/data:ro \
avocado-cloud:latest /bin/bash
参数解释
  • 命令格式:[[HOST-DIR:]CONTAINER-DIR[:OPTIONS]]]
  • 如果指定HOST-DIR则必须是绝对路径,如果路径不存在则会自动创建
  • 实例中的rw为读写,ro为只读,不写默认为rw读写

volume(数据卷挂载)

Docker数据卷是经过特殊设计的目录,可以绕过联合文件系统(UFS),为一个或多个容器提供访问。数据卷设计的目的在于对数据的持久化,它完全独立于容器的生命周期,因此Docker不会在删除容器时删除其挂载的数据卷,也不会存在类似垃圾收集的机制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌────────────────────────────────────────┐
│ Docker Host │
│ │
│┌────────────────┐┌────────────────────┐│
││ Docker ││ Local File System ││
││ ││ ││
││ ┌──────────┐ ││ ││
││ │Container1│------------- ││
││ └──────────┘ ││ | ││
││ ┌──────────┐ ││ ┌──────────────┐ ││
││ │Container2│--------│Directory/File│ ││
││ └──────────┘ ││ └──────────────┘ ││
││ ┌──────────┐ ││ | ││
││ │Container3│------------- ││
││ └──────────┘ ││ ││
│└────────────────┘└────────────────────┘│
└────────────────────────────────────────┘

数据卷特性

是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  • 数据卷 可以在容器之间共享和重用
  • 数据卷 的修改会立马生效
  • 数据卷 的更新,不会影响镜像
  • 数据卷 默认会一直存在,即使容器被删除

注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示和看的是挂载的 数据卷,而不是被作为挂载点儿的目录中原来的内容。

使用场景

卷是在Docker容器和服务中保存数据的首选方式。卷的一些用例包括

  • 在多个运行容器之间共享数据。如果您没有明确创建它,则会在第一次将其装入容器时创建卷。当该容器停止或被移除时,该卷仍然存在。多个容器可以同时安装相同的卷,无论是读写还是只读。仅当您明确删除卷时才会删除卷。

  • 当您想要将容器的数据存储在远程主机或云提供商上而不是本地时。

  • 当您需要备份,还原或将数据从一台Docker主机迁移到另一台时,卷是更好的选择。您可以停止使用卷的容器,然后备份卷的目录实现数据的备份和迁移(如/var/lib/docker/volumes/<volume-name>)。

语法

mount 方式

--mount type=volume, source=数据卷名, target=容器中的挂载点

  • type 的值可以是 volume , bind , tmpfs 默认是 volume
  • source 也可以简写成 src
  • target 可以简写成 dst
-v 方式

或者 -v 数据卷名:容器中的挂载点

两者区别

最初,-v--volume 标志用于独立容器,而 --mount 标志用于群集服务。但是,从 Docker 17.06 开始,也可以在独立容器上使用 --mount。一般来说,--mount 更明确和详细。最大的不同在于 -v 语法将所有选项组合在一个字段中,而 --mount 语法将它们分开。下面是每个标志的语法比较。

​ 初学者应该使用 --mount 语法。有经验的用户会更熟悉 -v--volume 语法,但是仍然建议使用 --mount 语法,因为调查显示它更加易用。

如果需要指定卷驱动器选项,必须使用 --mount

volume 标志

-v 或 –volume 标志:由三个由冒号(:)分隔的字段组成。这些字段必须按照正确的顺序排列,每个字段的含义并不明显。

  • 对于命名卷,第一个字段是卷的名称,并且在给定主机上是唯一的。对于匿名卷,第一个字段被省略。
  • 第二个字段是文件或目录在容器中的挂载路径。
  • 第三个字段是可选的,并且是一个逗号分隔的选项列表,例如roconsistentdelegatedcachedzZ
mount 标志

–mount 标志:由多个名值对组成,逗号分隔,每个键值由 = 元组组成。–mount 语法比 -v 或 –volume 更冗长,但键的顺序并不重要,并且标志的值更易于理解。

  • 要挂载的类型 type,可以是 bindvolumetmpfs。本主题主要使用 volume
  • 要挂载的源 source,对于有名字的卷,这里是卷的名字。对于匿名卷忽略这个字段。可以指定为 srcsource
  • 要挂载的目的地 destination,将文件或目录挂载在容器中的路径作为其值。 可能被指定为 destinationdsttarget
  • 只读选项 readonly,这个选项会使得挂载到容器中的绑定挂载只读。
  • 选项 bind-propagation,如果有这个选项则会改变绑定传播(bind propagation,参考下面部分)。可以是 rprivate, private, rshared, shared, rslave, slave
  • 选项 consistency,仅用于 Docker for Mac。
  • –mount 标志不支持用于修改 selinux 标签的 z 和 Z 选项。
两者之间的表现差异
  • 因为 -v--volume 标志在 Docker 已经使用了很久了,无法改变其表现。这意味着在 --mount-v 之间的表现有差异。

  • 如果使用 -v--volume 标志来绑定挂载 Docker 主机中不存在的文件或目录,-v 会为你创建一个端点。它会始终创建目录。

  • 如果使用 --mount 标志来绑定挂载 Docker 主机中不存在的文件或目录,Docker 不会自动为你创建,而是产生报错。

关数据卷和挂载目录提示
  • 如果将**空卷挂载到容器中的含有内容的目录中,则会将这些内容复制到卷中。同样,如果您启动容器并指定一个尚不存在的卷,则会为您创建一个空卷**。

  • 如果将一个**bind mount** 或**非空的数据卷** 挂载到容器中的一个非空目录中,则这些内容会被遮盖隐藏。隐藏的内容不会被删除或更改,此时也不可被访问。就像在 Linux 机器中使用 mount 命令一样的效果

基本使用

操作步骤
  1. 创建一个新的数据卷,之后运行一个容器,并把数据卷挂载到容器的一个目录/webapp 下。

  2. 在容器的 /webapp 目录下创建文件 nginx.txt 并写入一些内容

  3. 接着退出这个容器,并删除这个容器。

  4. 运行一个新的容器,并挂载刚才的数据卷到容器的目录 /yangge

  5. 观察容器目录 /yangge 下是否有之前创建的文件 nginx.txt

创建数据卷
1
2
3
docker volume create volume_test
----------------------------------
volume_test
挂载数据卷

启动一个容器并挂载一个已经创建好的数据卷

在用 docker run 命令的时候,使用 --mount 标记来将 数据卷 挂载到容器里。

下面创建一个名为 volume_test 的容器,并加载一个 数据卷 到容器的 /webapp 目录。

1
docker run -it  --mount source=volume_test,target=/webapp --name myanginx nginx  /bin/bash
创建文件

进入容器后,创建文件

1
2
3
cd /webapp
touch nginx.txt
echo "nginx server" > nginx.txt

退出并删除容器
1
docker rm myanginx

启动新容器

启动一个新的容器,并且把刚才的数据卷 volume_test 挂载到新的容器里

1
docker run -it --name alpine --mount source=volume_test,target=/yangge alpine /bin/bash
查看挂载点

接着查看容器中目录 /yangge 下是否有个刚才的文件 nginx.txt

1
cat /yangge/nginx.txt

查看数据卷

查看所有的数据卷
1
2
3
docker volume ls
.................
local volume_test
查看数据卷元数据
1
docker volume inspect volume_test

显示信息

1
2
3
4
5
6
7
8
9
10
11
[
{
"CreatedAt": "2020-06-12T23:19:13-04:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/volume_test/_data",
"Name": "volume_test",
"Options": {},
"Scope": "local"
}
]
删除数据卷
1
docker volume rm volume_test

数据卷 是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令。

数据卷可能会占据很多空间,可以使用以下命令清理掉没有容器使用的 数据卷
谨慎操作,这需要你确认现在暂时没有使用数据卷在以后也不会再使用,里面也没有有价值的数据。

1
docker volume prune

数据卷容器

命名的容器挂载数据卷,其他容器通过挂载这个容器实现数据共享,挂载数据卷的容器,就叫做数据卷容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌────────────────────────────────────────────────────────────┐
│ Docker Host │
│ │
│┌───────────────────────────────────┐┌─────────────────────┐│
││ Docker ││ Local File System ││
││ ││ ││
││ ┌──────────┐ ││ ││
││ │Container1│---------- ││ ││
││ └──────────┘ | ││ ││
││ ┌──────────┐ ┌──────────────┐ ││ ┌──────────────┐ ││
││ │Container2│----│Data Container│-------│Directory/File│ ││
││ └──────────┘ └──────────────┘ ││ └──────────────┘ ││
││ ┌──────────┐ | ││ ││
││ │Container3│---------- ││ ││
││ └──────────┘ ││ ││
│└───────────────────────────────────┘└─────────────────────┘│
└────────────────────────────────────────────────────────────┘
语法格式

我们可以通过使用docker run命令的--volumes-from选项挂载数据卷容器。该命令的完整格式为:$ docker run --volumes-from [CONTAINER_NAME],这里的“CONTAINER_NAME”就是包含有数据卷的容器的名字

1
docker run -it  --volumes-from ubuntu_volume ubuntu /bin/bash
创建挂载容器

创建一个挂载容器并写入数据

1
docker run -it --name ubuntu_volume -v volume_test:/datavolume ubuntu /bin/bash

image-20210318150248217

创建数据卷容器

创建一个容器,使用--volumes-from命令使用刚刚创建的容器作为数据卷容器,并向上一个容器内部追加内容

1
2
3
4
# 创建容器并指定使用ubuntu_volume容器为数据卷容器
docker run -it --volumes-from ubuntu_volume ubuntu /bin/bash
# 追加内容
echo "xxxxxxxxxxxxxx" >> volume_test.txt

image-20210318151150192

我们发现我们新创建的容器已经有了ubuntu_volume容器数据卷,同样可以多创建几个容器挂载,这样就做到了数据共享

删除数据卷容器
删除数据卷容器

在容器全部停止的状态下,使用docker rm -v命令来删除容器ubuntu_volume,指定-v选项是为了告诉Docker在删除容器的同时,也删除掉与容器相关联的数据卷

1
2
3
4
# 删除ubuntu_volume数据卷容器
docker rm -v ubuntu_volume
# 重新启动一个容器
docker start 8ff2715d8e7e

image-20210318152023959

查看容器数据

我们发现数据依旧存在

image-20210318152124498

结果表明Docker并没有删除容器ubuntu_volume所关联的数据卷,这是因为该数据卷仍被其他容器所关联,在这种情况下Docker并不会删除这些数据卷。

小结

​ 使用数据卷容器,可以很方便地在容器之间共享数据,同时我们并不需要使用者确切地连接到一个已知的Docker宿主机上的文件或目录,这一点在多租户环境中十分重要,因为在这种情况下我们并不想暴露服务器过多的实际信息。

bind mount (绑定挂载)

就是挂载主机目录

​ 与卷相比,绑定安装具有有限的功能,当您使用绑定挂载时,主机上的文件或目录被挂载到容器中,主机上的文件或目录必须是完整路径,不存在,它会根据需求被创建,您不能使用Docker CLI命令直接管理被挂载的目录或文件,会非常高效,但这点,依赖于具有特定目录结构的主机的文件系统。

副作用是您可以通过容器中运行的进程更改主机文件系统 ,包括创建,修改或删除重要的系统文件或目录。这是一个强大的能力,可能会对安全产生影响,包括影响主机系统上的非Docker进程。

使用场景

  • 从主机共享配置文件到容器。例如,Docker如何为容器提供DNS解析,默认情况下,通过将本地的/etc/resolv.conf从主机挂载到每个容器,。

  • 在Docker主机上的开发环境与容器之间共享源码。

  • 当需要保证Docker主机的文件或目录结构与容器所需的一致时。

基本使用

挂载目录

挂载一个主机目录到容器中

mount命令挂载

使用 --mount 标记可以指定挂载一个本地主机的目录到容器中去。

1
docker run -it --mount type=bind,source=/src/webapp,target=/opt/webapp/  --name webapp  alpine  /bin/sh
-v 命令挂载

也可以使用 : -v /src/webapp:/opt/webapp:前面部分是宿主机目录,后面部分是容器的挂载点

1
docker run -it -v /src/webapp:/opt/webapp/  --name webapp  alpine  /bin/sh
创建文件

进入容器后,创建文件

1
2
3
4
cd /opt/webapp
touch web.txt
echo "webapp" > web.txt
cat web.txt

退出并删除容器

退出并删除容器发现文件还存在宿主机目录

1
2
docker rm webapp
cat /src/webapp/web.txt

设置权限

Docker 挂载主机目录的默认权限是 读写,用户也可以通过增加 ro 指定为 只读

1
docker run -it --mount type=bind,source=/src/webapp,target=/opt/webapp/:ro  --name webapp  alpine  /bin/sh

加了 ro之后,就挂载为 只读 了。如果你在容器内 /opt/webapp 目录新建文件,会显示如下错误

1
2
3
cat web.txt 
echo "webapp_readonly" > web.txt
touch webapp_readonly.txt

挂载文件

可以挂载单个本地文件到Docker容器中

1
docker run -it --mount type=bind,source=/src/webapp/web.txt,target=/opt/webapp/web.txt  --name webapp  alpine  /bin/sh
操作文件

操作挂载的web.txt

1
2
cat /opt/webapp/web.txt
echo "webapp_only_file" > /opt/webapp/web.txt
创建文件

在/opt/webapp 目录下 创建文件

1
2
touch /opt/webapp/webapp_only.txt
ls /opt/webapp/
退出并删除容器

退出并删除容器发现修改的文件已修改,但是容器内部创建的文件没有同步到外边

bind mount和volume差异

创建方式对比

对比项 bind mount volume
Source位置 用户指定 /var/lib/docker/volumes/
Source为空 覆盖dest为空 保留dest内容
Source非空 覆盖dest内容 覆盖dest内容
Source种类 文件或目录 只能是目录
可移植性 一般(自行维护) 强(docker托管)
宿主直接访问 容易(仅需chown) 受限(需登陆root用户)

​ Docker无法简单地通过sudo chown someuser: -R /var/lib/docker/volumes/somevolume来将volume的内容开放给主机上的普通用户访问,如果开放更多权限则有安全风险。

​ 而这点上Podman的设计就要理想得多,volume存放在$HOME/.local/share/containers/storage/volumes/路径下,即提供了便捷性,又保障了安全性。无需root权限即可运行容器,这正是Podman的优势之一,实际使用过程中的确受益良多

bind mount命令区别

对比项 --volume-v --mount type=bind
如果主机路径不存在 自动创建 命令报错

评论