Docker 持久存储介绍(十三)

1、Docker 数据存储

咱们都知道 Docker 的数据能够存在容器的可写层,可是也存在如下几点不足:mysql

  1. 当该容器再也不运行时,数据将不会持久存储,若是另外一个进程须要它,就很难将数据从容器中取出。
  2. 容器的可写层与 Docker Host 在容器运行时紧密耦合,你不能轻易地把数据移到别的地方。
  3. 写入容器的可写层须要一个 storage driver 来管理。storage driver 使用 Linux 内核提供一个文件系统。与使用直接写入宿主文件系统的 volume 相比,这种额外的抽象下降了性能。

Docker 提供了三种不一样的方式将数据从 Docker Host 挂载到 Docker 容器,并实现数据的读取和存储:volumes、bind mounts、tmpfs 。nginx

不管咱们选择使用哪一种类型的挂载,数据从容器中看起来都同样的,它在容器的文件系统中做为目录或单个文件展现。sql

咱们能够经过数据存储在 Docker Host 的方式来简单的了解这三种挂载方式的不一样,以下图:docker

  • Volumes 存储在 Docker Host 文件系统的一个路径下,这个路径是由 Docker 来进行管理,路径默认是 /var/lib/docker/volumes/,非 Docker 的进程不能去修改这个路径下面的文件,因此说 Volumes 是持久存储数据最好的一种方式。
  • Bind mounts 能够存储在 Docker Host 文件系统的任何位置,它们甚至多是重要的系统文件或目录,非 Docker 的进程或者 Docker 容器可能随时对其进行修改,存在潜在的安全风险。
  • Tmpfs 只存储在 Docker Host 的系统内存中,不会写入到系统的文件系统中,不会持久存储。

全部说使用 volumes 是咱们很是推荐的一种方式。ubuntu

2、Bind mount

一、详细介绍

相对于 volume,bind mount 具备有限的功能。咱们使用 bind mount 时,host 上的文件或目录被挂载到容器中。挂载时须要咱们指定文件或目录在 host 上的完整路径。安全

bind mount 是很是高效的,但它依赖 host 的文件系统的目录结构。若是打算部署新的 Docker 应用,咱们能够考虑使用 volume 而命名,否则你不能使用 Docker CLI 命令直接管理 bind mount。bash

使用 bind mount 的一个缺点是,咱们能够经过在容器中运行的进程更改 host 的文件系统,包括建立、修改或删除重要的系统文件或目录。这会严重影响系统的安全,甚至影响 host 上面非 Docker 的进程。服务器

二、如何使用

以前咱们使用 bind mount 可使用-v或者--volume,这个参数在单容器的状况下使用,在 swarm 集群中使用--mount,从 Docker 17.06 以后,咱们能够统一使用参数--mountcurl

对于新接触 Docker 的咱们来讲建议使用--mount,老司机能够继续使用-v,可是咱们仍是建议使用--mount

-v or --volume 语法

它有三部分组成,使用:进行分割,这些字段必须以正确的顺序排列,而且每一个字段的含义不明显。

  • 第一个字段是 Docker Host 上的一个文件或者目录。
  • 第二个字段是将要挂载到容器上的一个文件或者目录。
  • 第三个字段是可选的,用来增长一些附加选项,好比 ro,consistent,delegated,cached,z,and Z。

--mount 语法

它由一组键值对组成,由,进行分割,每一个值为 <key>=<value>

Key Value
type bind、volume、tmpfs,如不指定,默认是 volume
source/src Docker Host 上的一个文件或者目录
destination/dst/target 被挂载容器上的一个文件或者目录
readonly 没有参数,只写这个词便可
bind-propagation rprivate、private、rshared、shared、rslave、slave
consistency consistent、delegated、cached,只在 Mac 系统上生效

二者区别

