继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

使用GitHub Actions和Google Cloud构建持续集成和持续交付流水线

慕尼黑的夜晚无繁华
关注TA
已关注
手记 409
粉丝 60
获赞 319
一目了然

在这份指南中,我将向你展示如何创建一个CI/CD流水线(持续集成和持续交付/部署),自动测试和构建我们应用程序的代码,並將其部署到Google云服务。

我将把这个持续集成/持续交付(CI/CD)功能添加到我之前提到的图像文本提取与翻译工具中。简单回顾一下该应用程序的基本架构:此处

图像文字翻译工具架构

所以我们这个 CI/CD 管道只需要部署到这两个服务:Google Cloud RunGoogle Cloud Functions。顺便提一句,Cloud Functions 现在改名了……Cloud Run 中的 Functions

CI/CD入门

CI/CD 是 持续集成和持续交付(Continuous Integration and Continuous Delivery,简称 CI/CD)。它主要的目标包括:

  • 提高软件交付的速度和质量。
  • 允许更高频率的软件变更(如果需要,还包括这些变更的生产发布)。
  • 实现更短的开发周期,并且伴随着更快的反馈循环。
  • 同时减少人工劳动和人为错误。

自动化是 CI/CD 的核心所在,

持续集成开发

我们可以这样描述它:

能够频繁修改代码,通过自动化测试验证这些更改,然后再定期合并到主代码分支。我们经常将代码集成。

所以通常:开发者将代码提交到特性分支,它属于共享仓库。这将自动触发代码的构建和测试。测试通过后,开发者可以发起拉取请求(PR),将他们的更改合并回主分支中。

持续交付(Continuous Delivery)

这可以这么说。

确保代码始终可以部署到生产环境的做法是持续交付。持续交付依赖于当更改推送到main分支时触发的工作流程。部署系统通常会将应用程序部署到staging环境,在这里可以运行自动化测试,包括集成测试等。如果测试通过,构建将被标记为发布候选。

一个常见的混淆来源:持续部署和持续交付不一样。在持续部署中,每个候选发布版本都会自动部署到生产环境中。但在持续交付中,部署工件可以被部署到生产环境中,如果我们选择这样做。

在Google云环境中的持续集成和持续交付工具链
Git 仓库服务

在我们用于 Google CI/CD 管道中,我们可以使用各种源代码托管服务。我们可以使用 Google 自家的 Cloud Source Repositories 云源代码库。但我们也能够使用 GitHub、GitLab 或 Bitbucket 等平台。

我将展示如何使用GitHub。

CI/CD(持续集成和持续部署)
  • Google Cloud 构建服务 — 这是 Google 提供的全托管、无服务器 CI/CD 工具。它可以与我上面提到的所有源代码仓库服务集成。它可以运行构建工作流,包括:测试、构建、漏洞检测和部署。它可以将例如容器镜像或 Java 归档文件等构建工件推送到 Google Cloud Artifact Registry。还可以部署到各种 Google Cloud 服务,例如 GKE、Cloud Run 和 Cloud Functions。
  • GitHub 操作 — 一个直接集成到 GitHub 中的 CI/CD 工具,因此与目标云环境无关。同样,可以运行测试、构建部署工件,推送到 谷歌Artifact存储库,并部署到 Google 服务,例如 Cloud Run 和 Cloud Functions。

在这个演示里,我将用到GitHub Actions

二进制文件管理

用于存储像容器镜像和语言包这样的工件的常用工具有:Sonatype Nexus、JFrog Artifactory、Docker Hub 和 Google Artifact Registry。

后者,也就是这个服务,是 Google 的全托管工件管理服务。它可以存储各种类型的工件,并集成了内置的漏洞扫描功能。(它取代了以前的服务 Google 容器镜像仓库。)

这里我将使用Google的Artifact Registry(简称AR)。

整体流程

这张图展示了整个CI/CD流水线,包括我们这次要用的各种工具。顶部显示了我们在本次演示中将使用的工具。

