数据卷
在前面使用Docker时,可能会遇到以下几个问题:
- 当Docker 里的容器挂了以后打不开,这时候只有删除该容器了,但删除容器会连容器中的产生的数据也一起删除了,大部分场景下这是不能接受的。
- Docker容器与容器之间不能直接交换数据。
- 容器和宿主机之间不能共享文件数据,如以nginx容器为例,需要修改默认页面内容时,需要先进入容器内部,再修改对应的index.html文件,极其不方便。
举个例子,假设我们的应用程序需要一个 MongoDB 数据库,因此,我们运行一个mongo容器:
docker run -d mongo
现在,如果容器被删除(无论是有意还是无意),我们将丢失 MongoDB 数据库中的所有数据。
这不是我们想要的。我们可能需要将数据持久地存储在数据库中,即使容器被删除了。
基于以上痛点,出现了容器数据卷的技术。
数据卷是宿主机中的一个目录或者文件,当容器目录挂载到宿主机的某个目录之后,宿主机的这个目录就成为了一个数据卷,这样容器和宿主机之间的文件就会同步。容器删除之后,容器产生的数据还会保留在宿主机上。这就解决了以上的问题。
数据卷类型
Docker 主要提供了两种不同的方式将数据从 Docker Host 挂载到 Docker 容器,来实现数据持久性:
- 绑定挂载(Bind Mount)
- 卷(Volumes)
Docker 卷主要有两种类型:
- 匿名卷
- 命名卷
中有三种数据卷类型:Bind Mount、匿名数据卷、命名数据卷,一般推荐大家使用 Bind Mount数据卷 方式持久化数据。
使用容器数据卷非常简单,只需在使用docker run命令运行容器的时候加上 -v 选项即可。
Bind Mount
bind mount自docker早期便开始为人们使用了,用于将host上的文件或目录被挂载到容器中。挂载时需要我们指定文件或目录在 host 上的完整路径。
docker run -d -v 主机目录:容器目录 镜像名称
通过这种方式运行命令后,会在宿主机上自动创建相应的目录,在宿主机上该目录下创建或者修改文件都会自动同步到容器中去。
下面通过Bind Mount形式启动一个nginx容器,并将宿主机目录/Users/qin/docker/volume/nginx_90映射到容器的/usr/share/nginx/html/目录。
进入到容器,查看/usr/share/nginx/html目录,发现容器内文件和宿主机内文件一致。
下面修改宿主机的index.html文件中v1改成v2,容器内对应的index.html文件内容也同步更改了:
删除容器后,数据卷还存在,这样保证了容器中相关数据的持久化。
需要注意的是:使用Bind Mount,宿主机文件夹将覆盖容器文件夹内容,因此如果宿主机文件夹为空,需要先将容器中要映射的文件复制到宿主机文件夹中。
Volumes
匿名数据卷
下面介绍如何通过匿名数据卷形式启动一个nginx容器,并将容器的/usr/share/nginx/html/目录映射到宿主机指定目录。
下面指令中的/usr/share/nginx/html是容器中的的路径,启动成功后docker 会将匿名卷映射到宿主机的某个途径,可以通过docker inspect查看到这个路径。
qin@linux-01:~$ docker run -itd -p 82:80 -v /usr/share/nginx/html --name nginx_82_anonymous_volume nginx
匿名数据卷的优点在于,它们不需要用户手动管理。如果容器启动时使用了 --rm 选项,容器停止时,容器被自动删除,匿名卷也会自动被删除。(注意直接通过docker rm删除容器匿名卷不会被删除,只有通过docker stop停止容器才会将匿名卷删除)
但是如果容器启动时没有–rm选项,即使使用命名 docker stop/rm my_container 停止了容器,匿名卷不会被自动删除。
命名数据卷
与匿名卷相反,命名数据卷不会被附加到具体容器上,如果容器被删除,命名卷会保留。
qin@linux-01:~$ docker run -itd --rm -p 86:80 -v nginx_name_volume:/usr/share/nginx/html --name nginx_86_name_volume nginx
此外需要注意的是不能在 Dockerfile 内创建命名卷,只能在运行容器时使用-v 选项指定。
卷和绑定挂载的读写权限
默认情况下,卷和绑定挂载都是可读写的,这意味着容器不仅可以读取主机上挂载的数据,还可以修改这些数据。
有时这可能不是我们所希望的,在某些情况下,你可能希望容器只具备读取权限,而不允许进行写入。
可以在 Docker 命令中的挂载选项后面添加:ro来指定挂载为只读。
例如,假设你有一个本地目录/path/to/local/html,你想把它挂载到容器内的/usr/share/nginx/html目录,并且希望这个挂载是只读的,你可以使用以下命令:
docker run -v /path/to/local/html:/usr/share/nginx/html:ro -d nginx
这条命令中的:ro确保了容器只能读取/usr/share/nginx/html中的内容,而不能修改它。
同样,如果你使用的是命名卷,你可以这样挂载:
docker run -v nginx-vol:/usr/share/nginx/html:ro -d nginx
多个容器间共享数据
另一个与持久性相关的问题是如何在多个容器之间共享文件/目录。
使用绑定挂载,容器只能在单个主机/机器内共享文件。
相比之下,使用卷可以实现更灵活的文件共享。通过卷,来自不同主机的容器可以共享底层数据。例如,可以使用 NFS(网络文件系统)或 S3(Amazon Simple Storage Service)等解决方案,让多个主机上此类的容器访问同一份数据。
案例
最后让我们看看如何使用绑定挂载和卷解决我们之前讨论过的 MongoDB 持久性问题。
要使用绑定挂载,我们需要指定宿主机的路径(/path/on/your/host)映射到容器内的/data/db目录。
docker run -v /path/on/your/host:/data/db -d mongo
使用匿名卷则不需要指定宿主机的路径:
docker run -v /data/db -d mongo
第一次运行时,Docker 会自动创建一个具有随机名称的新卷。
使用命名卷需要指定卷的名称,在本例中卷的名称指定为mongo_data
docker run -v mongo_data:/data/db -d mongo
如果指定名称的卷在本地不存在,则会自动创建。
数据卷相关命令
docker提供了一系列命令用于Docker数据卷管理,这些命令都是以docker volume开头,理解和熟练运用这些命令,将有助于我们高效地管理容器数据,确保数据的持久性和一致性。
#查看系统中有哪些数据卷
docker volume ls
#删除指定的一个或者多个数据卷
docker volume rm 数据卷名
#docker volume prune 命令删除所有没有被挂载到容器的数据卷,不管该数据卷是具名的还是匿名的。
docker volume prune
#查看指定数据卷的信息
docker volume inspect 数据卷名
#创建一个数据卷,该数据卷并没有挂载到任何容器
docker volume create 数据卷名