記事の内容
本記事では、AtlantisのHelm チャートを使った Terraform のCI/CD 環境構築について説明する。
この記事の作成に至った動機付けと背景
現在、私の携わっている案件では、GCP 環境で Terraform を手動で適用しており、作業負担や適用ミスのリスクが課題となっている。
さらに、以下のような問題もある。
- 商用環境では、適用当日にしか権限が付与されないため、事前に
terraform plan
の結果を確認できない -
terraform plan
やterraform apply
の結果を手動でコピー&ペーストし、証跡管理を行っている(手間がかかる&証跡がPR内で完結しない) - Terraform の適用が個人のローカル環境で実行されるため、チーム全体の可視性が低い
そこで、Terraform の適用を自動化するツールを検討した結果、Atlantis を利用することで、PR ベースのワークフローによる安全な適用が可能になると考えた。
現行の手動運用の流れ(問題点)
1. GitHub に Push
2. GitHub からクローン
3. ローカル環境で terraform plan
を実行(手動ミスのリスク)
4. 期待した結果が得られたら PR 作成
5. ローカル環境で terraform apply
を実行(ログ管理が手間)
6. PR を main ブランチにマージ
Atlantis を導入することで、手動適用の負担やミスのリスクを軽減し、安全な Terraform の適用が可能になると考えた。
Atlantisとは
Atlantis は、GitHub や GitLab のプルリクエストをトリガーに Terraform の plan や apply を実行できるOSSツール。
特徴:
- PR 作成時に
terraform plan
を自動実行し、結果を PR 上にコメントとして出力 - 承認後に PR 上で
atlantis apply
のコメントを記入することでterraform apply
を実行 - 手動での
terraform apply
実行を防ぎ、Terraform の適用の一貫性を確保 - Terraform の状態管理は Google Cloud Storage(GCS) などに保存可能
本記事では、Atlantis のHelm チャートを使用する。Helmチャートとは、再利用が可能なKubernetesリソースの一式を、所定の仕様でパッケージングしたファイルである。Helm チャートを使うことで、Kubernetes クラスタ上に 簡単に Atlantis をデプロイ・管理 でき、アップグレードや設定変更も柔軟に行える。
デフォルトの設定値はArtifact Hubに記載されているが、より詳細な設定やカスタマイズの方法については、GitHub の values.yaml を実際に確認しながら構築を進めていく。
Artifact Hub のデフォルト設定値
GitHub の values.yaml
アーキテクチャ
1. GitHub からGKEクラスター 上に構築した Atlantis に Webhook でリクエスト
2. Atlantis が Terraform を実行する
3. Terraform がGCP上のリソースを apply。状態ファイルはGoogle Cloud Storageバケットに保存。
Atlantis と Terraform は Helm チャートを利用しており、GKEクラスター上に構築している。
構築手順
⚠️ 注意:本記事の手順は検証目的の構成を想定している。本番環境での運用には、セキュリティ対策や可用性を考慮し、適切な設定変更が必要になる。
前提として、
- GKEクラスターは、Workload Identity を有効化して構築済みであること
- GCSへ状態ファイルを格納する権限を持つGSA(roles/storage.objectAdmin または最小権限 IAMロールが付与 )が作成済みであること
- コマンドは、GCP の
CloudShell
を利用すること
1.下記コマンドを実行し、RunAtlantis Helm チャートリポジトリを登録する。
helm repo add runatlantis https://runatlantis.github.io/helm-charts
このコマンドによって、Helm に runatlantis
というリポジトリを追加し、runatlantis/atlantis
という Helm チャートを利用できるようになる。
2.Helm チャートを設定するディレクトリに移動後、下記コマンドを実行し、設定ファイルvalues.yaml
を作成する。
helm inspect values runatlantis/atlantis > values.yaml
これによって、Helmチャートのデフォルトの設定値をvalues.yaml
として出力される。このファイルを編集していくことで、カスタマイズされたAtlantis の環境を構築できる。
3.Helm チャートの設定ファイルvalues.yaml
を編集し、LoadBalancerを作成する。
Artifact Hub に記載されているデフォルトの設定値を確認すると、service.type
がNodePort
であることが確認できた。外部IPを作成し、そこにGitHub から Webhook リクエストを送信し、Terraform の適用を実行するため、service.type
をLoadBalancer
に変更する。
service:
annotations: {}
externalTrafficPolicy: null
loadBalancerIP: null
loadBalancerSourceRanges: []
nodePort: null
port: 80
portName: atlantis
targetPort: 4141
type: LoadBalancer
4.下記コマンドを実行し、Helm で Atlantis の設定を適用する。
helm upgrade --install -f values.yaml atlantis runatlantis/atlantis
コマンドの内容としては、
helm upgrade --install -f <設定ファイルのパス> <リリース名> <Chart.yamlを格納するフォルダ>
helm upgrade --install
は、指定したリリースが存在しない場合は新規作成し、すでに存在する場合は設定を更新する。つまり、初回は helm install
と同じ動作になり、2回目以降は helm upgrade
の動作になる。
そして、このコマンドによってrunatlantis/atlantis
というHelmチャートからvalues.yaml
の設定を適用したatlantis
というリリースが作成される。
イメージとしては...
- Helmチャート(
runatlantis/atlantis
)はClass - Helmリリース(
atlantis
)はClassから生成されたインスタンス -
values.yaml
はインスタンス作成時のコンストラクタ引数や設定ファイル
runatlantis/atlantisというClass をもとにvalues.yaml
の設定を適用してatlantis というインスタンスが作成または更新される、というイメージですね。
5.下記コマンドを実行し、LoadBalancer の外部IP ( EXTERNAL-IP )を取得する。
kubectl get svc
入力内容は以下の通り。今回は簡単な検証のため、 SSL 通信は利用しない。
Payload URL:http://<LBの外部IP>/events
Content type:application/json
Secret:<Webhook 署名検証に使用する Secret>
SSL verification:Disable (not recommended)
Which events would you like to trigger this webhook?:
Let me select individual events.
・Pull requests:PR 作成時にPlan 実行
・Issue comment:atlantis apply などのコメントをトリガーに適用
7.Helm チャートの設定ファイルvalues.yaml
にて、対象のGitHubリポジトリの認証情報とWebhook Secret を編集して追加する。また、対象の GitHubリポジトリをorgAllowlist
に指定する。今回は、github.com/<org名>/*
と指定することで、 GitHub の指定した org 内の全てのリポジトリに対して、Terraform の適用を許可する設定にする。
github:
hostname: github.com
secret: <Webhook 署名検証に使用する Secret(GitHub Webhook 設定と同じ値)>
token: <GitHub認証トークン>
user: <GitHubユーザー名>
orgAllowlist: github.com/<org名>/*
8 Helm チャートの設定ファイル values.yaml
で、GSA のバインド設定を行う。annotations
に roles/storage.objectAdmin を持つ GSA を指定する。
Artifact Hub と GitHub の values.yaml
を確認し、適切な設定を行う。
Pod が GCP のサービスを利用できるようにするため、KSA と GSA をバインディングする設定に変更する。
serviceAccount:
create: true
mount: true
name: atlantis-ksa
annotations:
iam.gke.io/gcp-service-account: "<roles/storage.objectAdminを付与したGSA名>"
9.下記コマンドを実行し、Helm で Atlantis の設定を適用する。
helm upgrade --install -f values.yaml atlantis runatlantis/atlantis
10.下記コマンドを実行し、 roles/storage.objectAdmin を付与した GSA を自動作成された KSA にバインディングする。
gcloud iam service-accounts add-iam-policy-binding \
<roles/storage.objectAdminを付与したGSA> \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:<PROJECT>.svc.id.goog[atlantis/atlantis-ksa]"
⚠️ 注意
本手順では、Terraform の状態ファイルを保存するために GCS へのアクセス権限 (roles/storage.objectAdmin
) を持つ GSA を KSA にバインディングする。
しかし、Terraform で GCP のリソースをデプロイするには、対象リソース(例: Compute Engine, Cloud SQL, GKEなど)への適切な IAMロールを持つ GSA が別途必要である。
Terraform 適用時に権限エラーが発生する場合は、デプロイ対象のリソースに適した権限を持つ GSA を作成し、同様に KSA にバインディングしてください。
11.下記コマンドを実行し、ポッドを再起動し、設定を確実に反映する。
kubectl delete po -l app=atlantis
Atlantis の動作を確認しながら PR を作成する
PR 作成時の Pod の挙動を確認するため、下記コマンドを実行し、 PR を作成してみる。
kubectl logs -f <atlantisのPod名>
PR を作成してみると、以下のようにTerraform の Plan
結果が PR のコメントとして出力された。変更リソースが色付きで表示され、見やすくなっている。
また、CloudShell上のPodのログでも、terraform plan
を実行していることが確認できた。
{"level":"info","ts":"2025-02-04T13:31:03.524Z","caller":"vcs/github_client.go:913","msg":"Updating GitHub Check status for 'atlantis/plan' to 'pending'","json":{"repo":"<リポジトリ名>","pull":"8"}
このログは、Atlantis が plan
の結果を GitHub に送信し、PR 上の Checks に反映しようとしていることを示している。
その後、PR のコメントにatlantis apply -d .
と入力すると、 apply
が正常に実行されたことを確認できた。
構築を終えての感想
他の方のブログでは、多くが GCE インスタンス上に Atlantis を構築していた。
しかし、Helm チャートを活用することで、GKE 上に容易にデプロイできることが分かった。
また、GKE を採用することで、以下のような利点も得られる。
-
可用性・冗長性の向上
- マネージド Kubernetes による自動復旧・スケーリング
-
運用負担の軽減
- インスタンス管理不要
- Helm チャートを使った容易なデプロイ
TerraformのCI/CD ツールにはTerraform Cloud やGitHub Actions も選択肢にあるが、実際の案件で活用する場合、それらと比較してAtlantis を導入することで、以下のような大きなメリットがあると感じた。
- Terraform Cloud よりもコストを抑えられる(GKEクラスターが既に稼働している環境であれば追加コストが最小限)
- GitHub Actions よりも環境構築が容易(Helm チャートを適用するだけで Atlantis をセットアップ可能)
実際の案件では、Terraform の適用を Atlantis で管理し、その後 ArgoCD でマニフェストを適用することで、Terraform と Kubernetes の運用を分離し、効率化を図る。
また、認証情報(GitHubトークンやWebhookシークレット)を Secrets で安全に管理し、Webhook のSSL通信 を適用することで、セキュリティを強化していく。
さらに、Atlantis の自動デプロイをさらに最適化し、Terraform と Kubernetes の統合運用をより安定させていく。
⚠️ 本番環境での考慮点
本記事の手順は 検証環境向け に作成しており、本番環境での運用には以下のような追加の設定が推奨される。
-
GitHubトークンや Webhookシークレットの Secret 化
-
values.yaml
に認証情報を直接記載せず、Kubernetes Secret を利用する
-
-
SSL 対応
- Webhook の SSL 設定を有効化し、HTTPS を使用することでセキュリティを強化する
-
IAM ロールの最小権限化
- Workload Identity の IAM ロールを最小限に絞り、不要な権限を付与しない
本記事の内容をさらに発展させ、ArgoCD を活用して Helm チャートを自動適用する方法を以下の記事で解説しています。ぜひご覧ください!
PRベースでTerraformを適用!Atlantis × Helm × GKEでCI/CD環境を構築するPart2 HelmチャートをArgoCDで適用しよう!