再也不用担心数据卷不翼而飞了

docker的本意就是只作为容器存在,任何配置或重要文件都应该以挂载的方式存在,像mysql,redis这种专门用来做数据库存储的容器,本身只提供了服务,真正的数据库数据并不在容器内,而是以docker的volume数据卷的方式存在。

执行docker volume ls看到我的电脑上当前存在的数据卷列表,目前我只运行了一个本地的redis

DRIVER              VOLUME NAME
local               5c44cbef81c711c3b10ca64f79a7cb733655c05dc8949ab6005d44f103002a5f

此时我拉去一个mysql镜像并运行,顺便创建了一个test数据库,再次执行如下所示

DRIVER              VOLUME NAME
local               20265b8be590fcef965a1fa8e544d3cd1c1424330abd86e81445839fb6516de0
local               5c44cbef81c711c3b10ca64f79a7cb733655c05dc8949ab6005d44f103002a5f
local               f0042fac97c7c21710fd2a0e71306f7e34336fc30ac9b3f86e560edea32f0353

docker inspect mysql查看容器信息会发现这样的挂载形式,为了看清区别,我在启动mysql容器时额外挂在了一个普通文件

"Mounts": [
            {
                "Type": "bind",
                "Source": "/usr/share/zoneinfo/Asia/Shanghai",
                "Destination": "/etc/localtime",
                "Mode": "ro",
                "RW": false,
                "Propagation": "rprivate"
            },
            {
                "Type": "volume",
                "Name": "20265b8be590fcef965a1fa8e544d3cd1c1424330abd86e81445839fb6516de0",
                "Source": "/var/lib/docker/volumes/20265b8be590fcef965a1fa8e544d3cd1c1424330abd86e81445839fb6516de0/_data",
                "Destination": "/var/lib/mysql",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],

看得出普通的挂载type类型是bind,而数据卷的type类型是volume,且它就存在于宿主机中,source就是它的路径,可以cd进去看一下数据卷里都有什么

可以看到我之前创建的test数据库在这里是以文件夹方式存在的,进入文件夹发现只有一个db.opt配置文件,因为此时数据库是空的。为了看看数据是如何存放在数据卷中的,我随便建了个表并插入数据

create table a(a int); insert into a values(1);

此时再回到数据卷test目录,多出两个以表名命名的文件,上网查了一下这两种后缀是InnoDB引擎的存储方式

而以上都不是本文的重点。。只是顺便满足下好奇心,我想说的重点是在某种情况下,比如你发现当初运行mysql容器时忘记把配置文件挂载出来,此时不论你用docker原始的方式还是修改docker-compose之后up -d,都会让容器内的数据丢失,因为最终都是要执行删除容器并重新启动一个新容器,所以会自动创建一个新的数据卷,所以我想知道如何在这种情况下把原来的数据卷挂载回去,这样不论如何删除,都使用同一个数据卷,就不会有数据丢失的问题了。

本以为很麻烦,其实答案就是上面那个docker inspect mysql输出的容器信息里。里面明确地声明了把宿主机中的数据卷挂载到了容器中的/var/lib/mysql位置

  "Source": "/var/lib/docker/volumes/20265b8be590fcef965a1fa8e544d3cd1c1424330abd86e81445839fb6516de0/_data",
                "Destination": "/var/lib/mysql",

所以我们面临两种情况:

docker run -it -v /var/lib/docker/volumes/20265b8be590fcef965a1fa8e544d3cd1c1424330abd86e81445839fb6516de0/_data:/var/lib/mysql mysql

唯一的不同就是现在的挂载类型type由volume变成了bind

docker run -it -v /home/shingle/mysql/data/:/var/lib/mysql mysql

或者可以先创建一个带名字的数据卷,可以直接挂载使用

  volumes:
    mysql-data:
      driver: local
  volumes:
        - version-data:/var/lib/mysql

两种方式各有好处和弊端,使用默认的数据卷更方便docker进行管理,比如docker volume prune会移除所有没被使用的数据卷,而自行挂载则需要先看到该镜像的文档,查找数据卷应该被挂载到哪里(比如mysql的/var/lib/mysql),在以后不用的时候还需要手动删除。