如何使用Docker Compose v2使卷永久化

26

我知道其他人也有类似的问题,但这个使用了v2的compose文件格式,我没有找到相关的内容。

我想创建一个非常简单的测试应用程序来尝试MemSQL,但是我无法使数据卷在docker-compose down之后不被删除。如果我正确理解Docker Docs,未经明确告知,数据卷不应该被删除。一切似乎都可以通过docker-compose up正常工作,但是在关闭并重新启动后,所有数据都会从数据库中删除。

作为良好实践的推荐,我使用单独的memsqldata服务作为独立的数据层。

以下是我的docker-compose.yml:

version: '2'
services:
    app:
        build: .
        links:
            - memsql
    memsql:
        image: memsql/quickstart
        volumes_from:
            - memsqldata
        ports:
            - "3306:3306"
            - "9000:9000"
    memsqldata:
        image: memsql/quickstart
        command: /bin/true
        volumes:
            - memsqldatavolume:/data

volumes:
    memsqldatavolume:
        driver: local

1
我建议您尝试不使用memsqldata容器。由于您正在使用命名卷,因此不需要数据卷容器。我尝试了down命令,但默认情况下它不会删除任何卷。 - dnephin
MySQL的数据完全不在卷中,这种情况可能吗?我认为它通常不使用“/data”。 - dnephin
5个回答

32

我意识到这是一篇旧的和已解决的帖子,原帖作者指向容器中的目录而不是他们挂载的卷,但我想澄清一些我看到的错误信息。

docker-compose down 不会删除卷,如果您也想删除卷,则需要运行 docker-compose down -v。以下是来自 docker-compose 的帮助文本(请注意“默认情况下”列表):

$ docker-compose down --help
Stops containers and removes containers, networks, volumes, and images
created by `up`.

By default, the only things removed are:

- Containers for services defined in the Compose file
- Networks defined in the `networks` section of the Compose file
- The default network, if one is used

Networks and volumes defined as `external` are never removed.

Usage: down [options]

Options:
...
    -v, --volumes       Remove named volumes declared in the `volumes` section
                        of the Compose file and anonymous volumes
                        attached to containers.
...

$ docker-compose --version
docker-compose version 1.12.0, build b31ff33

这是一个带有命名卷和虚拟命令的yml示例,可用于测试:

$ cat docker-compose.vol-named.yml
version: '2'

volumes:
  data:

services:
  test:
    image: busybox
    command: tail -f /dev/null
    volumes:
    - data:/data

$ docker-compose -f docker-compose.vol-named.yml up -d
Creating volume "test_data" with default driver
Creating test_test_1

容器启动后,由于镜像在该位置为空,因此卷初始化为空。我在该位置创建了一个快速的Hello World:

$ docker exec -it test_test_1 /bin/sh
/ # ls -al /data
total 8
drwxr-xr-x    2 root     root          4096 May 23 01:24 .
drwxr-xr-x    1 root     root          4096 May 23 01:24 ..
/ # echo "hello volume" >/data/hello.txt
/ # ls -al /data
total 12
drwxr-xr-x    2 root     root          4096 May 23 01:24 .
drwxr-xr-x    1 root     root          4096 May 23 01:24 ..
-rw-r--r--    1 root     root            13 May 23 01:24 hello.txt
/ # cat /data/hello.txt
hello volume
/ # exit

在 Docker 外部可以看到该卷,并且在执行 docker-compose down 后仍然存在:

$ docker volume ls | grep test_
local               test_data

$ docker-compose -f docker-compose.vol-named.yml down
Stopping test_test_1 ... done
Removing test_test_1 ... done
Removing network test_default

$ docker volume ls | grep test_
local               test_data

重新创建容器会使用旧的卷,其中文件仍然可见:

$ docker-compose -f docker-compose.vol-named.yml up -d
Creating network "test_default" with the default driver
Creating test_test_1

$ docker exec -it test_test_1 /bin/sh
/ # cat /data/hello.txt
hello volume
/ # exit

最后运行docker-compose down -v命令将会移除容器和卷:

$ docker-compose -f docker-compose.vol-named.yml down -v
Stopping test_test_1 ... done
Removing test_test_1 ... done
Removing network test_default
Removing volume test_data

$ docker volume ls | grep test_

$
如果您发现只有在使用停止/启动而不是关闭/开启时才会持久化数据,那么您的数据存储在容器中(或可能是匿名卷),而不是您命名的卷,并且容器不具有持久性。确保容器内数据的位置正确以避免此问题。
为了调试容器中的数据存储位置,我建议您在容器上使用docker diff。这将显示在容器内创建、修改或删除的所有文件,当容器被删除时将丢失这些文件。例如:
$ docker run --name test-diff busybox \
  /bin/sh -c "echo hello docker >/etc/hello.conf"

