docker——5、docker存储卷volume

一、docker volume的集中形态

Docker 采用 AFUS 分层文件系统时,文件系统的改动都是发生在最上面的容器层。在容器的生命周期内,它是持续的,包括容器在被停止后。但是,当容器被删除后,该数据层也随之被删除了。因此,Docker 采用 volume (卷)的形式来向容器提供持久化存储。
1、Docker镜像由多个只读层叠加而成,启动容器时,Docker会加载只读镜像层并在镜像栈顶部添加一个读写层
2、如果运行中的容器修改了现有的一个已经存在的文件,那该文件会将从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏,此即“写时复制(COW)”机制

1.1 无 - 不使用 Docker volume

默认情况下,容器不使用任何 volume,此时,容器的数据被保存在容器之内,它只在容器的生命周期内存在,会随着容器的被删除而被删除。当然,也可以使用 docker commit 命令将它持久化为一个新的镜像。

1.2 Data volume (数据卷)

Why Data Volume?
1、关闭并重启容器,其数据不受影响;但删除Docker容器,则其更改将会全部丢失
2、存在的问题
(1)存储于联合文件系统中,不易于宿主机访问;
(2)容器间数据共享不便
(3)删除容器其数据会丢失
3、解决方案:“卷(volume)”
“卷”是容器上的一个或多个“目录”,此类目录可绕过联合文件系统,于宿主机上的某目录“绑定(关联)”

Data volumes:
Volume于容器初始化之时即会创建,由base image提供的卷中的数据会于此期间完成复制
volume的初衷是独立于容器的生命周期实现数据持久化,因此删除容器之时既不会删除卷,也不会对哪怕未被引用的卷做垃圾回收操作。

Volume types
docker有两种类型的卷,每种类型都在容器中存在一个挂载点,但其在宿主机上的位置有所不同。
(1)使用 “-v 容器内目录” 形式

[[email protected] ~]# docker run --name b2 -it -v /data busybox

#Docker 将本地一个 _data 目录 mount 为容器内的/data目录了

