Infrastructure As Code Day 2 — Collaboration
背景介紹
隨著 1) 使用 Terraform 時間增加 2) 架構越來越龐大複雜 3) IaC 的生態逐漸完整,因此最近在組織內開始調整 Terraform 的開發準則,希望可以透過此系列文章 (共四篇 Unity, Testing, Collaboration, Productivity) 拋磚引玉,讓大家一起來交流在團隊內使用 Terraform 時如何克服一些常見的問題
而此系列文章有其先後順序,沒有照著閱讀不打緊;但實作的時候必須照著Unity -> Testing -> Collaboration -> Productivity 的順序,不然會導致最終結果不符合預期,文章中所使用到的 Terraform Script 都放在這邊
解決問題
單人使用 IaC 工具 terraform 其實很輕鬆,因為當要下 terraform apply
的時候,其執行結果就只需要對自己負責即可,但是假如有兩個人以上在開發同一個 IaC 專案呢?常常會發生一件事情
A: Dev 環境怎麼壞掉了?!
B: 我剛剛有用 terraform 小改一下東西 (然後 B 開始挽起袖子來查詢問題)
…過了 15 分鐘之後,另外一位同事發言了…
C: 你要先 merge 我的 PR 啦,不然有一個東西會沒有先建立出來, 導致..blablabla
在多人一起開發 terraform 的情況下,這是相當常見的問題,當撰寫的人變成 5 個人甚至是 10 個人的時候,只能透過幹架解決這個問題了(誤) 因此社群有人提供工具來彌平紛爭,也就是底下要介紹的 Atlantis
Atlantis 介紹
個人覺得 Atlantis 是一套可以讓 Terraform 達成 GitOps 的工具,使用過後也理解為什麼 Terraform 官方會將這個開源專案納入麾下,他有點像是Terraform 程式碼 & 人類 & 雲端資源三者之間的溝通橋樑一般的存在