$ docker diff test-diff
C /etc
A /etc/hello.conf

1
不错的澄清。+1 - VonC
数据"在容器中"而不是在卷中会意味着什么?我假设存储在映射到卷的目录中的任何内容都会在卷中。 - Jonathan Stray
是的,但正如 OP 所示,如果您没有将卷映射到正确的位置,则数据不在卷中,而是在容器的 RW 层中。当您删除容器时,所有对该容器进行的更改(主要是其 RW 文件系统层)而未存储在卷中的更改都会丢失。 - BMitch
如果您发现只有在使用停止/启动而不是关闭/打开时才持久化数据,则说明您的数据存储在容器中而不是卷中,因此容器不具备持久性。但我不确定这是否正确。如果您依赖匿名卷(例如在Dockerfile中定义),那么当您从该镜像创建新容器(通过在docker-compose down之后调用docker-compose up)时,将会得到一个新的卷。 - jminardi
@jminardi 说得好,我忘记在匿名卷中包含免责声明了,因为我强烈建议不要使用它们。这也适用于建议永远不要在 Dockerfile 中定义 VOLUME。我相信在 compose 中有一些逻辑可以重用先前的匿名卷,但这可能无法在 down 命令后生效。 - BMitch
1
我同意VOLUME不应该在Dockerfile中使用的观点,但不幸的是,许多官方的Dockerfile都在使用它,包括memsqlmongo - jminardi

4
最简单的解决方案是使用docker-compose stop代替docker-compose down。然后使用docker-compose start重新启动。
根据文档down "停止容器并删除通过up创建的容器、网络、卷和映像"。

6
如果你不加 -v 选项,docker-compose down 不会删除卷。 - BMitch
2
这不是我所看到的,使用此docker-compose.yml文件。即使没有-v,Down命令肯定会删除卷。如果我做错了什么,我很想知道!我害怕意外丢失我的数据库。 - Jonathan Stray
这可能值得单独提出问题,附上所有细节,而不是在评论中尝试调试。 - BMitch

4
您正在使用 docker-compose down 命令,如果您查看 此处 的文档:

停止容器并删除由 up 创建的容器、网络、数据卷和镜像。默认情况下仅删除容器和网络。

您是正确的,它不应该默认删除数据卷。这可能是一个bug或者您已经更改了默认配置。但我认为适合您的正确命令是 docker-compose stop。我将尝试对于 down 命令进行一些简单的测试。

我正在使用默认配置。我不是Docker专家,所以希望选择了明智的默认值 ;) 昨天安装了工具箱,所以一切都应该是最新版本:docker-compose版本1.6.0,构建d99cad6,docker-py版本:1.7.0。 - jimmy
你是否已经尝试过单独构建并运行你的memsqldata,在卷中提供一些文件,然后停止它/移除容器并查看是否仍存在这些文件? - Adrien Chaussende
是的。我已经测试了同样的memsql结构几天了。我刚刚测试了一下,即使我执行docker-compose stop,它也可以正常工作。似乎是down的行为不像它应该的那样。 - jimmy
明天我会尝试使用版本2的docker-compose,并提供一些反馈。但目前来看,在第一个版本中,它还可以,仍在正常工作。 - Adrien Chaussende
我按照之前说的尝试了使用版本2的docker-compose文件。我将卷挂载在主机目录上。但是在执行docker-compose down命令后,这些卷并没有被删除。你的数据库真的使用/data目录来保存数据吗? - Adrien Chaussende

4

这是由于MemSQL的文档不够清晰导致的。在memsql/quickstart容器中,MemSQL数据路径为/memsql,而不像独立安装(以及MemSQL文档)中的/var/lib/memsql,也绝对不是像有人告诉我那样的/data


3
不确定这是否有帮助。当您使用 docker-compose up -d 命令时,容器会被下载并创建镜像。要停止 docker 镜像,请使用 docker-compose down 命令,然后镜像会保留下来,并可以使用 docker-compose start 命令重新启动。

我曾经使用了 up/down 命令,但一直在丢失我的数据,直到我尝试了 stop/start 命令,现在我的数据得以保存。


几个小时的搜索后,这解决了我的问题! - mattsch
这是错误的。docker-compose down会删除卷。不过我认为那就是Bobby想说的。 - Jonathan Stray
2
停止/启动将保留相同的容器,如果您在关闭/打开过程中丢失数据,则说明您没有将数据存储在卷中,而是存储在容器本身中,这不应被视为持久性。docker-compose down命令不会删除卷,除非您也包括-v选项。 - BMitch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接