Day 11-Atlantis 做 Terraform Remote Plan & Remote Apply

使用 atlantis 做 terraform automation,Terraform Remote Plan & Remote Apply

课程内容与代码会放在 Github 上: https://github.com/chechiachang/terraform-30-days

赛後文章会整理放到个人的部落格上 http://chechia.net/

追踪粉专可以收到文章的主动推播

https://ithelp.ithome.com.tw/upload/images/20210901/20120327NvpHVr2QC0.jpg


terraform 团队协作的问题

  • terraform 是 local apply 的 command tool
    • 每个人都在自己的 local 电脑上 apply terraform
      • 同时改一样的东西就会 conflicts
    • Iac 的原则是 infrastructureas code,然而各自 apply 却违反这样的原则
      • 理想上 master branch 就是最新的 infrastructure,然而因为需要手动 apply,让 infra 可能停留在前几个 commit
      • 或是 feature branch 已经 apply 到 infra 上
  • IaC 後,需要自动化的 PR Review
    • 最好自动化产生 plan 结果当作 Review 依据
  • 最好有自动化的 auto terraform apply
    • 由 CI / CD trigger,依循 git-flow 流程,plan 与 apply infrastructure
    • 透过工具 apply,避免人为 apply 时造成的 human error

Why not Github Action

如果使用 github.com,完全可以使用 Github Action,来执行 terraform automation,除了本课程内有提供范例外,网路上已经有更多的范例可以参考

然而有些 version control system 是不方便使用 Github Action

  • 使用 self-host github enterprise,开启 Github Action 要耗费大量的算力,目前仍有效能问题
  • 使用其他 version control system 例如 gitlab,bitbucket...或是上述 vcs 的 self-hosted enterprise 版本,自然就没有 Github Action
  • 或是团队不想用 Github Action

这时可以考虑使用公司的 CI / CD 系统,自行写 terraform 的 pipeline,然後整理前述课程使用到的 command 写成 shell script 执行。只要是知名的 CI / CD 工具,都能找到许多 terraform 的 pipeline 范例,而这些范例多半是共通的。

如果不希望自己维护 terraform pipeline,现在已经有开源版本的整合工具,帮你做自动化,就是这张要介绍的 Atlantis: Pull Request Automation

参考 terraform-30-days 上 实际执行的 PR 范例

About atlantis

Atlantis 是一款开源免费的自动化 terraform 工具。基本工作流程很单纯

  • PR 产生的时候, atlantis 自动执行 terraform plan
  • Merge 到 master 之後,依据设定,atlantis 执行 terraform apply

Features

  • Self-hosted
  • 已支援许多 CVS,包含 Github, bitbucket, gitlab, azure-devops, ...

Local Run atlantis

官方说明文件试跑 Atlantis Local Run,主要步骤为

  • 安装 terraform
  • 使用 ngrok 暂时产生一个 dns forwarding 到本地的 4141 port
  • 设定 github webhook 将 event 从 github 推到本地的 atlantis
  • 设定 github personal access token,让本地的 atlantis server 可以将 plan 结果送到 github comment
  • 本地启动 atlantis server,使用上述参数
  • 到 Github 发 PR,使用 comment 控制 atlantis

Install atlantis

wget https://github.com/runatlantis/atlantis/releases/download/v0.17.2/atlantis_darwin_amd64.zip
unzip atlantis_darwin_amd64.zip
sudo mv atlantis /usr/local/bin/atlantis

atlantis -h

Install ngrok and run

  • 记下 forwarding url ,作为 URL 环境变数
  • 保持 ngrok 执行的状况下,开新 terminal 执行下列步骤
wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-amd64.zip
unzip ngrok-stable-darwin-amd64.zip
sudo mv ngrok /usr/local/bin/ngrok

./ngrok http 4141

...
Forwarding                    http://41eb-123-194-159-122.ngrok.io -> http://localhost:4141
...

Config Github Webhook

  • githob repository (ex. chechiachang/terraform-30-days)
  • settings -> webhook -> add webhook
  • application/json
  • https://41eb-123-194-159-122.ngrok.io/events
  • 记下 webhook token,放在安全的地方 -> 作为 SECRET 环境变数

Create Github Personal Access token

  • github user -> settings -> Developer settings -> Personal access tokens
  • atlantis
  • 只是本地试用,expiration 选 7 天
  • 记下 access token,放在安全的地方 -> 作为 TOKEN 环境变数

Local Run atlantis server

  • 保持 atlantis server 执行
export URL="https://...................ngrok.io"
export SECRET="ep.................quh"
export TOKEN="ghp_..........................."
export USERNAME="chechiachang"
export REPO_ALLOWLIST="github.com/chechiachang/terraform-30-days"

