Docker存储卷(V18.X)
简介
介绍
Docker的存储卷称之为volume,本质上容器上的一个或者多个目录,而这些目录绕过了联合文件系统,与宿主机中的目录或者其他容器目录进行了绑定关系,这种绑定关系可以看作Linux的mount操作,当容器中的程序对这些目录写入数据时,其实写入到的是与之绑定的宿主机目录上,这样就实现了数据的存储功能。特别说明:本文章所使用的docker版本基于v18.X,对于较早版本的docker并不适合,例如tmpfs类型卷是v17.06新加入的存储卷。
作用
默认情况下,容器不使用任何 volume时,容器的数据被保存在容器之内,它只在容器的生命周期内存在,会随着容器的删除而被删除,而想要持久化的存储这些数据,就得使用存储卷。特别的,保存容器中的数据也可以使用 docker commit 命令将容器提交为一个新的镜像,这个镜像中会保存容器运行时的所有数据(绑定的数据卷除外),但此种方法非常不推荐,因为这样的镜像通常会很大,镜像拉取以及运行容器都会变慢。当容器使用了存储卷,即使容器被删除了,但是与绑定的存储卷还在,对应目录的数据都会保存,如果想要恢复该容器,只要新建的容器绑定该存储卷,对应目录的数据也会随之恢复。
分类
Docker存储卷可分为两类:
- Volumes:数据卷,这类存储卷是被Docker Daemon管理,可使用docker volume create显示创建,被创建出来的卷位于/var/lib/docker/volumes/下,使用时候只需指定使用卷的名称以及对应的容器的一个目录,删除时候只需指定删除卷名即可,这类数据卷是存储数据最为推荐的方式。
- Bind mounts :绑定挂载卷,从早版本docker提供,这类存储卷可以是宿主机的任意目录,也可以是来自其他容器的目录,不受docker Daemon管理,删除时候需要手动清理目录。
- tmpfs mounts:临时挂载卷,该类存储卷数据存放在主机内存中,好处在于这类存储卷由于使用的tmpfs格式文件系统,读写性能好,但同时也增加了主机的内存开销,Docker早期版本不支持此类存储卷。
以下是其示意图:
存储卷使用
--volume&&--mount
存储卷的使用是在docker run命令时候使用-v或者--volume来指明使用的存储卷,但是如果需要指明更多选项如卷类型、驱动等那就需要使用--mount来指明更多的选项。以下是两种选择的使用方法。
-v或--volume选项:[volume_name]:container_path:[options]
解释:
由三个字段组成,用冒号字符(:)分隔,字段必须按正确的顺序排列
volume_name:卷名,可省略,省略的时候默认会分配一个随机目录,在/var/lib/docker/volumes/随机目录/_data。还可以指定宿主机目录。
container_path:绑定的容器中的目录,必须。
options:其他选项,是逗号分隔的选项列表,例如读写模式(ro、rw),可省略。
示例:创建一个绑定挂载卷
--mount:该选项从V17.06加入,由多个键值对组成,用逗号分隔,每个键值由<key>=<value>组成。常用的key如下:
- type:指明卷的类型,三种类型之一bind、volume、tmpfs
- source:可简写为src,指定挂载来源通常是卷名称,匿名卷时忽略该选项
- destination:可简写为dst或target,指定容器中的使用的目录
- readonly:指定卷是否为只读
- volume-opt:其他挂载选项可以多次使用,采用volume-opt=type=nfs
示例:将上述容器使用--mount启动(这里换一个名称为nginx-c2):
使用建议:两个命令能都实现数据卷的挂载,如果是老用户可以继续使用-v的方式来运行容器,如果是新用户推荐使用--mount,这样的语法比较简介明了,比如人性化的各种选项src、type、dest,但是如果需要使用tmpfs类型的数据卷时候必须使用--mount。
使用Volumes
volume类型的数据卷是比较推荐方式,它是能被Docker 管理的卷, 使用流程是先创建卷,在使用-v或--mount进行挂载。如果在docker run时候不指定其宿主机目录,则默认也属于volumes类型,也受Docker管理。
创建卷
[root@app51 ~]# docker volume create data-vol data-vol [root@app51 ~]# docker volume inspect data-vol [ { "CreatedAt": "2019-02-28T17:39:36+08:00", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/data-vol/_data", "Name": "data-vol", "Options": {}, "Scope": "local" } ] [root@app51 ~]#
挂载卷并写入数据
[root@app51 ~]# docker run -it --name bs1 -v data-vol:/data/tmp busybox:latest /bin/sh / # ls /data/tmp/ / # echo "hello world" > /data/tmp/index.html / #
在宿主机上查看:
创建卷是还可以指定其他,列如指定大小、uid等其他选项,这些选项都是mount命令的选项,卷的管理中会介绍:
[root@app51 ~]# docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 test-vol6 test-vol6 [root@app51 ~]# docker run -it --name bs6 -v test-vol6:/data/tmp busybox:latest /bin/sh / # ls /data tmp
上面的挂载命令使用等价--mount:
[root@app51 ~]# docker run -it --name bs7 --mount type=volume,src=test-vol6,dst=/data/tmp busybox:latest /bin/sh / #
容器内可以使用mount命令可以查看挂载点:
使用bind mounts
绑定挂载卷使用,无非指定宿主机的具体某个目录,可以使用-v HOST_PATH:CONTAINER_PATH 也可以使用--mount type=bind,src=HOST_PATH,dst=CONTAINER_PATH。
示例:
[root@app51 ~]# docker run -it --name bind-vol -v "$(pwd)":/data busybox:latest /bin/sh / # ls /data Dockerfile a.txt anaconda-ks.cfg dr.sh nat.sh nginx-bus.tar.gz nginx.tar / #
等价于使用--mount:
[root@app51 ~]# docker run -it --name bind-vol-1 --mount type=bind,src="$(pwd)",dst=/data busybox:latest /bin/sh / # ls /data Dockerfile a.txt anaconda-ks.cfg dr.sh nat.sh nginx-bus.tar.gz nginx.tar / #
以只读的方式挂载:
[root@app51 ~]# docker run -it --name bind-vol-2 --mount type=bind,src="$(pwd)",dst=/data,readonly busybox:latest /bin/sh / # ls /data/ Dockerfile a.txt anaconda-ks.cfg dr.sh nat.sh nginx-bus.tar.gz nginx.tar / # cd /data/ /data # touch 1.txt touch: 1.txt: Read-only file system /data #
查看卷(新开终端):
[root@app51 ~]# docker inspect bind-vol-2 "Mounts": [ { "Type": "bind", "Source": "/root", "Target": "/data", "ReadOnly": true } ],
使用tmpfs mounts
tmpfs类型的卷只适用于Linux系统,命令行中除了使用--mount指定外还可以使用--tmpfs指定其类型。特别注意的,与volume卷和绑定挂载卷相反,tmpfs挂载是临时的,并且仅在主机内存中持久存在,当容器停止时,将删除tmpfs挂载,并且不会保留写在那里的文件。
示例 使用--mount:
[root@app51 ~]# docker run -it --name tmpfs-c1 --mount type=tmpfs,dst=/app busybox:latest /bin/sh / # / # mount |grep /app tmpfs on /app type tmpfs (rw,nosuid,nodev,noexec,relatime)
等价于使用--tmpfs:
[root@app51 ~]# docker run -it --name tmpfs-c2 --tmpfs /app busybox:latest /bin/sh / # mount |grep /app tmpfs on /app type tmpfs (rw,nosuid,nodev,noexec,relatime) / #
查看挂载卷:
[root@app51 ~]# docker inspect tmpfs-c1 "Mounts": [ { "Type": "tmpfs", "Source": "", "Destination": "/app", "Mode": "", "RW": true, "Propagation": "" } ],
注意--mount选项还支持指定挂载目录的大小和权限,选项如下:
tmpfs-size | 设置tmpfs类型卷的大小,默认无限制(即由宿主机内存决定) |
tmpfs-mode | 设置tmpfs类型卷挂载的目录权限,如1770。默认1777 |
指定一个大小为50m,目录选项为1770类似为tmpfs的存储卷:
[root@app51 ~]# docker run -it --name tmpfs-c3 --mount type=tmpfs,dst=/data/tmp,tmpfs-mode=1770,tmpfs-size=50m busybox:latest /bin/sh / # / # / # mo modinfo modprobe more mount mountpoint / # mount |grep /data/tmp tmpfs on /data/tmp type tmpfs (rw,nosuid,nodev,noexec,relatime,size=51200k,mode=1770) / # ls /data/ -l total 0 drwxrwx--T 2 root root 40 Mar 1 01:47 tmp
查看其存储卷信息:
[root@app51 ~]# docker inspect tmpfs-c3 "Mounts": [ { "Type": "tmpfs", "Source": "", "Destination": "/data/tmp", "Mode": "", "RW": true, "Propagation": "" } ],
使用容器数据卷
除了以上数据卷外,还可以直接使用容器中已经挂载的数据卷,使用--volumes-from指定,此时两个容器的数据卷是共享的。从本质上来讲,这两数据卷共同挂载了宿主机上的同一个目录。
示例:创建一个容器share-c1挂载当前目录(确保不要退出)
[root@app51 ~]# docker run -it --name share-c1 -v $(pwd):/data busybox:latest /bin/sh / # ls /data/ Dockerfile a.txt anaconda-ks.cfg backup.tar dr.sh nat.sh nginx-bus.tar.gz nginx.tar / #
在运行一个容器挂载share-c1的卷:
[root@app51 ~]# docker run -it --name share-c2 --volumes-from share-c1 busybox:latest /bin/sh / # ls /data/ Dockerfile a.txt anaconda-ks.cfg backup.tar dr.sh nat.sh nginx-bus.tar.gz nginx.tar / #
存储卷管理
docker存储卷管理通过docker volume命令组实现,v18.09的命令集合如下:
1.创建存储卷
docker volume create [OPTIONS] [VOLUME_NAME]
通常这样创建的卷会保存在宿主机目录/var/lib/docker/volumes/VOLUME_NAME/_data如果不指名 VOLUME则会创建匿名卷,方式等同于在docker run -v不指定宿主机目录。
常用选项:
-o, --opt :指定存储卷挂载选项。常用选项如下:
- type: 挂载文件系统类型,可以是tmpfs、brtfs、甚至是nfs。
- device:挂载的设备,
- o:mount命令挂载选项,其选项可参考这里
示例一:创建一个普通的卷名称为test-vol-1。
[root@app51 test-vol-1]# docker volume create test-vol-1 test-vol-1 [root@app51 test-vol-1]# ls /var/ adm/ crash/ empty/ gopher/ lib/ lock/ mail/ opt/ run/ tmp/ yp/ cache/ db/ games/ kerberos/ local/ log/ nis/ preserve/ spool/ .updated [root@app51 test-vol-1]# ls /var/lib/docker/volumes/test-vol-1/ -l 总用量 0 drwxr-xr-x 2 root root 6 3月 1 11:08 _data [root@app51 test-vol-1]#
示例二:创建一个tmpfs类型的存储卷,名称为test-vol-2
[root@app51 test-vol-1]# docker volume create --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 test-vol-2 test-vol-2 ###挂载test-vol-2 [root@app51 test-vol-1]# docker run -it --name bs-c10 -v test-vol-2:/data busybox:latest /bin/sh / # mount |grep /data tmpfs on /data type tmpfs (rw,relatime,size=102400k,uid=1000) / #
示例三:创建一个nfs类型的存储卷,服务器地址为10.1.210.52,权限读写,目录为/data/tmp,名称为test-vol-3
[root@app51 test-vol-1]# docker volume create --opt type=nfs --opt o=addr=10.1.210.52,rw --opt device=:/data/tmp test-vol-3 test-vol-3
需要注意的是:创建的卷即使创建成功了但是挂载的时候很可能出错,docker不会在创建卷时候检查挂载选项是否符合要求。
2.查看存储卷详情
docker volume inspect [OPTIONS] VOLUME [VOLUME...]
常用选项:
-f, --format :输出格式,基于go模版
示例:
[root@app51 ~]# docker inspect test-vol-1 [ { "CreatedAt": "2019-03-01T11:08:00+08:00", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/test-vol-1/_data", "Name": "test-vol-1", "Options": {}, "Scope": "local" } ]
使用-f:.代表以根开头,Name是一级字段。
[root@app51 ~]# docker inspect test-vol-1 -f {{.Name}} test-vol-1 [root@app51 ~]#
3.查看所有存储卷
docker volume ls [OPTIONS]
常用选项:
-f, --filter 过滤具体存储卷
示例:
[root@app51 ~]# docker volume ls DRIVER VOLUME NAME local test-vol-1 local test-vol-2 local test-vol-3 [root@app51 ~]#
过滤某个卷:
[root@app51 ~]# docker volume ls -f name=test-vol-1 DRIVER VOLUME NAME local test-vol-1
4.删除一个或多个卷
docker volume rm [OPTIONS] VOLUME [VOLUME...]
常用选项:
-f, --force 强制删除存储卷,即使它还在被使用
[root@app51 ~]# docker volume ls DRIVER VOLUME NAME local test-vol-1 local test-vol-2 local test-vol-3 [root@app51 ~]# docker volume rm test-vol-3 test-vol-3 [root@app51 ~]# docker volume ls DRIVER VOLUME NAME local test-vol-1 local test-vol-2 [root@app51 ~]#
5.移除本地未使用的卷
docker volume prune [OPTIONS]
[root@app51 ~]# docker volume prune WARNING! This will remove all local volumes not used by at least one container. Are you sure you want to continue? [y/N] y Deleted Volumes: test-vol-1 Total reclaimed space: 0B [root@app51 ~]#
数据卷的备份与恢复
数据备份
容器中的数据卷备份有两种方式,一是你可以直接备份与之对应的宿主机目录,二则运行一个容器挂载有数据的容器将其备份到与之对应的宿主机目录。这里演示下第二种数据备份方法。
方法:
1.创建备份容器并挂载需要备份的目录,同时挂载宿主机目录进行备份任务。
2.运行备份指令并将备份数据放到挂载的宿主机目录,以下示例为/backup,将数据备份在来/backup就等于备份在了宿主机的$(pwd)目录下,也就是当前目录。
新建一个数据容器并写入数据:
[root@app51 ~]# docker run -it --name data-c1 -v /data busybox:latest /bin/sh / # cd /data/ /data # touch 1.txt /data # echo "hello world" > index.html /data # ls -l total 4 -rw-r--r-- 1 root root 0 Mar 1 07:40 1.txt -rw-r--r-- 1 root root 12 Mar 1 07:40 index.html /data #
创建备份容器同时挂载两个目录进行数据备份:
[root@app51 ~]# docker run --rm --volumes-from data-c1 -v $(pwd):/backup centos tar cvf /backup/backup.tar /data /data/ /data/1.txt /data/index.html tar: Removing leading `/' from member names
查看备份数据:
[root@app51 ~]# ls -l 总用量 220332 -rw-------. 1 root root 1258 1月 16 00:15 anaconda-ks.cfg -rw-r--r-- 1 root root 7 2月 27 10:25 a.txt -rw-r--r-- 1 root root 10240 3月 1 15:42 backup.tar -rw-r--r-- 1 root root 76 2月 27 19:14 Dockerfile -rw-r--r-- 1 root root 578 1月 16 10:41 dr.sh -rw-r--r-- 1 root root 559 1月 16 14:53 nat.sh -rw------- 1 root root 114356736 2月 24 10:56 nginx-bus.tar.gz -rw------- 1 root root 111224320 2月 23 19:18 nginx.tar [root@app51 ~]# tar tvf backup.tar drwxr-xr-x root/root 0 2019-03-01 15:40 data/ -rw-r--r-- root/root 0 2019-03-01 15:40 data/1.txt -rw-r--r-- root/root 12 2019-03-01 15:40 data/index.html
数据恢复
恢复数据的原理与备份类似,也是启动一个容器共享需要备份的目录,同时挂载宿主机的备份目录,最后解压数据到备份目录中。为了演示数据恢复,我将上述容器 data-c1中的data目录中文件全部删除:
[root@app51 ~]# docker run -it --name data-c1 -v /data busybox:latest /bin/sh / # cd /data/ /data # rm -rf * /data #
创建一个恢复容器:
[root@app51 ~]# docker run --rm --volumes-from data-c1 -v $(pwd):/backup centos bash -c "cd / && tar xvf /backup/backup.tar" data/ data/1.txt data/index.html [root@app51 ~]#
再次查看data-c1容器数据:
/data # rm -rf data /data # /data # /data # /data # /data # ls -l total 4 -rw-r--r-- 1 root root 0 Mar 1 07:40 1.txt -rw-r--r-- 1 root root 12 Mar 1 07:40 index.html /data #