更好的阅读体验:点这里 ( www.doubibiji.com
)
7 容器数据卷
什么是容器卷,为什么需要容器卷?
我们在运行容器的时候,产生的数据都是保存在容器内部的。如果使用Docker来运行mysql容器,数据都保存在容器内部,此时删除容器,数据就全部没了!没了!了!比删库跑路还容易,简直离谱他妈给离谱开门——离谱到家了。
所以需要容器卷,将主机的目录挂载到容器内部(宿主机目录相当于一个外部移动硬盘一样),将容器的目录与宿主机的目录进行映射,将数据保存到宿主机上,这样,即使容器被删除,挂载到容器卷中的数据仍然会被保留在宿主机上。使用容器卷既可以实现容器数据的持久化,也可以在容器间共享数据。
目录挂载后,两个目录进行了映射,在宿主机对应目录进行操作,都会反应到容器内的对应目录中;同样,在容器内对挂载的目录进行操作,也会同步到宿主机上,两个文件夹之间的内容始终保持一致。将容器停止,然后修改宿主机的目录,重启容器,还是会将宿主机的内容同步到容器内部。
Docker 提供了三种常见的挂载方式,分别是:绑定挂载(Bind Mounts)、卷挂载(Volume Mounts)和临时文件系统(tmpfs)挂载。下面分别介绍这三种挂载方式的使用。
7.1 绑定挂载
下面使用 Ubuntu 镜像来运行一个容器,使用绑定挂载,将宿主机的目录挂载到容器中。
命令:
# 容器卷使用-v参数
docker run -it --privileged=true -v 宿主机目录:容器内目录 镜像名称
# 例如,运行ubuntu并进行容器卷的挂载:
docker run -it --privileged=true \
-v /home/doubi/my-ubuntu/host_data:/root/docker_data \
--name my-ubuntu ubuntu
shell 命令太长,使用
\
进行换行。
解释一下:
-
--privileged=true
:建议都添加--privileged=true
参数来开启权限,否则可能出现ls: cannot open directory .: Permission denied
的错误。使用该命令,容器内的root拥有真正的root权限,否则容器内的root在外部只是一个普通用户权限,所以导致容器内的root权限不够。 -
-v /home/doubi/my-ubuntu/host_data:/root/docker_data
:表示将宿主机上的/home/doubi/my-ubuntu/host_data
目录挂载到容器内的/root/docker_data
目录,使两个目录进行映射。
运行上面的命令后,如果宿主机没有 /home/doubi/my-ubuntu/host_data
目录,则会自动创建,如果容器内部没有 /root/docker_data
目录,也会自动创建,挂载的路径必须是绝对路径,如果容器内/root/docker_data
目录有内容,内容将被覆盖删除。
-v
参数也是可以使用多个的,进行多个目录挂载的,例如:
docker run -it --privileged=true \
-v /home/doubi/my-ubuntu/host_data:/root/docker_data \
-v /home/doubi/my-ubuntu/config:/root/config \
--name my-ubuntu ubuntu
大多数情况下,我们都使用了绑定挂载的方式。
7.2 卷挂载
卷挂载方式,Docker管理宿主机文件系统的一部分,默认位于 /var/lib/docker/volumes
目录中。
卷挂载有两种方式:具名挂载 和 匿名挂载。
1 具名挂载
下面运行 ubuntu 并使用卷挂载:
docker run -it --privileged=true \
-v ubuntu_data:/root/docker_data \
--name my-ubuntu ubuntu
ubuntu_data
就是容器卷的名称,使用卷挂载,docker 会查看 ubuntu_data
容器卷是否存在,如果不存在就会创建 ubuntu_data
容器卷。docker 会将容器中的目录映射到 ubuntu_data
容器卷。
使用命令 docker volume ls
可以查看 docker 中存在的卷:
可以使用 docker inspect
命令查看容器卷的位置:
容器中对应的目录中的数据会被存储在 /var/lib/docker/volumes/容器卷名/_data
目录中。
我们也可以使用命令创建容器卷,然后在运行容器的时候,使用已存在的卷:
# 创建容器卷
docker volume create 卷名称
# 举个栗子,创建ubuntu_data2容器卷
docker volume create ubuntu_data2
我们可以使用多个容器使用同样的容器卷,实现容器间数据的共享:
# 创建容器1
docker run -it --privileged=true \
-v ubuntu_data:/root/docker_data \
--name my-ubuntu1 ubuntu
# 创建容器2,使用同样的数据卷
docker run -it --privileged=true \
-v ubuntu_data:/root/docker_data \
--name my-ubuntu2 ubuntu
2 匿名挂载
在上面使用命令进行容器卷挂载的时候,是指定了容器卷的名称。还有一种是匿名挂载,也就是只指定容器内部的目录,不指定宿主机的目录。
举个栗子:
docker run -it --privileged=true -v /root/docker_data --name my-ubuntu ubuntu
docker inspect
命令查看匿名挂载的容器的信息:
可以看到,使用匿名挂载,容器卷是被挂载到宿主机的 /var/lib/docker/volumes/xxxx/_data
目录下。
匿名挂载一般很少使用。
7.3 临时文件系统挂载
这种挂载方式,数据只存储在宿主机的内存中,不会写入到宿主机的文件系统中,当容器停止时,挂载的数据会被删除。
例如,使用如下命令可以实现临时文件系统挂载:
docker run --tmpfs /container/path image_name
不过一般不会使用这种方式,就当我没说,忽略。
7.4 查看挂载
使用 docker inspect 容器ID
命可以查看容器挂载的详细信息,里面就包含了容器的挂载信息。
7.5 理解挂载 - 重要
重新来说一下挂载,其实上面说的将宿主机和容器的目录进行映射是不准确的说法。
例如我们在运行容器的时候,使用了绑定挂载,指定了 -v /home/doubi/my-ubuntu/data:/root/docker_data
。
这表示将宿主机的 /home/doubi/my-ubuntu/host_data
目录挂载到容器内部的 /root/docker_data
目录,也就是容器内部的 /root/docker_data
目录变成了指向了宿主机的 /home/doubi/my-ubuntu/host_data
目录的一个链接,或快捷方式。在容器内部将数据保存到 /root/docker_data
目录,数据实际保存到的是宿主机的 /home/doubi/my-ubuntu/host_data
目录。
所以在执行挂载的时候,如果容器内的 /root/docker_data
目录是非空的,那么目录中的文件将会丢失,因为 /root/docker_data
目录变成了一个链接,指向了宿主机的目录的 /home/doubi/my-ubuntu/host_data
目录,显示的自然是宿主机 /home/doubi/my-ubuntu/host_data
目录的内容,如果 /home/doubi/my-ubuntu/host_data
目录是空的,那么容器内的 /root/docker_data
目录也将变成空的。
而卷挂载不一样:如果volume是空的而容器中的目录有内容,那么docker会将容器目录中的内容拷贝到volume中,但是如果volume中已经有内容,则会将容器中的目录覆盖。
7.6 容器卷读写规则
上面在进行容器卷挂载的时候,我们没有指定容器卷的读写规则,那么使用的就是默认规则,也就是说容器内部对挂载的目录具有可读可写权限。
还可以使用容器卷读写规则,限制容器内对挂载目录的读写权限。
命令:
docker run -it --privileged=true -v 宿主机目录:容器内目录 镜像名称
没有指定读写规则,默认是 rw
的读写规则,那么其实是这样的:
docker run -it --privileged=true -v 宿主机目录:容器内目录:rw 镜像名称
如果想限制容器内对挂载目录的操作权限,例如在容器内只能对挂载目录进行只读,那么,可以修改命令为:
# 容器内对挂载目录只能读
docker run -it --privileged=true -v 宿主机目录:容器内目录:ro 镜像名称
# 举例:
docker run -it --privileged=true -v /home/doubi/my-ubuntu/host_data:/root/docker_data:ro --name my-ubuntu ubuntu
ro
表示 readonly
,只读。目录下的内容只能通过宿主机来修改,容器内部是无法操作的,只能读!
一般情况下都是使用默认规则,可读可写。
7.7 容器卷的继承和共享
如果要在多个容器之间共享数据,那么可以使用相同的挂载目录。
举个栗子:
# 运行my-ubuntu1容器
docker run -it --privileged=true -v /home/doubi/my-ubuntu/host_data:/root/docker_data --name my-ubuntu1 ubuntu
# 运行my-ubuntu2容器
docker run -it --privileged=true -v /home/doubi/my-ubuntu/host_data:/root/docker_data --name my-ubuntu2 ubuntu
上面运行了两个 ubuntu 容器,使用的是宿主机相同的挂载目录,这样在宿主机修改目录下的内容,会同时同步到两个容器中;同样,在一个容器内修改目录下的内容,也可以同步到宿主机和其他的容器中。
上面是一种方式,我们还可以使用 --volumes-from
参数,让一个容器继承另一个容器的卷规则。
举个栗子:
# 运行my-ubuntu1容器
docker run -it --privileged=true \
-v /home/doubi/my-ubuntu/host_data:/root/docker_data \
--name my-ubuntu1 ubuntu
# 运行my-ubuntu2容器,继承my-ubuntu1容器的卷规则
docker run -it --privileged=true \
--volumes-from my-ubuntu1 \
--name my-ubuntu2 ubuntu
上面的 my-ubuntu2 容器,使用 --volumes-from
参数继承 my-ubuntu1容器的卷规则,那么他们的挂载将是相同的。
这样在宿主机修改目录下的内容,会同时同步到两个容器中;在一个容器内修改目录下的内容,也可以同步到宿主机和其他的容器中。
这种继承的方式在使用多个容器时更为方便。