LoginSignup
19
7

More than 3 years have passed since last update.

Atlantis で Terraform プルリクエストの自動化

Posted at

なぜ Atlantis が必要なのか

Terraform を使おうとすると、ふつうコンフィギュレーションを Git でバージョン管理したくなるだろう。
さらにチーム開発では、 GitHub/GitLab のプルリクエスト/マージリクエスト(以下、PR)でコンフィギュレーション変更のレビューを行いたくなる。
だが、レビューが通って変更がマージされたとして、 terraform apply に失敗するかもしれない。
急いで修正しなければならないが、その間、ほかの PR の進行を止める手段はなく、
最悪の場合 terraform apply に失敗するコンフィギュレーションが積みあがってしまう。
かといってマージ前に terraform apply してしまうと PR が同時進行しているときに、もっとひどい状態(どちらかしか適用されていない状態)になる可能性がある。

チーム開発で Terraform を使おうとすると、このような問題に陥る可能性がある。
規模が大きくなると運用ではカバーしきれなくなってくるだろうが、
Atlantis ならうまい具合に解決できる。

Atlantis のワークフローと動作概要

Atlantis を使ったときに PR の動きをもって、先述の問題をどう解決しているのかを説明する。

  1. レビューイが PR を作成する。
    • image.png
    • PR が作成されると Webhook で Atlantis を呼び出してくれる。
  2. Atlantis が自動で terraform plan を実行し、結果をコメントしてくれる。
    • image.png
    • 同じユーザなのでわかりにくいが、このコメントはAtlantisによるもの。
    • 変更のあったコンフィギュレーションのみ terraform plan が実行される。
    • このとき Atlantis はロックされ、他の PR を作成しても動かなくなっている。 ロックは PR がマージされるか、クローズされるまで続く。
  3. レビュアが確認して問題なければ(レビュアかレビューイのどちらかが) atlantis apply とコメントする。
    • image.png
    • PR にコメントされると Webhook で Atlantis を呼び出してくれる。
  4. Atlantis が自動で terraform apply を実行し、結果をコメントしてくれる。設定によっては、正常に完了すれば自動で PR のマージまでしてくれる。
    • image.png
    • やはり同じユーザなのでわかりにくいが atlantis apply 以下のコメントやマージはすべて Atlantis によるもの。

Atlantis は、ロック機能によって、複数の PR を同時進行できなくしている。
image.png

なお、ロックされていても PR を開いて進めていてよく、
ロックしていた PR がマージされロック解除された後にプッシュしたり atlantis plan とコメントするとロックを取り直せる。
image.png

時系列を整理して書くと、次のように、PR#1 作成後に PR#2 を作成するとエラーとなるが、
PR#1 マージ後に PR#2 で atlantis plan を実行すればロックを取り直せる:

PR#1
 |
plan (ロック取得)
 |
 |        PR#2
 |         | Atlantis のロック機能でエラー
 |         |
apply      |
merge      |
           |
          plan (ロック取得)
          apply
          merge

なお、ロックは変更のあったコンフィギュレーションごとに取られるので、
関連しないリソースは、下記のようにディレクトリを分けておくとよいだろう。

+admin/
| +-- main.tf
| +-- ...
|
+test/
| +-- main.tf
| +-- ...
|
+prod/
  +-- main.tf
  +-- ...

たとえば、test ディレクトリでロックされているときでも、 prod ディレクトリへの変更は可能である。
image.png

このようなワークフローで Atlantis はチームでの Terraform 運用上の問題を解決できる。

次節では、この Atlantis の構築方法について述べる。

Atlantis の構築

基本的に公式のドキュメントを参照すればよい。
ここでは特に Git リポジトリに GitHub を用い、EC2 インスタンス上に Docker コンテナとして動かす Atlantis について説明する。
Atlantis のバージョンは 0.7.1 を使用している。

注意すべき Atlantis のアーキテクチャ上の特徴としては、 Webhook を受けて駆動し PR にコメントしたり PR をマージするので、
GitHub や GitLab と相互にアクセスできなければならない、というものがある。
すなわち GitHub や GitLab.com を使用する場合はインターネット上に公開しなければならない。
共通鍵として Webhook シークレットを利用したり、
駆動するリポジトリをホワイトリストで指定したりすることで、不用意に動かされないようになっているが、
プライベートリポジトリを使うことを検討したほうがよいかもしれない。

