这篇说说如何将 Terraform 结合 Github action 实现自动化部署。
一、前置条件
1. 注册 Github 账号。
2. 注册腾讯云账号,并完成 实名认证。
3. 获取凭证,在 API密钥管理 页面中创建并复制 SecretId 和 SecretKey。
二、创建项目
在 GitHub 中新建代码仓库,目录结构如下:
.├── README.md├── environments│ ├── dev│ │ ├── main.tf│ │ └── provider.tf│ └── prod│ ├── cicd│ │ └── main.tf│ ├── local.tf│ ├── main.tf│ ├── provider.tf│ └── qta│ └── main.tf└── modules ├── network │ ├── main.tf │ ├── outputs.tf │ ├── provider.tf │ └── variables.tf ├── security_group │ ├── main.tf │ ├── outputs.tf │ ├── provider.tf │ └── variables.tf └── tke ├── main.tf ├── outputs.tf ├── provider.tf └── variables.tf
目录结构说明:
1. 项目结构主要分为environments和modules两个目录。
2. environments为目录方式隔离环境dev和prod,用来给不同环境设置各自的配置,每个环境目录都是独立的根模块。
注意:说明:
3. modules为封装的资源信息,用以复用。本目录中包含 vpc、安全组和容器服务 TKE 的 Module 演示。
4. 完整代码请参考 gitops-terraform。
三、流水线配置
1. 为防止 AKSK 等安全信息泄流造成安全问题,你需要在https://github.com/${USER}/${PROJECT}/settings/secrets/actions设置环境变量。请替换为已复制的 SecretId 和 SecretKey。
2. 通过 GitHub Actions 配置流水线。
你可以在项目中的 actions 选项处单击 New workflow,也可以在.github/workflows/目录里通过添加 yml 文件创建,流水线配置详情可以参考相关操作。
四、检查流水线
1. Terraform 根模块资源不能过多。同理,在执行检查的时候也应该尽可能的避免全部资源的读取,需要以细粒度的方式触发检查。
2. 这篇档采用按分支区分触发的环境。例如,dev 中的配置需要更新时,只能在 dev 分支上进行更新。配置更新完成后提交 PR 将代码合入到 main(主分支)。目的是每次更新的检查不需要全量扫描 environments 下的所有子目录(环境),减少不必要的状态同步消耗。
3. 该流水线主要通过执行terraform fmt、terraform init、terraform validate、terraform plan来检查代码和展示构建计划,方便判断是否执行部署。
五、部署流水线
1. 如果检查流水线中的检查操作都成功且terraform plan的输出符合预期,那么就可以进行 merge 操作。
2. 在 merge 完成的时候触发部署(即terraform apply)的操作。示意图如下:
六、相关操作
在进入 environment 的指定环境目录后,会判断是否还有子目录,如果有则通过 workspace 隔离不同业务环境(例如 qta,ci),如果没有则等价于普通的根模块。
七、创建校验流水线
参考以下代码,下载 Terraform 和校验 Terraform 代码:
# This is a basic workflow to help you get started with Actionsname: CI# Controls when the workflow will runon: pull_request:# A workflow run is made up of one or more jobs that can run sequentially or in paralleljobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on runs-on: ubuntu-latest env: TENCENTCLOUD_SECRET_KEY: ${{ secrets.TENCENTCLOUD_SECRET_KEY }} TENCENTCLOUD_SECRET_ID: ${{ secrets.TENCENTCLOUD_SECRET_ID }} # Steps represent a sequence of tasks that will be executed as part of the job steps: – uses: actions/checkout@v3 – uses: hashicorp/setup-terraform@v2 with: terraform_wrapper: false – name: check env run: | if [ ! -d "environments/$GITHUB_HEAD_REF" ]; then echo "*************************SKIPPING************************************" echo "Branch '$GITHUB_HEAD_REF' does not represent an oficial environment." echo "*********************************************************************" exit 1 fi – name: terraform fmt id: fmt run: terraform fmt -recursive -check – name: terraform init id: init working-directory: environments/${{ github.head_ref }} run: terraform init – name: terraform validate id: validate working-directory: environments/${{ github.head_ref }} run: terraform validate – name: terraform plan id: plan if: github.event_name == 'pull_request' working-directory: environments/${{ github.head_ref }} run: | plan_info="" dir_count=`ls -l | grep "^d" | wc -l` if [ $dir_count -gt 0 ]; then for dir in ./*/ do env=${dir%*/} env=${env#*/} echo "" echo "========> Terraform Plan <========" echo "At environment: ${{ github.head_ref }}" echo "At workspace: ${env}" echo "==================================" terraform workspace select ${env} || terraform workspace new ${env} plan_info="$plan_info\\n$(terraform plan -no-color)" done else plan_info="$(terraform plan -no-color)" fi plan_info="${plan_info//'%'/'%25'}" plan_info="${plan_info//$'\\n'/'%0A'}" plan_info="${plan_info//$'\\r'/'%0D'}" echo "::set-output name=plan_info::$plan_info" continue-on-error: true – uses: actions/github-script@v6 if: github.event_name == 'pull_request' with: 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 ${{ steps.plan.outputs.plan_info }} \\`\\`\\` </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: Applyon: pull_request: types: – closed branches: – mainjobs: build: if: github.event.pull_request.merged == true runs-on: ubuntu-latest env: TENCENTCLOUD_SECRET_KEY: ${{ secrets.TENCENTCLOUD_SECRET_KEY }} TENCENTCLOUD_SECRET_ID: ${{ secrets.TENCENTCLOUD_SECRET_ID }} steps: – uses: actions/checkout@v3 – uses: hashicorp/setup-terraform@v2 – name: terraform init id: init working-directory: environments/${{ github.head_ref }} run: terraform init – name: terraform apply working-directory: environments/${{ github.head_ref }} run: | dir_count=`ls -l | grep "^d" | wc -l` if [ $dir_count -gt 0 ]; then for dir in ./*/ do env=${dir%*/} env=${env#*/} echo "" echo "========> Terraform Apply <========" echo "At environment: ${{ github.head_ref }}" echo "At workspace: ${env}" echo "==================================" terraform workspace select ${env} || terraform workspace new ${env} terraform apply -auto-approve done else terraform apply -auto-approve fi