Carpe Diem

備忘録

Atlantisを用いてPRを介したTerraformオペレーションの自動化

背景

TerraformはInfrastructure as Codeを実現してくれるとても素晴らしいツールである一方、運用時に以下のような問題が発生します。

  • 個々人がローカルで実行する場合のオペミス
  • パブリッククラウドリソースを扱うクレデンシャルの管理

そのため実行環境を統一(CIと連携など)したり、GitHubでplan結果を貼ってレビュー→問題なければマージ→(自動)applyといった運用をしているチームも多いことでしょう。
しかしTerraformはplanで成功してもapplyでコケることはちょくちょくあるため、そのPRが正しい状態かどうかは実行するまで分かりません。

またCIと連携することで神権限を持ったクレデンシャルを外部サービスに預けることとなり、漏洩リスクが増大しサービスに致命的な被害をもたらす可能性もあります。

このような諸々の課題を解決していい感じに自動化してくれるのがAtlantisです。

環境

  • Atlantis v0.14.0
  • Terraform v0.12.28

Atlantis

まずAtlantisについて簡単に説明します。

アーキテクチャ

Atlantisを用いたオペレーションのアーキテクチャは以下です。

f:id:quoll00:20200721033252p:plain

GitHubなどVCSでのPRやコメントをwebhookで検知してterraformを実行します。

クレデンシャルは真ん中のセルフホスティングするVMやコンテナに保持されるため、外部に埋め込んだりすることがありません。

ワークフロー

Terraform Pull Request Automation | Atlantis にある通りです。

  1. PRを送る
  2. Atlantisが自動でterraform planを実行し、結果がコメントとして返る
  3. チームメンバーがレビューしてapproveする
  4. atlantis applyとコメントする
  5. Atlantisがterraform applyを実行し、結果がコメントとして返る
  6. マージする

terraform planに失敗した場合はコードを修正して2を繰り返せばよく、terraform applyに失敗した場合は4を繰り返して再実行したり、.tfファイルを修正してやり直す形になります。

こうしてapplyに失敗して不整合を持った状態でマージされる、といったことがなくなります。

導入してみる

チュートリアル

Test Drive | Atlantis

こちらのチュートリアルを一度実行すると流れがつかめるため、環境を構築する前に試すと良いです。

ローカルに構築する

今回実行する環境はローカルとします。

Testing Locally | Atlantis

に則って進めます。

各種インストール

  • Terraform
  • Atlantis
  • ngrok

をダウンロードしてpathを通しておきます。

ngrokを起動しておきます。

$ ngrok http 4141

f:id:quoll00:20200726030845p:plain

github webhookの設定

先程のngrokのURLに/eventsを付けてPayload URLにセットします。

f:id:quoll00:20200726031749p:plain

Secretにはランダムな文字列を入れておきます。この文字列はAtlantis側でも使います。

Let me select individual eventsを選んで

  • Pull request reviews
  • Pushes
  • Issue comments
  • Pull requests

にチェックをします。

f:id:quoll00:20200726031950p:plain

github access tokenの生成

Personal access tokensrepoの権限を付けます。

f:id:quoll00:20200726024409p:plain

Atlantis起動

諸々のパラメータをセットしてサーバを起動します。

$ export URL="https://16f0f2cc4e40.ngrok.io"
$ export USERNAME="jun06t"
$ export TOKEN="xxx"
$ export SECRET="yyy"
$ export REPO_WHITELIST="github.com/jun06t/atlantis-example"
atlantis server \
--atlantis-url="$URL" \
--gh-user="$USERNAME" \
--gh-token="$TOKEN" \
--gh-webhook-secret="$SECRET" \
--repo-whitelist="$REPO_WHITELIST"

これでローカル環境の構築は完了です。

検証

PRの作成

リポジトリで適当なディレクトリ、ファイルを作成します。

$ mkdir dev
$ touch main.tf

main.tfの中身はテストでnull_resourceを使います。

resource "null_resource" "example" {
}

PRする

PRすると自動で変更点のある*.tfのあるディレクトリでterraform planを実行してくれます。

f:id:quoll00:20200726034628p:plain

planの実行結果がコメントに貼り付けられます。
問題なければレビューしてもらい、atlantis applyとコメントします。

f:id:quoll00:20200726034820p:plain

applyが完了したらマージしてロックを解除(自動)します。

f:id:quoll00:20200726035515p:plain

鍵情報の設定

上記のようなnull_resourceでなく、実際はAWSGCPのリソースをイジることになります。
その場合各リソースにアクセスするための鍵情報が必要になりますが、Atlantisでは

Provider Credentials | Atlantis

で解説されています。

どれも鍵発行自体しないか、発行するにしろ自分のホスティングするサービス上なので安全性が高いです。

AWS

AWSでAtlantisを起動していれば

christina04.hatenablog.com

で紹介したAssume Roleの仕組みを使うことで鍵自体を発行せずに済みます。

GCP

GCE上でAtlantisを起動していれば、Instance Service Accountを使うことでEC2のRoleのような状態を実現できます。

クロスプラットフォームの場合

環境変数(AWS_ACCESS_KEYなど)を使ったり、Hashicorp Vaultを使うと良いです。

その他

オペミスを防ぐための仕組みや設定

同じディレクトリのファイルでマージ前にPRを送るとロックでコケる

Atlantisはロックによる安全機構があり、terraformでありがちな同じファイルをいじってしまう問題を防ぐことができます。

あるディレクトリのPRがある状態で、同じディレクトリを変更してPRを送ると以下のようにロックによってコケます。

f:id:quoll00:20200726035221p:plain

ロックが外れてからatlantis planしたり再プッシュするとplanが通ります。

applyにapprovedを必須にする

repos.yamlというファイルでより細かい設定が可能です。

実行時にオプションを付けてファイルを指定してください。

atlantis server \
--atlantis-url="$URL" \
--gh-user="$USERNAME" \
--gh-token="$TOKEN" \
--gh-webhook-secret="$SECRET" \
--repo-whitelist="$REPO_WHITELIST" \
--repo-config=repos.yaml

例えば以下のようにapply_requirementsを設定することで、apply前にレビューのapprovedを必須にすることができます。

repos:
- id: /.*/
  apply_requirements:
  - approved

approvedがない状態でapplyしようとすると、以下のようにコケます。

f:id:quoll00:20200726041614p:plain

Branch protection rules

planapply成功していないとマージできないようにbranch protection rulesを設定すると良いです。

f:id:quoll00:20200726025346p:plain

すると以下のようになり、planapplyでコケているとマージできなくなります。
↓は成功しているのでチェックが通った状態です。

f:id:quoll00:20200726025336p:plain

terraform fmtを行う

以下のようにallow_custom_workflows: trueにして独自のstepsを登録することができます。

repos:
- id: /.*/
  allow_custom_workflows: true
workflows:
  default:
    plan:
      steps:
      - init
      - run: terraform fmt -check -diff
      - plan
    apply:
      steps:
      - apply

これによりterraform fmtで差分が出るとplanがエラーになります。

f:id:quoll00:20201210171605p:plain

ただしこれはatlantisのterraform versionに依存するので、required_versionを指定していてもそのバージョンではないfmtになるので注意が必要です。

workaroundとしては$ATLANTIS_TERRAFORM_VERSIONを使ったpath指定で実行させることで、custom commandでも期待するterraform versionで実行できます。

github.com

まとめ

Terraformは非常に便利なツールである一方、オペミスや漏洩リスクなど運用上気にしないといけない点も多いです。
Atlantisはそれらの問題を解決してくれるツールとしてオススメです。

参照