目录
一、Docker数据存储简介
我们知道Docker的数据可以存在容器的可写层。但是当容器当容器不再运行时,数据不能持久存储,我们无法从容器外部获得这些数据;并且,Docker的可写层与宿主机(Host)在容器运行时紧密耦合,我们不能轻易地把数据移动到别的地方。
Docker提供了三种方式,把数据从Host挂载到Docker容器中,并实现持久化存储。这三种方式分别是Bind mounts, Volumes,Tmpfs.
- Bind mounts:可以实现将Host中的任意路径挂载到容器中,可以存储在任意位置,非Docker的进程或者容器可能随时对其进行修改,存在一定的风险。
- Volumes:实现将Docker中的某一个volume挂载到容器中,存储在Docker Host文件系统的某一个路径下,默认是/var/lib/docker/volumes,由Docker进行管理,非Docker的进程不能修改该路径下的文件,比较安全。
- Tmpfs:存储在Host系统的内存中,没有在磁盘上进行存储,不能实现持久化。
二、Bind mounts
1.简介
Bind mounts可以将Host上的任意路径进行挂载,包括重要的文件系统路径,因此就存在一个缺点,我们可以通过容器更改Host的文件系统,进行文件的增删改等操作,若我们将文件系统的路径进行挂载时,这会严重影响到系统的安全性,包括那些非Docker进程。
2.如何使用
在单容器情况下,使用Bind mount可以用 -v 或者 --volume,在swarm集群中,我们使用 --mount,在Docker17.06之后,可以统一使用 --mount,这也是非常推荐使用的。
-v 或者 --volume语法
它有三部分组成,使用 :
进行分割,这些字段必须以正确的顺序排列,并且每个字段的含义不明显。
- 第一个字段是 Docker Host 上的一个文件或者目录。
- 第二个字段是将要挂载到容器上的一个文件或者目录。
- 第三个字段是可选的,用来增加一些附加选项,比如 ro,consistent,delegated,cached,z,and Z。
--mount语法
它由一组键值对组成,由,
进行分割
Key | Value |
---|---|
type | bind、volume、tmpfs,如不指定,默认是 volume |
source或者src | Docker Host 上的一个文件或者目录 |
destination或者dst或者target | 被挂载容器上的一个文件或者目录 |
readonly | 默认可读可写,加上readonly表示是只读的文件系统 |
bind-propagation | rprivate、private、rshared、shared、rslave、slave |
两者的区别
使用-v
的时候,如果在 Docker Host 不存在要挂载的文件或者目录,Docker 将会自动进行创建,通常是一个目录。
使用--mount
的时候,如果在 Docker Host 不存在要挂载的文件或者目录,Docker 不会自动创建目录,并生成一个错误
3.使用场景
- 把 host 中的配置文件共享给 host 上面的容器。容器为什么自带 DNS 解析呢,那是因为默认情况下 host 把 /etc/resolv.conf 挂载到它上面的容器里面。
- 在 Docker Host 上面的开发环境和容器直接共享程序的源代码或者构建要素。例如,你可以挂载一个 Maven 目录到一个容器中,每当你在 Docker Host 重新建立 maven 项目,容器都可以直接获取你重新构建的 maven 项目。
- 我们可以将源代码目录挂载到容器中,在 host 中修改代码就能看到应用的实时效果。
- 将 mysql 容器的数据放在 bind mount 里,这样 host 可以方便地备份和迁移数据。
- 只需要向容器添加文件,不希望覆盖整个目录。
4.动手实践
在下面的例子中,我们使用nginx镜像来创建容器,进行举例说明,若没有该镜像先获取,默认使用latest标签。
docker pull nginx
在Host上创建目录(因为通过--mount进行挂载,要求我们在Host上先要有这个目录)
mkdir /root/sample/html
运行容器
docker run -d -it --name devtest -p 80:80 --mount type=bind,source=/root/sample/html,target=/usr/share/nginx/html nginx:latest
使用docker inspect xx 查看容器是否正确挂载,Source为宿主机上的路径,Destination为容器上的路径,RW表示是可读可写的模式,也是默认的模式。
docker inspect devtest
进入到容器中可以看到目录为空(因为Host中的相应的路径下没有内容)
docker exec -it devtext bash
在html目录下创建文件index.html并写入内容:This is a bind mount test!
vim index.html
可以通过暂停容器、销毁容器验证文件是否还存在。从下面的图中我们可以看到,关闭容器后,便不能访问80端口了;然而在移除容器之后,index.html还在容器中,因此可以实现持久化。
docker stop devtest
docker rm devtest
以上为默认的读写方式运行容器。
若我们仅仅是想读取Host上的文件,而不想修改其中的内容,我们可以将默认的读写模式修改为只读模式:
docker run -d -it --name devtest -p 80:80 --mount type=bind,source=/root/sample/html,target=/usr/share/nginx/html,readonly nginx:latest
然后我们进入容器测试一下
docker exec -it devtest bash
echo "readonly test" > /usr/share/nginx/html/index.html
在外部修改文件进行测试:
echo "Host write" >> /root/sample/html/index.html
cat /root/sample/html/index.html
内部查看
最后还有一种单文件挂载的方式,但意义不是很大。
docker run -d -it --name devtest -p 80:80 --mount type=bind,source=/root/sample/html/index.html,target=/usr/share/nginx/html/index.html nginx:latest
三、Volume
1.详细介绍
- Volume 完全由 Docker 来进行管理,比如 volume 的创建,我们可以使用命令
docker volume create
来简单的创建一个 volume,当容器或者服务创建的时候,Docker 也可以自动的创建一个 volume。- 当我们创建了一个 volume,它存储在 Docker Host 的存储目录下,并有Docker进行管理。
- 一个给定的 volume 可以同时挂载到多个容器中。当没有容器使用 volume 时, volume 对 Docker 仍然是可用的并且不会被自动删除,使用docker volume rm 可以对volume进行删除
- 我们在挂载 volume 时,可以对其命名,也可以是默认随机生成的名字。如果我们没有指定名称,当 volume 第一次挂载到一个容器时,Docker 会用一个随机字符串对其进行命名,这样可以保证 volume 在 Docker Host 的唯一性。
- Volume 还支持使用 volume drivers,它允许您将数据存储挂载到远程主机或云提供商上等。
volume对比bind方式的优点如下:
- Volumes 的备份和迁移更加容易。
- 可以使用 Docker CLI 或者 Docker API 管理 volumes。
- Volumes 既可以在 Linux 的容器中使用,也可以在 Windows 的容器中使用。
- Volumes 在多容器中共享更加的安全。
- Volume drivers 允许我们把数据存储在远程主或云提供商。
2.使用场景
- 当我们需要在多个正在运行的容器之间共享数据时,我们需要volume 。如果我们没有明确指定创建它,那么它第一次装入容器时就会创建一个 volume。当容器停止或删除掉,volume 仍然存在。多个容器可以同时读写一个 volume。只有当我们明确指定要删除某个 volume 时,它才会被删除。
- 当我们需要把容器的数据永久存储在一个远程主机或者一个云服务器上,我们需要 volume。
- 当我们的 Docker Host 无法保证可以提供一个目录或者文件来作为数据存储时,我们也需要 volume,它可以减少我们对配置文件的依赖。
- 当我们需要备份数据,或者恢复数据,以及需要把数据从一个 Docker Host 迁移到另外一个 Docker Host 的时候,volume 是我们最好的一个选择,我们可以停掉正在使用 volume 的容器,然后把 volume 的目录备份下来即可,volume 的目录一般在
/var/lib/docker/volumes/
下。
3.使用举例
在使用volume进行持久化时,我们先要创建一个volume,可以通过以下的命令查看volume的具体信息:
docker volume create my_vol
docker volume inspect my_vol
我们刚刚创建了一个名为my_vol的volume,可以进入到以上的路劲中查看,可以看到是一个空的卷,然后我们用这个空的volume启动一个容器。
ll /var/lib/docker/volumes/my_vol/_data
docker run -d -it --name devtest -p 80:80 --mount source=my_vol,target=/usr/share/nginx/html nginx:latest
通过docker inspect devtest查看容器的详细情况。
然后,我们再次查看Host上的volume所在的文件夹,发现多了两个文件,因为我们用一个空的volume来启动容器时,会将容器中的内容复制到我们的Host上。
ll /var/lib/docker/volumes/my_vol/_data
访问80端口,可以看到能访问到nginx的欢迎页
curl localhost
删除容器,可以看到volume仍然还在。
下面我们用有数据的volume来启动容器,看看会有什么效果。我们对my_vol里面的index.html进行修改,并删除50x.html
echo "This is a volume mount test" > /var/lib/docker/volumes/my_vol/_data/index.html
rm -f 50x.html
然后启动容器,并直接访问容器
docker run -d -it --name devtest -p 80:80 --mount source=my_vol,target=/usr/share/nginx/html nginx:latest
我们可以看到,用一个现有的volume也是可以进行挂载的,并且将文件挂载到容器中。
最后,我们看一下没有提前创建volume,会是什么情况。
我们直接启动容器,其中的source=my_vol2,这个volume在我们的Host上并没有提前创建。
docker run -d -it --name devtest -p 80:80 --mount source=my_vol2,target=/usr/share/nginx/html nginx:latest
然后我们查看volume的情况
docker volume ls
docker volume inspect my_vol
可以看到自动给我们创建了一个名为my_vol2的卷,并且其存放位置也是/var/lib/docker/volumes,因此也是受到docker管理的。
查看数据情况,能看到将容器中的文件直接复制到了Host上。
ll /var/lib/docker/volumes/my_vol2/_data
和bind mount方式相同,若我们需要将读写模式改为只读模式,只需加上readonly标识符即可。
docker run -d -it --name devtest -p 80:80 --mount source=my_vol2,target=/usr/share/nginx/html,readonly nginx:latest
下面对bind mount和volume两种方式做一个对比
不同点 | bind mount | volume |
---|---|---|
Source位置 | 可以任意指定 | /var/lib/docker/volumes/... |
Host源地址为空 | 覆盖掉容器的内容 | 容器内数据复制到 volume |
是否支持单个文件 | 支持 | 不支持,只能是目录 |
权限控制 | 读写或者只读 | 读写或者只读 |
移植性 | 弱,与 host path 绑定 | 强,无需指定 host 目录 |
四、Tmpfs
Tmpfs方式将数据存储在Host内存中,在容器的整个生命周期内被容器使用,不能持久化。出于安全原因,或者是提升容器的性能,比如我们的程序需要写入很多不需要存储的状态数据时,我们就会使用 tmpfs。
使用的语法和以上两者类似,主要是将type指定为tmpfs
Key | Value |
---|---|
type | bind、volume、tmpfs,如不指定,默认为 volume |
destination(或者dst/target) | 容器中的路径 |
tmpfs-type(或者tmpfs-mode) | 附加参数 |
使用举例
docker run -d -it --name devtest -p 80:80 --mount type=tmpfs,destination=/usr/share/nginx/html nginx:latest
docker inspect devtest