ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Docker 中处理持久性存储(例如数据库)

人们如何处理 Docker 容器的持久存储?

我目前正在使用这种方法:构建图像,例如为 PostgreSQL,然后启动容器

docker run --volumes-from c0dbc34fd631 -d app_name/postgres

恕我直言,这有一个缺点,我绝不能(偶然)删除容器“c0dbc34fd631”。

另一个想法是将主机卷“-v”挂载到容器中,但是,容器中的用户 ID 不一定与主机的用户 ID 匹配,然后权限可能会混乱。

注意:您也可以使用 --volumes-from my-data-container 而不是 --volumes-from 'cryptic_id',其中 my-data-container 是您分配给仅数据容器的名称,例如 docker run --name my-data-container ...(请参阅接受的答案)

抱歉,我说错了,我的意思是说:我未来所有来自该图像的实例都依赖于该容器。如果我不小心删除了那个容器,我就有麻烦了。
@AntonStrogonoff - 是的,措辞错误 - 我的意思是:我需要确保我永远不会删除那个(可能)旧容器,因为那样的话“持久”存储的引用也会消失
它应该是 --name。你有 -name

P
Peter Mortensen

Docker 1.9.0 及以上

使用volume API

docker volume create --name hello
docker run -d -v hello:/container/path/for/volume container_image my_command

这意味着必须放弃纯数据容器模式以支持新卷。

实际上,volume API 只是实现数据容器模式的更好方法。

如果您使用 -v volume_name:/container/fs/path 创建容器,Docker 将自动为您创建一个命名卷,它可以:

通过 docker volume ls 列出 通过 docker volume inspect volume_name 识别 备份为普通目录 通过 --volumes-from 连接像以前一样备份

新的卷 API 添加了一个有用的命令,可让您识别悬空卷:

docker volume ls -f dangling=true

然后通过其名称将其删除:

docker volume rm <volume name>

正如@mpugach 在评论中强调的那样,您可以用一个漂亮的单线摆脱所有悬空的卷:

docker volume rm $(docker volume ls -f dangling=true -q)
# Or using 1.13.x
docker volume prune

Docker 1.8.x 及以下

似乎最适合生产的方法是使用仅数据容器。

仅数据容器在准系统映像上运行,实际上除了公开数据卷之外什么都不做。

然后,您可以运行任何其他容器来访问数据容器卷:

docker run --volumes-from data-container some-other-container command-to-execute

在这里,您可以很好地了解如何安排不同的容器。

这里有一个关于卷如何工作的很好的见解。

this blog post 中,对所谓的container as volume pattern 进行了很好的描述,它阐明了data only container 的要点。

Docker documentation has now the DEFINITIVE description of the container as volume/s pattern.

以下是 Docker 1.8.x 及以下版本的备份/恢复过程。

备份:

sudo docker run --rm --volumes-from DATA -v $(pwd):/backup busybox tar cvf /backup/backup.tar /data

--rm:容器退出时删除

--volumes-from DATA:附加到 DATA 容器共享的卷

-v $(pwd):/backup:绑定挂载当前目录到容器中;将 tar 文件写入

busybox:一个小而简单的图像 - 适合快速维护

tar cvf /backup/backup.tar /data:为/data目录下的所有文件创建一个未压缩的tar文件

恢复:

# Create a new data container
$ sudo docker run -v /data -name DATA2 busybox true
# untar the backup files into the new container᾿s data volume
$ sudo docker run --rm --volumes-from DATA2 -v $(pwd):/backup busybox tar xvf /backup/backup.tar
data/
data/sven.txt
# Compare to the original container
$ sudo docker run --rm --volumes-from DATA -v `pwd`:/backup busybox ls /data
sven.txt

这是一个很好的article from the excellent Brian Goff,它解释了为什么对容器和数据容器使用相同的图像是好的。


这是满足不同需求的不同工具。 --volumes-from 让您共享磁盘空间 --link 让您共享服务。
作品中还有另一个专门针对这种事情的项目,也许将其添加到此答案中作为观看的参考? github.com/ClusterHQ/flocker
数据容器没有任何意义,而且真的是个坏主意!容器仅在进程在其中运行时才有意义,否则它只是一个主机文件系统。您可以使用 -v 挂载一个卷,这是唯一也是最好的选择。您可以控制您使用的文件系统和物理磁盘。
是的,从 Docker 1.9 开始,使用 Volumes API (docker volume create --name mydata) 创建命名卷优于数据卷容器。 Docker 的人自己建议数据卷容器“are no longer considered a recommended pattern”、“named volumes should be able to replace data-only volumes in most (if not all) cases”和“no reason I can see to use data-only containers”。
@coding,我很难过你很难过,部分原因是你延迟了 3 年来判断答案,部分原因是答案在历史上基本上是正确的。如果您有任何建议,请随时发表评论,以便我可以整合答案并帮助人们不要难过
P
Peter Mortensen