CI/CD 流水线和工具流程

注意,我会构建这个管道系统,使其只在我们的分支被合并到主分支后才部署到 Google 云。一个典型的流程如下……

  1. 工程师创建一个代码分支,用于开发功能或修复bug。
  2. 工程师提交并推送他们的更改。流水线随后运行,进行构建和测试。
  3. 如果测试通过,工程师会发起合并请求(PR),将他们的更改合并到主分支。
  4. PR获批后,更改会被合并到主分支。
  5. 主分支更新后,流水线会重新运行,进行构建并部署到Google Cloud。
如果你想跟着一起学 前提条件如下

我的博客这里介绍了如何手动构建和部署图像文本翻译和提取应用程序。(我真的需要给它起一个更短的名字……以后它将被简称为:ImgTranslactor!)它提供了源代码和在本地进行开发、测试及部署到Google云的详细步骤说明。

在这篇博客中,我们将在基础上增加持续集成和持续交付(CI/CD)功能,包括单元测试。在继续之前,你可能希望先做些必要的准备。

  • 熟悉 ImgTranslactor 应用及其相关仓库。
  • 完成一次性的 Google 项目设置步骤。
  • 确保你已按照之前的博客中的说明,给服务账号授予相应权限。
  • 配置本地开发环境。
  • 克隆仓库并切换到 v1.0 标签。
    # 克隆仓库
    git clone https://github.com/derailed-dash/image-text-translator.git  # (按回车运行)

    # 切换到 v1.0 标签 - 这是“早期”的标签
    git checkout v1.0  # (按回车运行)

    cd image-text-translator  # (按回车运行)

    # 创建一个 Python 虚拟环境。例如...
    python3 -m venv .venv  # (按回车运行)
    # 然后激活它。例如
    source .venv/bin/activate  # (按回车运行)

    # 安装所需的 Python 依赖
    python3 -m pip install -r requirements.txt  # (按回车运行)

确保你设置的是正确的项目,并在安装了 Google gcloud 的环境里运行这些命令。

    # 检查是否选中了正确的项目  
    gcloud auth login  
    gcloud config list project  
    export PROJECT_ID=<输入正确的项目ID>  

    gcloud auth application-default login  

    # 如果项目设置错误  
    gcloud config set project $PROJECT_ID  
    gcloud auth application-default set-quota-project $PROJECT_ID  

    # 设置环境变量,包括应用默认凭据  
    source scripts/setup.sh

检查你能否在本地运行应用程序。比如。

    cd app/ui_cr/  
    source ../../scripts/setup.sh  # 初始化变量(如果这是新打开的终端)  

    # 启动 Flask 应用程序  
    python app.py

确保你能成功地通过Artifact Registry将应用部署到Google Cloud。

Google Artifact Registry

已部署的服务

创建 GitHub 动作流程

注释:创建 GitHub 动作流程

概述

一个 GitHub Actions 工作流 在触发诸如代码推送之类的 事件 时启动。工作流可以包含一个或多个 作业 ,这些作业可以按顺序执行,也可以并行执行。每个作业包含一个或多个顺序步骤,这些步骤可以是脚本或可重复使用的作业。需要注意的是,每个作业都在自己的 runner 中运行,即运行作业步骤的运行器。

GitHub Actions 的工作流

每个工作流程都是通过一个yaml文件来定义的,并存放在你仓库的.github/workflows目录下。

那么,简单回顾一下:

GitHub Actions (吉特动作) 组织

GitHub密钥

