MRベースでterraformを操作できるatlantisをAWSEKS上に導入する
atlantis
atlantisは、Terraformを自動実行するためのOSSで、GithubAppsとして登録して動作する。
また、何がしかの手段でサーバ構築する必要がある。Docker、HelmChart、ECS用Terraformなどいろいろあるので好きな形で作れる。作業自体はそこまで大変ではない印象。
GithubActionsでTerraformを操作するためのものは出揃って入るが、MRベースで操作するというものはないはず。
ここがミソで、MR上のコメントでterraform plan,terraform applyを実行できるため、ローカル実行環境を整備する必要がなかったり、plan結果が履歴としてちゃんと残るというのもいい。
あと、GithubActionsだとワークフロー的に、マージをトリガーに動作させるという形がよく見かけるが、MRベースだとデプロイ完了後にマージできるというのもいいと思っている。
構築
今回は、Helm Chartで構築する。
クラスタはAWSEKS v1.23で、GitRepositoryはGithub。
こちらのインストレーションガイドをそのままやる。
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://$ATLANTIS_HOST/github-app/setup へアクセス
- GithubApps作成画面にリダイレクトされる
- 成功すると、AppID、秘密鍵、Secretが表示されるのでメモる
- 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
リポジトリ側にマニフェストファイルを配置する
リポジトリ側ではなく、サーバ設定、サーバ側にマニフェストファイルを配置でもよい。
リポジトリの場合は、ルート直下に置く必要がありそう。
また、配置しなければ、デフォルト値が適用されると思うので配置しなくてもいいかも。
ルート配下に複数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が実行される。
参考
Discussion