使用 GitHub Actions 进行 GitOps 配置





0/5 (0投票)
这是一个实践教程,循序渐进地展示了如何设置 GitHub Actions,先验证更新的 Terraform 计划,然后运行该计划并预配 Azure 基础结构。
在本文中,我们在上一篇文章的基础上,展示了如何构建一个 CI/CD 管道,该管道可预配我们通过拉取请求合并到 Terraform 计划中的更改。
必备组件
这是一个实践教程,需要以下设置:
- Azure 订阅(免费试用)
- 已安装 Azure CLI(如何安装)
- 已本地安装 Terraform(下载)
- 注意:Terraform 下载只是一个单独的二进制文件,不是安装程序。您需要将其复制到执行路径中的某个位置。
- PowerShell
- GitHub 上的空存储库(说明)
如果您跳过了此系列的前一篇文章,可以通过您喜欢的 Git UI 或命令行从此存储库克隆完整的代码。
git clone https://github.com/benbhall/gitopsdemo-terraform-azure.git
创建 Azure 帐户后,登录门户,然后在页面顶部的搜索栏中搜索“Subscriptions”(订阅)部分。记下分配给它的订阅 ID - 稍后您将需要它。
本文的完整代码在此处提供,并且贯穿全文以供您参考。
步骤 1:设置后端
Terraform 需要一个地方来持久化其状态文件(后端)。我们将使用 Azure Blob 存储。由于它是一个 Terraform 本身依赖的资源,因此我们需要自己预配它。
在新打开的 PowerShell 终端中,对 Azure 进行身份验证(使用您注册试用版时使用的帐户)。
az login
创建一个资源组。您可以随意更改区域,但在此处遵循 `uksouth` 应该没有区别。
az group create -n gitopsdemo-tfstates-rg -l uksouth
创建一个 `storage account`(存储帐户)。
az storage account create -n gitopsdemostore -g gitopsdemo-tfstates-rg
-l uksouth --sku Standard_LRS
在该帐户中创建一个 `storage container`(存储容器)。
az storage container create -n gitopsdemotfstates --account-name gitopsdemostore
步骤 2:创建服务主体
“服务主体”只是一个用于 GitHub Actions 用于向 Azure 进行身份验证以访问我们刚创建的 `storage account`(存储帐户)并创建所有新资源的帐户的专用名称。
在同一个 PowerShell 终端中,执行以下代码,将 `<subscription-id>` 替换为您之前记录的 ID。
az ad sp create-for-rbac --name gitopsdemo-tf-sp --role Contributor
--scopes /subscriptions/<subscription-id> | ConvertFrom-Json
记下输出的值,因为您将在下一步中需要它们。
步骤 3:将机密存储在 GitHub 中
步骤 2 中的三个值与我们需要设置的机密对应关系如下(第四个机密是您的订阅 ID):
GitHub Action Secret Name (GitHub 操作机密名称) | Service Principal Create Output (服务主体创建输出) |
AZURE_AD_CLIENT_ID | appId |
AZURE_AD_CLIENT_SECRET | 密码 |
AZURE_AD_TENANT_ID | tenant |
AZURE_SUBSCRIPTION_ID | Subscription ID recorded earlier (之前记录的订阅 ID) |
通过访问 GitHub 网站上的存储库,导航到 **Settings** > **Secrets** > **Actions**,然后点击 **New repository secret**,将这些存储为存储库中的加密机密。
步骤 4:在 Terraform 中配置后端
在初始化阶段,Terraform 将在您的代码中查找后端配置,以便知道在哪里存储状态文件。在您的 _main.tf_ 文件中,将新配置添加到 terraform 部分的顶部。
terraform {
backend "azurerm" {
resource_group_name = "gitopsdemo-tfstates-rg"
storage_account_name = "gitopsdemostore"
container_name = "gitopsdemotfstates"
key = "gitopsdemo.tfstate"
}
如果您使用了上面的脚本来创建 Terraform 状态的存储帐户,则无需更改任何这些值。
步骤 5:构建 GitHub Action 工作流
对于不熟悉 GitHub Actions 的人来说,GitHub Actions 的 YAML 文件可能看起来有点压倒性,所以我们将在这里进行分解。但如果您遇到困难,可以在存储库中找到完整的 YAML 文件。
GitHub Actions 会在 _.github/workflows_ 文件夹中查找 YAML 文件。这些文件中的代码指定了要监听的事件以及要执行的操作。操作由作业组成,每个作业由一系列步骤组成。
步骤 5.1:创建 YAML 文件
您可以在 GitHub 网站上完成此操作,但最好在本地进行操作,然后提交并推送更改。此时不要担心 Git 分支 - 我们可以直接在 main 分支上工作。
首先在存储库的根目录下创建一个名为 _.github_ 的文件夹,然后在其中创建另一个名为 _workflows_ 的文件夹。在 _workflows_ 文件夹中,创建一个新文件:_main.yml_。
暂存(添加)新文件,提交更改,并通过您的 UI 或使用以下命令将其推送到 GitHub。
git add main.yml
git commit -m "Add empty action file"
git push
养成定期推送更改的习惯!
步骤 5.2:设置触发工作流的事件
在 _main.yml_ 文件顶部添加以下几行,以控制工作流的运行时间和方式。
此工作流将在对 main 分支的任何 push 或 pull request 事件上触发。有关触发工作流的事件的完整列表,请参考文档。
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
最后一行将确保我们仍然可以从 GitHub 网站上的 Actions 选项卡手动运行工作流。
步骤 5.3:添加作业
在 _main.yml_ 中我们到目前为止的配置下方,添加一个名为 `build` 的作业。我们将首先将机密拉取到 Terraform Azure 提供程序在向 Azure 进行身份验证时会查找的环境变量中。我们还将工作目录设置为存储库根目录,但您可以将 Terraform 文件保留在子目录中并指向那里。
jobs:
build:
runs-on: ubuntu-latest
env:
ARM_CLIENT_ID: ${{ secrets.AZURE_AD_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.AZURE_AD_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.AZURE_AD_TENANT_ID }}
defaults:
run:
working-directory: .
步骤 5.4:检出并设置 Terraform
我们需要检出我们的存储库,以便工作流可以访问我们的 Terraform。我们还需要确保已下载并配置了 Terraform CLI。我们将通过在刚刚添加的代码下添加我们的前两个步骤来实现这一点。
steps:
- uses: actions/checkout@v3
- uses: hashicorp/setup-terraform@v2
这利用了 GitHub 的 action `checkout@v3` 和来自 Hashicorp 的一个 action。后者默认获取最新版本的 Terraform,但如果需要特定版本,则可以进行配置(请参阅使用文档)。
步骤 5.5:Terraform 步骤
Terraform 在 Azure 中预配我们资源的强制性步骤是 init、plan 和 apply。但我们将构建一个生产就绪的工作流,该工作流将使用具有自动检查的拉取请求流程来限制 terraform apply 预配步骤,如下所示:
Format
- 强制执行 Terraform 最佳实践,并在配置格式不正确时产生错误。Init
- 初始化 Terraform 配置/设置工作目录。Validate
- 不查看实际资源/远程状态,而是检查代码语法。Plan
- 查看实际远程状态,确定需要创建、更新或销毁什么才能达到代码中的新/所需状态。
像这样添加新步骤:
- name: Terraform fmt
id: fmt
run: terraform fmt -check
- name: Terraform Init
id: init
run: terraform init -input=false -migrate-state -force-copy
- name: Terraform Validate
id: validate
run: terraform validate -no-color
- name: Terraform Plan
id: plan
if: github.event_name == 'pull_request'
run: terraform plan -no-color -input=false
continue-on-error: true
这些步骤中引发的错误将作为注释添加到拉取请求中,导致检查失败,并阻止更改合并到 main 分支以预配资源。要实现这一点,我们需要添加另外两个步骤:
- uses: actions/github-script@v6
if: github.event_name == 'pull_request'
env:
PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
#### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${process.env.PLAN}
\`\`\`
</details>
*Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
- name: Terraform Plan Status
if: steps.plan.outcome == 'failure'
run: exit 1
我无法声称拥有这段巧妙代码的功劳 - 它直接改编自示例。
最后,我们需要一个 `Apply` 步骤,它将继续预配资源更改。在我们的工作流中,我们通过合并到 main 来触发它。
- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve -input=false
步骤 6:尝试工作流
在您的 Git UI 中或使用命令行在本地创建一个名为 `bad-formatting-demo` 的新分支:
git checkout -b bad-formatting-demo
破坏 _main.yml_ 中的格式 - 如果您只是将 locals 块稍微向内缩进一点,那应该就足够了。
暂存更新的 _main.tf_,提交更改,然后将新分支推送到 GitHub。
git add main.tf
git commit -m "Break formatting"
git push --set-upstream origin bad-formatting-demo
导航到 GitHub 网站上的存储库,然后转到 **Pull requests**(拉取请求)选项卡。它应该已经注意到您的新更改,并提供 **Compare & pull request**(比较和拉取请求)按钮。点击该按钮。
然后点击 **Create pull request**(创建拉取请求)。
点击 **Details**(详细信息)查看失败的步骤。
修复错误
在本地,在包含 Terraform 代码的文件夹中,使用命令行运行Terraform format 命令。
terraform fmt
如果您在自己的未来 Terraform 代码中包含子文件夹,则需要添加 `-recursive`。
这将自动修复您文件中的所有格式错误。
推送修复的文件。PR 检查将自动再次运行。如果其他所有步骤都正常,那么您就可以 **Merge pull request**(合并拉取请求)了。
如上图所示,这将重新触发检查,并且假设所有检查都仍然通过,它将继续运行 **Apply** 步骤,这将预配 Azure 中的资源。
对状态更改的可见性
在上一次运行时,我们无法看到 Terraform 在 PR 等待代码视图时如何向我们展示预期更改。
请在新的本地分支上移除 Application Insights 资源,将其推送到 GitHub,然后打开一个新的拉取请求。
检查应该都会通过,但还是点击 **Show all checks**(显示所有检查)> **Details**(详细信息),然后展开 **Terraform Plan**(Terraform 计划)部分。
代码审阅者可以轻松地在拉取请求中看到当前实时状态正在进行的更改。
当我们将此更改合并,触发工作流再次运行直到 **Apply** 步骤时,我们可以看到 App Insights 资源已从 Azure 中的资源组中移除。
摘要
在此系列中,我们通过实践体验构建了一个 CI/CD 管道,该管道预配已合并到 Terraform 计划中并通过拉取请求进行的更改。我们演示了如何使用自动化检查来支持“门禁”部署中的代码审阅流程,并且我们体验了进行更改并看到这些更改在实时 Azure 资源中体现的端到端过程。
要了解更多关于 GitOps 的信息以及如何使用 GitOps 管理部署到 Kubernetes 的应用程序的配置,请查阅资源如何将 GitOps 与 Microsoft Azure 结合使用。