在 Docker v1.0 版本中,绑定主机上的文件或目录的挂载可以通过给定的命令完成:

$ docker run -v /host:/container ...

上述卷可以用作运行 Docker 的主机上的持久存储。


这应该是推荐的答案,因为它远没有目前拥有更多投票的卷容器方法复杂
我希望在使用此卷安装命令时有一个标志来指定 host-uid : container-uid 和 host-gid : container-gid 映射。
P
Peter Mortensen

从 Docker Compose 1.6 开始,现在对 Docker Compose 中的数据卷的支持得到了改进。以下 compose 文件将创建一个数据图像,该图像将在父容器的重新启动(甚至删除)之间持续存在:

以下是博客公告:Compose 1.6: New Compose file for defining networks and volumes

这是一个示例撰写文件:

version: "2"

services:
  db:
    restart: on-failure:10
    image: postgres:9.4
    volumes:
      - "db-data:/var/lib/postgresql/data"
  web:
    restart: on-failure:10
    build: .
    command: gunicorn mypythonapp.wsgi:application -b :8000 --reload
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    links:
      - db

volumes:
  db-data:

据我了解:这将创建一个数据卷容器 (db_data),它将在重新启动之间持续存在。

如果您运行:docker volume ls,您应该会看到列出的卷:

local               mypthonapp_db-data
...

您可以获得有关数据量的更多详细信息:

docker volume inspect mypthonapp_db-data
[
  {
    "Name": "mypthonapp_db-data",
    "Driver": "local",
    "Mountpoint": "/mnt/sda1/var/lib/docker/volumes/mypthonapp_db-data/_data"
  }
]

一些测试:

# Start the containers
docker-compose up -d

# .. input some data into the database
docker-compose run --rm web python manage.py migrate
docker-compose run --rm web python manage.py createsuperuser
...

# Stop and remove the containers:
docker-compose stop
docker-compose rm -f

# Start it back up again
docker-compose up -d

# Verify the data is still there
...
(it is)

# Stop and remove with the -v (volumes) tag:

docker-compose stop
docker=compose rm -f -v

# Up again ..
docker-compose up -d

# Check the data is still there:
...
(it is).

笔记:

您还可以在卷块中指定各种驱动程序。例如,您可以为 db_data 指定 Flocker 驱动程序:volumes:db-data:driver:flocker