使用-v的时候,若是在 Docker Host 不存在要挂载的文件或者目录,Docker 将会自动进行建立,一般是一个目录。
使用--mount的时候,若是在 Docker Host 不存在要挂载的文件或者目录,Docker 不会自动建立目录,并生成一个错误。

三、使用场景

  1. 把 host 中的配置文件共享给 host 上面的容器。容器为何自带 DNS 解析呢,那是由于默认状况下 host 把 /etc/resolv.conf 挂载到它上面的容器里面。
  2. 在 Docker Host 上面的开发环境和容器直接共享程序的源代码或者构建要素。例如,你能够挂载一个 Maven 目录到一个容器中,每当你在 Docker Host 从新创建 maven 项目,容器均可以直接获取你从新构建的 maven 项目。
  3. 咱们能够将源代码目录 mount 到容器中,在 host 中修改代码就能看到应用的实时效果。
  4. 将 mysql 容器的数据放在 bind mount 里,这样 host 能够方便地备份和迁移数据。
  5. 只须要向容器添加文件,不但愿覆盖整个目录。

四、使用案例

存在目录 bind mount

好比咱们想把 Docker Host 的目录source/html/挂载到 nginx 容器的/usr/share/nginx/html/,咱们对 html 目录的更改但愿能够马上在容器 html 目录生效,这样咱们就能够很是方便的修改网页的文件了。

docker run -d \
  -it \
  --name devtest \
  -p 80:80 \
  --mount type=bind,source="$(pwd)"/html,target=/usr/share/nginx/html/ \
  nginx:latest

使用命令docker inspect devtest来查看挂载是否正确挂载。

