我如何在使用 docker:dind 构建 docker 图像时在 gitlab ci 中缓存

How do i cache in gitlab ci while building docker images with docker:dind

我有一个 gitlab-ci.yml 这样的:

build and push docker image:
  stage: publish  
  variables:
    DOCKER_REGISTRY: amazon-registry
    AWS_DEFAULT_REGION: ap-south-1
    APP_NAME: sample-app
    DOCKER_HOST: tcp://docker:2375
  image: 
    name: amazon/aws-cli
    entrypoint: [""]
  services:
    - docker:dind 
  before_script:
    - amazon-linux-extras install docker
  script:
    - docker build -t $DOCKER_REGISTRY/$APP_NAME:master .
    - aws ecr get-login-password | docker login --username AWS --password-stdin $DOCKER_REGISTRY
    - docker push $DOCKER_REGISTRY/$APP_NAME:master

此步骤需要 19=8 分钟才能完成,因为 docker 图像步骤未缓存。我希望能够缓存 before_script amazon-linux-extras install docker 以及我正在构建的 docker 图像。我们 运行 在我们自己的 gitlab runners 上。我搜索了答案,但找到了 4 年前的解决方案。有办法解决这个问题吗?另外,离开 docker:dind 会有帮助吗?

Gitlab CI 缓存并不是那样工作的。例如,如果你有一个安装 npm 依赖项的工作,你可以缓存生成的 node_modules 目录,这样 npm install 就不需要再次成为 运行,但这无济于事用于安装系统软件包。

关于 docker:dind 服务,如果没有该服务,您将无法 运行 像 docker build...docker push ... 这样的命令,即使您切换了图像工作用于 docker:latest。这有点违反直觉,但能够 运行 这些命令的唯一方法是使用 docker-in-docker 服务。

但是,您并非不走运。我建议您将 before_script 阶段中的步骤移动到您自己的扩展 amazon/aws-cli 的 docker 图像。只要您可以访问 docker hub,Gitlab 包含的注册表(如果使用 gitlab.com 它是可用的,否则管理员必须 enable/configure 它),Amazon 的注册表(我认为是 ECR?),或私人 运行 注册表,您可以创建自己的自定义图像并在 Gitlab CI 管道中使用它们。

这是一个例子Docker文件:

FROM amazon/aws-cli
RUN amazon-linux-extras install docker

这就是扩展现有 amazon/aws-cli 映像并将 before_script 安装移动到 Docker 所需的全部内容。文件完成后,运行

docker build /path/to/dockerfile-directory -t my_tag:latest

之后,您需要登录到您的注册表,docker login my.registry.example.com,然后上传图像 docker push my_tag:latest。如果您不使用 Gitlab 的注册表或 public docker 集线器,则需要配置您的作业或 运行ners(其中之一)以便他们可以使用您的注册表进行身份验证。你可以在这里阅读:https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#define-an-image-from-a-private-container-registry

接下来您只需在您的管道中使用它:

build and push docker image:
  stage: publish  
  variables:
    DOCKER_REGISTRY: amazon-registry
    AWS_DEFAULT_REGION: ap-south-1
    APP_NAME: sample-app
    DOCKER_HOST: tcp://docker:2375
  image: 
    name: my_tag:latest
    entrypoint: [""]
  services:
    - docker:dind
  script:
    - docker build -t $DOCKER_REGISTRY/$APP_NAME:master .
    - aws ecr get-login-password | docker login --username AWS --password-stdin $DOCKER_REGISTRY
    - docker push $DOCKER_REGISTRY/$APP_NAME:master

您可以做的另一件事来节省流水线时间(如果它适用),就是在您的 Docker 文件发生更改时仅 运行 这一步。这样,如果它没有但其他工作依赖它,他们可以重复使用最后创建的图像。您可以使用 rules 关键字和 changes:

build and push docker image:
  stage: publish  
  variables:
    DOCKER_REGISTRY: amazon-registry
    AWS_DEFAULT_REGION: ap-south-1
    APP_NAME: sample-app
    DOCKER_HOST: tcp://docker:2375
  image: 
    name: my_tag:latest
    entrypoint: [""]
  services:
    - docker:dind
  when: never
  rules:
    - changes:
      - Dockerfile
      when: always
  script:
    - docker build -t $DOCKER_REGISTRY/$APP_NAME:master .
    - aws ecr get-login-password | docker login --username AWS --password-stdin $DOCKER_REGISTRY
    - docker push $DOCKER_REGISTRY/$APP_NAME:master

根级别 when: never 将作业的默认值设置为从不 运行,但 rules 部分检查 Docker 是否有更改文件(如果需要,接受多个文件)。如果有更改,作业将始终为 运行.

您可以在此处查看有关 rules 关键字的详细信息:https://docs.gitlab.com/ee/ci/yaml/#rules

您可以在此处查看有关 Gitlab CI 的自定义 docker 图像的详细信息:https://docs.gitlab.com/ee/ci/docker/using_docker_images.html

我尝试过的一件事是在 docker 构建中使用缓存层。
您可以从注册表中提取现有图像,然后使用 --cache-from 参数进行构建。

这份工作 shell 会是这样的:

  variables:
    IMAGE_TAG: $DOCKER_REGISTRY/$APP_NAME:master
  script:
    - aws ecr get-login-password | docker login --username AWS --password-stdin $DOCKER_REGISTRY
    - docker pull $IMAGE_TAG || true
    - docker build --cache-from $IMAGE_TAG -t $IMAGE_TAG .
    - docker push $IMAGE_TAG

gitlab-ciofficial document中也提到了这个方法