Docker数据管理
数据卷概述
数据卷实际上就是宿主机上的目录或者是文件,可以被直接mount 到容器当中使用 。
实际生成 环境中,需要针对不同类型的服务、不同类型的数据存储要求做相应的规划, 最终保证服务的可扩展性、稳定性以及数据的安全性。
为docker run 命令使用-v 选项即可使用volume,其实也就是将宿主机的文件挂载到容器中,默认挂载为rw的权限。
数据卷的特点
- 数据卷是目录或者文件,并且可以在多个容器之间共同使用 。
- 对数据卷更改数据容器里面,会立即更新 。
- 数据卷的数据可以持久保存,即使删除使用使用该容器卷的容器也不 影响 。
- 在容器里面的写入数据,不会影响到镜像本身
数据卷使用场景
- 日志输出
- 静态web页面
- 用配置文件
- 多容器间目录或文件共享
存储卷用法选项
- -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
挂载存储在宿主机系统的内存中,而不会写入宿主机的文件系统;(一般都不会用的方式)
三者区别
三种方式的示意图如下所示:
命令格式
docker run -v 路径:容器内部路径:权限 镜像id
示例
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不会在删除容器时删除其挂载的数据卷,也不会存在类似垃圾收集的机制。
┌────────────────────────────────────────┐
│ 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 标志:由三个由冒号(:)分隔的字段组成。这些字段必须按照正确的顺序排列,每个字段的含义并不明显。
- 对于命名卷,第一个字段是卷的名称,并且在给定主机上是唯一的。对于匿名卷,第一个字段被省略。
- 第二个字段是文件或目录在容器中的挂载路径。
- 第三个字段是可选的,并且是一个逗号分隔的选项列表,例如
ro
、consistent
、delegated
、cached
、z
和Z
。
mount 标志
–mount 标志:由多个名值对组成,逗号分隔,每个键值由=元组组成。–mount 语法比 -v 或 –volume 更冗长,但键的顺序并不重要,并且标志的值更易于理解。
- 要挂载的类型
type
,可以是bind
、volume
或tmpfs
。本主题主要使用volume
。 - 要挂载的源
source
,对于有名字的卷,这里是卷的名字。对于匿名卷忽略这个字段。可以指定为src
或source
。 - 要挂载的目的地
destination
,将文件或目录挂载在容器中的路径作为其值。 可能被指定为destination
、dst
或target
。 - 只读选项
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
命令一样的效果
基本使用
操作步骤
- 创建一个新的数据卷,之后运行一个容器,并把数据卷挂载到容器的一个目录
/webapp
下。 - 在容器的
/webapp
目录下创建文件nginx.txt
并写入一些内容 - 接着退出这个容器,并删除这个容器。
- 运行一个新的容器,并挂载刚才的数据卷到容器的目录
/yangge
- 观察容器目录
/yangge
下是否有之前创建的文件nginx.txt
创建数据卷
docker volume create volume_test
----------------------------------
volume_test
挂载数据卷
启动一个容器并挂载一个已经创建好的数据卷
在用
docker run
命令的时候,使用--mount
标记来将数据卷
挂载到容器里。
下面创建一个名为 volume_test
的容器,并加载一个 数据卷
到容器的 /webapp
目录。
docker run -it --mount source=volume_test,target=/webapp --name myanginx nginx /bin/bash
创建文件
进入容器后,创建文件
cd /webapp
touch nginx.txt
echo "nginx server" > nginx.txt
退出并删除容器
docker rm myanginx
启动新容器
启动一个新的容器,并且把刚才的数据卷
volume_test
挂载到新的容器里
docker run -it --name alpine --mount source=volume_test,target=/yangge alpine /bin/bash
查看挂载点
接着查看容器中目录
/yangge
下是否有个刚才的文件nginx.txt
cat /yangge/nginx.txt
查看数据卷
查看所有的数据卷
docker volume ls
.................
local volume_test
查看数据卷元数据
docker volume inspect volume_test
显示信息
[
{
"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"
}
]
删除数据卷
docker volume rm volume_test
数据卷
是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷
,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷
。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v
这个命令。
数据卷可能会占据很多空间,可以使用以下命令清理掉没有容器使用的 数据卷
。
谨慎操作,这需要你确认现在暂时没有使用数据卷
在以后也不会再使用,里面也没有有价值的数据。
docker volume prune
数据卷容器
命名的容器挂载数据卷,其他容器通过挂载这个容器实现数据共享,挂载数据卷的容器,就叫做数据卷容器。
┌────────────────────────────────────────────────────────────┐
│ 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”就是包含有数据卷的容器的名字
docker run -it --volumes-from ubuntu_volume ubuntu /bin/bash
创建挂载容器
创建一个挂载容器并写入数据
docker run -it --name ubuntu_volume -v volume_test:/datavolume ubuntu /bin/bash
创建数据卷容器
创建一个容器,使用
--volumes-from
命令使用刚刚创建的容器作为数据卷容器,并向上一个容器内部追加内容
# 创建容器并指定使用ubuntu_volume容器为数据卷容器
docker run -it --volumes-from ubuntu_volume ubuntu /bin/bash
# 追加内容
echo "xxxxxxxxxxxxxx" >> volume_test.txt
我们发现我们新创建的容器已经有了
ubuntu_volume
容器数据卷,同样可以多创建几个容器挂载,这样就做到了数据共享
删除数据卷容器
删除数据卷容器
在容器全部停止的状态下,使用
docker rm -v
命令来删除容器ubuntu_volume
,指定-v
选项是为了告诉Docker在删除容器的同时,也删除掉与容器相关联的数据卷
# 删除ubuntu_volume数据卷容器
docker rm -v ubuntu_volume
# 重新启动一个容器
docker start 8ff2715d8e7e
查看容器数据
我们发现数据依旧存在
结果表明Docker并没有删除容器
ubuntu_volume
所关联的数据卷,这是因为该数据卷仍被其他容器所关联,在这种情况下Docker并不会删除这些数据卷。
小结
使用数据卷容器,可以很方便地在容器之间共享数据,同时我们并不需要使用者确切地连接到一个已知的Docker宿主机上的文件或目录,这一点在多租户环境中十分重要,因为在这种情况下我们并不想暴露服务器过多的实际信息。
bind mount (绑定挂载)
就是挂载主机目录
与卷相比,绑定安装具有有限的功能,当您使用绑定挂载时,主机上的文件或目录被挂载到容器中,主机上的文件或目录必须是完整路径,不存在,它会根据需求被创建,您不能使用Docker CLI命令直接管理被挂载的目录或文件,会非常高效,但这点,依赖于具有特定目录结构的主机的文件系统。
副作用是您可以通过容器中运行的进程更改主机文件系统 ,包括创建,修改或删除重要的系统文件或目录。这是一个强大的能力,可能会对安全产生影响,包括影响主机系统上的非Docker进程。
使用场景
- 从主机共享配置文件到容器。例如,Docker如何为容器提供DNS解析,默认情况下,通过将本地的
/etc/resolv.conf
从主机挂载到每个容器,。 - 在Docker主机上的开发环境与容器之间共享源码。
- 当需要保证Docker主机的文件或目录结构与容器所需的一致时。
基本使用
挂载目录
挂载一个主机目录到容器中
mount命令挂载
使用
--mount
标记可以指定挂载一个本地主机的目录到容器中去。
docker run -it --mount type=bind,source=/src/webapp,target=/opt/webapp/ --name webapp alpine /bin/sh
-v 命令挂载
也可以使用 :
-v /src/webapp:/opt/webapp
,:
前面部分是宿主机目录,后面部分是容器的挂载点
docker run -it -v /src/webapp:/opt/webapp/ --name webapp alpine /bin/sh
创建文件
进入容器后,创建文件
cd /opt/webapp
touch web.txt
echo "webapp" > web.txt
cat web.txt
退出并删除容器
退出并删除容器发现文件还存在宿主机目录
docker rm webapp
cat /src/webapp/web.txt
设置权限
Docker 挂载主机目录的默认权限是
读写
,用户也可以通过增加ro
指定为只读
。
docker run -it --mount type=bind,source=/src/webapp,target=/opt/webapp/:ro --name webapp alpine /bin/sh
加了
ro
之后,就挂载为只读
了。如果你在容器内/opt/webapp
目录新建文件,会显示如下错误
cat web.txt
echo "webapp_readonly" > web.txt
touch webapp_readonly.txt
挂载文件
可以挂载单个本地文件到Docker容器中
docker run -it --mount type=bind,source=/src/webapp/web.txt,target=/opt/webapp/web.txt --name webapp alpine /bin/sh
操作文件
操作挂载的web.txt
cat /opt/webapp/web.txt
echo "webapp_only_file" > /opt/webapp/web.txt
创建文件
在/opt/webapp 目录下 创建文件
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 |
---|---|---|
如果主机路径不存在 | 自动创建 | 命令报错 |