我们的应用使用了 Google 的 API,需要一个权限合适的 Google 服务账户。尽管我们在之前的博客中已创建了一个服务账户,但为了使 GitHub Actions 能够部署 Google 云服务,我们仍然需要创建一个新的单独的服务账户。

    # 要以可以创建服务帐户的身份进行身份验证  
    # gcloud auth login  

    # 检查是否选择了正确的项目  
    # gcloud config list project  
    # 填写你的项目ID  
    # gcloud config set project $PROJECT_ID  

    export PROJECT_ID=$(gcloud config list --format='value(core.project)')  

    export MY_ORG=填写你的组织域名  
    export GH_SVC_ACCOUNT=image-text-translator-gh-sa  
    export GH_SVC_ACCOUNT_EMAIL=$GH_SVC_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com  

    gcloud iam service-accounts create $GH_SVC_ACCOUNT  

    ######################################  
    # 为服务帐户赋予角色权限 #  
    ######################################  

    # 允许服务帐户访问GCS Cloud Build存储桶  
    gcloud projects add-iam-policy-binding $PROJECT_ID \  
      --member="serviceAccount:$GH_SVC_ACCOUNT_EMAIL" \  
      --role="roles/storage.admin"  

    # 允许服务帐户运行和管理Cloud Build任务  
    gcloud projects add-iam-policy-binding $PROJECT_ID \  
      --member="serviceAccount:$GH_SVC_ACCOUNT_EMAIL" \  
      --role="roles/cloudbuild.builds.editor"  

    # 允许服务帐户查看日志  
    gcloud projects add-iam-policy-binding $PROJECT_ID \  
      --member="serviceAccount:$GH_SVC_ACCOUNT_EMAIL" \  
      --role="roles/logging.viewer"  

    # 允许该服务帐户部署  
    gcloud iam service-accounts add-iam-policy-binding $GH_SVC_ACCOUNT_EMAIL \  
      --member="serviceAccount:$GH_SVC_ACCOUNT_EMAIL" \  
      --role="roles/iam.serviceAccountUser"  

    gcloud projects add-iam-policy-binding $PROJECT_ID \  
      --member="serviceAccount:$GH_SVC_ACCOUNT_EMAIL" \  
      --role=roles/run.admin  

    gcloud projects add-iam-policy-binding $PROJECT_ID \  
      --member="serviceAccount:$GH_SVC_ACCOUNT_EMAIL" \  
      --role=roles/cloudfunctions.admin

为了让我们的 GitHub 工作流能够访问 Google Cloud 的 API,它需要能够向 Google Cloud 提供凭证。我们是通过让 GitHub 工作流由我们的服务账号来运行来实现的。有几种方法可以做到这一点,具体如下:

  1. (我们)更简单的办法 — 将服务账户的 JSON 凭据上传到 GitHub 作为秘密凭据。
  2. (我们)更好的方法 — 使用 Workload Identity Federation。这使 GitHub 能够通过 OIDC 向 Google Cloud 请求短期访问令牌。这种方法的好处在于,在 GitHub 和 Google Cloud 之间建立信任关系,从而使 GitHub 在无需服务账户密钥的情况下,验证身份并访问 Google Cloud 服务。

在这个演示中,我会采取简单的方法。所以首先我们需要上传服务账号密钥到 GitHub 作为秘密,避免重复使用“上传到”。这里将“秘密”改为“机密”,以保持术语的专业性。修正后为:在这个演示中,我会采取简单的方法。首先,我们需要将服务账号密钥上传到 GitHub 作为机密。

在 GitHub 页面上,导航到 设置,然后选择 机密和变量,并点击 操作项

把秘密存储在 GitHub 上

我们将创建以下秘密:

  • GCP_PROJECT_ID
  • GCP_GH_SVC_ACCOUNT — 仅账户名。(我们在流程中会将其拼接起来,以生成完整的电子邮件地址。)
  • GCP_GH_SVC_ACCOUNT_CREDS

对于服务账户凭证,我建议存储JSON文件的base64编码形式。这可以避免我们在读取JSON时可能出现的问题。我们可以这样来编码JSON文件:

    ### 本地生成服务账户密钥 ###
    gcloud iam service-accounts keys create ~/.config/gcloud/$GH_SVC_ACCOUNT.json \  
      --iam-account=$GH_SVC_ACCOUNT_EMAIL  

    # Base64编码密钥 #
    base64 ~/.config/gcloud/$GH_SVC_ACCOUNT.json > "${GH_SVC_ACCOUNT}_encoded.txt"

