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

uklbhaso  于 2023-03-17  发布在  Kubernetes
关注(0)|答案(1)|浏览(141)

当我尝试在一个容器中挂载一个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复制文件时,只有目录才能工作,但这不是我能处理的。
我希望这涵盖了整个问题。一些帮助真的很感激!如果有任何问题的设置或类似的东西,请问我会尽快回应!

5t7ly7z5

5t7ly7z51#

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

问题摘要:

volumes:
    - .:/var/www/html
    # or 
    # - /builds/projects/laravel-api:/var/www/html

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

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

溶液1:在Docker服务和作业Pod之间共享永久卷

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

  • 创建PVC,例如:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: gitlab-claim
spec:
  accessModes:
    - ReadWriteOnce
    # or ReadWriteMany
resources:
  requests:
    storage: 8Gi
  • 配置GitLab Runner Kubernetes执行器在/builds上挂载PVC。例如:
[[runners]]
  [runners.kubernetes]
      [[runners.kubernetes.volumes.pvc]]
      name = "gitlab-claim"
      mount_path = "/builds"
  • 将Docker部署配置为在/builds上装载PVC。这取决于您配置Docker服务的方式,但您可能必须配置容器规范,例如:
spec:
  volumes:
    - name: job-volume
      persistentVolumeClaim:
        claimName: gitlab-claim
  containers:
    - name: docker
      image: docker:dind
      # ...
      volumeMounts:
        - name: job-volume
          mountPath: /builds

您的设置将看起来像这样。两个pod将共享同一个卷挂载在/builds

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

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

溶液2:使用GitLab服务来运行Dinker-in-Docker(DinD)

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

  • 为构建容器定义的卷也会自动为所有服务容器装载。*.然后,您可以在作业的pod和Docker DinD服务之间共享emptyDir
  • 配置DinD service on your CI,例如:
image: docker:20.10.16-dind
variables:
  DOCKER_HOST: tcp://docker:2375
  DOCKER_TLS_CERTDIR: ""

services:
- docker:20.10.16-dind
  • 将Kubernetes执行器配置为在/builds上装载emptyDir,该emptyDir将在作业容器和服务容器之间共享:
[runners.kubernetes]
  [[runners.kubernetes.volumes.empty_dir]]
    name = "builds-data"
    mount_path = "/builds"

在本例中,您将有一个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)
这是一个有点复杂,涉及到很多不同的概念。不要犹豫,提出问题,我会回答或编辑尽可能。

相关问题