docker-compose未选择共享卷中的文件系统更改

时间:2018-12-12 06:28:15

标签: docker docker-compose

大家好。

我在这里的任务是使用一些将在多个服务之间共享的卷。该卷将由某些服务Dockerfile中的ADDCOPY命令填充。我遇到的问题是通过docker-compose up启动服务时,卷没有得到更新。

请考虑以下设置:

# docker-compose.yml
version: "3"

services:
  service-a:
    build:
      context: service-a
      dockerfile: Dockerfile
    volumes:
      - shared:/test/dir

  service-b:
    build:
      context: service-b
      dockerfile: Dockerfile
    volumes:
      - shared:/another-test/dir

volumes:
  shared:

# ./service-a/Dockerfile
FROM alpine:3.8
COPY test-dir /test/dir
CMD [ "tail", "-f", "/dev/null" ]

# ./service-b/Dockerfile
FROM alpine:3.8
CMD [ "tail", "-f", "/dev/null" ]

然后我们将创建./service-a/test-dir文件夹。现在,我们来构建它:

> docker-compose build
Building service-a
Step 1/3 : FROM alpine:3.8
 ---> 196d12cf6ab1
Step 2/3 : COPY test-dir /test/dir
 ---> ac66ed92b442
Step 3/3 : CMD [ "tail", "-f", "/dev/null" ]
 ---> Running in 932eb32b6184
Removing intermediate container 932eb32b6184
 ---> 7e0385d17f96
Successfully built 7e0385d17f96
Successfully tagged docker-compose-test_service-a:latest
Building service-b
Step 1/2 : FROM alpine:3.8
 ---> 196d12cf6ab1
Step 2/2 : CMD [ "tail", "-f", "/dev/null" ]
 ---> Running in 59a8b91c6b2d
Removing intermediate container 59a8b91c6b2d
 ---> 4e2c16ea5a80
Successfully built 4e2c16ea5a80
Successfully tagged docker-compose-test_service-b:latest

并启动服务:

> docker-compose up --no-build -d
Creating network "docker-compose-test_default" with the default driver
Creating volume "docker-compose-test_shared" with default driver
Creating docker-compose-test_service-a_1 ... done
Creating docker-compose-test_service-b_1 ... done

让我们检查映射目录:

> docker-compose exec service-a ls -lah /test/dir
total 8
drwxr-xr-x    2 root     root        4.0K Dec 12 06:14 .
drwxr-xr-x    3 root     root        4.0K Dec 12 06:14 ..
> docker-compose exec service-b ls -lah /another-test/dir
total 8
drwxr-xr-x    2 root     root        4.0K Dec 12 06:14 .
drwxr-xr-x    3 root     root        4.0K Dec 12 06:14 ..

现在让我们将少量文本文件放入主机上的./service-a/test-dir中并重新构建:

> docker-compose build
Building service-a
Step 1/3 : FROM alpine:3.8
 ---> 196d12cf6ab1
Step 2/3 : COPY test-dir /test/dir
 ---> bd168b0fc8cc
Step 3/3 : CMD [ "tail", "-f", "/dev/null" ]
 ---> Running in 6e81b32243e1
Removing intermediate container 6e81b32243e1
 ---> cc28fc6de9ac
Successfully built cc28fc6de9ac
Successfully tagged docker-compose-test_service-a:latest
Building service-b
Step 1/2 : FROM alpine:3.8
 ---> 196d12cf6ab1
Step 2/2 : CMD [ "tail", "-f", "/dev/null" ]
 ---> Using cache
 ---> 4e2c16ea5a80
Successfully built 4e2c16ea5a80
Successfully tagged docker-compose-test_service-b:latest

如您所见,COPY的{​​{1}}步骤未使用缓存,这意味着更改被烘焙到图像中。现在让我们开始服务:

service-a

再次> docker-compose up --no-build -d Recreating docker-compose-test_service-a_1 ... Recreating docker-compose-test_service-a_1 ... done 保持不变,只有service-b被重新创建。让我们检查实际的服务(这是发生问题的地方):

service-a

因此不会反映文件...但是,如果我们基于> docker-compose exec service-a ls -lah /test/dir total 8 drwxr-xr-x 2 root root 4.0K Dec 12 06:17 . drwxr-xr-x 3 root root 4.0K Dec 12 06:20 .. > docker-compose exec service-b ls -lah /another-test/dir total 8 drwxr-xr-x 2 root root 4.0K Dec 12 06:17 . drwxr-xr-x 3 root root 4.0K Dec 12 06:14 .. 图片启动临时容器,它将显示正确的列表:

service-a

有任何解决方法的想法吗?到目前为止,似乎只有通过> docker run --rm docker-compose-test_service-a ls -lah /test/dir total 8 drwxr-xr-x 2 root root 4.0K Dec 12 06:20 . drwxr-xr-x 3 root root 4.0K Dec 12 06:20 .. -rwxr-xr-x 1 root root 0 Dec 12 06:20 rrrrrr.txt -rwxr-xr-x 1 root root 0 Dec 12 06:16 test.txt 完全关闭才能进行卷销毁。虽然不是最佳解决方案,但与真实项目一样,它会导致严重的停机。

我希望配置是可读的,但是如果需要,我可以将其放入小型git repo中。

谢谢。

1 个答案:

答案 0 :(得分:2)

Docker仅会auto-populate a volume when it is first created。在您描述的工作流程中,当删除并重新创建带有其新映像的“第一个”容器时,您也需要删除并重新创建该卷,这可能意味着删除并重新创建整个堆栈。

有两种方法可以解决此问题:

  1. 重新设计您的应用程序,无需共享文件。 (显然,这是最多的工作。)如果存在某种半静态内容,则可能需要将它们“吸收”到使用的映像中,或者设置系统的两个部分以通过HTTP进行通信。 (这种跨容器的数据共享并不是Docker擅长的领域,当您开始研究Swarm或Kubernetes等多主机解决方案时,情况会变得更糟。)

  2. 如果仅涉及两个部分,则构建一个运行两个进程的单个映像。我还看到了其他一些问题,这些问题针对的是提供动态内容的PHP-FPM组件以及提供静态内容并向PHP-FPM转发一些请求的nginx服务器,但是其中所有静态和动态内容都是“应用程序”,而HTTP-via-nginx入口点是“容器中的单个入口点”。 supervisord是必要时的事实上默认控制过程。

  3. 在“生产”容器中,编写一些启动时代码,以将数据从图像中的某些内容复制到共享数据位置

    #!/bin/sh
    # Run me as the image's ENTRYPOINT.
    if [ -d /data ]; then
      cp -r /app/static /data
    done
    exec "$@"
    

    这将在每次启动时重新填充数据量。