以上命令会在本地生成服务账户密钥文件,并将该密钥进行Base64编码保存为一个文本文件。

复制你创建的文件内容,并将它存到我们 GitHub 的秘密里面。完成之后,我们仓库里的秘密设置应该像下面这样,请参见下面的示例。

GitHub仓库秘密信息

保存一些变量:

既然我们在这里,我们可以存放一些变量。这些变量不敏感。我打算从已有的 setup.sh 脚本中提取一些变量,该脚本已存在于我的仓库中。

在 GitHub 上存储仓库中的变量

开始最初的CI/CD流程

在GitHub上选择Actions。我创建了一个新的工作流文件build-and-test.yml。这个文件的内容如下:

    # 此工作流用于构建和测试我们的 Python 应用程序。
    name: 构建和测试
    run-name: ${{ github.actor }} 启动了构建和测试工作流 🚀
    on:
      push:
        paths:
          - 'app/**' # 仅在推送包含 app 文件夹中的文件时运行
      pull_request:
        branches:
          - 'master' # 确保在合并时工作流也会运行
        paths:
          - 'app/**' # 仅在推送包含 app 文件夹中的文件时运行
      workflow_dispatch:  # 允许手动启动工作流

    permissions:
      contents: read

    env:
      PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
      SVC_ACCOUNT: ${{ secrets.GCP_SVC_ACCOUNT }}
      GOOGLE_APPLICATION_CREDENTIALS: ${{ github.workspace }}/${{ secrets.GCP_SVC_ACCOUNT }}.json
      SVC_ACCOUNT_EMAIL: ${{ secrets.GCP_SVC_ACCOUNT }}@${{ secrets.GCP_PROJECT_ID }}.iam.gserviceaccount.com
      REGION: ${{ vars.GCP_REGION }}
      FUNCTIONS_PORT: ${{ vars.GCP_DEV_FUNCTIONS_PORT }}
      FLASK_RUN_PORT: ${{ vars.FLASK_RUN_PORT }}
      BACKEND_GCF: https://${ vars.GCP_REGION }-${{ secrets.GCP_PROJECT_ID }}.${{ vars.GCP_FUNCTION_URI_SUFFIX }}

    jobs:
      build_and_test:
        runs-on: ubuntu-latest

        # 使用矩阵策略来运行后端和 UI 的构建和测试步骤
        # 这允许我们重用此作业并为每个应用文件夹运行它
        strategy:
          matrix:
            app-folder:
              - app/backend_gcf
              - app/ui_cr

        steps:
        - uses: actions/checkout@v4

        - name: 检查环境变量
          run: |
            echo "已配置的环境变量:"
            echo PROJECT_ID="$PROJECT_ID"
            echo REGION="$REGION"
            echo SVC_ACCOUNT="$SVC_ACCOUNT"
            echo SVC_ACCOUNT_EMAIL="$SVC_ACCOUNT_EMAIL"
            echo GOOGLE_APPLICATION_CREDENTIALS="$GOOGLE_APPLICATION_CREDENTIALS"
            echo BACKEND_GCF="$BACKEND_GCF"
            echo FUNCTIONS_PORT="$FUNCTIONS_PORT"
            echo FLASK_RUN_PORT="$FLASK_RUN_PORT"

        - name: 创建凭证文件
          run: |
            echo "${{ secrets.GCP_SVC_ACCOUNT_CREDS }}" | base64 --decode > $GOOGLE_APPLICATION_CREDENTIALS
            echo "检查凭证文件:"
            head -n 3 $GOOGLE_APPLICATION_CREDENTIALS
          shell: bash

        - name: 设置 Python 3.10
          uses: actions/setup-python@v5
          with:
            python-version: "3.10"
            cache: 'pip' # 缓存 pip 依赖

        - name: 安装依赖项
          run: |
            echo "正在安装 Python 依赖项"
            python -m pip install --upgrade pip
            pip install flake8 pytest
            if [ -f requirements.txt ]; then pip install -r requirements.txt; fi

        - name: 使用 flake8 进行代码风格检查
          working-directory: ${{ matrix.app-folder }}
          run: |
            # 如果有 Python 语法错误或未定义的名称,停止构建
            echo "运行第一个 flake8:"
            flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
            echo "flake8 退出代码: $?"
            echo "运行第二个 flake8:"
            # exit-zero 将所有错误视为警告。GitHub 编辑器宽度为 127 个字符
            flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
            echo "flake8 退出代码: $?"

        - name: 使用 pytest 进行测试
          working-directory: ${{ matrix.app-folder }}
          run: |
            pytest || if [ $? -eq 5 ]; then echo "未找到测试,但继续..."; else exit 1; fi

