GitLab CI 安装的 docker 卷在管道中保持为空

A. *_*ijk 4 gitlab docker kubernetes docker-compose

当我尝试在容器内安装 docker 卷时,我的 GitLab 管道出现问题。在解释问题之前,首先我将描述我的整个设置,因为我认为这对于理解问题非常重要,因为我认为这就是我遇到这个问题的原因。

设置

好的,首先,我有一个 kubernetes 集群。这个集群运行我的gitlab/gitlab-ee:15.8.0-ee.0图像。我还在这个集群中安装了 GitLab 运行程序,这样我当然就可以运行管道了。然后我安装的最后一个是一个docker实例,因为我看到你可以docker.sock从你的主机挂载到gitlab管道,但是不推荐这样做,因为整个集群都依赖它docker.sock,所以我有另一个docker实例在运行我docker.sock只为管道安装它。我使用这 3 个部署来运行 GitLab 管道。

问题

我对一切设置方式感到满意,但我认为我仍然缺少一些配置,因为 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"
Run Code Online (Sandbox Code Playgroud)

使用以下 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
Run Code Online (Sandbox Code Playgroud)

现在这个管道的作用是构建 docker 容器,然后启动它们。然后它运行ls命令打印出容器工作目录中的所有文件。但是,该工作目录是空的。docker-compose-test.yml这是由以下行中的卷安装引起的:

volumes:
  - .:/var/www/html
Run Code Online (Sandbox Code Playgroud)

在 Dockerfile 中我也有这个:

COPY . /var/www/html/
Run Code Online (Sandbox Code Playgroud)

因此,当我删除 中的卷装载时docker-compose-test.yml,所有文件都在那里,因此复制确实适用于Dockerfile,但稍后无法装载它。我看到了这个线程并尝试了他们的一些解决方案并使用他们的测试脚本进行了测试:

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
Run Code Online (Sandbox Code Playgroud)

但这仍然导致一个空/mnt目录,而本test_file应该在那里。在 GitLab 运行程序中,我将此行添加到配置中:

volumes = ["/cache", "/builds:/builds"]
Run Code Online (Sandbox Code Playgroud)

不幸的是,这并没有改变任何事情。我不确定,但我的猜测是我需要从其他 docker 实例访问 /builds,因为我有一种感觉,我正在从主机安装 /builds,这不是我在我的 docker 中使用的 docker管道。如果是这种情况,我不确定如何配置我的 Kubernetes 集群以使用另一个集群。奇怪的是,当我这样做时cd /builds/projects/laravel-api(我的存储库已命名laravel-api并且位于projects组内),然后ls在我的管道中,我确实看到我的存储库包含所有文件。但是当我尝试在 docker-compose-test.yml 中安装该目录时,我仍然得到一个空目录。所以我的意思是:

volumes:
  - /builds/projects/laravel-api:/var/www/html
Run Code Online (Sandbox Code Playgroud)

因此,构建后安装卷的每种方法都会导致空目录......

包起来

所以总结一下这个问题。我在管道中执行的每种形式的安装最终都会产生一个空目录。当从 Dockerfile 复制文件时,只有目录可以工作,但这不是我可以使用的。

我希望这能涵盖整个问题。非常感谢一些帮助!如果对设置或类似问题有任何疑问,请询问我会尽快回复!

Pie*_* B. 6

这是一个棘手的问题,需要深入解释 GitLab 和 Kubernetes 动态。

您的问题摘要:

  volumes:
    - .:/var/www/html
    # or 
    # - /builds/projects/laravel-api:/var/www/html
Run Code Online (Sandbox Code Playgroud)

您正在使用的 Docker 服务与您作业的 Pod 不共享相同的文件系统。当您指示 Docker 挂载/builds/projects/laravel-api(或.解析为相同)时,Docker 将从其自己的文件系统挂载此目录,该文件系统实际上是空的。

正如您已经指出的,您必须以某种方式/builds在作业的 Pod 和 Docker 服务之间共享目录。

解决方案1:在Docker服务和作业的Pod之间共享持久卷

创建持久卷声明 (PVC),以便它们共享/builds目录:

  • 创建 PVC,例如:
      volumes:
        - .:/var/www/html
        # or 
        # - /builds/projects/laravel-api:/var/www/html
    
    Run Code Online (Sandbox Code Playgroud)
  • 配置 GitLab Runner Kubernetes 执行器以将 PVC 挂载在/builds. 例如:
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: gitlab-claim
    spec:
      accessModes:
        - ReadWriteOnce
        # or ReadWriteMany
    resources:
      requests:
        storage: 8Gi
    
    Run Code Online (Sandbox Code Playgroud)
  • 配置 Docker 部署以将 PVC 挂载于/builds。这取决于您如何配置 Docker 服务,但您可能必须配置容器规范,例如:
    [[runners]]
      [runners.kubernetes]
          [[runners.kubernetes.volumes.pvc]]
          name = "gitlab-claim"
          mount_path = "/builds"
    
    Run Code Online (Sandbox Code Playgroud)

你的设置看起来像这样。两个 Pod 将共享安装在 的相同卷/builds

重要提示:仔细选择ReadWriteOnce/ReadWriteMany访问模式

  • 如果您的提供商支持,请使用ReadWriteMany它,它将允许在多个节点之间共享相同的卷。
  • 如果没有,ReadWriteOnce将要求作业的 Pod 与 Docker 服务在同一节点上运行,因为卷无法在 Kubernetes 节点之间共享。

解决方案2:使用GitLab服务运行Docker-in-Docker (DinD)

此设置有点不同,因为每次构建都会部署一个新的 Docker 服务。但是,数据不会跨作业持久保存,因为将为每个作业重新创建 Docker 服务。

为构建容器定义的卷也会自动安装到所有服务容器。emptyDir然后,您可以在作业的 pod 和 Docker DinD 服务之间共享

在这种情况下,您将拥有一个 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)


这有点复杂,涉及很多不同的概念。不要犹豫提出问题,我会尽可能回答或编辑。