まず Atlantis で動かすのに必要な権限を用意する。
AWS のリソースを操作する場合は、EC2 にインスタンスロールを与える。
Terraform で書くと、以下のようになる。

data "aws_iam_policy_document" "atlantis_assume_role_policy" {
  // Atlantis の AssumeRole ポリシー。
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "atlantis" {
  // Atlantis の AssumeRole。
  name               = "atlantis_role"
  assume_role_policy = "${data.aws_iam_policy_document.atlantis_assume_role_policy.json}"
}

data "aws_iam_policy_document" "atlantis" {
  // Atlantis のインスタンス用のロールのポリシー。
  // 管理権限を与えているが、強すぎるかもしれないので検討の余地がある。
  statement {
    effect    = "Allow"
    actions   = ["*"]
    resources = ["*"]
  }
}

resource "aws_iam_role_policy" "atlantis" {
  // Atlantis のインスタンス用のロール。
  name   = "atlantis_policy"
  role   = "${aws_iam_role.atlantis.id}"
  policy = "${data.aws_iam_policy_document.atlantis.json}"
}

resource "aws_iam_instance_profile" "atlantis" {
  // Atlantis のインスタンスプロファイル。
  name = "atlantis_profile"
  role = "${aws_iam_role.atlantis.name}"
}

resource "aws_key_pair" "atlantis" {
  // Atlantis インスタンスのキーペア。
  key_name   = "atlantis-key"
  public_key = "ssh-rsa ..."
}

resource "aws_instance" "atlantis" {
  // Atlantis の EC2 インスタンス。
  ami                  = "ami-0eb48a19a8d81e20b" // Ubuntu 18.04
  instance_type        = "t2.micro"
  key_name             = "atlantis-key"
  iam_instance_profile = "${aws_iam_instance_profile.atlantis.name}"
}

GCP の操作権限を与える場合は、秘密鍵ファイルをダウンロードし、EC2 インスタンス上にアップロードする。
/atlantis/gcp-credentials/private_key.json にアップロードしたとする。

EC2 インスタンスで Docker をインストールする。
参考:https://docs.docker.com/install/linux/docker-ce/ubuntu/

sudo apt-get update
sudo apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo apt-key fingerprint 0EBFCD88
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io

https://www.runatlantis.io/docs/server-configuration.html#environment-variables
にある通り、必要な起動オプションを環境変数で指定して起動する。

sudo docker run --name atlantis \
  -d \
  -v /atlantis/gcp-credentials:/atlantis/gcp-credentials \
  --restart=always \
  -p 4141:4141 \
  -e ATLANTIS_AUTOMERGE=true \
  -e ATLANTIS_GH_TOKEN=<github_token> \
  -e ATLANTIS_GH_USER=<github_username> \
  -e ATLANTIS_GH_WEBHOOK_SECRET=<webhook_secret> \
  -e ATLANTIS_REPO_WHITELIST=github.com/<github_username>/* \
  -e GOOGLE_CLOUD_KEYFILE_JSON=/atlantis/gcp-credentials/private_key.json \
  runatlantis/atlantis:v0.7.1
  • github_username は Atlantis が使用する GitHub のユーザ名であり、 github_tolen はそのユーザの GitHub のパーソナルアクセストークンを、repo スコープで作成したものを指定する。 普段使いのユーザにすると混乱を招きかねないので、マシンユーザを使うことが推奨されている。
  • webhook_secret は24文字より長く、適当にランダムな文字列を指定する。 公式のドキュメントではランダム文字列生成に https://www.random.org/passwords/?num=2&len=20&format=html&rnd=new を参照している。20文字が2列出るので、連結して40文字として使うことが想定されていると思われる。
  • GCP の操作権限が不要な場合は GOOGLE_CLOUD_KEYFILE_JSON は不要である。
  • ATLANTIS_AUTOMERGE=true とすると apply に成功したとき自動でマージしてくれる。

コンテナ起動後、 EC2 インスタンスの4141ポートにHTTPで接続しに行くと、atlantisの画面が見られるようになる。
image.png

最後に GitHub 上のコンフィギュレーションリポジトリに Webhook の設定を行えば、Atlantis が使えるようになる。
image.png

  • Secret は Docker コンテナ起動時に指定した webhook_secret を入力する。

コンフィギュレーションリポジトリの中身は、次の点に注意:

  • コンフィギュレーションリポジトリのディレクトリ構成は指定があるので、もし既存のリポジトリがあっても、そのまま Atlantis に移行はできないはずである。 公式ドキュメントに記載されている。
  • 公式ドキュメントによるとリモートステートは必須のようなので、指定しておく。

動作確認

  1. GitHub にコンフィギュレーションリポジトリを作る。
    • READMEで初期化しておくとよい。
    • プライベートにしておいたほうがよい。
  2. test ディレクトリを切って、main.tf ファイルを追加する PR を作成する。
    • 本当はリモートステートを指定しておいたほうがよいが、省略する。
provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_instance" "test" {
  ami           = "ami-0f9ae750e8274075b"
  instance_type = "t2.micro"
}
  • image.png
  1. Atlantis が自動で terraform plan を実行し、結果をコメントしてくれるはず。

    • image.png
  2. atlantis apply とコメントする。(実際にインスタンスが作成されるかもしれないので注意)

    • image.png
  3. Atlantis が自動で terraform apply を実行し、結果をコメントしてくれる。

    • image.png
  4. 作成されたインスタンスを削除する。

まとめ

今回はチームで GitHub 上の Terraform コンフィギュレーションを運用する際に
生じる問題点を解決できる Atlantis を EC2 インスタンスとして立て、 Atlantis ワークフローを試しに動かしてみた。

Atlantis は比較的活発に開発されており(数週間に1回のリリース?)、メイン開発者の方も2018年10月に HashiCorp に入社したそうなので、今後の発展が期待できる。
ただ実際に導入しているところはまだあまり多くなさそうで、とくに日本語の記事は皆無の状況。
実際に使ってみると出てくる課題もありそうだが、使っているのは Terraform であり進退窮まることもないので、個人的には積極的に試していきたい。

補遺:Atlantis は GitOps なのか

GitOps という概念が Weaveworks によって提唱されているが、
2018年7月にはアンチパターン CIOps と煽ってきたりもしていて、
気軽に GitOps と言えない感じになってきている(気がする)。
ただただ GitOps という言葉だけ聞くと、コンフィギュレーションをGitに管理していればよさそう、とか、
コンフィギュレーションの変更をプルリクエスト送って管理していればよさそう、とか思うだろう。
すると、Atlantis は GitOps なのでは、と言えそうに思う。

ここで上記記事に記載された GitOps の原理を見てみる。

  1. The entire system described declaratively.
  2. The canonical desired system state versioned in Git.
  3. Approved changes can be automatically applied to the system.
  4. Software agents to ensure correctness and alert on divergence.

1番の declaratively = 宣言的にというのは、Terraform を使っている以上(多分)満たされる。
2番は Git を使っていることを言っているので、これも満たされる。
3番も atlantis apply で自動的に適用できるので、満たされる。
4番は、Git = 実環境であることを保証し、差異があったらアラートすると言っているが、これはどうだろうか。

Terraform 単体では apply に失敗するなどして、 Git = 実環境とならない可能性があったため、
Atlantis による PR ワークフローとロックが必要となった。
これにより、 PR をマージした時点では Git = 実環境となるようになったが、
実環境が変化したときに追随できないという問題が残っている。
Atlantis はこの「差異があったらアラートする」機能が不足している。
おそらく、次の PR を作ったときに plan に妙な差分が出て気づくが、これでは混乱をきたしかねない。

Weaveworks のブログでは、
Terraform について Git と実環境の差異をチェックする機能を Terradiff として簡単に紹介されている。

Terradiff - Terraform has a “plan” mode, which shows us if our configuration matches reality – we periodically execute this and export the exit code to Prometheus, from where we monitor and alert on it.

つまり、Atlantis で GitOps をやっていくには、Git と実環境に差異があったらアラートするため、
定期的に Terraform の plan でコンフィギュレーションが実環境に一致するかを示し、Prometheus に終了ステータスをエクスポートする Terradiff 機能を別途用意する必要がありそうである。

19
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
7