docker - 使用多个容器填充卷

标签 docker volumes

我正在检查 docker 文档,了解如何使用命名卷在容器之间共享数据。 在 Populate a volume using a container明确规定:

If you start a container which creates a new volume, as above, and the container has files or directories in the directory to be mounted (such as /app/ above), the directory’s contents are copied into the volume. The container then mounts and uses the volume, and other containers which use the volume also have access to the pre-populated content.

所以我做了一个简单的例子:

  • 我启动一个容器,该容器创建卷并将其安装到包含现有文件的目录
  • 我启动了第二个容器,我可以在其上安装卷,实际上我可以看到第一个容器的文件。

到目前为止一切顺利。 不过,我想看看是否可以从多个容器中预先填充内容。 我所做的是

  1. 创建两个简单的镜像,它们各自的配置文件位于同一目录中
FROM alpine:latest

WORKDIR /opt/test

RUN mkdir -p "/opt/test/conf" && \
    echo "container from image 1" > /opt/test/conf/config_1.cfg
FROM alpine:latest

WORKDIR /opt/test

RUN mkdir -p "/opt/test/conf" && \
    echo "container from image 2" > /opt/test/conf/config_2.cfg
  • 创建一个 docker compose,定义一个安装在两个服务上的命名卷
  • services:
        test_container_1:
            image:
              test_image_1
            volumes:
              - test_volume:/opt/test/conf
            tty: true
    
        test_container_2:
            image:
              test_image_2
            volumes:
              - test_volume:/opt/test/conf
            tty: true
    
    volumes:
        test_volume:
    
  • 已启动服务。
  • > docker-compose -p example up
    Creating network "example_default" with the default driver
    Creating volume "example_test_volume" with default driver
    Creating example_test_container_2_1 ... done
    Creating example_test_container_1_1 ... done
    Attaching to example_test_container_1_1, example_test_container_2_1
    

    根据日志,container_2 首先被创建并预填充了该卷。然而,该卷随后被挂载到了container_1,挂载上唯一可用的文件显然是/opt/test/conf/config_2.cfg,有效地删除了config_1。

    所以我的问题是,是否可以使用来自 2 个或更多容器的数据填充一个卷。

    我想探索这一点的原因是,我可以从不同的容器加载额外的应用程序配置,以支持 Multi-Tenancy 场景,而无需重新设计应用程序以从不同的文件夹读取租户配置。

    提前谢谢

    最佳答案

    一旦命名卷中有任何内容,Docker 就永远不会自动将内容复制到其中。它不会合并来自两个不同图像的内容,不会在其中一个图像发生更改时更新音量,也不会发生其他任何情况。

    我建议您忽略 Docker 文档中引用的段落。假设您安装到容器中的任何卷最初都是空的。这与 Docker 绑定(bind)挂载(主机目录)、Kubernetes 持久卷以及除​​ Docker 命名卷之外的任何其他类型的存储所获得的行为相匹配。不要在镜像中的内容上安装卷。

    如果可以的话,重组您的应用程序以避免共享文件。例如,我看到的命名卷的一种常见用途是尝试将静态 Assets 重新发布到反向代理;您可以将静态资源COPY到专用的Web 服务器镜像中,而不是尝试使用命名卷(它永远不会自行更新)。这避免了尝试在此处使用卷的各种复杂性。


    如果您确实在这件事上别无选择,那么您可以在两个容器中使用专用代码来解决此问题。这里的基本设置是:

    1. 在应用程序目录之外的某个位置有一个数据目录,并将卷挂载到那里。
    2. 将原始文件包含在图像中不同的位置。
    3. 在入口点包装器脚本中,将原始文件复制到数据目录(已安装的卷)中。

    为了便于论证,假设您已将应用程序安装到 /opt/test 中,数据目录将为 /etc/test。入口点包装器脚本可以少至

    #!/bin/sh
    
    # Copy config files from the application tree into the config tree
    # (overwriting anything that's already there)
    cp /opt/test/* "$TEST_CONFIG_DIR"
    
    # Run the main container command
    exec "$@"
    

    在 Dockerfile 中,您需要确保该目录存在(如果您使用非 root 用户,则该用户需要拥有写入该目录的权限)。

    FROM alpine
    
    WORKDIR /opt/test
    COPY ./ ./
    
    ENV TEST_CONFIG_DIR=/etc/test
    RUN mkdir "$TEST_CONFIG_DIR"
    
    ENTRYPOINT ["./entrypoint.sh"]
    CMD ["./my_app"]
    

    最后,在 Compose 设置中,将卷挂载到该数据目录上(您不能使用环境变量,但请考虑图像 API 的文件系统路径部分):

    version: '3.8'
    volumes:
      test_config:
    services:
      one:
        build: ./one
        volumes:
          - test_config:/etc/test
      two:
        build: ./two
        volumes:
          - test_config:/etc/test
    

    例如,您可以运行:

    docker-compose run one ls /etc/test
    docker-compose run two ls /etc/test
    

    查看两组文件都出现在那里。

    入口点脚本是您控制的代码。除了最后的 exec "$@"行来运行主容器命令之外,没有什么特别神奇的地方。例如,如果您想忽略已经存在的文件,或者如果您有办法合并更改,那么您可以实现比简单的 cp 命令更聪明的东西。

    关于docker - 使用多个容器填充卷,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72761941/

    相关文章:

    postgresql - 具有一个挂载点的 Docker 多 postgres 容器

    filesystems - 设备 vs 分区 vs 文件系统 vs 卷 : how do these concepts relate to each other, 准确

    docker - docker nginx-尝试为nginx.conf/default.conf添加卷时不会上升

    docker - 将本地卷安装到远程 docker 容器,可能吗?

    tomcat - Docker Compose - 使用卷在 tomcat 上运行 java web 应用程序(war 文件)

    docker - Docker:我应该将apt-get的install/build/cleanup步骤组合成一个大的RUN吗?

    django - 为了在docker中运行celery队列,我想指定多个具有不同并发度的队列

    docker - 如何仅使用 webpack bundle node_modules?

    docker - 进行dockerized构建,缓存依赖项拉取层

    docker - Docker PGAdmin容器永久配置