当我尝试在一个容器中挂载一个Docker卷时,我的GitLab管道出现了一个问题。在我解释这个问题之前,首先我将描述我的整个设置,因为我认为这对理解这个问题是非常必要的,因为我认为这就是我出现这个问题的原因。
设置
好的,首先,我有一个kubernetes集群。这个集群运行我的gitlab/gitlab-ee:15.8.0-ee.0
映像。我在这个集群中安装了一个GitLab runner,这样我就可以运行管道了。然后我安装的最后一个东西是一个docker示例,因为我看到你可以从你的主机挂载docker.sock
到gitlab管道,但这是不推荐的。因为整个集群都依赖于这个docker.sock
,所以我运行了另一个docker示例,并且只为管道安装了这个docker.sock
。
问题是
我对所有设置的方式都很满意,但我认为我仍然缺少一些配置,因为Docker卷的挂载在管道中不能正常工作。我有这个脚本来测试它,它包含以下代码:
image: docker:20.10.16-dind
variables:
DOCKER_HOST: "tcp://docker-service:2375" # <-- Address to reach the docker instance from my cluster
DOCKER_COMPOSE_CMD: "docker-compose -f docker-compose-test.yml"
stages:
- test
test:
stage: test
script:
- $DOCKER_COMPOSE_CMD down --volumes --remove-orphans
- $DOCKER_COMPOSE_CMD build
- $DOCKER_COMPOSE_CMD --env-file .env.pipeline up -d
- $DOCKER_COMPOSE_CMD exec -T -e APP_ENV=testing laravel-api-test sh -c "ls"
使用以下docker-compose-test.yml:
version: '3.7'
services:
laravel-api-test:
build:
context: .
dockerfile: docker/development/Dockerfile
volumes:
- .:/var/www/html
environment:
- COMPOSER_MEMORY_LIMIT=-1
depends_on:
- database-test
database-test:
image: postgres:15.1-alpine
ports:
- ${DB_PORT}:5432
environment:
POSTGRES_DB: ${DB_DATABASE}
POSTGRES_PASSWORD: ${DB_PASSWORD_SECRET}
POSTGRES_USER: ${DB_USERNAME_SECRET}
redis-test:
image: redis:7.0.8
ports:
- ${REDIS_PORT}:6379
networks:
default:
name: application
现在这个管道所做的是,它构建docker容器,然后启动它们。然后它运行ls
命令,打印出容器的工作目录中的所有文件。但是,这个工作目录是空的。这是由于docker-compose-test.yml
中的卷挂载导致的,其中有以下行:
volumes:
- .:/var/www/html
在Dockerfile中,我还有以下内容:
COPY . /var/www/html/
因此,当我删除docker-compose-test.yml
中的卷挂载时,所有文件都在那里,所以复制确实适用于Dockerfile
,但稍后不会挂载它。我看到了this线程,尝试了他们的一些解决方案,并使用他们的测试脚本进行了测试:
variables:
SHARED_PATH: /builds/shared/$CI_PROJECT_PATH
script:
- mkdir -p ${SHARED_PATH}
- touch ${SHARED_PATH}/test_file
- docker run -v ${SHARED_PATH}:/mnt ubuntu ls /mnt
但这仍然导致了一个空的/mnt
目录,而test_file
应该在那里。在GitLab runner中,我在config中添加了这一行:
volumes = ["/cache", "/builds:/builds"]
不幸的是,这并没有改变什么,我不确定,但我的猜测是我需要从我的另一个Docker示例访问/build,因为我有一种感觉,我正在从主机挂载/build,而这不是我在我的管道中使用的Docker,如果是这样的话,我不知道如何配置我的Kubernetes集群来使用另一个。奇怪的是,当我做cd /builds/projects/laravel-api
时(我的repo名为laravel-api
,它在projects
组中),然后在我的pipeline中ls
,我确实看到我的存储库包含了所有的文件,但是当我尝试在我的docker-compose-test.yml中挂载该目录时,我仍然得到一个空目录,所以我的意思是:
volumes:
- /builds/projects/laravel-api:/var/www/html
因此,在构建后装载卷的每种方法都会导致空目录...
结束
所以总结一下这个问题。我在管道中所做的每一种形式的挂载最终都会导致一个空目录。当从Dockerfile复制文件时,只有目录才能工作,但这不是我能处理的。
我希望这涵盖了整个问题。一些帮助真的很感激!如果有任何问题的设置或类似的东西,请问我会尽快回应!
1条答案
按热度按时间5t7ly7z51#
这是一个棘手的问题,需要深入解释GitLab和Kubernetes动力学。
问题摘要:
您正在使用的Docker服务与作业的Pod共享不同的文件系统。当您指示Docker装载
/builds/projects/laravel-api
(或解析为相同的.
)时,Docker将 * 从其自己的文件系统 * 装载此目录,该文件系统实际上是空的。正如您已经指出的,必须以某种方式在作业的Pod和Docker服务之间共享
/builds
目录。溶液1:在Docker服务和作业Pod之间共享永久卷
创建永久卷声明(PVC),以便它们共享
/builds
目录:/builds
上挂载PVC。例如:/builds
上装载PVC。这取决于您配置Docker服务的方式,但您可能必须配置容器规范,例如:您的设置将看起来像这样。两个pod将共享同一个卷挂载在
/builds
。重要提示:在
ReadWriteOnce
/ReadWriteMany
访问模式之间仔细选择:ReadWriteMany
,它将允许在多个节点之间共享同一卷。ReadWriteOnce
将要求您的作业Pod与Docker服务在同一节点上运行,因为卷无法在Kubernetes节点之间共享。溶液2:使用GitLab服务来运行Dinker-in-Docker(DinD)
这个设置有点不同,因为每次构建都要部署一个新的Docker服务。但是,数据不会跨作业持久化,因为Docker服务将为每个作业重新创建。
emptyDir
:/builds
上装载emptyDir
,该emptyDir
将在作业容器和服务容器之间共享:在本例中,您将有一个Pod,其中包含作业容器和DinD服务容器,两者共享
emptyDir
卷/builds
。您的laravel
和其他容器将在DinD服务容器 * 内部 * 运行。哪种解决方案更好?
取决于上下文:
效率方面,解决方案1更好,因为您的Docker服务将保持静态,并保留构建缓存、下载的映像等,从而允许更快的Docker构建和部署。但是,您的Docker服务可能会与多个项目/实体共享,从而导致安全风险:访问Docker服务的任何人也可以访问与其项目或范围无关的容器,并最终从另一个项目容器/卷/......中检索敏感数据。
例如:考虑使用Docker服务来部署测试应用的项目A和项目B,每个项目都被配置为访问AWS帐户A和B。具有项目A访问权限的实体可以容易地从为项目B运行的容器获取凭证并访问AWS帐户B以在EC2上启动加密挖掘器。
可以根据您的需要部署每个项目或范围专用的Docker服务,和/或通过SSH/TLS进行安全保护以仅允许某些实体使用它,从而降低风险。
解决方案2更易于设置和保护,但效率较低:每次构建都将启动一个新的CI Docker服务,因此您必须在每个管道中再次下载Docker映像,并且任何Docker构建缓存都将丢失。您可以优化CI配置以下载缓存等,但它需要更复杂的设置(如果您准备增加复杂性,不妨选择解决方案2)
这是一个有点复杂,涉及到很多不同的概念。不要犹豫,提出问题,我会回答或编辑尽可能。