GitHub Actions:在容器外部与内部构建?

Max*_*Max 13 github docker asp.net-core github-actions

假设我们正在使用 GitHub Actions 来构建和发布我们应用程序的容器映像。我将在这里选择 ASP.NET Core 作为应用程序的技术堆栈,尽管这无关紧要。

我想讨论两种不同的方法:

1.“构建外部”:在 GitHub Actions runner 中构建/编译应用程序,将输出复制到容器镜像中

例如,我们的 GitHub 操作工作流文件可能如下所示...

name: build-outside
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout repo
      uses: actions/checkout@v2
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
    - name: .NET Publish
      run: dotnet publish --configuration Release --nologo -p:CI=true -o $GITHUB_WORKSPACE/buildOutput src
    - name: Build and push Docker image
      uses: docker/build-push-action@v1
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }}
        repository: ${{ format('{0}/build-outside-test', secrets.DOCKERHUB_USERNAME) }}
        tags: latest
Run Code Online (Sandbox Code Playgroud)

...有一个简单的 Dockerfile,如下所示:

FROM mcr.microsoft.com/dotnet/core/aspnet:latest
WORKDIR /app
COPY buildOutput /app
ENTRYPOINT ["dotnet", "MyTestApp.dll"]
Run Code Online (Sandbox Code Playgroud)

2.“内部构建”:在一个容器中构建,将输出复制到另一个容器镜像

在这种情况下,工作流文件更短...

name: build-inside
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout repo
      uses: actions/checkout@v2
    - name: Build and push Docker image
      uses: docker/build-push-action@v1
      with:
        dockerfile: Dockerfile_build_inside
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }}
        repository: ${{ format('{0}/build-inside-test', secrets.DOCKERHUB_USERNAME) }}
        tags: latest
Run Code Online (Sandbox Code Playgroud)

... 而 Dockerfile 更长,因为这是我们现在构建应用程序本身最终容器镜像的地方:

FROM mcr.microsoft.com/dotnet/core/sdk:latest AS build
WORKDIR /src
COPY src /src
RUN dotnet publish --configuration Release --nologo -p:CI=true -o ./buildOutput

FROM mcr.microsoft.com/dotnet/core/aspnet:latest AS runtime
WORKDIR /app
COPY --from=build /src/buildOutput ./
ENTRYPOINT ["dotnet", "MyTestApp.dll"]
Run Code Online (Sandbox Code Playgroud)

旁白:如果您不熟悉多阶段构建,请注意FROM第二个 Dockerfile 中的两个语句。我们在第一个临时容器中构建,然后将构建输出复制到最终(运行时优化的)容器映像中。

请注意,官方 ASP.NET Core 文档中明确推荐了第二种方法。

权衡

我已经确认这两种方法都有效并生成了一个有效的容器映像。值得注意的是,使用两种方法构建对拉取请求“just work”™ 的检查

在此处输入图片说明

现在离开这个具体的例子,这是我目前对每种方法的优点的总体看法:

  1. 在外面建造:
  • 构建可以利用市场操作
  • 如果构建很复杂并且包含多个步骤,那么使用 GitHub Actions 原语(即一系列作业/任务)进行设置可能会有所帮助。这样,我们可以将它留给 GH 来优化构建,根据需要分配额外的资源,并行运行作业等。
  • 更容易检查构建失败(UI 将准确显示哪个步骤失败)
  • 在构建过程中无需下载第二个容器映像,因此可能会节省一点网络带宽
  1. 内部构建:
  • 准确、确定性的构建输出
  • 完全控制构建环境;独立于构建运行器
  • 容器构建也可以在本地开发机器上运行,产生完全相同的输出

问题

  1. 我是否准确地描述了这两种方法的优点?

  2. 在容器内和容器外构建是否还有其他方面值得一提,特别是在 GitHub Actions 中?

Mei*_*bay 6

听起来你说得很好,我只是指出一些事情。

使用多阶段构建很棒(在内部构建),这实际上取决于您的用例。例如,如果构建步骤不太复杂,就像您的示例一样,那么使用多阶段就足够了,而且它还有将小图像作为工件的好处。

继续进行复杂的构建 - 假设您需要 -

  1. 从 GitHub 下载 artifact,一个 release.tar.gz 文件并解压
  2. 以某种方式构建解压文件/编译它
  3. 从 AWS S3 下载另一个工件
  4. 你明白了...
  5. 最终构建您的应用程序

所以这里我们有多个步骤,可能并行运行其中一些步骤,例如下载工件,可以减少构建时间。此外,如果您将构建拆分为步骤,您可以监控哪些步骤失败,并相应地发送通知。

把它们加起来 -

  • 在同一个 Dockerfile 中进行长时间的多阶段构建是很困难的,如果您发现自己在 Dockerfile 中写入了 30 多行,您可能会考虑拆分为 Steps
  • 如果您想缩小 Docker 映像工件,使用 Docker 的多阶段构建非常有用。我不会将它用作管道
  • 拆分为步骤可提供更好的监控,并由于并行性而节省构建时间

如果您有更多想法,请告诉我,这是一个很棒的话题

  • @Max我认为能够在本地重现构建将是一个很大的好处。它也不会将您与 GH Actions 或任何其他 CI 工具联系起来。如果是我,我会在 GH Actions 调用的存储库的根目录中有一个构建脚本,以便可以在本地和 CI/CD 工具中运行完全相同的构建步骤。 (2认同)