如何使用 Gitlab CI 在跨两个阶段的工作期间保持 docker 镜像构建?

How*_*ins 10 docker gitlab-ci gitlab-ci-runner docker-compose

我在 EC2 上使用 Gitlab 运行程序build,在 ECS 上test使用docker 映像。deploy

我使用“推/拉”逻辑开始我的 CI 工作流程:我在第一阶段构建所有 docker 映像并将它们推送到我的 gitlab 存储库,然后在该阶段拉它们test

我认为通过保持阶段之间构建的图像可以大大缩短工作流程build时间。buildtest

我的gitlab-ci.yml看起来像这样:

stages:
  - build
  - test
  - deploy

build_backend:
  stage: build
  image: docker
  services:
    - docker:dind
  before_script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
  script:
    - docker build -t backend:$CI_COMMIT_BRANCH ./backend
  only:
    refs:
      - develop
      - master

build_generator:
  stage: build
  image: docker
  services:
    - docker:dind
  before_script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
  script:
    - docker build -t generator:$CI_COMMIT_BRANCH ./generator
  only:
    refs:
      - develop
      - master

build_frontend:
  stage: build
  image: docker
  services:
    - docker:dind
  before_script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
  script:
    - docker build -t frontend:$CI_COMMIT_BRANCH ./frontend
  only:
    refs:
      - develop
      - master

build_scraping:
  stage: build
  image: docker
  services:
    - docker:dind
  before_script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
  script:
    - docker build -t scraping:$CI_COMMIT_BRANCH ./scraping
  only:
    refs:
      - develop
      - master


test_backend:
  stage: test
  needs: ["build_backend"]
  image: docker
  services:
    - docker:dind
  before_script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
    - DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
    - mkdir -p $DOCKER_CONFIG/cli-plugins
    - apk add curl
    - curl -SL https://github.com/docker/compose/releases/download/v2.3.2/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
    - chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
  script:
    - docker compose -f docker-compose-ci.yml up -d backend
    - docker exec backend pip3 install --no-cache-dir --upgrade -r requirements-test.txt
    - docker exec db sh mongo_init.sh
    - docker exec backend pytest test --junitxml=report.xml -p no:cacheprovider
  artifacts:
    when: always
    reports:
      junit: backend/report.xml
  only:
    refs:
      - develop
      - master

test_generator:
  stage: test
  needs: ["build_generator"]
  image: docker
  services:
    - docker:dind
  before_script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
    - DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
    - mkdir -p $DOCKER_CONFIG/cli-plugins
    - apk add curl
    - curl -SL https://github.com/docker/compose/releases/download/v2.3.2/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
    - chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
  script:
    - docker compose -f docker-compose-ci.yml up -d generator
    - docker exec generator pip3 install --no-cache-dir --upgrade -r requirements-test.txt
    - docker exec generator pip3 install --no-cache-dir --upgrade -r requirements.txt
    - docker exec db sh mongo_init.sh
    - docker exec generator pytest test --junitxml=report.xml -p no:cacheprovider
  artifacts:
    when: always
    reports:
      junit: generator/report.xml
  only:
    refs:
      - develop
      - master
   
[...]
Run Code Online (Sandbox Code Playgroud)

gitlab-runner/config.toml:

concurrent = 5
check_interval = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "Docker Runner"
  url = "https://gitlab.com/"
  token = ""
  executor = "docker"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
  [runners.docker]
    tls_verify = false
    image = "docker:19.03.12"
    privileged = true
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/certs/client", "/cache"]
    shm_size = 0
Run Code Online (Sandbox Code Playgroud)

docker-compose-ci.yml:

services:
  backend:
    container_name: backend
    image: backend:$CI_COMMIT_BRANCH
    build:
      context: backend
    volumes:
      - ./backend:/app
    networks:
      default:
    ports:
      - 8000:8000
      - 587:587
      - 443:443
    environment:
      - ENVIRONMENT=development
    depends_on:
      - db

  generator:
    container_name: generator
    image: generator:$CI_COMMIT_BRANCH
    build:
      context: generator
    volumes:
      - ./generator:/var/task
    networks:
      default:
    ports:
      - 9000:8080
    environment:
      - ENVIRONMENT=development
    depends_on:
      - db

  db:
    container_name: db
    image: mongo
    volumes:
      - ./mongo_init.sh:/mongo_init.sh:ro
    networks:
      default:
    environment:
      MONGO_INITDB_DATABASE: DB
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: admin
    ports:
      - 27017:27017

  frontend:
    container_name: frontend
    image: frontend:$CI_COMMIT_BRANCH
    build:
      context: frontend
    volumes:
      - ./frontend:/app
    networks:
      default:
    ports:
      - 8080:8080
    depends_on:
      - backend

networks:
  default:
    driver: bridge
Run Code Online (Sandbox Code Playgroud)

当我context:在我的 中发表评论时docker-compose-ci.yml,Docker 找不到我的镜像,而且它确实没有在作业之间保留。

buildCI期间最好的 Docker 方法是test什么deploy?我应该压缩我的 docker 映像并使用工件在阶段之间共享它们吗?这似乎不是最有效的方法。

我有点不知道应该使用哪种方法来使用 Docker 在 Gitlab CI 中执行这样的常见工作流程。

syt*_*ech 10

最好的方法是将映像推送到注册表并在需要的其他阶段拉取它。您似乎缺少推/拉逻辑。

您还想确保在 docker 构建中利用了 docker 缓存。您可能需要cache_from:在撰写文件中指定密钥。

例如:

build:
  stage: build
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    # pull latest image to leverage cached layers
    - docker pull $CI_REGISTRY_IMAGE:latest || true

    # build and push the image to be used in subsequent stages
    - docker build --cache-from $CI_REGISTRY_IMAGE:latest --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA  # push the image

test:
  stage: test
  needs: [build]
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    # pull the image that was built in the previous stage
    - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker-compose up # or docker run or whatever
Run Code Online (Sandbox Code Playgroud)

编辑:

在带有 Docker buildkit/buildx 的现代 Docker 版本中,您可以使用 buildkit内联缓存,而不是提前拉取映像。这需要将更大的图像推送到您的存储库,但会使缓存拉取速度更快,因为 docker 可以在拉取它们之前判断哪些层对于缓存有效。正常拉动速度不受影响。

export DOCKER_BUILDKIT=1

docker build --build-arg BUILDKIT_INLINE_CACHE=1 \
             --cache-from "$CI_REGISTRY_IMAGE:latest"
             --cache-from "$CI_REGISTRY_IMAGE:$CI_REF_NAME" \
             --tag "$CI_REGISTRY_IMAGE:$CI_REF_NAME" \
             --tag "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" \ 
             .

docker push "$CI_REGISTRY_IMAGE:$CI_REF_NAME"
docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
Run Code Online (Sandbox Code Playgroud)

  • @RakeshGupta OP 遇到了问题,因为他们最终(意外地)在测试阶段重新构建了图像,而不是在构建阶段重新使用已经构建的图像。OP 期望 docker 构建过程已被缓存。所以,这个答案解决了这个问题。拉取图像以利用缓存层几乎总是比构建(和重新构建)它快得多。 (2认同)