BASEプロダクトチームブログ

ネットショップ作成サービス「BASE ( https://thebase.in )」、ショッピングアプリ「BASE ( https://thebase.in/sp )」のプロダクトチームによるブログです。

CodeBuildとtfnotifyを用いたterraform planの実行

f:id:fumikony:20191218184925p:plain

この記事はBASE Advent Calendar 2019 22日目の記事です。 devblog.thebase.in

こんにちは。最近はCorporate Engineeringをやっている山根 (@fumikony)です。すこし前まで、即時に資金調達ができる金融サービス「YELL BANK(エールバンク)」のインフラまわりに関わっていました。

今回は「YELL BANK」のインフラにおけるTerraform運用について紹介します。

概要

「YELL BANK」のインフラはAWS上に構築していて、Terraformによるコード管理を行っています。Terraformのコード(tfファイル)はGitHubで管理し、変更を加えたいときはプルリクエストを作成します。普通ですね。

さて、Terraformをプルリクエストベースで運用する場合、いくつか考えることが出てきます。具体的には

  • 実行する場所
  • レビューのやりかた
  • プルリクエスト作成からterraform applyまでのワークフロー

です。

実行する場所について

「YELL BANK」では、 terraform plan をAWS CodeBuild上で実行し、terraform applyはTerraform実行用のEC2インスタンス上で実行しています。

「YELL BANK」の開発では主にCircleCIを使っているのでTerraformもCircleCI上で実行することも考えました。しかし、CircleCIにあまり強いIAMの権限を付けることがためらわれたので、CodeBuildを選択しました。

レビューのやり方について

tfファイルの変更をプルリクエストにすると、その差分としては当然、tfファイルのみが出てきます。しかしながらレビューの際には terraform plan の結果を確認したいものです。

はじめのころはプルリクエストに terraform plan の結果を(手で)貼ってレビューしたりしていたのですが、ここにCodeBuildとtfnotifyを導入しました。 terraform plan の結果がプルリクエストのコメントとして(自動的に)返ってくるようになり、レビューがやりやすくなりました。

プルリクエスト作成からterraform applyまでのワークフロー

現在のところ、「YELL BANK」のTerraform変更のフローは以下のようなものになっています。

  1. ブランチを切ってtfを編集
  2. git commit, git push
  3. プルリクエストを作成
    • terraform planの結果がプルリクエストのコメントとして返ってくる
  4. レビュー
  5. マージ
  6. Terraform実行サーバ上で terraform apply

詳細

以下、上でのべた構成の各部分について、詳しく説明していきます。

tfnotifyについて

tfnotify はメルカリさんがOSSとして公開しているツールで、teffaform planterraform apply の出力をGitHubやSlackなどに通知するためのものです。CIの中で使う想定で作られています。

以下に設定ファイルの例を示します。これはCodeBuildからtfnotifyを使用して、terraform plan の結果をGitHubに通知するための設定です。

.tfnotify.yaml

---
ci: codebuild

notifier:
  github:
    token: $GITHUB_TOKEN
    repository:
      owner: "orgname"
      name: "reponame"

terraform:
  plan:
    template: |
      {{ .Title }} for Production <sup>[CI link]( {{ .Link }} )</sup>
      {{ .Message }}
      {{if .Result}}
      <pre><code> {{ .Result }}
      </pre></code>
      {{end}}
      <details><summary>Details (Click me)</summary>

      <pre><code>
      {{ .Body }}
      </pre></code>
      </details>

これを terraform コマンドを実行するディレクトリに置いておくことで、template に記載した内容のコメントがプルリクエストに通知されます。{{ .Title }} などはtfnotifyが埋めてくれます。詳細はtfnotifyのREADMEをご覧ください。

AWSアカウントとTerraformディレクトリ構成について

「YELL BANK」のインフラには本番(prd), ステージング(stg), 開発(dev)の環境ごとにAWSアカウントがあります。このうちprdとstgをTerraform管理下に置いています。

Terraformのディレクトリ構成としては、 - 環境ごとに別のディレクトリを作成 - リソースの種類ごとにtfファイルを作成 という方針をとっています。

具体的には以下のようなディレクトリ構成になっています。

.
├── .gitignore
├── README.md
├── aws-basebank-prd
│   └── bb-prd
│       ├── .tfnotify.yaml
│       ├── README.md
│       ├── cloudfront.tf
...
│       └── vpc.tf
├── aws-basebank-stg
│   └── bb-stg
│       ├── .tfnotify.yaml
│       ├── README.md
│       ├── cloudfront.tf
...
│       └── vpc.tf
├── bin
│   └── dir_is_changed
└── buildspec.yml

buildspec.yml がCodeBuildの設定ファイル、.tfnotify.yaml がtfnotifyの設定ファイルです。

CodeBuildについて

AWS CodeBuildは、AWSのCIです。

CodeBuildを実行のトリガとしてはGitHubのプルリクエストの作成と更新を使います。 また、prd,stgそれぞれのAWSアカウントにおいてCodeBuildを設定しています。

これは実際に使用している buildspec.yml です。

buildspec.yml

phases:
  install:
    runtime-versions:
      golang: 1.12
    commands:
      - git clone https://github.com/tfutils/tfenv.git ~/.tfenv
      - ln -s ~/.tfenv/bin/* /usr/local/bin
      - tfenv install 0.12.6
      - wget https://github.com/mercari/tfnotify/releases/download/v0.3.1/tfnotify_v0.3.1_linux_amd64.tar.gz
      - tar xzf tfnotify_v0.3.1_linux_amd64.tar.gz
      - cp tfnotify_v0.3.1_linux_amd64/tfnotify /usr/local/bin/
      - cp bin/dir_is_changed /usr/local/bin/
  build:
    commands:
      # ref. https://blog.hatappi.me/entry/2018/10/08/232625
      - |
        if dir_is_changed $TERRAFORM_DIR; then
          cd $TERRAFORM_DIR
          terraform init -no-color
          terraform plan -no-color | tfnotify plan
        fi

ここで行っているちょっとした工夫として、dir_is_changed$TERRAFORM_DIR があります。 $TERRAFORM_DIR はCodeBuildで設定している環境変数で、terraform plan を実行するディレクトリを指定します。 また dir_is_changed は以下のようなシェルスクリプトで、引数にあたえたディレクトリ以下に変更があったかどうかを判定します。

dir_is_changed

#!/bin/bash
# ref. https://blog.hatappi.me/entry/2018/10/08/232625

DIFF_FILES=(`git diff origin/master --name-only --relative=${1}`)

if [ ${#DIFF_FILES[@]} -eq 0 ]; then
  exit 1
else
  exit 0
fi

これによって、prdかstg、変更があった方だけでplanを実行するという振る舞いを実現しています。CodeBuild自体はプルリクエストの作成・更新をトリガとして常に両方の環境で動くのですが、変更が無いほうでは何もせずに終了します。 ちなみに、この方法では一つのプルリクエストでprdとstgの両方を変更するとうまく動かないため、別々のプルリクエストにしておく必要があります。

せっかくなのでCodeBuildの設定画面のスクリーンショットも貼っておきます。よかったら参考にしてください。 f:id:fumikony:20191218183102p:plain

f:id:fumikony:20191218183252p:plain

実行例

下図は、CodeBuild上でterraform planが実行され、その出力がtfnotify経由でプルリクエストコメントとして通知されている様子です。 Details (Click me) というところをクリックすると、びろ〜んと伸びてterraform planの出力が出てきます。

f:id:fumikony:20191218183450p:plain

今後の発展など

今のところはCodeBuildで実行するのは terraform plan までにとどめており、terraform applyはサーバ上で手動実行しています。apply までやってしまいたい気持ちはありましたが、

  • GitHubでプルリクエストをマージしただけで本番インフラが変更できてしまうのはちょっと怖い
  • 手動でAWSリソースを作ったあとで terraform import して辻褄をあわせるようなケースが、たまに有る

というのがあり、いったんplanまでにしました。 上については、applyの前に人間による承認をはさみたいのでCodePiplelineにするのがよいだろうか?🤔 と思っていますが未検証です。 下については、そういうことをなるべくしないというのと、それようの場所をそれはそれで用意した上で、普段はCIに任せるのが良いかなあと思っています。

おわりに

若干とりとめがなくなりましたが、「YELL BANK」のインフラにおけるTerraform運用について紹介しました。 tfnotifyはかなり便利で、オススメできるツールだと思います。

最後に、同じようなジャンルのツールを紹介しておきます。まだ詳しく調べていないのですが、面白そうなので機会を見てさわっておきたいところです。CI+tfnotifyの構成を検討する場合は、これらも検討に入れておくといいと思います。

  • Atlantis
    • これはGitHubのプルリクエストコメントにコマンドを書くと裏でterraformを実行してくれる感じのツールです。
  • Terraform Cloud
    • Terraform開発元のHashiCorp自身が運営しているSaaSです。

さて明日は、BASE BANKの清水さんとProduct Managementの山田さんです。お楽しみに。

参考リンク