Docker实现容器数据持久化的两种方法 Bind volume 和 Volume

William
2020-08-20 / 0 评论 / 27 阅读 / 正在检测是否收录...

Docker中的数据,比如MysqlRedis这些,在容器重启或被删除以后,数据是不会保留的,也就是说数据没有持久化。

Docker中实现数据持久化有两种方式:

Bind Mount:

Bind mount 方式是 docker 早期使用的容器与宿主机数据共享的方式,可以实现将宿主机上的文件或目录挂载(mount)到 docker 容器中使用。

相对于 volume 方式,bind mount 方式存在不少的局限。例如,bind mountLinuxWindows 操作系统下不可移植。因此 docker 官方推荐使用 volume 方式。

William在这里以Mysql镜像为例子,将/home/mysql目录挂载到容器的/var/lib/mysql目录中

docker run --name mysql -it -p 3306:3306 -v /home/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql

对于bind mount,有几点需要注意。

  • -v 宿主机目录路径必须以 /~/ 开头,否则 docker 会将其当成是 volume 而不是 bind mount
  • 如果宿主机上的目录不存在,docker 会自动创建该目录
  • 如果容器中的目录不存在,docker 会自动创建该目录
  • 如果容器中的目录已有内容,那么 docker 会使用宿主机上目录的内容覆盖容器目录的内容

Volume:

bind mount 不同,volumedocker 创建和管理,docker 所有的 volume 都保存在宿主机文件系统的 /var/lib/docker/volumes 目录下(但是macOS 是以虚拟机形式运行 docker的,因此并不存在该目录,可以参考 stackoverflow)。

Docker 引入 volume 的原因有:

  • 删除容器时,volume 不会被删除
  • 在不同的容器之间共享 volume (存储 / 数据)
  • 容器与存储分离
  • volume 存储在远程主机或云上

可以使用 docker run -v 参数为启动容器加载一个 volume,例如(最新版本镜像默认不用加tag,特定版本要加):

docker run --name mysql_test -d -p 3306:3306 -v /data -e MYSQL_ROOT_PASSWORD=123456 mysql:tag

这时候就启动了一个名为mysql_test的mysql容器,使用docker exec -it mysql_test bash进入到mysql_test容器中的data目录创建一个文件 touch test.txt 然后退出容器,使用inspect命令查看刚才容器启动时做了什么:docker inspect mysql_test

可以看到一大串 JSON 格式的输出,我们重点关注 Mounts 字段的输出:

"Mounts": [
            {
                "Type": "volume",
                "Name": "57be41568e142560aaf333beb9a4dd7b79a37c78a45a4d930a31455f24fe7d13",
                "Source": "/var/lib/docker/volumes/57be41568e142560aaf333beb9a4dd7b79a37c78a45a4d930a31455f24fe7d13/_data",
                "Destination": "/data",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            },
            {
                "Type": "volume",
                "Name": "6d826d6b7b2c00d4f78cedadf997d393b51cb79b5a65b531372274c806efc3ce",
                "Source": "/var/lib/docker/volumes/6d826d6b7b2c00d4f78cedadf997d393b51cb79b5a65b531372274c806efc3ce/_data",
                "Destination": "/var/lib/mysql",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
]

可以看到,当容器 mysql_test 加载一个 volume (/data)时,docker 在目录 /var/lib/docker/volumes/ 创建一个新的目录,用来存储容器中产生的文件。同时,我们注意到 /dataRW 属性为 true,即可读可写。

重新启动 container1 容器,再进入容器会发现刚才创建的test.txt还在,说明即使容器关闭,之前在 volume 存储的文件仍然会保留下来,即数据持久化没问题!

先停掉容器docker stop mysql_test,再删掉容器 docker rm mysql_test,使用命令 docker volume ls可以看到,刚才的两个volume

57be41568e142560aaf333beb9a4dd7b79a37c78a45a4d930a31455f24fe7d13
6d826d6b7b2c00d4f78cedadf997d393b51cb79b5a65b531372274c806efc3ce

仍然存在。也就是说,即使容器不存在了,volume 仍可在宿主机上保存下来。

如果需要在其他容器也使用这个 volume,可以使用以下命令加载指定的 volume

docker run -it -v 57be41568e142560aaf333beb9a4dd7b79a37c78a45a4d930a31455f24fe7d13:/data container_name

挂载指定的 volume

上面的 docker run -v 命令中,我们并没有指定 volume 的名称,这样 docker 会默认给我们创建一个匿名的 volume,就是很长的那字符串。
我们也可以挂载指定名称的 volume
docker run -it -v my-volume:/data --name 自定义容器名 镜像名:tag

这样,我们在启动容器该容器时,将挂载一个名为 my-volume 的 volume,并挂载到容器的 /data 目录。对于 docker 来说,如果 my-volume 不存在,那么 docker 就会自动创建该 volume,并挂载到 /data 目录。

比如运行的是Reids

docker run -d -it -v redis_volume:/data --name myRedis redis

执行 docker volume inspect redis_volume 查看redis挂载的volume信息

可以看到 my-volume 的 JSON 输出信息:

[
    {
        "CreatedAt": "2020-08-24T09:33:30-04:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/redis_volume/_data",
        "Name": "redis_volume",
        "Options": null,
        "Scope": "local"
    }
]

可以看到redis_volume的宿主机目录位置是:/var/lib/docker/volumes/redis_volume/_data

除了让 docker 帮我们自动创建 volume,我们也可以自行创建 volume
docker volume create my-volume

删除volume命令是:
docker volume rm volume-name
然后将这个手工创建的 volume 挂载到容器:
docker run -d -v redis_volume2:/data --name myRedis001 redis

0

评论 (0)

取消