🌊

MRベースでterraformを操作できるatlantisをAWSEKS上に導入する

2022/11/02に公開

atlantis

atlantisは、Terraformを自動実行するためのOSSで、GithubAppsとして登録して動作する。
また、何がしかの手段でサーバ構築する必要がある。Docker、HelmChart、ECS用Terraformなどいろいろあるので好きな形で作れる。作業自体はそこまで大変ではない印象。

GithubActionsでTerraformを操作するためのものは出揃って入るが、MRベースで操作するというものはないはず。
ここがミソで、MR上のコメントでterraform plan,terraform applyを実行できるため、ローカル実行環境を整備する必要がなかったり、plan結果が履歴としてちゃんと残るというのもいい。
あと、GithubActionsだとワークフロー的に、マージをトリガーに動作させるという形がよく見かけるが、MRベースだとデプロイ完了後にマージできるというのもいいと思っている。

https://www.runatlantis.io/

構築

今回は、Helm Chartで構築する。
クラスタはAWSEKS v1.23で、GitRepositoryはGithub。

こちらのインストレーションガイドをそのままやる。
https://www.runatlantis.io/docs/installation-guide.html

Helm Chartインストール

インストレーションガイドの順番的に、GithubのCredential系をセットアップしてからの流れだが、いきなりChartをインストールする。
というのも、起動させたAtlantisからGithubAppsを作るAPIを呼ぶことができるので、手元で最初にセットアップしておくという必要がない。

4. Deploy Atlantis into your infrastructure

リポジトリ登録

helm repo add runatlantis https://runatlantis.github.io/helm-charts

valuesをダウンロード

helm inspect values runatlantis/atlantis > values.yaml

以下のように変更する
このパラメータは、GithubAppsを使う場合一時的に起動するために必要

# for example
github:
  user: fake
  token: fake
  secret: fake

atlantisを利用していいリポジトリを指定する
テストなので、アスタで設定

# Replace this with your own repo allowlist:
orgAllowlist: "*"

ingressを設定する
AWS Loadbaranser ControllerからALBをデプロイしているため、それ用のAnnotationをつけている