随着他们改进 Docker Swarm 和 Docker Compose 之间的集成(并且可能开始将 Flocker 集成到 Docker 生态系统中(我听说 Docker 已经收购了 Flocker),我认为这种方法应该会变得越来越强大。

免责声明:这种方法很有前途,我在开发环境中成功使用了它。我会担心在生产中使用它!


Flocker 已shut downgithub repo 上没有很多活动
P
Peter Mortensen

如果从所选答案的更新 5 中不清楚,从 Docker 1.9 开始,您可以创建可以存在而不与特定容器关联的卷,从而使“仅数据容器”模式过时。

请参阅 Data-only containers obsolete with docker 1.9.0? #17798

我认为 Docker 维护人员意识到纯数据容器模式有点设计味道,并决定将卷作为一个单独的实体,无需关联容器即可存在。


P
Peter Mortensen

虽然这仍然是 Docker that needs some work 的一部分,但您应该将卷放在带有 the VOLUME instruction 的 Dockerfile 中,这样您就不需要从另一个容器复制卷。

这将使您的容器之间的相互依赖程度降低,您不必担心删除一个容器会影响另一个容器。


另一方面的论点是“仅数据”容器最终成为对数据卷的最后引用(一旦使用 docker rm 删除引用该卷的最后一个容器,Docker 就会销毁数据卷)
Docker 的这份官方指南另有建议:docs.docker.com/userguide/dockervolumes/…“数据卷旨在持久保存数据,独立于容器的生命周期。因此,当您删除容器时,Docker 永远不会自动删除卷,也不会“垃圾收集”不存在的卷容器引用的时间更长。”
A
Arsen Khachaturyan

使用 Docker Compose 时,只需附加一个命名卷,例如:

version: '2'
services:
  db:
    image: mysql:5.6
    volumes:
      - db_data:/var/lib/mysql:rw
    environment:
      MYSQL_ROOT_PASSWORD: root
volumes:
  db_data:

P
Peter Mortensen

@tommasop 的回答很好,并解释了使用纯数据容器的一些机制。但是作为一个最初认为数据容器很傻的人,当一个人可以将一个卷绑定到主机时(正如其他几个答案所建议的那样),但现在意识到实际上只有数据的容器非常整洁,我可以建议我自己的关于此主题的博文:Why Docker Data Containers (Volumes!) are Good

另请参阅:问题“What is the (best) way to manage permissions for Docker shared volumes?”的my answer,以获取有关如何使用数据容器来避免权限和与主机的 uid/gid 映射等问题的示例。

为了解决 OP 最初的担忧之一:不能删除数据容器。即使删除了数据容器,只要任何容器引用了该卷,即任何通过 --volumes-from 挂载该卷的容器,数据本身也不会丢失。因此,除非所有相关容器都停止并删除(可以认为这相当于意外 rm -fr /),否则数据是安全的。您始终可以通过执行 --volumes-from 任何引用该卷的容器来重新创建数据容器。

与往常一样,请进行备份!

更新:Docker 现在有可以独立于容器管理的卷,这进一步使管理更容易。


P
Peter Mortensen

根据您的需要,管理持久数据有几个级别:

将其存储在您的主机上使用标志 -v host-path:container-path 将容器目录数据持久保存到主机目录。通过运行安装到同一目录的备份/恢复容器(例如 tutumcloud/dockup)来进行备份/恢复。

使用标志 -v host-path:container-path 将容器目录数据持久保存到主机目录。

通过运行安装到同一目录的备份/恢复容器(例如 tutumcloud/dockup)来进行备份/恢复。

创建一个数据容器并将其卷安装到您的应用程序容器创建一个导出数据卷的容器,使用 --volumes-from 将该数据安装到您的应用程序容器中。备份/恢复与上述解决方案相同。

创建一个导出数据卷的容器,使用 --volumes-from 将该数据挂载到您的应用程序容器中。

备份/恢复与上述解决方案相同。

使用支持外部/第三方服务的 Docker 卷插件 Docker 卷插件允许您的数据源来自任何地方 - NFS、AWS(S3、EFS 和 EBS) 根据插件/服务,您可以附加单个或多个容器到一卷。根据服务的不同,备份/恢复可能会为您自动执行。虽然手动执行这可能很麻烦,但一些编排解决方案 - 例如 Rancher - 已经内置并且易于使用。 Convoy 是手动执行此操作的最简单的解决方案。

Docker 卷插件允许您的数据源来自任何地方 - NFS、AWS(S3、EFS 和 EBS)

根据插件/服务,您可以将单个或多个容器附加到单个卷。

根据服务的不同,备份/恢复可能会为您自动执行。

虽然手动执行这可能很麻烦,但一些编排解决方案 - 例如 Rancher - 已经内置并且易于使用。

Convoy 是手动执行此操作的最简单的解决方案。


C
Community

如果您想移动您的卷,您还应该查看 Flocker

从自述文件:

Flocker 是一个数据卷管理器和多主机 Docker 集群管理工具。有了它,您可以利用 Linux 上 ZFS 的强大功能,使用用于无状态应用程序的相同工具来控制数据。这意味着您可以在 Docker 中运行您的数据库、队列和键值存储,并像其他应用程序一样轻松地移动它们。


谢谢约翰。我在 ClusterHQ 工作,我只想指出,我们已经超越了仅基于 ZFS 的存储。您现在可以将 Flocker 与 Amazon EBS 或 Google Persistent Disk 等存储结合使用。以下是存储选项的完整列表:docs.clusterhq.com/en/latest/supported/…
Flocker 已停止,不应使用 portworx.com/…
P
Peter Mortensen

这取决于您的方案(这并不真正适合生产环境),但这是一种方法:

Creating a MySQL Docker Container

它的要点是使用主机上的目录进行数据持久性。


但是,谢谢 Ben - 我可以通过这种方法看到的问题之一:文件系统资源(目录、文件)将由 docker/lxc 容器(来宾)中的 uid 拥有 - 一个可能与 uid 冲突的资源在主机...
我认为你很安全,因为它是由 root 运行的,但我同意它是一个 hack - 充其量适合本地开发/临时集成测试。这绝对是我希望看到更多模式/思维出现的领域。您应该查看/将此问题发布到 docker-dev google 组
本,感谢您的解决方案!不过,我不会称其为 hack,它似乎比容器更可靠。如果仅使用容器中的数据,您是否看到任何缺点? (在这种情况下,UID 无关紧要)
s
slth

我最近写了一个潜在的解决方案和一个演示该技术的应用程序。我发现它在开发和生产过程中非常有效。希望它能帮助或激发一些想法。

回购: https://github.com/LevInteractive/docker-nodejs-example
文章: http://lev-interactive.com/2015/03/30/docker-load-balanced-mongodb-persistence/


P
Peter Mortensen

我只是在主机上使用预定义的目录来为 PostgreSQL 保存数据。此外,通过这种方式可以轻松地将现有 PostgreSQL 安装迁移到 Docker 容器:https://crondev.com/persistent-postgresql-inside-docker/


P
Peter Mortensen

我的解决方案是使用新的 docker cp,它现在能够从容器中复制数据,无论它是否正在运行,并将主机卷共享到数据库应用程序正在创建其数据库文件的完全相同的位置容器内。这种双重解决方案在没有纯数据容器的情况下工作,直接来自原始数据库容器。

因此,我的 systemd 初始化脚本负责将数据库备份到主机上的存档中。我在文件名中放置了一个时间戳,以从不重写文件。

它在 ExecStartPre 上执行:

ExecStartPre=-/usr/bin/docker cp lanti-debian-mariadb:/var/lib/mysql /home/core/sql
ExecStartPre=-/bin/bash -c '/usr/bin/tar -zcvf /home/core/sql/sqlbackup_$$(date +%%Y-%%m-%%d_%%H-%%M-%%S)_ExecStartPre.tar.gz /home/core/sql/mysql --remove-files'

它也在 ExecStopPost 上做同样的事情:

ExecStopPost=-/usr/bin/docker cp lanti-debian-mariadb:/var/lib/mysql /home/core/sql
ExecStopPost=-/bin/bash -c 'tar -zcvf /home/core/sql/sqlbackup_$$(date +%%Y-%%m-%%d_%%H-%%M-%%S)_ExecStopPost.tar.gz /home/core/sql/mysql --remove-files'

另外,我将主机中的一个文件夹作为一个卷公开到存储数据库的完全相同的位置:

mariadb:
  build: ./mariadb
  volumes:
    - $HOME/server/mysql/:/var/lib/mysql/:rw

它在我的 VM 上运行良好(我为自己构建了一个 LEMP 堆栈):https://github.com/DJviolin/LEMP

但是我只是不知道当您的生活实际上依赖于它时它是否是“防弹”解决方案(例如,在任何可能的毫秒内进行交易的网上商店)?

在这个 Docker 官方主题视频的 20 分 20 秒处,演示者对数据库做了同样的事情:

Getting Started with Docker

“对于数据库,我们有一个卷,因此我们可以确保随着数据库的上升和下降,当数据库容器停止时,我们不会丢失数据。”


“……使用……”是什么意思?和“......在任何可能的毫秒内交易”?
P
Peter Mortensen

使用来自 Kubernetes 的 Persistent Volume Claim (PVC),它是一个 Docker 容器管理和调度工具:

Persistent Volumes

为此目的使用 Kubernetes 的优点是:

您可以使用任何存储,如 NFS 或其他存储,即使节点关闭,也不需要存储。

此外,这些卷中的数据可以配置为即使在容器本身被销毁后仍保留 - 以便在必要时可以由另一个容器回收。


H
Hassan Saeed

要保存或存储数据库数据,请确保您的 docker-compose.yml 看起来像如果您想使用 Dockerfile

version: '3.1'

services:
  php:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html/
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: example
    volumes:
      - mysql-data:/var/lib/mysql

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
volumes:
  mysql-data:

如果您想使用图像而不是 Dockerfile,您的 docker-compose.yml 将看起来像

version: '3.1'   

services:
  php:
    image: php:7.4-apache
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html/
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: example
    volumes:
      - mysql-data:/var/lib/mysql

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
volumes:

如果你想存储或保存 mysql 的数据,那么必须记住在你的 docker-compose.yml 中添加两行

volumes:
  - mysql-data:/var/lib/mysql

volumes:
  mysql-data:

之后使用此命令

docker-compose up -d

现在您的数据将持久保存,即使使用此命令也不会被删除

docker-compose down

额外:-但如果您想删除所有数据,那么您将使用

docker-compose down -v

另外,您可以使用此命令检查您的数据库数据列表

docker volume ls

DRIVER              VOLUME NAME
local               35c819179d883cf8a4355ae2ce391844fcaa534cb71dc9a3fd5c6a4ed862b0d4
local               133db2cc48919575fc35457d104cb126b1e7eb3792b8e69249c1cfd20826aac4
local               483d7b8fe09d9e96b483295c6e7e4a9d58443b2321e0862818159ba8cf0e1d39
local               725aa19ad0e864688788576c5f46e1f62dfc8cdf154f243d68fa186da04bc5ec
local               de265ce8fc271fc0ae49850650f9d3bf0492b6f58162698c26fce35694e6231c
local               phphelloworld_mysql-data