[[email protected] ~]# docker inspect b2
 "Mounts": [
            {
                "Type": "volume",
                "Name": "29ad55fbd35f05a196ab8548ce36ff01a08d628ed3a635c3a7ad5d0326ce5045",
                "Source": "/var/lib/docker/volumes/29ad55fbd35f05a196ab8548ce36ff01a08d628ed3a635c3a7ad5d0326ce5045/_data",
                "Destination": "/data",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }

/var/lib/docker/volumes/29ad55fbd35f05a196ab8548ce36ff01a08d628ed3a635c3a7ad5d0326ce5045/_data目录下新增的内容都会在容器b2的/data目录下出现。在 web 容器被删后,
/var/lib/docker/volumes/29ad55fbd35f05a196ab8548ce36ff01a08d628ed3a635c3a7ad5d0326ce5045/_data目录及其中的内容都还会保留下来,但是,新启动的容器无法再使用这个目录,也就是说,已有的数据不能自动地被重复使用了。

(2)使用 -v 来挂载一个主机上的目录到容器的目录

[[email protected] ~]# docker run --name b2 -it --rm -v /data/volumes/b2:/data busybox
[[email protected] ~]# docker inspect b2
  "Mounts": [
            {
                "Type": "bind",
                "Source": "/data/volumes/b2",
                "Destination": "/data",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
#查看容器b2相关参数
[[email protected] ~]# docker inspect -f {{.Mounts}} b2
[{bind  /data/volumes/b2 /data   true rprivate}]
[[email protected] ~]# docker inspect -f {{.NetworkSettings}} b2
{{ 0d9eba72680bc912a54cefdbe2687bee53f7e98ebb042e91973ff5a3cc84c79b false  0 map[] /var/run/docker/netns/0d9eba72680b [] []} {73965043cbc6f5ee0c5298f601ea7c37d3acec37875680065ad81221bdbf06f8 10.0.0.1  0 10.0.0.2 16  02:42:0a:00:00:02} map[bridge:0xc420152000]}
[[email protected] ~]# docker inspect -f {{.NetworkSettings.IPAddress}} b2
10.0.0.2

主机上的目录可以是一个本地目录,也可以在一个 NFS share 内,或者在一个已经格式化好了的块设备上。
其实这种形式和第一种没有本质的区别,容器内对 /data的操作都会反映到主机上的/data/volumes/b2目录内。只是,重新启动容器时,可以再次使用同样的方式来将/data/volumes/b2 目录挂载到新的容器内,这样就可以实现数据持久化的目标。
(3)Sharing volumes
多个容器的卷使用同一个主机目录,例如:

# docker run -it --name c1 -v /docker/volumes/v1:/data busybox
# docker run -it --name c2 -v /docker/volumes/v1:/data busybox

复制使用其他容器的卷,为docker run命令使用 --volumes-from选项

#docker run -it --name bbox1 -v /docker/volumes/v1:/data busybox
#docker run -it --name bbox2 --volumes-from bbox1 busybox

示例:2个容器共享同一个卷

[[email protected] ~]# docker run --name b2 -it --rm -v /data/volumes/b2:/data busybox
[[email protected] ~]# docker run --name b3 -it --rm -v /data/volumes/b2:/data busybox
[[email protected] ~]# docker inspect -f {{.Mounts}} b3
[{bind  /data/volumes/b2 /data   true rprivate}]

复制使用其他容器的卷

[[email protected] ~]# docker run --name infracon -it -v /data/infracon/volume/:/data/web/html busybox
[[email protected] ~]# docker run --name nginx --network container:infracon --volumes-from infracon -it busybox
[[email protected] ~]#  docker inspect -f {{.Mounts}} infracon
[{bind  /data/infracon/volume /data/web/html   true rprivate}]
[[email protected] ~]#  docker inspect -f {{.Mounts}} nginx
[{bind  /data/infracon/volume /data/web/html   true rprivate}]

1.3 使用 data container

如果要在容器之间共享数据,最好是使用 data container。这种 container 中不会跑应用,而只是挂载一个卷。
比如:
创建一个 data container:

docker create -v /dbdata --name dbstore training/webapp  /bin/true

启动一个 app container:

docker run -d -P --name web3 --volumes-from dbstore training/webapp python app.py

其实,对 web3 这个容器来说,volume 的本质没变,它只是将 dbstore 容器的 /dbdata 目录映射的主机上的目录映射到自身的 /dbdata 目录。

"Mounts": [
            {
                "Name": "5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330",
                "Source": "/var/lib/docker/volumes/5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330/_data",
                "Destination": "/dbdata",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],

但是,其好处是,可以不管其目录的临时性而不断地重复使用它。

1.4 使用 docker volume 命令

Docker 新版本中引入了 docker volume 命令来管理 Docker volume。
(1)使用默认的 ‘local’ driver 创建一个 volume

[email protected]:/home/sammy# docker volume create --name vol1
vol1
[email protected]:/home/sammy# docker volume inspect vol1
[
    {
        "Name": "vol1",
        "Driver": "local",
        "Mountpoint": "/var/lib/docker/volumes/vol1/_data",
        "Labels": {},
        "Scope": "local"
    }
]

(2)使用这个 volume

docker run -d -P --name web4 -v vol1:/volume training/webapp python app.p

结果还是一样的,即将 vol1 对应的主机上的目录挂载给容器内的 /volume 目录。

"Mounts": [
            {
                "Name": "vol1",
                "Source": "/var/lib/docker/volumes/vol1/_data",
                "Destination": "/volume",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],

1.5 Volume 删除和孤单 volume 清理

1.5.1 在删除容器时删除 volume
可以使用 docker rm -v 命令在删除容器时删除该容器的卷。

[email protected]:/home/sammy# docker run -d -P --name web5 -v /webapp training/webapp python app.py
69199905a74cb360935e32f4e99f7f11319f6aa36033a920aa0bae25874f5c69
[email protected]:/home/sammy# docker volume ls
DRIVER              VOLUME NAME
local               5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330
local               838f4dd99721a9445be22a6b42d35e04cb43ad145ecf26107a9025f428587f76
local               vol1
[email protected]:/home/sammy# docker rm -vf web5
web5
[email protected]:/home/sammy# docker volume ls
DRIVER              VOLUME NAME
local               5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330
local               vol1

1.5.2 批量删除孤单 volumes
从上面的介绍可以看出,使用 docker run -v 启动的容器被删除以后,在主机上会遗留下来孤单的卷。可以使用下面的简单方法来做清理:

[email protected]:/home/sammy# docker volume ls -qf dangling=true
244a23f3ab11f17345a68e77f96bb46a8dbaf445760dd86ab0faa07dfbd84236
c864cfac232e8728b1805abc8c363d324124b38e6297544a8cbbf61d883c7e46
f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b
[email protected]:/home/sammy# docker volume rm $(docker volume ls -qf dangling=true)
244a23f3ab11f17345a68e77f96bb46a8dbaf445760dd86ab0faa07dfbd84236
c864cfac232e8728b1805abc8c363d324124b38e6297544a8cbbf61d883c7e46
f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b
[email protected]:/home/sammy# docker volume ls
DRIVER              VOLUME NAME
local               5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330
local               vol1

github 上有很多脚本可以自动化地清理孤单卷,比如:
https://github.com/chadoe/docker-cleanup-volumes/blob/master/docker-cleanup-volumes.sh
https://github.com/meltwater/docker-cleanup
1.6 小结
对以上内容的两点小结:
1.容器内的数据是临时性的,它会随着容器生命周期的结束而消失
默认的 Docker volume (driver = ‘loclal’)不管是哪种形式,本质上都是将容器所在的主机上的一个目录 mount 到容器内的一个目录,因此,它不具备可移植性。
2. Flocker:容器的分布式存储平台
第一部分提到过,原生的 Docker volume 不具备可移植性。于是,出现了Docker 的分布式卷解决方案 Flocker。先来看看 Flocker volume 和 Docker 原生 volume 的对比:
docker——5、docker存储卷volume
启动一个使用 Flocker 卷的容器:

docker run --volume-driver flocker -v flocker-volume:/container/dir --name=container-xyz

它带来的好处包括:
容器的数据会被写入 Flocker 后端存储而不是主机上,因此,在主机出现故障时可以保证数据不丢失
在容器迁移时,Flocker 会自动地将卷从一个 host 移植到另一个 host
Flocker 的结构:
docker——5、docker存储卷volume