一、前言
上一篇 Docker的基本操作按照构建流程练习了docker的一些常见基本指令。
本篇主要讲述Docker数据管理相关的内容。
二、背景
该系列《Docker in Production》内容包含如下部分:
- 容器简介
- Docker简介
- Docker的基本操作
- Docker数据存储
- Docker网络
- Docker安全
- 多主机部署
- 服务发现
- 日志、跟踪、监控
本章主要通过练习数据卷
和数据卷容器
来理解Docker的数据管理。
三、Docker的存储方式
Docker存储驱动包含:AUFS、Device mapper、OverlayFS、Btrfs、ZF,它们提供了接口支持镜像分层与写时复制机制Cow,这两种技术满足了容器的核心价值,即极快的创建速度,极小的存储资源消耗以及容器迁移的便捷性。 容器的Root Image存储分为以下三类:
- AUFS,Overlay : 联合文件系统。
- DeviceMapper:CoW块存储。
- ZFS,btrfs: CoW文件系统。
Docker并不推荐采用Root Image的存储方式来存储应用数据。因为应用数据对安全,可用性,共享,性能等方面的要求和Root Image的要求是完全不一样的。 Docker采用了Volume这样一个独立的数据访问接口,应用通过Volume去访问相关的数据,Volume的实现和CoW的分层文件系统完全独立。 Volume通过Rancher Convoy或者Flocker这样的存储驱动去管理和访问具体的存储设备。
四、卷是什么
Docker的理念之一是将应用与其运行的环境进行打包,因此通常docker容器的生存周期与在容器中运行的程序的生存周期是一致的,当容器被销毁时,容器里的数据也会随之消失。
为了能够保存(持久化)数据以及共享容器间的数据,Docker提出了Volume的概念。 简单来说,Volume就是目录或者文件,它可以绕过默认的联合文件系统,而以正常的文件或者目录的形式存在于宿主机上。
五、管理数据的两种方式
在容器中管理数据主要有两种方式:
- 数据卷(Data volumes)
- 数据卷容器(Data volume containers)
1. 数据卷(Data volumes)
数据卷是容器内的一个特殊目录,该目录绕过UFS,不向顶层的可读写layer写入。数据卷用来保存、固化数据,独立于容器的生存周期,不会主动被回收。
数据卷特性:
- 处于UFS(Union File System)之外
- 主机文件系统中的普通目录
- 在卷上的I/O性能应与主机上的完全相同
- 卷的内容不包含在Docker镜像中
- 任何对卷内容的修改不是镜像的一部分
- 可被多个容器共享和重用
- 持久化数据(即使容器已被删除
注意:数据卷的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的数据卷。
可以使用以下两种方式创建:
- 在Dockerfile中指定VOLUME /some/dir
- 执行docker run -v /some/dir命令来指定 两种方式的区别在于
run的-v
可以指定挂载到宿主机的哪个目录,而Dockerfile的VOLUME
不能,其挂载目录由docker随机生成。
【练习1】用-v添加一个数据卷
1)启动一个基于busybox镜像的容器volume-example,并在其根目录下挂载一个data卷
$ docker run -d -it --name volume-example -v /data busybox
2)查看data是否挂载成功
$ docker exec volume-example ls | grep data
3) 查看data卷对应主机的目录;
$ docker inspect -f volume-example
[{093c2cccfcc37e01d0ae4c145dcfb25a6c65a4b94617944efaa8f130463bcc6f /var/lib/docker/volumes/093c2cccfcc37e01d0ae4c145dcfb25a6c65a4b94617944efaa8f130463bcc6f/_data /data local true}]
如果
步骤1)
,没有指定host的挂载目录,那么docker会自动创建一个挂载文件夹放在/var/lib/docker/volumes/
下。上述例子中,在主机下的挂载目录是:/var/lib/docker/volumes/093c2cccfcc37e01d0ae4c145dcfb25a6c65a4b94617944efaa8f130463bcc6f/_data
如果
步骤1)
,指定了主机目录:$ docker run -d -it --name volume-example -v ~/volume/data:/data busybox $ docker inspect -f volume-example [{ /home/vagrant/volume/data /data true}]
那么在主机下挂载的目录则是自己指定的
~/volume/data
了。
4) 在主机对应的目录创建一个文件
$ sudo touch <paste the copied host directory location>/test-file
5) 在容器里面检查一下刚才创建的文件
$ docker exec volume-example ls data/
6) 查看所有volumn
$ docker volume ls
DRIVER VOLUME NAME
local bd0769d0b9a94e475b370d08ceafd9417499a8f3549a7b0f55f3be96f46b1b56
local 093c2cccfcc37e01d0ae4c145dcfb25a6c65a4b94617944efaa8f130463bcc6f
7) 查看volumn详细信息
$ docker volume inspect 093c2cccfcc37e01d0ae4c145dcfb25a6c65a4b94617944efaa8f130463bcc6f
[
{
"Name": "093c2cccfcc37e01d0ae4c145dcfb25a6c65a4b94617944efaa8f130463bcc6f",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/093c2cccfcc37e01d0ae4c145dcfb25a6c65a4b94617944efaa8f130463bcc6f/_data"
}
]
【练习2】用Dockerfile挂载数据卷
1) 创建一个Dockerfile
FROM busybox
RUN mkdir /test-dir
COPY test.yml /test-dir
VOLUME /test-dir
2) 创建一个test.yml
文件
$ echo "This is a test file for testing volume from Dockerfile." > test.yml
3) 构建一个镜像
$ docker build -t volume-example:test .
Sending build context to Docker daemon 8.192 kB
Step 1 : FROM busybox
---> 103e96d345c0
Step 2 : RUN mkdir /test-dir
---> Running in 10aa732b4099
---> ef6b4c6bde95
Removing intermediate container 10aa732b4099
Step 3 : COPY test.yml /test-dir
---> 7fe861cb8aef
Removing intermediate container 50b4cec740b7
Step 4 : VOLUME /test-dir
---> Running in c20cfc350b8f
---> 8ea4d7630233
Removing intermediate container c20cfc350b8f
Successfully built 8ea4d7630233
4) 查看该镜像所包含的卷信息
$ docker inspect -f volume-example:test
map[/test-dir:{}]
5) Dockerfile中声明卷会占用镜像体积么?
$ docker history volume-example:test
IMAGE CREATED CREATED BY SIZE COMMENT
8ea4d7630233 39 seconds ago /bin/sh -c #(nop) VOLUME [/test-dir] 0 B
7fe861cb8aef 39 seconds ago /bin/sh -c #(nop) COPY file:6d145c083d0383b1b 56 B
ef6b4c6bde95 39 seconds ago /bin/sh -c mkdir /test-dir 0 B
103e96d345c0 4 weeks ago /bin/sh -c #(nop) CMD ["sh"] 0 B
f16f9e1c2f42 4 weeks ago /bin/sh -c #(nop) ADD file:aa56bc8f2fea9c0c81 1.106 MB
6) 运行容器
$ docker run -d --name volume-example-container volume-example:test
c063baa2d2bd07373a563316a05a0c2793bca41883bc0964039dd96a8887cc3b
$ docker inspect -f volume-example-container
[{054802484b2f99171b7d96be0b1e8028662d151b5491c80296d9b92e7d7d392b /var/lib/docker/volumes/054802484b2f99171b7d96be0b1e8028662d151b5491c80296d9b92e7d7d392b/_data /test-dir local true}]
$ docker exec volume-example-container ls /test-dir
test.yml
7) 查看挂载的test.yml
文件
$ sudo ls /var/lib/docker/volumes/054802484b2f99171b7d96be0b1e8028662d151b5491c80296d9b92e7d7d392b/_data
test.yml
Dockerfile中每一句指令,都会生成一个临时的容器,如:
Step 4 : VOLUME /test-dir ---> Running in c20cfc350b8f ---> 8ea4d7630233
首先,
Step 4
里面生成了一个临时容器c20cfc350b8f
; 然后,commit容器得到了镜像8ea4d7630233
; 因此,VOLUME /test-dir
是通过是通过docker run -v /test-dir
来实现的,随后由于容器的commit,该配置保存到了镜像8ea4d7630233
里,可以通过如下质量查看$ docker inspect -f 8ea4d7630233 map[/test-dir:{}]
由于没有指定挂载到的宿主机目录,因此会默认挂载到宿主机的/var/lib/docker/volumes下的一个随机名称的目录下,因此Dockerfile里面的VOLUME不能指定主机挂载目录。
2. 数据卷容器(Data volume containers)
如果你有一些持续更新的数据需要在容器之间共享,最好创建数据卷容器。 数据卷容器,其实就是一个正常的容器,是一个挂载了数据卷但是不执行任何命令的容器,其目的只是为其他容器提供数据卷,方便数据在多容器之间共享、复用。
使用数据容器的两个注意点:
- 不要运行数据容器,这纯粹是在浪费资源。
- 不要为了数据容器而使用”最小的镜像”,如busybox或scratch,只使用数据库镜像本身就可以了。你已经拥有该镜像,所以并不需要占用额外的空间。
【练习3】创建数据卷容器
练习2里面已经创建了一个挂载数据卷的镜像了,这里我们只需要用该镜像创建(create,不用run)一个数据卷容器
$ docker create --name volume-data-container volume-example:test
90cd6d51d364e6054c715f554c9a7921519ef4e4cddb2f5a54e1f86d8d202840
$ docker run -d --volumes-from volume-data-container --name volume-other volume-example:test
db57d89a10ee101df15e5fbbfa2b084526382a6ba5c15d133728011865f09a67
$ docker inspect -f volume-data-container
[{2ce7145b0c5c286b13ef653349979a0f9f22048d6d80113b7edf98ceb61fc264 /var/lib/docker/volumes/2ce7145b0c5c286b13ef653349979a0f9f22048d6d80113b7edf98ceb61fc264/_data /test-dir local true}]
$ docker inspect -f volume-other
[{2ce7145b0c5c286b13ef653349979a0f9f22048d6d80113b7edf98ceb61fc264 /var/lib/docker/volumes/2ce7145b0c5c286b13ef653349979a0f9f22048d6d80113b7edf98ceb61fc264/_data /test-dir local true}]
- 可以使用超过一个的 –volumes-from 参数来指定从多个容器挂载不同的数据卷。
- 使用 –volumes-from 参数所挂载数据卷的容器自己并不需要保持在运行状态。
- 也可以从其他已经挂载了数据卷的容器来级联挂载数据卷。
【练习4】备份数据
$ sudo docker run --volumes-from volume-data-container -v ~/backup:/backup --name volume-backup volume-example:test tar cvf /backup/backup.tar /test-dir
test-dir/
test-dir/test.yml
- 首先利用
volume-example:test
镜像创建了一个叫做volume-backup
的容器。 - 使用
--volumes-from volume-data-container
来让volume-backup
容器挂载volume-data-container
的数据卷。 - 使用-v参数挂载本地的
~/backup
目录到容器的/backup
目录下。 - 容器启动之后,使用了
tar cvf /backup/backup.tar /test-dir
来将数据容器/test-dir
下的内容备份为/backup/backup.tar
(对应到宿主机下~/bakup.tar)。$ ls ~/backup/ backup.tar
【练习5】恢复数据
1)创建一个带有数据卷的容器
$ sudo docker run -v /test-dir --name volumn-data-container2 volume-example:test /bin/sh
2)创建另外一个新的容器,挂载上面的数据卷容器,并使用untar解压备份文件到所挂载的容器卷
$ sudo docker run --volumes-from volumn-data-container2 -v ~/backup:/backup busybox tar xvf /backup/backup.tar
【练习6】删除数据卷
如果你已经使用docker rm来删除你的容器,那可能有很多的孤立的Volume仍在占用着空间。
Volume只有在下列情况下才能被删除:
- 该容器是用docker rm -v命令来删除的(-v是必不可少的)。
- docker run中使用了–rm参数
- 手动去
/var/lib/docker/volumes/
删除
3. 数据卷和数据卷容器的比较
六、卷插件
数据卷
优点:
- 跟主机磁盘性能一样
- 容器删除后依然保留
缺点:
- 仅限本地磁盘
- 不能随容器迁移
Docker推出了Volume plugin接口机制,让第三方的存储厂商来支持Docker Volume并且在此基础上进行功能拓展。
-
Rancher Convoy:Convoy是Rancher Labs用go开发的支持Device Mapper、NFS、EBS、GlusterFS多种后端存储的Docker Volume plugin driver. Convoy还提供了一个存储拓展功能(如快照、备份恢复等)的接口框架。
-
Flocker:Flocker volume plugin driver主要用于多主机环境Docker数据卷的迁移,从而支持数据库应用等stateful有状态应用的主机间迁移。
最后
本篇文章主要是讲述了Docker的数据存储以及数据管理。
下一篇将讲述Docker的网络。