atlantis server \
--atlantis-url="${URL}" \
--gh-user="${USERNAME}" \
--gh-token="${TOKEN}" \
--gh-webhook-secret="${SECRET}" \
--repo-allowlist="$REPO_ALLOWLIST"

...
{"level":"info","ts":"2021-08-31T23:03:31.616+0800","caller":"server/server.go:680","msg":"Atlantis started - listening on port 4141","json":{}}

Use atlantis

Atlantis Official Doc: Usage

  • help
  • plan
  • apply

Github terraform-30-days 的范例

atlnatis help

comment atlantis help

atlantis
Terraform Pull Request Automation
...

atlnatis plan

comment atlantis plan -d azure/_poc/compute/

  • 这边使用 -d 指定想要 plan 的 root module 的 directroy 进行 plan
  • 这边使用的是 local 本机的 credential,如果有 az login 的 credential 就可以 apply,没有就会失败
    • 如果使用 remote vm / k8s 跑 atlantis server,就需要远端设定 credential
  • 从 server log 可以看到
    • github webhook 的 json event
    • event parse 看 comment 内容有无 help, plan, 或 apply command 及参数
    • 如有就执行 help, plan 或 apply
    • 使用 Github personal access token 再打回去 Github API,产生 comment
# atlantis log

# parse comment as command
{"level":"info","ts":"2021-08-31T23:47:23.483+0800","caller":"events/events_controller.go:417","msg":"parsed comment as command=\"plan\" verbose=false dir=\"azure/_poc/compute\" workspace=\"\" project=\"\" flags=\"\"","json":{}}

# acquired lock with id
{"level":"info","ts":"2021-08-31T23:47:24.378+0800","caller":"events/project_locker.go:80","msg":"acquired lock with id \"chechiachang/terraform-30-days/azure/_poc/compute/default\"","json":{"repo":"chechiachang/terraform-30-days","pull":"6"}}

# terraform init
{"level":"info","ts":"2021-08-31T23:47:31.435+0800","caller":"terraform/terraform_client.go:280","msg":"successfully ran \"/Users/che-chia/.asdf/shims/terraform init -input=false -no-color\" in \"/Users/che-chia/.atlantis/repos/chechiachang/terraform-30-days/6/default/azure/_poc/compute\"","json":{"repo":"chechiachang/terraform-30-days","pull":"6"}}

# terraform workspace
{"level":"info","ts":"2021-08-31T23:47:31.959+0800","caller":"terraform/terraform_client.go:280","msg":"successfully ran \"/Users/che-chia/.asdf/shims/terraform workspace show\" in \"/Users/che-chia/.atlantis/repos/chechiachang/terraform-30-days/6/default/azure/_poc/compute\"","json":{"repo":"chechiachang/terraform-30-days","pull":"6"}}

# terraform plan
{"level":"info","ts":"2021-08-31T23:47:48.746+0800","caller":"terraform/terraform_client.go:280","msg":"successfully ran \"/Users/che-chia/.asdf/shims/terraform plan -input=false -refresh -no-color -out \\\"/Users/che-chia/.atlantis/repos/chechiachang/terraform-30-days/6/default/azure/_poc/compute/default.tfplan\\\"\" in \"/Users/che-chia/.atlantis/repos/chechiachang/terraform-30-days/6/default/azure/_poc/compute\"","json":{"repo":"chechiachang/terraform-30-days","pull":"6"}}

# policy check
{"level":"info","ts":"2021-08-31T23:47:50.017+0800","caller":"events/plan_command_runner.go:214","msg":"Running policy check for command=\"plan\" verbose=false dir=\"azure/_poc/compute\" workspace=\"\" project=\"\" flags=\"\"","json":{"repo":"chechiachang/terraform-30-days","pull":"6"}}

{"level":"info","ts":"2021-08-31T23:47:50.017+0800","caller":"events/policy_check_command_runner.go:36","msg":"no projects to run policy_check in","json":{"repo":"chechiachang/terraform-30-days","pull":"6"}}
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+ create
 <= read (data resources)

Terraform will perform the following actions:

  # module.linuxservers.data.azurerm_public_ip.vm[0] will be read during apply
  # (config refers to values not yet known)
 <= data "azurerm_public_ip" "vm"  {
   ...
  }

Plan: 9 to add, 0 to change, 0 to destroy.

atlantis apply

Apply 也很单纯,就是 apply

  • comment: atlantis apply -d azure/_poc/compute/

注意:这边 apply 下去就会自动 apply,没有 double comfirm yes or no 了

