目录html
咱们都知道 Docker 的数据能够存在容器的可写层,可是也存在如下几点不足:mysql
Docker 提供了三种不一样的方式将数据从 Docker Host 挂载到 Docker 容器,并实现数据的读取和存储:volumes、bind mounts、tmpfs 。nginx
不管咱们选择使用哪一种类型的挂载,数据从容器中看起来都同样的,它在容器的文件系统中做为目录或单个文件展现。sql
咱们能够经过数据存储在 Docker Host 的方式来简单的了解这三种挂载方式的不一样,以下图:docker
全部说使用 volumes 是咱们很是推荐的一种方式。ubuntu
相对于 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 以后,咱们能够统一使用参数--mount
。curl
对于新接触 Docker 的咱们来讲建议使用--mount
,老司机能够继续使用-v
,可是咱们仍是建议使用--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 不会自动建立目录,并生成一个错误。
好比咱们想把 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
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 具有如下几点有点:
咱们推荐使用--mount
,全部这里咱们只写它的使用方法。
Key | Value |
---|---|
type | bind、volume、tmpfs ,如不指定,默认是 volume |
source/src | Docker Host 上的一个文件或者目录 |
destination/dst/target | 被挂载容器上的一个文件或者目录 |
readonly | 没有参数,只写这个词便可 |
volume-opt | 能够指定更多的附加参数 |
使用容器技术,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 里面是否有数据
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 进行测试,咱们知道里面已经存在文件 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 进行挂载的,此次咱们不提早建立,直接指定,看看会出现什么状况。
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。
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 目录 |
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/