ingress:
  enabled: true
  ingressClassName:
  annotations:
    alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig":
      { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-1:xxxxxxxxxxxxxx:certificate/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    alb.ingress.kubernetes.io/group.name: k8s-dev-group
    alb.ingress.kubernetes.io/ip-address-type: ipv4
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
    alb.ingress.kubernetes.io/scheme: internet-facing
    external-dns.alpha.kubernetes.io/hostname: atlantis.dev.vamdemic.jp
    kubernetes.io/ingress.class: alb
  path: /*
## this is in case we want several paths under the same host, with different backend services
#  paths:
#    - path: "/path1"
#      service: test1
#      port:
#    - path: "/path2"
#      service: test2
#      port:
  pathType: ImplementationSpecific
  host: atlantis.dev.vamdemic.jp

## in case we need several hosts:
  hosts:
    - host: chart-example.local
      paths: ["/"]
#      service: chart-example1
    - host: chart-example.local2
#      service: chart-example1
      paths: ["/lala"]
  tls: []
  #  - secretName: chart-example-tls
  #    hosts:
  #      - chart-example.local
  labels: {}

Chartインストール

helm install atlantis runatlantis/atlantis -f values.yaml

GithubApp作成

https://www.runatlantis.io/docs/access-credentials.html#github-app

  1. https://$ATLANTIS_HOST/github-app/setup へアクセス
  2. GithubApps作成画面にリダイレクトされる
  3. 成功すると、AppID、秘密鍵、Secretが表示されるのでメモる
  4. values.yamlのGithubAppの箇所を更新する
# If using a GitHub App, please enter your values as follows:
githubApp:
  id: 123456
  key: |
      -----BEGIN RSA PRIVATE KEY-----
      -----END RSA PRIVATE KEY-----
  secret: xxxxxxxxxxxxxxxxxxxx
# (The chart will perform the base64 encoding for you for values that are stored in secrets.)

※この際には、github.userなどに設定しているダミーの値は削除する。そうしないと、次のようなメッセージが出る。

{"level":"warn","ts":"2022-11-12T13:38:39.569Z","caller":"logging/simple_logger.go:161","msg":"payload signature check failed","json":{},"stacktrace":"github.com/runatlantis/atlantis/server/logging.(*StructuredLogger).Log\n\tgithub.com/runatlantis/atlantis/server/logging/simple_logger.go:161\ngithub.com/runatlantis/atlantis/server/controllers/events.(*VCSEventsController).respond\n\tgithub.com/runatlantis/atlantis/server/controllers/events/events_controller.go:699\ngithub.com/runatlantis/atlantis/server/controllers/events.(*VCSEventsController).handleGithubPost\n\tgithub.com/runatlantis/atlantis/server/controllers/events/events_controller.go:155\ngithub.com/runatlantis/atlantis/server/controllers/events.(*VCSEventsController).Post\n\tgithub.com/runatlantis/atlantis/server/controllers/events/events_controller.go:99\nnet/http.HandlerFunc.ServeHTTP\n\tnet/http/server.go:2109\ngithub.com/gorilla/mux.(*Router).ServeHTTP\n\tgithub.com/gorilla/mux@v1.8.0/mux.go:210\ngithub.com/urfave/negroni.Wrap.func1\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:46\ngithub.com/urfave/negroni.HandlerFunc.ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:29\ngithub.com/urfave/negroni.middleware.ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:38\ngithub.com/runatlantis/atlantis/server.(*RequestLogger).ServeHTTP\n\tgithub.com/runatlantis/atlantis/server/middleware.go:70\ngithub.com/urfave/negroni.middleware.ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:38\ngithub.com/urfave/negroni.(*Recovery).ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/recovery.go:193\ngithub.com/urfave/negroni.middleware.ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:38\ngithub.com/urfave/negroni.(*Negroni).ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:96\nnet/http.serverHandler.ServeHTTP\n\tnet/http/server.go:2947\nnet/http.(*conn).serve\n\tnet/http/server.go:1991"}

awsCredentialを設定する

  • userにはAssumeRoleができるポリシーのみを付与
  • AssumeRoleにはAdministratorAccessポリシーを不要
# To specify AWS credentials to be mapped to ~/.aws:
aws:
  credentials: |
    [vamdemic-yuta]
    aws_access_key_id = xxxxxxxxxxxxxxxxxxxxxxxx
    aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    region = ap-northeast-1
  config: |
    [profile vamdemic]
    role_arn = arn:aws:iam::195341589990:role/assume-role
    source_profile = vamdemic-yuta

ServiceAccountをアタッチすることでも設定できる

ロールベースになるのでこちらのほうが推奨のはず

valuesに利用するserviceAccountを指定する

serviceAccount
  name: atlantis-admin

eksctlでserviceAccountを作成する。ここでは、AdministratorAccessを割り当てている。

eksctl create iamserviceaccount \
	 --name atlantis-admin \
	 --cluster vamdemic-k8s-cluster \
	 --attach-policy-arn arn:aws:iam::aws:policy/AdministratorAccess \
	 --namespace atlantis \
	 --profile vamdemic \
	 --override-existing-serviceaccounts \
	 --approve

リポジトリ側にマニフェストファイルを配置する

リポジトリ側ではなく、サーバ設定、サーバ側にマニフェストファイルを配置でもよい。
https://www.runatlantis.io/docs/configuring-atlantis.html

リポジトリの場合は、ルート直下に置く必要がありそう。
また、配置しなければ、デフォルト値が適用されると思うので配置しなくてもいいかも。
ルート配下に複数Terraformを管理している場合は、配置しない場合は自動探索的なことをしてくれるというのを何処かで見かけた。
terraform workspacを利用している場合もこのファイルで指定することになる。

version: 3
projects:
- dir: .
  terraform_version: v1.2.7

terraformのsample

  • vpcを作るだけのもの
  • stateファイルはs3上に上げておく必要がある
  • assume-roleで実行するようにしている
    • ユーザーの権限で直接というのがどうもできなかった(やり方としてはあまりいけていないのは知っていたのでいい機会)
    • というのも、EKS上でAtlantisが起動しているわけだが、assume-roleを利用しない形だと、terraform applyを実行しようとしたときに、NodeにアタッチされているEC2Roleの権限に従っているようだったため
resource "aws_vpc" "main" {
  cidr_block       = "10.0.0.0/16"
  instance_tenancy = "default"

  tags = {
    Name = "aiueo aaa iiia aa a"
  }
}

terraform {
  required_version = "= 1.2.7"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 4.24.0"
    }
  }
  backend "s3" {
    bucket  = "vamdemic-tf"
    region  = "ap-northeast-1"
    profile = "vamdemic"
    key     = "infra/terraform.tfstate"
    encrypt = true
  }
}


provider "aws" {
  region  = "ap-northeast-1"
  profile = "vamdemic-yuta"
  assume_role {
    role_arn = "arn:aws:iam::195341589990:role/assume-role"
  }
}

動かす

terraform plan

MRを出すと自動的にTerraform planを実行してくれる。
コメントでatlantis planでも実行してくれる。

terraform apply

コメントでatlantis applyとうつと、terraform applyが実行される。

参考

https://zenn.dev/tatsuo48/articles/c358e86ad9e1ce
https://qiita.com/chroju/items/f77e8391d6ef7c7cb59a

Discussion