{"level":"info","ts":"2021-08-31T23:38:43.963+0800","caller":"events/events_controller.go:417","msg":"parsed comment as command=\"apply\" verbose=false dir=\"azure/_poc/compute\" workspace=\"\" project=\"\" flags=\"\"","json":{}}
{"level":"info","ts":"2021-08-31T23:38:45.151+0800","caller":"events/apply_command_runner.go:110","msg":"pull request mergeable status: true","json":{"repo":"chechiachang/terraform-30-days","pull":"6"}}
{"level":"info","ts":"2021-08-31T23:38:45.157+0800","caller":"runtime/apply_step_runner.go:38","msg":"starting apply","json":{"repo":"chechiachang/terraform-30-days","pull":"6"}}
{"level":"info","ts":"2021-08-31T23:40:05.868+0800","caller":"terraform/terraform_client.go:280","msg":"successfully ran \"/Users/che-chia/.asdf/shims/terraform apply -input=false -no-color \\\"/Users/che-chia/.atlantis/repos/chechiachang/terraform-30-days/6/default/azure/_poc/compute/default.tfplan\\\"\" in \"/Users/che-chia/.atlantis/repos/chechiachang/terraform-30-days/6/default/azure/_poc/compute\"","json":{"repo":"chechiachang/terraform-30-days","pull":"6"}}
{"level":"info","ts":"2021-08-31T23:40:05.868+0800","caller":"runtime/apply_step_runner.go:57","msg":"apply successful, deleting planfile","json":{"repo":"chechiachang/terraform-30-days","pull":"6"}}

Workflow

实际运作的 gitflow,大约是这样

  • push PR
  • PR review
    • 执行 atlantis plan
  • merge 进入 main
  • automatically atlantis apply main,确保 remote infra 紧跟 main branch 的变更

Pros & cons

使用 atlnatis 有底下优缺点

  • 不再需要工程再在本地 apply
  • 安全性来说,工程师 local 电脑也不再需要 azure 权限的 credential。毕竟 terraform 所需的帐号权限还是拥有 plublic cloud 上许多资源的生杀大权。减少暴露到只有 atlantis server 上有
  • 使用 github comment 控制

缺点是要多养一台或多台 atlantis server,然而 atlantis server 基本上是 stateless server,如果有 k8s 的话非常好养

TODOs for production

上面只是在本地电脑测试一下 atlantis 的功能,实际上如果要让 production 环境使用,还有以下代办事项要处理

  • Deployment: 使用适合 production 环境的 VM / k8s 来执行 atlantis
  • High Availability: 执行多个 atlantis server replicas,当其中一个故障时整体 gitflow 功能仍有效
  • github personal access token 可以使用专属的 bot user,而不要用 chechiachang
  • credential 改用 azure service principal 的设定
    • 放在 atlantis server 可及的安全之处
    • 如果是 k8s 可以搭配 hashicorp vault 使用

Config Atlantis

atlantis default 使用 terraform cmd,然而本课程有许多范例使用 terragrunt,atlantis 也支援,需要底下额外设定

Atlantis: terrragrunt support

(大家先自己研究,我有时间会来补的(汗))

Homework

  • 依照范例,在本地电脑起一个 atlantis server
  • 透过 github comment 操作,terragrunt plan 及 apply 任一 module

Alternative: terraform cloud

Terraform Cloud 是 terraform 官方提供的 Terraform automation Saas 服务

需要收费,请见 terraform cloud pricing。然而也提供更多强大的功能,除了 atlantis 的 remote plan 与 remote apply 外,还有私有 module registry,state file 版本控管...等功能

目前不是本课程推荐的解决方案,但 terraform cloud 不断推陈出新许多新功能,未来值得期待。本课程会依据後续参赛进度调整,有机会再分享 terraform cloud 内容。


<<:  从零开始的8-bit迷宫探险【Level 3】Swift 基础语法 (一)

>>:  EP 3: Use Shell to layout TopStore App

Day 07 : 操作基础篇 4 - 做好笔记备份 ,使用 iCloud 和 Google Drive 进行双重备份

前言 这是 Obsidian 使用教学 — 基础篇的第 4 篇文章。 上一篇文章 我详细介绍了「Th...

Day28 | 获取安装的extension进行操作

大家好,我是韦恩,今天是第二十八天,让我们会练习获取extension的api,为专案的重点功能做准...

Youtube Reports API 教学 - 频道中出报表

「鲑鱼均,因为一场鲑鱼之乱被主管称为鲑鱼世代,广义来说以年龄和脸蛋分类的话这应该算是一种 KNN 的...

[Python 爬虫这样学,一定是大拇指拉!] DAY22 - 实战演练:HTML Response - 抓取股票代码清单 (1)

承接上篇,抓日成交资讯时,我们得知道股票代码,那如果我想要有一个可以定时更新的股票代码清单,要去哪里...

Day21 Vue data属

以前我们在写Vue时data都是这样写 但是到了元件这可能就会出现错误,前面我们的data属性总是以...