有几点需要注意:

  • 工作流在工程师将更改推送到任何分支时触发。(也可以通过 GitHub UI 手动触发。)
  • 我使用 paths 过滤器仅在提交 app 文件夹下的文件时才触发工作流。
  • 我们通过读取 GitHub 变量和密钥来定义一系列环境变量。这些环境变量在工作流级别定义,因此可以在工作流中的任何任务中使用。
  • 我们可以将多个密钥或变量连接起来,以创建新变量。
  • 我使用预定义的 actions/setup-python@v5 操作来设置 Python 环境,并缓存任何 pip 依赖项。(这会加快未来的运行。)
  • 尽管我只定义了一个名为 build-and-test 的任务,但我们使用矩阵策略运行该任务两次;即,我们为 app 文件夹下的两个子文件夹分别运行整个任务。
  • 当我读取服务账户凭证的密钥时,需要从 base64 编码中解码,我使用 base64 --decode 来完成。

保存工作流并将更改提交到代码库(repo)。从现在开始,每次我们推送代码更改到应用时,这个工作流就会自动触发。

我可以手动试试。几秒钟后,结果如下:

启动 GitHub Actions 工作流程

它起作用!!

我们可以深入查看这两个任务中的任何一个,并查看日志记录。请注意,GitHub 会自动从日志中隐藏我们的任何机密信息,例如,密码或访问令牌。

查看工作流程日志

我还需要将我们测试过的应用程序部署到谷歌云平台。我以后再回来处理这个。

增加单元测试

现在是引入一些单元测试的好时机。我们可以使用这些测试来确保我们的 Python 应用程序运行正常,并检查我们所做的任何代码更改是否没有破坏测试。CI/CD 管道的一个好处是,每次我们推送代码更改到仓库时,都可以自动运行这些单元测试。

由于这是一篇关于CI/CD(持续集成与持续部署)的文章,而不是关于单元测试的,所以我不会详细讲解单元测试。

给云函数写个单元测试

我首先创建了一个名为 test_main.py 的单元测试文件。

    import pytest  
    from unittest.mock import patch, MagicMock # 用于模拟外部 API 调用  
    from flask import Flask, request # 用于模拟 HTTP 请求  
    from io import BytesIO  

    # 从 main.py 中导入函数  
    from backend_gcf.main import extract_and_translate  

    @pytest.fixture  
    def app():  
        """ 创建 HTTP 请求的 Flask 应用上下文的 fixture """  
        app = Flask(__name__)  
        return app  

    # 使用 patch 替换 Google 客户端为模拟对象  
    @patch('backend_gcf.main.vision_client')  
    @patch('backend_gcf.main.translate_client')  
    def test_extract_and_translate_with_posted_image(mock_translate_client, mock_vision_client, app: Flask):  
        with app.test_request_context(  
            method='POST',  
            data={  
                'uploaded': (BytesIO(b'sample image data'), 'test_image.jpg'),  
                'to_lang': 'en'  
            },  
            content_type='multipart/form-data'  
        ):  
            # 模拟 Vision API 和 Translate API 的响应  
            mock_vision_client.text_detection.return_value = MagicMock(  
                text_annotations=[MagicMock(description="Put a glass of rice and three glasses of water in a saucepan")]  
            )  
            mock_translate_client.detect_language.return_value = {"language": "uk"}  
            mock_translate_client.translate.return_value = {"translatedText": "Put a glass of rice and three glasses of water in a saucepan"}  

            # 调用函数  
            response = extract_and_translate(request)  

            # 验证响应等于 'Put a glass of rice and three glasses of water in a saucepan'  
            assert response == "Put a glass of rice and three glasses of water in a saucepan"

