【问题标题】:GitHub Actions: build outside vs inside container?GitHub Actions:在容器外构建还是在容器内构建?
【发布时间】:2020-10-25 21:25:39
【问题描述】:

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

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

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

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

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

...还有一个像这样的简单 Dockerfile:

FROM mcr.microsoft.com/dotnet/core/aspnet:latest
WORKDIR /app
COPY buildOutput /app
ENTRYPOINT ["dotnet", "MyTestApp.dll"]

2。 “Build inside”:在一个容器中构建,将输出复制到另一个容器映像

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

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

...而 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"]

旁白:如果你不熟悉multi-stage builds, 注意第二个 Dockerfile 中的两个 FROM 语句。是 在第一个临时容器中构建,然后复制 将输出构建到最终的(运行时优化的)容器映像中。

请注意,第二种方法在 ASP.NET Core 官方文档中明确表示为 recommended

权衡

confirmed 认为这两种方法都可以工作并生成工作容器映像。值得注意的是,build checks 在拉取请求上使用这两种方法都可以“正常工作”™:

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

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

问题

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

  2. 在容器内部和外部构建的其他方面,特别是在 GitHub Actions 中,哪些方面值得一提?

【问题讨论】:

    标签: docker asp.net-core github github-actions


    【解决方案1】:

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

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

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

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

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

    总结一下——

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

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

    【讨论】:

    • @Max 我认为能够在本地复制构建将是一个很大的好处。它也不会将您绑定到 GH Actions 或任何其他 CI 工具。如果是我,我会在 GH Actions 调用的 repo 的根目录中有一个构建脚本,以便可以在本地和 CI/CD 工具中运行完全相同的构建步骤。
    • 或者你可以使用臭名昭著的act,它提供了在本地运行 GitHub Actions 工作流的能力。关于与 CI/CD 工具绑定的问题,最终您将使用相同的 CI/CD 工具,直到它无法提供足够的功能。在那个时候到来之前,我认为利用所有可用资源来提高工作效率是很棒的。再说一次,这是我的观点,你应该做最适合(或你工作的公司)的事情
    猜你喜欢
    • 2016-08-25
    • 2018-07-17
    • 1970-01-01
    • 2021-04-29
    • 2021-06-10
    • 2022-06-13
    • 2021-01-29
    • 2017-07-05
    • 2023-03-14
    相关资源
    最近更新 更多