他主要可以幫忙做到的事情有兩件 1) 將以前偷偷摸摸再開發者電腦執行的 Terraform plan/apply 跟 Git PR 整合在一起 2) 避免同一個 Terraform 內同一個資料夾或是 Workspace 同時有人去修改,底下會先從環境的設定開始,接著實際感受 Atlantis 的強大之處,最後再提一些細部的設定說明
事前準備
安裝必要元件
下面只有提到 Atlantis 和 Ngrok 的安裝步驟,有關於 Terraform 及 Terragrunt 的安裝方式請參閱此系列文章的第一篇
# 安裝 Atlantis
(從官方下載最新版 https://github.com/runatlantis/atlantis/releases)~$ atlantis version
atlantis 0.8.3~$ atlantis version
atlantis 0.8.3# 安裝 Ngrok
~$ brew cask install ngrok# 啟動 ngrok
~$ ngrok http 4141# 應該可以看到類似如下的輸出,然後這個 Terminal Tab 請不要關閉,就讓其在前景執行...
Session Status online
Version 2.3.34
Web Interface http://127.0.0.1:4040
Forwarding http://6613da45.ngrok.io -> http://localhost:4141
Forwarding https://6613da45.ngrok.io -> http://localhost:4141
...# 接著請開啟第二個新的 Terminal Tab 把 ngrok 幫你建立的公開通道加到環境變數中
~$ export URL="https://{YOUR_HOSTNAME}.ngrok.io"
因為 Atlantis 跟 Git Service 需要整合在一起才能夠發揮效用,而本篇文章選擇 GitHub 當作示範平台,因此接著會提到如何設定 GitHub 的 Webhook 和 Access Token 給 Atlantis 使用
創建 Repository
首先請先 Folk smalltown/iac-day-2 repository 到自己的 GitHub 帳號下,然後使用第二個 Terminal Tab 將其下載到本機端
~$ git clone git@github.com:${YOUR_GITHUB_ACCOUNT}/iac-day-2.git
建立 GitHub Webhook Secret
GitHub 的 Webhook Secret 的功用是讓 Client 知道此請求是真的從 GitHub 而來,換句話說,就是要讓 Atlantis 驗證請求是從 GitHub 來的;可以使用此網站 (http://www.unit-conversion.info/texttools/random-string-generator/) 產生任意長度的隨機字串來當成 Webhook Secret
# 然後將此字串加入到環境變數當中
~$ export SECRET="{YOUR_RANDOM_STRING}"
新增 GitHub Webhook
接著去剛剛 Folk 的 Repository 設定頁面
- 然後點選側欄的 Webhook,接著按下 Add webhook
- 在 Payload URL 的地方填上剛剛 Ngrok 產生的網址加上 /events,例如:https://c5004d84.ngrok.io/events (請確定有加上 /events 在後面喔!)
- Content type 的欄位選擇 application/json,把剛剛產生的 Secret 填入
- 選擇 Let me select individual events,然後把這四個事件勾起來: Pull request reviews, Pushes, Issue comments, Pull requests
- 確定 Active 有勾起來後,按下 Add webhook
新增 GitHub Access Token
根據 GitHub 官方文件來新增 Access Token 給 Atlantis 使用,Scope 選擇 repo 即可,然後將該 Token 也加到環境變數中
~$ export TOKEN="{YOUR_TOKEN}"
準備 Atlantis Server repo.yaml
因為此系列文的架構是比照真實環境所設計,因此相對複雜一些,所以這邊需要另外新增一個 repo.yaml 組態檔案在本機端給 Atlantis 使用,將所要管理 Repository 的某些行為進行客製化,畢竟使用了不少個 AWS Account,又跑去另外多使用了 Terragrunt,後面會再詳細提檔案裡面偷做了什麼事情
啟動 Atlantis Server
設定了一堆東西之後,總算可以準備來把 Atlantis Server 跑起來了 (累)
# 把你的 GitHub 帳號名稱加入環境變數
~$ export USERNAME="{YOUR_GITHUB_ACCOUNT}"# 把想要被 Atlantis 管理的 Repository 也加入環境變數,就是剛剛 Folk 的 Repository
~$ export REPO_WHITELIST=github.com/{YOUR_GITHUB_ACCOUNT}/iac-day-2# 需要的組態都已經放置到環境變數後,就可以正式啟動 Atlantis
~$ atlantis server \
--atlantis-url="$URL" \
--gh-user="$USERNAME" \
--gh-token="$TOKEN" \
--gh-webhook-secret="$SECRET" \
--repo-whitelist="$REPO_WHITELIST" \
--repo-config="{REPO_YAML_PATH}/repo.yaml"2019/08/03 14:39:19+0800 [INFO] server: Atlantis started - listening on port 4141
實際演練
太棒了!恭喜已經將 Atlantis Server 端設定完畢且啟動成功, Terminal 應該已經有兩個不能被關掉的 Tab 正在分別執行著 Ngrok 及 Atlantis,接著再打開第三個 Tab 開始體驗 Atlantis 的功能!
首先試著修改剛剛 Folk 回來的 Repository 檔案 demo-atlantis/password/terragrunt.hcl,這個 terraform 專案並不會去修改任何 AWS 的資源,他只會產生一個自訂長度的密碼回傳
# 隨便修改密碼長度變數值,譬如像我把它改成 30
inputs = {
password_length = 24
}
接著提交一個 PR 到此 Folk 的 Repository,然後神奇的事情馬上發生了!(參考用 PR 連結),從下圖可以看出來 Atlantis Server 幫忙執行完 terraform plan 並且將結果自動輸出至 PR 的 Comment 當中,所以幫忙 Code Review 的同事不像以前只能單看程式碼想像,而是可以在 PR 中得知有哪些東西會被 Terraform 新增/修改/刪除,而且也知道被執行了沒有

接著我照上面 Atlantis 提示的在 PR Comment 留下 atlantis apply,Atlantis Server 就幫忙 apply 好了!

細節解說
第一次整合 Atlantis 成功的時候,覺得這樣的流程好棒,配合公司內部在 GitHub Status Check 整合 RFC (ITIL 的 Request For Change 流程),好像以前在個人電腦執行 Terraform 時見不得人的一切都赤裸裸被揭露在眾人面前XD 接著讓我們來看一些設定上的細節
Atlantis Server 的 repo.yaml
在這個檔案中定義了在 PR 中什麼時候才能夠被 apply,像我這裡是設定只有當這個 PR 可以被 Merge 的時候才可以執行 apply,也可以設定被 approved 之後才能夠 apply,更多設定方式請參考官方文件,並且允許讓 repository 可以選擇預先設定的 Workflow,還有設定預設的 Workflow 要使用哪一個
# 訂定什麼什麼情況下才能夠執行 terraform apply
apply_requirements: [mergeable]# 允許 Repository 可以選擇 Server 預先定義好的 Workflow
allowed_overrides: [workflow]# 預設的 Workflow
workflow: terragrunt
而因為我使用了 Terragrunt,所以我定義了自己的 Workflow,其實就是當 Atlantis Server 要去執行 plan/apply 的時候,希望他真正要執行的指令是什麼,可以看到下面就是把一般的 plan/apply 改成 terragrunt 的指令,更多細節請參考官方文件
# workflows lists server-side custom workflows
workflows:
terragrunt:
plan:
steps:
- run: terragrunt plan -no-color -out $PLANFILE
apply:
steps:
- run: terragrunt apply -no-color -auto-approve
Repo 層級的 atlantis.yaml
而在 iac-day-2 這個 Repository 內有一個 atlantis.yaml 檔案用來控制什麼資料夾該使用哪一個 Workflow,可以看到下面設定說假如在資料夾 demo-atlantis/password 偵測到某些類型的檔案有修改時,就會在 PR 內自動執行 plan,而這個 Project 並沒有特別指定 Workflow 類型,所以就會使用剛剛上面預設的 Terragrunt Workflow
version: 3
projects:
- name: demo-atlantis
dir: demo-atlantis/password
autoplan:
when_modified: ["**/*.hcl", "**/*.tf*"]
Locking 機制
除了讓偷偷摸摸的作業見光死之外,使用 Atlantis 還會有 Locking 機制,也就是說當該 PR 還沒有被 Merged 或是 Closed 的話,該 Terraform 的資料夾跟其 Workspace 將會被鎖定,假如有另外一個使用者想要發一個使用到相同的資料夾或是 Workspace 的 PR,就會看到類似如下錯誤,如此一來便可以避免兩個人改到同樣的東西

而在 Atlantis Server 頁面也可以看到目前有什麼東西是被上鎖的,也可以在這邊或是剛剛的 PR 內進行 Unlocking 的動作,更多說明請參閱官方文件

結論
雖然設定的過程有點麻煩,但是整合完成之後,讓 Terraform 被修改過後會對雲端資源所做的任何新增/修改/刪除的資訊都可以在 PR 中一目了然;而且還有 Locking 的功能,使得管理同樣雲端資源的 Terraform 在同一時間只有一個人能夠修改,同事間感情和睦不再爭吵XD 實作完第三篇系列文之後,應該解決掉不少使用 Terraform 時會遭遇到的管理問題,並且降低管理雲端資源的風險性,也使 Terraform 達成了 GitOps,下一篇將會進一步探討如何降低同事間 Code Review 的負擔,並且同時提升 Terraform 程式碼的品質
參考資料
- Atlantis Document: https://www.runatlantis.io/docs/