这是通过使用unittest.mock模块来替换vision_clienttranslate_client的API调用,用模拟对象来模拟它们的响应行为。同时,它还创建了一个Flask应用上下文来模拟对Cloud Function的HTTP请求场景。

请注意,我们还需要在 backend_gcf 文件夹和 tests 子文件夹中各创建一个 __init__.py 文件。这样可以让 pytest 更好地理解我们的包结构。

在分支上提交和推送

这里我们会开始:

  • 创建一个新的功能分支,用于我们的单元测试。
  • 这会触发我们的 GitHub Actions 工作流。
  • 创建一个拉取请求(PR),将功能分支并入主分支。
  • 批准该拉取请求,并进行合并。
  • 删除这个功能分支。

下面列出具体步骤:

    # 切换到新分支来添加单元测试
    git checkout -b feature/add-unit-tests
    git add .
    # 提交更改,注释为 '为extract_and_translate函数添加单元测试'
    git commit -m "为extract_and_translate函数添加单元测试"
    # 将新分支推送到远程仓库
    git push -u origin feature/add-unit-tests

工作流程成功触发并运行!

我们还能看看日志,查看一下单元测试是否已经运行。

使用Pytest运行我们的单元测试

我们现在在 GitHub 上发起拉取请求:

创建公关

新的分支可以合并

我们现在可以合并 PR 了。

发起合并请求

合并成功了:

搞定,合并成功了

现在我们可以在 GitHub 上删除功能分支,切换回主分支,更新本地代码库,并删除任何孤立的本地分支。

    git checkout main  
    git pull # 拉取更新,因为远程仓库现在包含了最新的合并更改  
    git branch -d feature/add-unit-tests # 删除本地的feature/add-unit-tests分支  
    git fetch --prune # 清除任何已删除远程分支的本地引用
为 Flask UI 应用编写单元测试用例

我们会继续之前的流程。

我首先创建了 app/ui_cr/tests/test_app.py 文件。如果你对单元测试代码感兴趣,可以在仓库中查看它这里。然后我在一个新的分支中提交了代码,工作流就开始运行了:

带有新单元测试的工作流运行了。

和之前一样,我们现在需要发起拉取请求,批准并合并拉取请求,切换回主分支,接着删除或修剪特性分支。

添加一个工作流程用于部署到 Google 云平台

我们之前已经创建了一个工作流,用于构建和测试应用程序。接下来,我们将创建一个单独的工作流,将其部署到 Google Cloud Functions 和 Google Cloud Run。

