数据主要分为两类,持久化的与非持久化的。
持久化数据是需要保存的数据。例如客户信息、财务、预定、审计日志以及某些应用日志数据。非持久化数据是不需要保存的那些数据。
两者都很重要,并且 Docker 均有对应的支持方式。
每个 Docker 容器都有自己的非持久化存储。非持久化存储自动创建,从属于容器,生命周期与容器相同。这意味着删除容器也会删除全部非持久化数据。
如果希望自己的容器数据保留下来(持久化),则需要将数据存储在卷上。卷与容器是解耦的,从而可以独立地创建并管理卷,并且卷并未与任意容器生命周期绑定。最终效果即用户可以删除一个关联了卷的容器,但是卷并不会被删除。
Docker管理数据的方式有两种:
- 数据卷
- 数据卷容器
一、数据卷
数据卷是一个或多个容器专门指定绕过Union File System的目录,为持续性或共享数据提供一些有用的功能:
- 数据卷 可以在容器之间共享和重用
- 对 数据卷 的修改会立马生效
- 对 数据卷 的更新,不会影响镜像
- 数据卷 默认会一直存在,即使容器被删除
注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的 数据卷。
总体来说,用户创建卷,然后创建容器,接着将卷挂载到容器上。卷会挂载到容器文件系统的某个目录之下,任何写到该目录下的内容都会写到卷中。即使容器被删除,卷与其上面的数据仍然存在。
如下图所示,Docker 卷挂载到容器的 /code 目录。任何写入 /code 目录的数据都会保存到卷当中,并且在容器删除后依然存在。
上图中,/code 目录是一个 Docker 卷。容器其他目录均使用临时的本地存储。卷与目录 /code 之间采用带箭头的虚线连接,这是为了表明卷与容器是非耦合的关系。
1、创建数据卷
使用下面的命令创建新卷。
Usage: docker volume create [OPTIONS] [VOLUME]
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]# docker volume create my-vol
my-vol
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]#
默认情况下,Docker 创建新卷时采用内置的 local 驱动。恰如其名,本地卷只能被所在节点的容器使用。使用 -d 参数可以指定不同的驱动。
第三方驱动可以通过插件方式接入。这些驱动提供了高级存储特性,并为 Docker 集成了外部存储系统。下图展示的就是外部存储系统被用作卷存储。驱动集成了外部存储系统到 Docker 环境当中,同时能使用其高级特性。
截止到目前为止,已经存在 25 种卷插件,涵盖了块存储、文件存储、对象存储等。
- 块存储:相对性能更高,适用于对小块数据的随机访问负载。目前支持 Docker 卷插件的块存储例子包括 HPE 3PAR、Amazon EBS 以及 OpenStack 块存储服务(Cinder)。
- 文件存储:包括 NFS 和 SMB 协议的系统,同样在高性能场景下表现优异。支持 Docker 卷插件的文件存储系统包括 NetApp FAS、Azure 文件存储以及 Amazon EFS。
- 对象存储:适用于较大且长期存储的、很少变更的二进制数据存储。通常对象存储是根据内容寻址,并且性能较低。支持 Docker 卷驱动的例子包括 Amazon S3、Ceph 以及 Minio。
2、查看数据卷
现在卷已经创建成功,可以通过 docker volume ls
命令进行查看,还可以使用 docker volume inspect
命令在主机里查看指定 数据卷
的信息详情。
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]# docker volume ls
DRIVER VOLUME NAME
local my-vol
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]# docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]#
inspect 命令输出中有几点很有意思。Driver 和 Scope 都是 local。这意味着卷使用默认 local 驱动创建,只能用于当前 Docker 主机上的容器。Mountpoint 属性说明卷位于 Docker 主机上的位置。
在本例中卷位于 Docker 主机的 /var/lib/docker/volumes/my-vol/_data 目录。
使用 local 驱动创建的卷在 Docker 主机上均有其专属目录,在 Linux 中位于 /var/lib/docker/volumes 目录下,这意味着可以在 Docker 主机文件系统中查看卷,甚至在 Docker 主机中对其进行读取数据或者写入数据操作。
3、删除数据卷
有两种方式删除 Docker 卷。
Usage: docker volume prune [OPTIONS]
Usage: docker volume rm [OPTIONS] VOLUME [VOLUME...]
docker volume prune
会删除未装入到某个容器或者服务的所有卷,所以谨慎使用!
docker volume rm
允许删除指定卷。两种删除命令都不能删除正在被容器或者服务使用的卷。
因为没有使用 myvol 卷,所以请通过 prune 命令进行删除。
现在已经完成创建、查看以及删除卷的操作了。上述操作均未涉及容器,这也验证了卷是独立的这一特性。
到目前,已经了解了卷的创建、列表、查看以及删除命令。此外,还可以通过在 Dockerfile 中使用 VOLUME 指令的方式部署卷。具体的格式为 VOLUME < container-mount-point。
但是,在 Dockerfile 中无法指定主机目录。这是因为主机目录通常情况下是相对主机的一个目录,意味着这个目录在不同主机间会变化,并且可能导致构建失败。如果通过 Dockerfile 指定,那么每次部署时都需要指定主机目录。
二、数据卷容器
如果你有一些持久性的数据并且想在容器间共享,或者想用在非持久性的容器上,最好的方法是创建一个数据卷容器,然后从此容器上挂载数据。
1、创建数据卷容器
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]# docker run -t -i -d -v /test --name test ubuntu:14.04 echo hello
使用--volumes-from选项在另一个容器中挂载 /test 卷。不管 test 容器是否运行,其它容器都可以挂载该容器数据卷,当然如果只是单独的数据卷是没必要运行容器的。
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]# docker run -t -i -d --volumes-from test --name test1 ubuntu:14.04 /bin/bash
添加另一个容器
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]# docker run -t -i -d --volumes-from test --name test2 ubuntu:14.04 /bin/bash
也可以继承其它挂载有 /test 卷的容器
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]# docker run -t -i -d --volumes-from test1 --name test3 ubuntu:14.04 /bin/bash
三、备份、恢复或迁移数据卷
备份
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]# docker run --rm --volumes-from test -v $(pwd):/backup ubuntu:14.04 tar cvf /backup/test.tar /test
tar: Removing leading `/' from member names
/test/
/test/b
/test/d
/test/c
/test/a
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]#
启动一个新的容器并且从test容器中挂载卷,然后挂载当前目录到容器中为 backup,并备份 test 卷中所有的数据为 test.tar,执行完成之后删除容器--rm,此时备份就在当前的目录下,名为test.tar。
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]# ls # 宿主机当前目录下产生了 test 卷的备份文件 test.tar test.tar
恢复
你可以恢复给同一个容器或者另外的容器,新建容器并解压备份文件到新的容器数据卷
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]# docker run -t -i -d -v /test --name test4 ubuntu:14.04 /bin/bash
[root@iZ8vbdmp7p7nu6o38oalgoZ ~]# docker run --rm --volumes-from test4 -v $(pwd):/backup ubuntu:14.04 tar xvf /backup/test.tar -C / # 恢复之前的文件到新建卷中,执行完后自动删除容器 test/ test/b test/d test/c test/a
四、删除 Volumes
Volume 只有在下列情况下才能被删除:
- docker rm -v删除容器时添加了-v选项
- docker run --rm运行容器时添加了--rm选项
否则,会在/var/lib/docker/vfs/dir目录中遗留很多不明目录。
五、在集群节点间共享存储
Docker 能够集成外部存储系统,使得集群间节点共享外部存储数据变得简单。例如,独立存储 LUN 或者 NFS 共享可以应用到多个 Docker 主机,因此无论容器或者服务副本运行在哪个节点上,都可以共享该存储。下图展示了位于共享存储的卷被两个 Docker 节点共享的场景。接下来这些 Docker 节点可以将共享卷应用到容器之上。
构建这样的环境需要外部存储系统的相关知识,并了解应用如何从共享存储读取或者写入数据。这种配置主要关注数据损坏(Data Corruption)。
基于下图,设想下面的场景:Node 1 上的容器 A 在共享卷中更新了部分数据。但是为了快速返回,数据实际写入了本地缓存而不是卷中。此时,容器 A 认为数据已经更新。但是,在 Node 1 的容器 A 将缓存数据刷新并提交到卷前,Node 2 的容器 B 更新了相同部分的数据,但是值不同,并且更新方式为直接写入卷中。
此时,两个容器均认为自己已经将数据写入卷中,但实际上只有容器 B 写入了。容器 A 会在稍后将自己的缓存数据写入缓存,覆盖了 Node 2 的容器 B 所做的一些变更。但是 Node 2 上的容器 B 对此一无所知。数据损坏就是这样发生的。
为了避免这种情况,需要在应用程序中进行控制。