話題の記事

TerraformをPull Request上のコマンドで実行!Atlantisを試してみた

2019.06.16

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。かたいなかです。

現在関わっているプロジェクトで、Terraformの適用をイイ感じに行う方法を検討しています。

そのなかで、GitHubのPRコメントのコマンドでTerraformのplanapplyを行う、Atlantisというツールが良さそうだったのでご紹介します。

Atlantisとは

Cloud Posseにより開発されている、GitHub/GitLab/BitBucketのPull Request上のコマンドでTerraformのワークフローを実行するツールです。

Atlantisを使用すると、例えば以下のような開発フローが実現できます

  1. Terraformのコードを変更し、GitHub上でPull Requestをオープン
  2. 開発環境と本番環境それぞれのディレクトリで plan が自動で実行され、結果がPR上にコメントとして追加
  3. PR上からコマンドで変更を開発環境に適用する
  4. 開発環境への適用が問題ないことを確認する
  5. PR上からコマンドで本番環境にも適用する
  6. 開発・本番の両方の環境への適用が成功したら自動でPRをマージ

個人のローカルマシンに依存せず、かつPR上で記録が残る方法で実行でき、複数stateにも対応できるのが魅力です。

実際にやってみる

環境

  • Terraform v0.12.1

事前準備

  • Route53でpublic hosted zoneが使用できるドメインが必要です。Webhookの送信先として指定する必要があるためです。

Atlantisで適用するTerraformのコード

今回は以下のようなディレクトリ構造のTerraformのリポジトリの内容をGitHubのPRから適用できるようにしていきます。

.
├── README.md
└── example-project
    ├── environments
    │   ├── development
    │   │   └── main.tf
    │   └── production
    │       └── main.tf
    └── modules
        └── example
            └── main.tf

example-project/envoronments 以下の developmentproduction がそれぞれ、開発環境と本番環境に対応しているディレクトリ構成です。

TerraformでAtlantisをデプロイ

FargateにデプロイするためのTerraformのモジュールが提供されています。今回はそれを利用してデプロイします。

まず、以下のようなファイルを作成します。

atlantis/main.tf

provider "aws" {
  version = "~> 2.0"
  region  = "ap-northeast-1"
}

module "atlantis" {
  source  = "terraform-aws-modules/atlantis/aws"
  version = "~> 2.0"

  name = "atlantis"

  cidr            = "10.20.0.0/16"
  azs             = ["ap-northeast-1a", "ap-northeast-1c"]
  private_subnets = ["10.20.1.0/24", "10.20.2.0/24"]
  public_subnets  = ["10.20.101.0/24", "10.20.102.0/24"]

  route53_zone_name = "<atlantisの名前を登録するRoute53のPublic HZのドメイン名>"
  
  policies_arn = [
    # Terraformを実行するため、
    # AdministratorAccessをAtlantisのECSのタスク実行ロールにアタッチしています。
    # 必要に応じてロールの指定を置き換えてください。
    "arn:aws:iam::aws:policy/AdministratorAccess"
  ]

  atlantis_github_user       = "<Botに使用するGitHubユーザ>"
  atlantis_github_user_token = "<ボットユーザのGitHubトークン(repo権限を付けてください)>"
  atlantis_repo_whitelist    = ["github.com/<リポジトリのGitHubユーザ名>/*"]
}

output "webhook_url" {
  description = "Github webhook URL"
  value       = module.atlantis.atlantis_url_events
}

output "webhook_secret" {
  description = "Github webhook secret"
  value       = module.atlantis.webhook_secret
}

次に以下のコマンドで準備したTerraformのファイルをapply します。

$ cd <atlantis用のmain.tfを作成したディレクトリ>
$ terraform init
$ terraform apply

terraform apply が完了すると以下のような結果が出力されます。

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

webhook_secret = "XXXXXXXXXXXXXXXXXX"
webhook_url = https://<ドメイン名>/events

出力されたwebhook_secretwebhook_urlの2つの値は後ほどGitHubのリポジトリでWebhookを登録する際に使用するのでメモっておきます。

Webhookを適用

次にWebhookを登録します。Atlantisのドキュメントを参考に設定していきます。

GitHubのリポジトリのトップ画面から Settings -> Webhook -> Add Webhook と順番に選択し、Webhookの追加画面に移動します。

その画面で以下のような設定を行い、Webhookを作成します。

項目 設定値
Payload URL <webhook_url として出力されたURL>
Content type application/json
Secret <webhook_secret として出力された文字列>
Which events would you like to trigger this webhook?  Let me select individual events
イベントの種類 設定値
Pull request reviews
Pushes
Issue comments
Pull requests
その他のイベント チェックしない

リポジトリに設定ファイルを設置

リポジトリのルートに atlantis.yaml という名前で以下のような内容のファイルを作成し、GitHubにプッシュしておきます。

version: 3
# applyに成功したら自動でPRをマージ
automerge: true
projects:
  # 開発環境の設定
  - name: development
    dir: example-project/environments/develepment
    workspace: default
    terraform_version: v0.12.1
    autoplan:
      # 指定したファイルが変更されたら
      # 自動でplanを実行して結果をGitHubのコメントで追加
      when_modified: ["*.tf", "../../modules/**/*.tf"]
      enabled: true
  # 本番環境の設定
  - name: production
    dir: example-project/environments/production
    workspace: default
    terraform_version: v0.12.1
    autoplan:
      when_modified: ["*.tf", "../../modules/**/*.tf"]
      enabled: true

動作確認

ここまでできたら正しく動くようになっているはずです。

実際にPRを作って動作を確認してみましょう。今回は開発環境と本番環境の両方に影響するモジュールを修正してPRを作成しました。

すると、environments/developmentenvironments/production のそれぞれのディレクトリでplanを行った結果がGitHubのコメントとして追加されます。

まず、開発環境に適用したいので、devの結果を確認し、

問題ないのでbotが付けたコメントの指示にに従って以下のようなコメントをつけます。

すると、開発環境へのapplyが実行されます!実行が終わると結果のコメントが追加されます。

開発環境への適用で問題なかったので次は本番にも適用してみます

すると、本番環境へのapplyが実行されます!今回は他にapplyする対象がないのでPRのマージまで自動で行われました。

GitHubのPR上からコメントを打つだけで、開発環境、本番環境と順番にTerraformを適用していくことができました!!!

まとめ

ご覧いただいたようにAtlantisを使用することで、GitHub上でコマンドを使用してTerraformのplan,applyが行なえます。CIツールでの適用では複雑になってしまう複数のstateがあるリポジトリの管理も、このフローであれば問題なく対応できそうです。

個人的にマージする前に apply するというフローが癖があるのかなと思ったのですが、これはこれで実際に正しくapplyできるもののみ マージされるということで良いのではないかと思います。

Atlantisは設定項目が充実しており、設定によってはapply前にレビューを必須にする、といったことも行えるようです。また、複数のPRがあるときに競合して不具合が発生しないようにするロック機能もあるようで、複数人での開発にももちろん対応できるようになっています。

参考リンク