在CircleCI工作流中构建Docker镜像并在后续步骤中使用,而无需使用外部注册表

112次阅读
没有评论

问题描述

在CircleCI的工作流中,有三个作业(jobs)需要执行:
1. 编译和测试软件
2. 将新的Docker镜像推送到ECR(Amazon Elastic Container Registry)
3. 部署到EKS(Amazon Elastic Kubernetes Service)

第一个作业使用了cimg/elixir镜像来进行编译和测试,但是目前在第一个作业的CircleCI YAML配置和第二个作业的Dockerfile中出现了很多重复的步骤,这些步骤是在构建和运行时都需要的依赖。现在我正在寻找一种方法来消除这种重复。

我的想法是调整Dockerfile,使其成为一个多阶段构建,其中包含一个特定的build_env目标,可以获得在编译开始之前使用的镜像。我想在第一个作业中使用这个准确的镜像来进行编译和测试。CircleCI工作流可以从初始作业开始构建镜像(通过传递--target参数给docker build命令),然后第1)个作业可以在该镜像内执行。然而,这似乎需要使用外部Docker注册表——在互联网上推送几百兆的镜像,然后在紧接着的下一个作业中重新下载似乎有点浪费。

一个非常简短的支持文章建议使用docker savedocker load命令,但我不清楚docker load如何帮助在新加载的镜像内运行CircleCI作业:我如何在CircleCI工作流中构建Docker镜像,然后使后续作业在该容器内执行,而无需将其推送到任何地方?

评论回复:docker load命令会将镜像加载到本地Docker守护程序中,然后你可以使用docker run命令使用该镜像启动容器。听起来这正是你想做的。

解决方案

使用docker savedocker load

你可以使用docker save命令将Docker镜像保存到本地文件中,然后使用docker load命令将它加载回本地Docker守护程序。接下来,你可以使用加载后的镜像来运行后续的CircleCI作业。

以下是一种可能的解决方案:
1. 在第一个作业中,使用docker save命令将编译和测试所需的镜像保存到本地文件,例如:my_image.tar
2. 在第二个作业中,使用docker load命令将保存的镜像加载回本地Docker守护程序中。
3. 在后续作业中,使用加载后的镜像运行所需的任务。

以下是一个示例步骤:

version: 2.1
jobs:
  build:
    docker:
      - image: cimg/elixir
    steps:
      # 编译和测试步骤

  save_image:
    docker:
      - image: cimg/elixir
    steps:
      - setup_remote_docker:
          version: 20.10.10
      - run:
          name: Save Docker image
          command: |
            docker save -o my_image.tar cimg/elixir

  use_saved_image:
    docker:
      - image: cimg/elixir
    steps:
      - setup_remote_docker:
          version: 20.10.10
      - run:
          name: Load and use saved Docker image
          command: |
            docker load -i my_image.tar
            # 在加载的镜像上运行任务

多阶段构建

你的初衷是使用多阶段构建来减少重复步骤。你可以将Dockerfile改为多阶段构建,其中一个阶段用于构建镜像,另一个阶段用于运行编译和测试任务。这样,你只需要在第一个作业中构建镜像,并在后续作业中运行编译和测试任务,而无需将镜像保存到本地文件。

以下是一个示例Dockerfile

# 阶段1:构建镜像
FROM cimg/elixir AS build_env
# 添加构建所需的步骤

# 阶段2:运行编译和测试
FROM build_env AS test_env
# 添加编译和测试所需的步骤

然后,在CircleCI配置中,你可以按照以下方式使用多阶段构建:

version: 2.1
jobs:
  build_and_test:
    docker:
      - image: cimg/elixir
    steps:
      - checkout
      - run:
          name: Build and test
          command: |
            docker build -t my_image:latest .
            docker run my_image:latest /path/to/test/script.sh

在这个示例中,Dockerfile使用了多阶段构建,首先构建了一个镜像build_env,然后在另一个阶段test_env中运行编译和测试任务。在CircleCI配置中,我们直接使用了docker build命令构建镜像,并使用docker run命令在构建后的镜像上运行测试脚本。

注意:这些解决方案可能需要根据你的实际需求进行适当的调整和配置。

提示:无论选择哪种方法,请确保在实际应用中进行测试,以确保解决方案适用于你的工作流程。

正文完