`Docker`中的数据,比如`Mysql`,`Redis`这些,在容器重启或被删除以后,数据是不会保留的,也就是说数据没有持久化。
在`Docker`中实现数据持久化有两种方式:

Bind Mount:

`Bind mount` 方式是 `docker` 早期使用的容器与宿主机数据共享的方式,可以实现将宿主机上的文件或目录挂载(`mount`)到 `docker` 容器中使用。
相对于 `volume` 方式,`bind mount` 方式存在不少的局限。例如,`bind mount` 在 `Linux` 和 `Windows` 操作系统下不可移植。因此 `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` 不同,`volume` 由 `docker` 创建和管理,`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/` 创建一个新的目录,用来存储容器中产生的文件。同时,我们注意到 `/data` 的 RW 属性为 `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`

最后修改:2022 年 04 月 07 日
如果觉得我的文章对你有用,请随意赞赏