本文介绍如何将 Terraform 结合 Github action 实现自动化部署。
在 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
目录结构说明:
项目结构主要分为environments
和modules
两个目录。
environments
为目录方式隔离环境dev
和prod
,用来给不同环境设置各自的配置,每个环境目录都是独立的根模块。
说明:
- dev 中演示创建一个 vpc。
- prod 中演示通过 workespace 进行业务隔离。在 cicd 目录中创建 vpc, 在 qta 目录下创建容器集群。
modules
为封装的资源信息,用以复用。本目录中包含 vpc、安全组和容器服务 TKE 的 Module 演示。
完整代码请参考 gitops-terraform。
为防止 AKSK 等安全信息泄流造成安全问题,您需要在https://github.com/${USER}/${PROJECT}/settings/secrets/actions
设置环境变量。请替换为已复制的 SecretId 和 SecretKey。
通过 GitHub Actions 配置流水线。
您可以在项目中的 actions 选项处单击 New workflow,也可以在.github/workflows/
目录里通过添加 yml 文件创建,流水线配置详情可以参考相关操作。
Terraform 根模块资源不能过多。同理,在执行检查的时候也应该尽可能的避免全部资源的读取,需要以细粒度的方式触发检查。
本文档采用按分支区分触发的环境。例如,dev 中的配置需要更新时,只能在 dev 分支上进行更新。配置更新完成后提交 PR 将代码合入到 main(主分支)。目的是每次更新的检查不需要全量扫描 environments 下的所有子目录(环境),减少不必要的状态同步消耗。
该流水线主要通过执行terraform fmt
、terraform init
、terraform validate
、terraform plan
来检查代码和展示构建计划,方便判断是否执行部署。
如果检查流水线中的检查操作都成功且terraform plan
的输出复合预期,那么就可以进行 merge 操作。
在 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
name: Apply
on: pull_request: types: - closed branches: - main
jobs: 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
本页内容是否解决了您的问题?