我们将创建一个新的工作流程,名为 deploy.yml 的文件。

    name: 部署到 GCF 和 Cloud Run  
    run-name: ${{ github.actor }} 启动了部署工作流 🚀  
    on:  
      workflow_run:  
        workflows: ["Build and Test"] # 在成功的构建和测试之后执行部署  
        types:  
          - completed       

      workflow_dispatch:  # 允许手动触发工作流。        

    permissions:  
      contents: read  

    env:  
      PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}  
      SVC_ACCOUNT: ${{ secrets.GCP_SVC_ACCOUNT }}  
      GOOGLE_APPLICATION_CREDENTIALS: ${{ github.workspace }}/${{ secrets.GCP_SVC_ACCOUNT }}.json  
      SVC_ACCOUNT_EMAIL: ${{ secrets.GCP_SVC_ACCOUNT }}@${{ secrets.GCP_PROJECT_ID }}.iam.gserviceaccount.com  
      REGION: ${{ vars.GCP_REGION }}  
      BACKEND_GCF: https://${ vars.GCP_REGION }}-${{ secrets.GCP_PROJECT_ID }}.${{ vars.GCP_FUNCTION_URI_SUFFIX }}  
      CR_UI_IMAGE_NAME: ${{ vars.GCP_REGION }}-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/image-text-translator-artifacts/image-text-translator-ui:${{ github.sha }}  
      BUILD_LOGS_BUCKET: gs://${{ secrets.GCP_PROJECT_ID }}-gcloud-logs  

    jobs:  
      deploy_backend_gcf: # 只有在 master 分支上成功的构建和测试后才部署  
        if: ${{ (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') || github.event_name == 'workflow_dispatch' }} && github.ref == 'refs/heads/master'  
        runs-on: ubuntu-latest  

        steps:  
        - uses: actions/checkout@v4  

        - name: 检查环境变量  
          run: |  
            echo "配置的环境变量:"  
            echo PROJECT_ID="$PROJECT_ID"  
            echo REGION="$REGION"  
            echo SVC_ACCOUNT="$SVC_ACCOUNT"  
            echo SVC_ACCOUNT_EMAIL="$SVC_ACCOUNT_EMAIL"  
            echo BACKEND_GCF="$BACKEND_GCF"        

        # 通过 Google Cloud 进行身份验证  
        - name: 与 Google Cloud 进行身份验证  
          run: |  
            echo "${{ secrets.GCP_SVC_ACCOUNT_CREDS }}" | base64 --decode > $GOOGLE_APPLICATION_CREDENTIALS  
            gcloud auth activate-service-account $SVC_ACCOUNT_EMAIL --key-file=$GOOGLE_APPLICATION_CREDENTIALS  
            gcloud config set project $PROJECT_ID  
            gcloud config set functions/region $REGION  

        # 部署到 Google Cloud Functions  
        - name: 部署到 Google Cloud Functions  
          working-directory: app/backend_gcf  
          run: |  
            gcloud functions deploy extract-and-translate \  
              --gen2 \  
              --max-instances 1 \  
              --region $REGION \  
              --runtime=python312 \  
              --source=. \  
              --trigger-http \  
              --entry-point=extract_and_translate \  
              --no-allow-unauthenticated \  
              --service-account=$SVC_ACCOUNT_EMAIL  

            # 允许此函数由服务账户调用  
            gcloud functions add-invoker-policy-binding extract-and-translate \  
              --region=$REGION \  
              --member="serviceAccount:$SVC_ACCOUNT_EMAIL"  

      deploy_ui_cr:  
        if: ${{ (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') || github.event_name == 'workflow_dispatch' }} && github.ref == 'refs/heads/master'  
        runs-on: ubuntu-latest  

        steps:  
        - uses: actions/checkout@v4  

        # 通过 Google Cloud 进行身份验证  
        - name: 与 Google Cloud 进行身份验证  
          run: |  
            echo "${{ secrets.GCP_SVC_ACCOUNT_CREDS }}" | base64 --decode > $GOOGLE_APPLICATION_CREDENTIALS  
            gcloud auth activate-service-account $SVC_ACCOUNT_EMAIL --key-file=$GOOGLE_APPLICATION_CREDENTIALS  
            gcloud config set project $PROJECT_ID  
            gcloud config set run/region $REGION  

        # 构建并部署 ui_cr 容器到 Cloud Run  
        - name: 构建 Docker 镜像  
          working-directory: app/ui_cr  
          run: |  
            gcloud auth configure-docker $REGION-docker.pkg.dev  
            gcloud builds submit --gcs-log-dir $BUILD_LOGS_BUCKET --tag $CR_UI_IMAGE_NAME  

        - name: 部署到 Cloud Run  
          run: |  
            export RANDOM_SECRET_KEY=$(openssl rand -base64 32)  
            gcloud run deploy image-text-translator-ui \  
              --image=$CR_UI_IMAGE_NAME \  
              --region=$REGION \  
              --platform=managed \  
              --allow-unauthenticated \  
              --max-instances=1 \  
              --service-account=$SVC_ACCOUNT_EMAIL \  
              --set-env-vars BACKEND_GCF=$BACKEND_GCF,FLASK_SECRET_KEY=$RANDOM_SECRET_KEY

这里有一些注意的地方:

  • 我们希望这个工作流在 Build and Test 工作流成功执行在主分支上后才运行。
  • 我们定义了 Cloud Run 镜像的名称,并用 git 提交的 SHA 标记它,这为我们提供了独一无二的镜像标签
  • 我们有两个任务:一个用于部署到 Google Cloud Functions,另一个用于部署到 Cloud Run。它们之间没有依赖关系,因此可以并行执行。
  • 在两个任务中,我们首先运行 gcloud auth 以使用服务帐户身份执行命令。
  • 然后,我们基本上使用了与之前手动部署时相同的命令,如 此指南 中所述。
  • 我们的 gcloud builds submit 命令指定了一个 GCS 日志目录。接下来我会详细说明一下……
为构建日志创建一个 GCS 存储桶空间

GCS 代表 Google Cloud Storage。

当我们运行 gcloud builds submit 命令来创建我们的应用容器镜像时,Google Cloud Build 会尝试将日志写入一个 GCS 存储桶中。服务账号没有权限访问默认存储桶,因此,我们需要为它创建一个专用的存储桶。

为了创建存储桶并让服务账户能够访问该桶,请运行以下命令来,使用有足够权限的身份,比如组织管理员或项目编辑者。

# 创建存储桶并导出变量 BUILD_LOGS_BUCKET  
export BUILD_LOGS_BUCKET=gs://${PROJECT_ID}-gcloud-logs  
gsutil mb $BUILD_LOGS_BUCKET  
# 给我们的服务账号设置存储管理员权限  
gsutil iam ch serviceAccount:$SVC_ACCOUNT_EMAIL:roles/storage.admin $BUILD_LOGS_BUCKET  

# 如果想通过终端测试...  
gcloud auth activate-service-account $SVC_ACCOUNT_EMAIL --key-file=$GOOGLE_APPLICATION_CREDENTIALS  
gsutil ls $BUILD_LOGS_BUCKET
测试一下工作流程.

我们可以在GitHub上手工运行它。

运行我们的部署流程

嗯 …

我们可以查看这张新的图片是否存在于 Google Artifact Registry 中,是否带有 SHA 提交标识。

浏览 Google Artifact Registry 中的容器图像

我们可以确认新版本是否已经部署到Cloud Run上了。

部署了!

结尾:

就这样!没想到这竟然不需要太多的时间和精力来引入一个工作的 CI/CD 流水线使用 GitHub Actions。从现在开始,我可以放心地对这个应用程序进行修改,知道这些修改会自动进行测试,并且每次我将经过测试的修改合并回主分支后,它都会被自动部署到 Google Cloud 上。

要使用的链接
图片翻译器!
Google云端
GitHub 及其操作
单元测试
Before You Go
  • 请将这篇文章分享给你觉得会对它感兴趣的任何人。这可能会帮助他们,也确实帮到我了!
  • 请给我点赞!你知道你可以多次点赞吧?
  • 随时欢迎各位留言💬。
  • 关注我订阅,你就能不错过任何精彩内容了。点击进入我的个人主页,从那里找到并点击这些图标:

关注和订阅
[OOP]:面向对象编程
[CRUD]:增删改查
[JVM]:Java虚拟机
[SUT]:测试系统

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP