为您的应用程序配置CI / CD

预计阅读时间:12分钟

该页面将指导您完成使用Docker容器设置GitHub Action CI / CD管道的过程。在建立新管道之前,我们建议您先阅读Ben的CI / CD最佳实践博客

本指南包含有关如何执行以下操作的说明:

  1. 以一个示例Docker项目为例来配置GitHub Actions
  2. 设置GitHub Actions工作流程
  3. 优化您的工作流程,以减少拉取请求的数量和总构建时间
  4. 仅将特定版本推送到Docker Hub
  5. 使用多阶段构建优化图像

设置一个Docker项目

让我们开始吧。本指南以一个简单的Docker项目为例。该SimpleWhaleDemo库包含Ngnix高山图像。您可以克隆此存储库,也可以使用自己的Docker项目。

SimpleWhaleDemo

在开始之前,请确保您可以从创建的任何工作流程访问Docker Hub。去做这个:

  1. 将您的Docker ID作为秘密添加到GitHub。导航到您的GitHub存储库,然后单击设置>秘密>新秘密

  2. 使用名称DOCKER_HUB_USERNAME和您的Docker ID作为值创建一个新的秘密。

  3. 创建一个新的个人访问令牌(PAT)。要创建新令牌,请转到Docker Hub设置,然后单击新建访问令牌

  4. 我们将此令牌称为simplewhaleci

    新的访问令牌

  5. 现在,将此个人访问令牌(PAT)作为第二个秘密添加到名称为的GitHub秘密UI中DOCKER_HUB_ACCESS_TOKEN

    GitHub的秘密

设置GitHub Actions工作流程

在上一节中,我们创建了一个PAT并将其添加到GitHub,以确保我们可以从任何工作流程访问Docker Hub。现在,让我们设置GitHub Actions工作流程,以在Hub中构建和存储图像。我们可以通过创建两个Docker操作来实现此目的:

  1. 第一项操作使我们能够使用存储在GitHub存储库中的秘密登录Docker Hub。
  2. 第二个是构建和推送操作。

在此示例中,让我们将push标志设置为,true因为我们也要推送。然后,我们将添加一个标签以指定始终使用最新版本。最后,我们将回显图像摘要以查看被推送的内容。

设置工作流程:

  1. 转到GitHub中的存储库,然后单击“操作” >“新建工作流”
  2. 单击自己设置工作流程,然后添加以下内容:

首先,我们将这个工作流程命名为:

name: CI to Docker Hub

然后,我们将选择运行此工作流程的时间。在我们的示例中,我们将针对项目的主分支进行每次尝试:

on:
  push:
    branches: [ main ]

现在,我们需要指定我们在操作中实际想要发生的事情(做什么工作),我们将添加构建版本1,并选择它在最新的可用Ubuntu实例上运行:

jobs:

  build:
    runs-on: ubuntu-latest

现在,我们可以添加所需的步骤。第一个在检出我们的存储库$GITHUB_WORKSPACE,以便我们的工作流可以访问它。第二个是使用我们的PAT和用户名登录Docker Hub。第三个是Builder,该动作通过一个简单的Buildx动作在后台使用BuildKit,我们还将对其进行设置

    steps:

      - name: Check Out Repo 
        uses: actions/checkout@v2

      - name: Login to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@v1

      - name: Build and push
        id: docker_build
        uses: docker/build-push-action@v2
        with:
          context: ./
          file: ./Dockerfile
          push: true
          tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest

      - name: Image digest
        run: echo ${{ steps.docker_build.outputs.digest }}

现在,让工作流首次运行,然后调整Dockerfile以确保CI正在运行并推送新的映像更改:

CI到Docker Hub

优化工作流程

接下来,让我们看看如何通过构建缓存优化GitHub Actions工作流程。这有两个主要优点:

  1. 构建缓存减少了构建时间,因为它不必重新下载所有映像,并且
  2. 这也减少了我们针对Docker Hub完成的拉动次数。我们需要利用GitHub缓存来利用这一点。

让我们用构建缓存设置一个Builder。首先,我们需要为构建器设置缓存。在此示例中,让我们添加路径和键以使用GitHub缓存对此进行存储。


      - name: Cache Docker layers
        uses: actions/cache@v2
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-buildx-

最后,在将构建器和构建缓存片段添加到Actions文件的顶部之后,我们需要向构建和推送步骤添加一些额外的属性。这涉及:

设置构建器以使用buildx步骤的输出,然后使用缓存,我们在前面进行了设置,以用于存储和检索

      - name: Login to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
      - name: Build and push
        id: docker_build
        uses: docker/build-push-action@v2
        with:
          context: ./
          file: ./Dockerfile
          builder: ${{ steps.buildx.outputs.name }}
          push: true
          tags:  ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
          cache-from: type=local,src=/tmp/.buildx-cache
          cache-to: type=local,dest=/tmp/.buildx-cache
      - name: Image digest
        run: echo ${{ steps.docker_build.outputs.digest }}

现在,再次运行工作流程,并验证它是否使用了构建缓存。

将标记的版本推送到Docker Hub

之前,我们学习了如何为Docker项目设置GitHub Actions工作流,如何通过设置带有构建缓存的构建器来优化工作流。现在让我们看一下如何进一步改进它。我们可以通过添加使标记版本与所有提交到master的行为不同的功能来做到这一点。这意味着只推送特定版本,而不是每次提交都会更新Docker Hub上的最新版本。

您可以考虑使用这种方法,使您的提交进入本地注册表,然后在每晚测试中使用。这样,您就可以始终测试最新版本,同时保留标记的版本以发布到Docker Hub。

这涉及两个步骤:

  1. 修改GitHub工作流程以仅将具有特定标签的提交推送到Docker Hub
  2. 设置GitHub Actions文件以将最新提交作为映像存储在GitHub注册表中

首先,让我们修改现有的GitHub工作流程,使其仅在存在特定标签时才推送到Hub。例如:

on:
  push:
    tags:
      - "v*.*.*"

这样可以确保仅当我们使用“V.n.n.n.让我们测试一下”标记提交时,主配置项才会触发。例如,运行以下命令:

git tag -a v1.0.2
git push origin v1.0.2

现在,转到GitHub并检查您的操作

推送标记版本

现在,让我们设置第二个GitHub动作文件,以将最新提交作为映像存储在GitHub注册表中。您可能想要这样做:

  1. 运行您的每晚测试或重复测试,或者
  2. 与同事共享进行中的工作图像。

让我们克隆我们之前的GitHub动作,并为所有推送添加回我们先前的逻辑。这意味着我们有两个工作流程文件,我们将处理上一个工作流程文件和新工作流程文件。接下来,将您的Docker Hub登录名更改为GitHub容器注册表登录名:

        if: github.event_name != 'pull_request'
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GHCR_TOKEN }}

切记更改图像的标记方式。以下示例将“最新”保留为唯一标记。但是,您可以根据需要添加任何逻辑:

  tags: ghcr.io/${{ github.repository_owner }}/simplewhale:latest

更新标记的图像

现在,我们将有两种不同的流程:一种用于更改对master的流程,另一种用于我们的拉取请求。接下来,我们需要修改之前的内容,以确保将PR推送到GitHub注册表而不是Docker Hub。

使用多阶段构建优化图像

现在,让我们看一下Dockerfile,看看如何优化它以在开发中工作,以及获取较小的映像以在生产环境中运行容器。由于这是文件中的最后一步,因此在您运行docker build而不指定目标时,默认情况下将使用它来构建映像:

$ docker build --tag java-docker .
docker build --tag java-docker .

[+] Building 1.2s (15/15) FINISHED
 => [internal] load build definition from Dockerfile
 => => transferring dockerfile: 37B
 => [internal] load .dockerignore
 => => transferring context: 2B
 => [internal] load metadata for docker.io/library/openjdk:11-jre-slim
 => [internal] load metadata for docker.io/library/openjdk:16-alpine3.13
 => [internal] load build context
 => => transferring context: 11.48kB
 => [production 1/2] FROM docker.io/library/openjdk:11-jre-slim@sha256:85795599f4c765182c414a1eb4e272841e18e2f267ce5010ea6a266f7f26e7f6
 => [base 1/6] FROM docker.io/library/openjdk:16-alpine3.13@sha256:49d822f4fa4deb5f9d0201ffeec9f4d113bcb4e7e49bd6bc063d3ba93aacbcae
 => CACHED [base 2/6] WORKDIR /app
 => CACHED [base 3/6] COPY .mvn/ .mvn
 => CACHED [base 4/6] COPY mvnw pom.xml ./
 => CACHED [base 5/6] RUN ./mvnw dependency:go-offline
 => CACHED [base 6/6] COPY src ./src
 => CACHED [build 1/1] RUN ./mvnw package
 => CACHED [production 2/2] COPY --from=build /app/target/spring-petclinic-*.jar /spring-petclinic.jar
 => exporting to image
 => => exporting layers
 => => writing image sha256:c17469b9e2f30537060f48bbe5d9d22003dd35edef7092348824a2438101ab3a
 => => naming to docker.io/library/java-docker

第二个有趣的点是,此步骤不将基本目标或JDK映像作为参考。相反,它使用Java Runtime Environment映像。请注意,您不需要具有所有开发依赖关系的大型图像即可在生产环境中运行您的应用程序。限制生产映像中的依赖项数量可能会大大限制攻击面。

FROM openjdk:11-jre-slim as production
EXPOSE 8080

COPY --from=build /app/target/spring-petclinic-*.jar /spring-petclinic.jar

CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/spring-petclinic.jar"]

容器还将自动公开其8080端口,并复制在构建步骤中生成的Java归档文件,以在容器启动时使用它。

以这种方式构建的生产映像仅包含带有最终应用程序存档的运行时环境,而这正是启动Spring Pet Clinic应用程序所需的内容。

$ docker build --tag java-docker:jdk . --target development
$ docker build --tag java-docker:jre .
$  docker images
REPOSITORY       TAG        IMAGE ID         CREATED        SIZE
java-docker      jre        c17469b9e2f3     3 hours ago    270MB
java-docker      jdk        4c15436d8ab7     5 hours ago    567MB

下一步

在本模块中,您学习了如何为现有Docker项目设置GitHub Actions工作流,优化工作流以缩短构建时间并减少请求请求的数量,最后,我们学习了如何仅将特定版本推送到Docker Hub。您还可以针对最新标签设置夜间测试,测试每个PR或对我们正在使用的标签进行更精美的处理,并对图像中的同一标签使用Git标签。

您也可以考虑将应用程序部署到云中。有关详细说明,请参阅:

将您的应用程序部署到云

反馈

通过提供您的反馈帮助我们改善此主题。通过在Docker Docs GitHub存储库中创建问题,让我们知道您的想法。或者,创建PR以建议更新。


JavaCI / CD本地开发