使用Github Actions从单一代码库部署各个服务

45

我有大约10个独立的微服务,它们大多是用于各种数据处理作业的云函数,所有这些函数都存储在一个Github代码库中。

目标是在推送到分支时,触发选择性部署这些服务到Google Cloud Functions - 当单个函数已经更新。

我必须避免更新单个服务导致所有云函数都被部署的情况发生。

我的当前代码库结构:

/repo
--/service_A
----/function
----/notebook
--/service_B
----/function
----/notebook

顺便问一下,使用Github Actions和Google Cloud Build进行自动化的优缺点是什么?


你的 services 是否彼此有依赖关系?如果没有,那么这不是一个单体仓库,你应该更改问题标题以避免混淆单体仓库的主题。 - airtonix
5个回答

77

2
好的,谢谢。这个方法可以行得通,但需要每个服务本质上都有自己的工作流程。 - dendog
2
@dendog 是的,完全正确。我认为在许多情况下,单体存储库中的不同服务将由不同的团队拥有,因此每个团队都有自己的工作流程是有意义的。虽然可能会有很多重复的工作流步骤,但我认为 GitHub Actions 没有任何特性可以像 CircleCI orbs 一样在工作流之间共享步骤。 - peterevans
2
我知道这已经是一个老的讨论串了,但是我们已经发现可以通过创建自定义操作来完成这个任务。例如,我们在IBM Cloud上托管并使用IBM Cloud容器注册表,因此我们为登录IBM Cloud CLI创建了一个自定义操作,该操作可用于多个作业。 - AndyRyan
4
被 downvote 是因为如果你是用这种方式部署应用程序的话,那么它就不是一个单一存储库,而只是一个带有多个应用程序的存储库。通常情况下,一个单一存储库被分成两个主要区域:可交付和依赖项。在这里的主要问题是,仅使用路径通配符来检测更改意味着您会错过部署依赖项的更改。一个比路径通配符更好的解决方案是使用可以将您的 git 历史记录和确定您的依赖图所看起来像的内容相结合的工具。请参考:https://nx.dev。 - airtonix
2
请访问monorepo.tools,它有助于解释单一代码库的实际好处 - 不需要为每个项目创建重复的yaml文件。 - Missilexent
显示剩余2条评论

11
您也可以运行一些脚本,基于 git diff 发现哪些服务被更改,并通过GitHub REST API触发相应的作业。
可能会有两个工作流程 main.ymlservices.yml. 主要工作流程 将配置为始终在推送(push)时启动,并且它只会启动脚本来查找哪些服务已更改。对于每个更改的服务,将使用服务名称在负载中触发仓库调度事件(repository dispatch event)服务工作流将配置为在repository_dispatch上启动,并且它将包含一个作业(job)用于每个服务。作业将基于事件负载(payload)具有其他条件。
请参阅类似设置的展示: https://github.com/zladovan/monorepo

6

这不是一个Monorepo

如果你只有应用程序,那我很抱歉……但你只是拥有了许多应用程序的代码仓库。

Monorepo是一个包的集合,你可以在其中映射依赖关系图。

啊哈,我有一个Monorepo

但如果你有一组相互依赖的包,那么请继续阅读。

apps/
  one/
    depends:
      pkg/foo
  two/
    depends:
      pkg/bar
      pkg/foo
pkg/
  foo/
  bar/
  baz/

答案是切换到一种工具,该工具可以描述当前 git ref 和其他 git ref 之间哪些软件包发生了变化。
以下两个示例在 apps/* 下更改的每个软件包上运行 release npm 脚本以及所有它们依赖的软件包
我不确定 pnpm 方法是否会默默跳过没有release目标/命令/脚本的软件包。
使用 NX Dev
使用 NX.dev,它可以通过其 nx affected 命令为您解决这个问题。
  • 您需要在 monorepo 的根目录中有一个 nx.json
  • 它假设您正在使用 nx.dev 的 package.json 方法,如果您在每个软件包中有 project.json,则target 将驻留在那里。
  • 您的 CI 看起来像:
pnpx nx affected --target=release

Pnpm 过滤器

另一个选择是切换到 pnpm 并使用其过滤语法:

pnpm --filter "...{apps/**}[origin/master]" release

Naive Path Filtering

如果你只是试图依赖于在这个git提交中“哪些路径”发生了变化,那么你会错过影响你实际想要部署的包的瞬态变化。

如果你有一个类似于github动作的:

on:
  push:
    paths:
      - 'app/**'

如果您只推送更改任何内容在pkg/**中的提交,则永远不会收到任何构建。

其他有趣的Github Actions


3
说实话,我不确定为什么人们会对这个答案投反对票,单一代码库意味着比在同一个根目录下拥有几个项目或应用程序更多的东西,你可以共享配置和基础设施定义,定义指南(如eslint和prettier)等等。我还建议访问turborepomonorepo.tools了解更多关于单一代码库的目的。 - Missilexent
1
@Missilexent 我认为这是因为CICD是一项需要很多专注的任务,许多开发人员将其视为事后思考,因此他们觉得只进行路径过滤就可以逃脱。没关系,最终他们会回到我的答案。 - airtonix
@airtonix 这个回答非常好,但我想要更多的信息!我喜欢使用 nx affected 进行 CI,但问题标题是关于“部署”的。我正在寻找一种方法,在 CI 允许评审员合并功能分支后,确定需要使用 NPM 工作区进行部署的内容。lerna 似乎过于复杂了。我不认为 nx 有任何帮助。 - cyrf
@DirkLachowski 无论你如何称呼为消费者提供有用工作的可交付成果,都无关紧要。你有:可分发物和内部依赖。不管你使用的是哪种语言引擎,这都是一个单一代码库。事实上,我在我的Golang单一代码库中仍然使用nx.dev,因为没有其他可用的工具可以轻松地实现准确知道要构建和部署什么的可靠能力。 - airtonix
1
@DirkLachowski 是的,我正在完成这个模板 https://github.com/airtonix/golang-monorepo-template - airtonix
显示剩余7条评论

2

路径已更改 可以尝试的操作:

name: Conditional Deploy

on: push

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 100

      - uses: marceloprado/has-changed-path@v1
        id: service_A_deployment
        with:
          paths: service_A

      - name: Deploy front
        if: steps.service_A_deployment.outputs.changed == 'true'
        run: /deploy-service_A.sh

0

如果子目录中的某些文件发生更改,您可以使用dorny/paths-filter@v2来在工作流程中执行作业。如果您提交到frontend,它将仅执行frontend作业。同样适用于backend。因此,只有工作流程中所需的作业才会运行:

/
 frontend/
 backend/

YAML:

jobs:
  # JOB to run change detection
  changes:
    runs-on: ubuntu-latest
    # Required permissions
    permissions:
      pull-requests: read
    # Set job outputs to values from filter step
    outputs:
      backend: ${{ steps.filter.outputs.backend }}
      frontend: ${{ steps.filter.outputs.frontend }}
    steps:
    # For pull requests it's not necessary to checkout the code
    - uses: dorny/paths-filter@v2
      id: filter
      with:
        filters: |
          backend:
            - 'backend/**'
          frontend:
            - 'frontend/**'

  # JOB to build and test backend code
  backend:
    needs: changes
    if: ${{ needs.changes.outputs.backend == 'true' }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - ...

  # JOB to build and test frontend code
  frontend:
    needs: changes
    if: ${{ needs.changes.outputs.frontend == 'true' }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - ...

了解更多相关信息:https://github.com/dorny/paths-filter#examples

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接