"Mounts": [
            {
                "Type": "bind",
                "Source": "/root/sample/html",
                "Destination": "/usr/share/nginx/html",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],

从里面能够出这是一个 bind mount,而且是只读挂载。

咱们在 html 目录下建立一个 index.html,而且写入内容为“This is a bind mount test!”。而且访问本地的 80 端口查看结果。

从这里例子之中咱们能够看出若是咱们挂载到容器的目录中有文件,文件会被咱们的源地址文件进行覆盖。

咱们把容器销毁掉,查看一下咱们建立的文件是否还存在。

docker stop devtest
docker rm devtest

咱们发现文件仍是存在的,可见,即便容器没有了,bind mount 也还在。这也合理,bind mount 是 host 文件系统中的数据,只是借给容器用用,哪能随便就删了啊。

只读挂载

另外,bind mount 时还能够指定数据的读写权限,默认是可读可写,可指定为只读:

docker run -d \
  -it \
  --name devtest \
  -p 80:80 \
  --mount type=bind,source="$(pwd)"/html,target=/usr/share/nginx/html/,readonly\
  nginx:latest

查看挂载详情,看看是否是只读模式。

"Mounts": [
            {
                "Type": "bind",
                "Source": "/root/sample/html",
                "Destination": "/usr/share/nginx/html",
                "Mode": "",
                "RW": false,
                "Propagation": "rprivate"
            }
        ],

咱们命令docker exec -ti devtest bash进入到容器内部,修改文件测试一下。

readonly 设置了只读权限,在容器中是没法对 bind mount 数据进行修改的。只有 host 有权修改数据,提升了安全性。

单文件挂载

除了制定目录外,咱们也能够指定单个文件进行覆盖,以下:

docker run -d \
  -it \
  --name devtest \
  -p 80:80 \
  --mount type=bind,source="$(pwd)"/html/index.html,target=/usr/share/nginx/html/index.html \
  nginx:latest

3、Volume

一、详细介绍

Volume 彻底由 Docker 来进行管理,好比 volume 的建立,咱们可使用命令 docker volume create 来简单的建立一个 volume,当容器或者服务建立的时候,Docker 也能够自动的建立一个 volume。

当咱们建立了一个 volume,它存储在 Docker Host 的存储目录下。当咱们把 volume 挂载入容器时,此目录就是挂载到容器中的目录。这相似于 bind mount 的工做方式,不一样的是 volume 是由 Docker 来管理而且和 Docker Host 的核心功能进行隔离。

一个给定的 volume 能够同时挂载到多个容器中。当没有容器的使用 volume 时, volume 对 Docker 仍然是可用的而且不会被自动删除。咱们可使用命令docker volume prune来删除一个已经不使用的 volume。

咱们在挂载 volume 时,能够对其命名,也能够是默认随机生成的名字。若是咱们没有指定名称,当 volume 第一次挂载到一个容器时,Docker 会用一个随机字符串对其进行命名,这样能够保证 volume 在 Docker Host 的惟一性。

Volume 还支持使用 volume drivers,它容许您将数据存储挂载到远程主机或云提供商上等。

Volumes 对比 bind mounts 具有如下几点有点:

  • Volumes 的备份和迁移更加容易。
  • 可使用 Docker CLI 或者 Docker API 管理 volumes。
  • Volumes 既能够在 Linux 的容器中使用,也能够在 Windows 的容器中使用。
  • Volumes 在多容器中共享更加的安全。
  • Volume drivers 容许咱们把数据存储在远程主或云提供商。

二、使用语法

咱们推荐使用--mount,全部这里咱们只写它的使用方法。

Key Value
type bind、volume、tmpfs ,如不指定,默认是 volume
source/src Docker Host 上的一个文件或者目录
destination/dst/target 被挂载容器上的一个文件或者目录
readonly 没有参数,只写这个词便可
volume-opt 能够指定更多的附加参数

三、使用场景

使用容器技术,volume 是最推荐的一种持久存储数据的方式。volume 的一些使用场景以下:

  • 当咱们须要在多个正在运行的容器之间共享数据时,咱们须要volume 。若是咱们没有明确指定建立它,那么它第一次装入容器时就会建立一个 volume。当容器中止或删除掉,volume 仍然存在。多个容器能够同时读写一个 volume。只有当咱们明确指定要删除某个 volume 时,它才会被删除。
  • 当咱们须要把容器的数据永久存储在一个远程主机或者一个云服务器上,咱们须要 volume。
  • 当咱们的 Docker Host 没法保证能够提供一个目录或者文件来做为数据存储时,咱们也须要 volume,它能够减小咱们对配置文件的依赖。
  • 当咱们须要备份数据,或者恢复数据,以及须要把数据从一个 Docker Host 迁移到另一个 Docker Host 的时候,volume 是咱们最好的一个选择,咱们能够停掉正在使用 volume 的容器,而后把 volume 的目录备份下来便可,volume 的目录通常在 /var/lib/docker/volumes/<volume-name>下。

四、使用案例

不像 bind mount,咱们首先须要建立一个 volume。

docker volume create my_vol

列出 volumes:

root@ubuntu:~# docker volume ls
DRIVER              VOLUME NAME
local               my_vol

查看指定卷的详细信息:

root@ubuntu:~# docker volume inspect my_vol
[
    {
        "CreatedAt": "2017-12-07T10:27:26+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my_vol/_data",
        "Name": "my_vol",
        "Options": {},
        "Scope": "local"
    }
]

删除卷:

docker volume rm my_vol

使用无数据 volume 启动容器

咱们查看一下刚刚建立的 volume 里面是否有数据

root@ubuntu:~# ll /var/lib/docker/volumes/my_vol/_data
total 8
drwxr-xr-x 2 root root 4096 Dec  7 10:27 ./
drwxr-xr-x 3 root root 4096 Dec  7 10:27 ../

咱们看到里面并无数据,那咱们启动容器查看一下。

docker run -d \
  -it \
  -p 80:80 \
  --name devtest \
  --mount source=my_vol,target=/usr/share/nginx/html \
  nginx:latest

使用命令docker inspect devtest查看一下挂载详情。

"Mounts": [
            {
                "Type": "volume",
                "Name": "my_vol",
                "Source": "/var/lib/docker/volumes/my_vol/_data",
                "Destination": "/usr/share/nginx/html",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],

咱们再次查看一下 Source 目录。

root@ubuntu:~# ll /var/lib/docker/volumes/my_vol/_data
total 16
drwxr-xr-x 2 root root 4096 Dec  7 09:33 ./
drwxr-xr-x 3 root root 4096 Dec  6 20:18 ../
-rw-r--r-- 1 root root  537 Nov 21 22:28 50x.html
-rw-r--r-- 1 root root  612 Nov 21 22:28 index.html

咱们能够看到 volume 的内容跟容器原有 /usr/share/nginx/html 彻底同样,由于咱们挂载的 volume 是刚刚建立没有数据的,容器原有的数据会被复制到 volume 中,咱们一样的能够对其进行修改操做,直接反映到容器中。

咱们删掉容器查看一下 volume 的数据是否被删除。

docker stop devtest
docker rm devtest

再次进行查看。

root@ubuntu:~# ll /var/lib/docker/volumes/my_vol/_data
total 16
drwxr-xr-x 2 root root 4096 Dec  7 10:13 ./
drwxr-xr-x 3 root root 4096 Dec  6 20:18 ../
-rw-r--r-- 1 root root  537 Nov 21 22:28 50x.html
-rw-r--r-- 1 root root   31 Dec  7 10:13 index.html

咱们能够看到,数据没有被删除。

使用有数据 volume 启动容器

咱们接着上个 volume 进行测试,咱们知道里面已经存在文件 index.html,咱们更改里面的内容,而且把 50x.html 删掉。

root@ubuntu:~# ll /var/lib/docker/volumes/my_vol/_data
total 12
drwxr-xr-x 2 root root 4096 Dec  7 10:16 ./
drwxr-xr-x 3 root root 4096 Dec  6 20:18 ../
-rw-r--r-- 1 root root   31 Dec  7 10:13 index.html
root@ubuntu:~# cat /var/lib/docker/volumes/my_vol/_data/index.html   
This is a volume mount test!

确认好 volume 以后,咱们启动容器。

docker run -d \
  -it \
  -p 80:80 \
  --name devtest \
  --mount source=my_vol,target=/usr/share/nginx/html \
  nginx:latest

而后访问一下容器。

root@ubuntu:~# curl localhost
This is a volume mount test!

咱们能够看到,当咱们的 volume 里面有数据的时候,容器内的数据就被 volume 覆盖了,一样的,当咱们删除容器以后,volume 里面的数据会依然存在的。

不提早建立 volume 启动容器

以前的状况都说咱们提早建立好 volume 进行挂载的,此次咱们不提早建立,直接指定,看看会出现什么状况。

docker run -d \
  -it \
  -p 80:80 \
  --name devtest \
  --mount source=my_vol2,target=/usr/share/nginx/html \
  nginx:latest

咱们能够看到,也建立成功了,此次咱们对 volume 的命名为 my_vol2。

咱们使用名称查看一下 volume 的状况。

root@ubuntu:~# docker volume  ls
DRIVER              VOLUME NAME
local               15a731acddee296080b56ddd5faf27748bdfbc422ce2e6b9574ca755e878f434
local               aaf128a2672ffe8994bd080c83bcd4540796a47fd404a8c85e2e1f48f3086855
local               e157aff9c4db4bcf935484b99f119dbe8faeac2de6408197f25b6f1ea798975c
local               my_vol
local               my_vol2

咱们能够看到 Docker 给咱们自动建立了一个 volume,那咱们使用docker inspect devtest查看一下挂载详情。

"Mounts": [
            {
                "Type": "volume",
                "Name": "my_vol2",
                "Source": "/var/lib/docker/volumes/my_vol2/_data",
                "Destination": "/usr/share/nginx/html",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],

咱们猜想,这种状况应该和第一种空 volume 挂载相似,volume 里面的内容应该是容器复制过来的,咱们查看一下是否这样的。

root@ubuntu:~# ll /var/lib/docker/volumes/my_vol2/_data
total 16
drwxr-xr-x 2 root root 4096 Dec  7 10:38 ./
drwxr-xr-x 3 root root 4096 Dec  7 10:38 ../
-rw-r--r-- 1 root root  537 Nov 21 22:28 50x.html
-rw-r--r-- 1 root root  612 Nov 21 22:28 index.html

状况确实如咱们所说,从这里咱们能够看出,若是使用 bind mount,咱们的源目录必须存在,否则docker 会报错,然而咱们使用 volume,若是源不存在,docker 会为咱们进行建立。

这是由于 bind mount 挂载的路径并非 docker 进行管理的,他没有权限随便建立目录,而后 volume 是 docker 进行管理的,它能够在本身的存储目录下面建立 volume。

当咱们想把容器内的数据导出来时,使用这种方式很是方便。

只读模式挂载 volume

在某些状况下,咱们使用多容器进行挂载的时候,咱们不容许容器对 volume 里面的数据进行修改,这样能够保证全部的容器挂载的是相同的 volume。

docker run -d \
  -it \
  -p 80:80 \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \
  nginx:latest

使用命令docker inspect nginxtest查看一下挂载状况。

"Mounts": [
            {
                "Type": "volume",
                "Name": "nginx-vol",
                "Source": "/var/lib/docker/volumes/nginx-vol/_data",
                "Destination": "/usr/share/nginx/html",
                "Driver": "local",
                "Mode": "z",
                "RW": false,
                "Propagation": ""
            }
        ],

一样的咱们能够看到容器的数据被复制到了 volume 里面,咱们进入容器,修改文件看看。

root@ubuntu:~# docker exec -ti nginxtest bash
root@28f1d32e08be:/# echo "nginxtest" > /usr/share/nginx/html/index.html 
bash: /usr/share/nginx/html/index.html: Read-only file system

能够看到,容器内部是不能修改 volume 里面的数据的。

到这里咱们简单对比一下 bind mount 和 volume 的不一样点。

不一样点 bind mount volume
Source位置 能够任意指定 /var/lib/docker/volumes/...
空 source 覆盖掉容器的内容 容器内数据复制到 volume
是否支持单个文件 支持 不支持,只能是目录
权限控制 读写或者只读 读写或者只读
移植性 弱,与 host path 绑定 强,无需指定 host 目录

4、tmpfs

一、详细介绍

tmpfs 不在磁盘上持久存储,也不在 Docker Host 容器里面存储,他存储在 host 的内存中,它能够在容器的整个生命周期内被容器所使用。

二、使用场景

当你不须要持久保留数据在 host 或容器内。这多是出于安全缘由,或者是提高容器的性能,好比咱们的程序须要写入不少不须要存储的状态数据时,咱们就会使用 tmpfs。

三、使用语法

一样的,咱们能够在单容器的状况下使用--tmpfs,而且不能指定参数,在集群的状况下使用--mount,能够指定一些参数,具体以下:

Key Value
type bind、volume、tmpfs,如不指定,默认为 volume
destination/dst/target 容器中的路径
tmpfs-type/tmpfs-mode 一些附加参数

四、使用案例

使用 tmpfs 启动容器。

docker run -d \
  -it \
  -p 80:80 \
  --name tmptest \
  --mount type=tmpfs,destination=/usr/share/nginx/html \
  nginx:latest

容器对目录全部的读写操做都在内存中。

咱们也能够指定 tmpfs 的权限状况。

docker run -d \
  -it \
  -p 80:80 \
  --name tmptest \
  --mount type=tmpfs,destination=/usr/share/nginx/html,tmpfs-mode=1770 \
  nginx:latest

参考文档:https://docs.docker.com/engine/admin/volumes/

相关文章
相关标签/搜索