`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`
此处评论已关闭