0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Terraform + GitHub ActionsでGCPのインフラを管理するCI/CDパイプラインの構築

0
Posted at

はじめに

  • terraformでインフラを管理してアプリを構築したい
  • localからマニュアルでterraform applyすることもできるが、毎回マニュアルで実施するのは手間がかかる。github actionsでCI/CDパイプラインを構築し自動でデプロイできるのが望ましい

本記事で扱う内容

  • GCPとterraformの初心者向けに、GCPプロジェクトの作成から、github actionsによるterraformのCI/CDを構築するまでの流れ
    • ハンズオンで実行することを想定しています
  • ※インフラは全くの門外漢なので、技術的な詳細には言及が難しい点はご理解ください

ディレクトリ構成

  • terraform配下に下記のように.tfを配置する
  • ちなみに、terraformは.tfをすべて統合して実行するので、ファイルを分割するのは管理のしやすさの観点
|-- [APP_NAME]
    |-- terraform
        |-- main.tf
        |-- variables.tf
        |-- outputs.tf

流れ

1.GCPプロジェクトの設定

  • projectを作成する。billing accountも紐づける。
    • GCPアカウントの作成が完了していない型はこちらから作成
gcloud projects create [PROJECT_ID] --name=[PROJECT_NAME]
gcloud config set project [PROJECT_ID]
gcloud billing projects link [PROJECT_ID] --billing-account=[BILLING_ACCOUNT_ID]
  • 必要なAPIを有効化する
gcloud services enable cloudbuild.googleapis.com
gcloud services enable iam.googleapis.com
gcloud services enable iamcredentials.googleapis.com
gcloud services enable cloudresourcemanager.googleapis.com

2. workload identity federationのみlocalからapplyする

  • workload identity federationについてはこちらの記事がわかりやすい

  • workload identity federationは、GitHub ActionsのOIDCトークンをGCPが信頼できるよう橋渡しする仕組み。これが設定されていないと、GitHub ActionsからGCPのリソースにアクセスできない。

  • 最初はlocalからapplyする必要がある。

  • 下記のterraformコードを用意する。

  • varにてproject_id, app_name, github_repositoryを設定する。

    • 1アプリで、1リポジトリ1プロジェクトの場合は、すべて同じ名前でよい

main.tf

# terraformのバージョン・プロバイダの設定
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

# google providerの設定(google関連のリソースを作成するために必要)
provider "google" {
  project = var.project_id
  region  = var.region
}

resource "google_iam_workload_identity_pool" "github" {
  workload_identity_pool_id = "github-pool"
  display_name              = "GitHub Actions Pool"
}

resource "google_iam_workload_identity_pool_provider" "github" {
  workload_identity_pool_id          = google_iam_workload_identity_pool.github.workload_identity_pool_id
  workload_identity_pool_provider_id = "github-provider"
  display_name                       = "GitHub Actions Provider"

  oidc {
    issuer_uri = "https://token.actions.githubusercontent.com"
  }

  attribute_mapping = {
    "google.subject"       = "assertion.sub",
    "attribute.actor"      = "assertion.actor",
    "attribute.repository" = "assertion.repository"
  }

  attribute_condition = "attribute.repository == '${var.github_repository}'"
}

# github actionsからGCPリソースにアクセスするためのサービスアカウントを作成
resource "google_service_account" "github_actions" {
  account_id   = "github-actions-sa"
  display_name = "GitHub Actions Service Account"
}

# 上記SAにeditorロールを付与
# ※本番運用では管理するリソースに必要な権限のみに絞ることを推奨
resource "google_project_iam_member" "github_actions_editor" {
  project = var.project_id
  role    = "roles/editor"
  member  = "serviceAccount:${google_service_account.github_actions.email}"
}

# workload identityにgithub actionsのSAを紐づける(これでgithub actionsがworkload identityを通じて github_actions用のSAを利用できるようになる)
resource "google_service_account_iam_member" "github_actions_wi" {
  service_account_id = google_service_account.github_actions.name
  role               = "roles/iam.workloadIdentityUser"
  member             = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github.name}/attribute.repository/${var.github_repository}"
}
  • 変数を定義する

variables.tf

variable "project_id" {
  type    = string
  default = "[PROJECT_ID]"
}

variable "region" {
  type    = string
  default = "asia-northeast1"
}

variable "app_name" {
  type    = string
  default = "[APP_NAME]"
}

variable "github_repository" {
  type    = string
  default = "[GITHUB_REPOSITORY]"
}
  • 出力する値を定義する

outputs.tf

output "workload_identity_provider" {
  value = "projects/${var.project_id}/locations/global/workloadIdentityPools/github-pool/providers/github-provider"
}
output "github_actions_sa_email" {
  value = google_service_account.github_actions.email
}

下記の作成が完了したら、shellからterraform applyする

cd terraform
terraform init
terraform apply

apply完了後、terminalにoutputの値が表示される。この値は次のステップ(4. GitHub Actionsの設定)で使用するのでメモしておく。

workload_identity_provider = "projects/[PROJECT_NUMBER]/locations/global/workloadIdentityPools/github-pool/providers/github-provider"
github_actions_sa_email    = "github-actions-sa@[PROJECT_ID].iam.gserviceaccount.com"

3. tfstateをGCSに移行

  • terraformのstateファイルをGCSに保存するように設定する。これにより、複数人でterraformコードを管理する際に、stateファイルの競合を防ぐことができる。
gcloud storage buckets create gs://[PROJECT_ID]-tfstate \
  --project=[PROJECT_ID] \
  --location=asia-northeast1 \
  --uniform-bucket-level-access
  • terraformは現在localにstateファイルを保存しているため、これをGCSに移行するための設定を記載する。
  • main.tfのterraformブロックにbackendを追加
terraform {
  ...

  backend "gcs" {
    bucket = "[PROJECT_ID]-tfstate"
    prefix = "[APP_NAME]"
  }
}
  • stateをGCSに移行する
terraform init -migrate-state

これで、terraformコマンド実施時は、GCS上の[PROJECT_ID]-tfstateバケット内の[APP_NAME]ディレクトリにstateファイルが保存されるようになる。

4. GitHub Actionsによるterraform plan/applyの設定

.github/workflows/[APP_NAME].ymlを作成し、以下のように記述する。
workload_identity_providerservice_accountには、手順2のapply後にoutputに出力された値を設定する。

name: Terraform Deploy

# mainブランチへのpushをトリガーとする
on:
  push:
    branches:
      - main
    paths:
      - '[APP_NAME]/terraform/**'
  pull_request:
    branches:
      - main
    paths:
      - '[APP_NAME]/terraform/**'
  
permissions:
  id-token: write # OIDCトークンを発行する権限
  contents: read # 

jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      # リポジトリのコードをactions上にDL
      - uses: actions/checkout@v4
    
      # workload identity federationを使用してGCPに認証
      # workload_identity_provider, service_accountは手順2のterraform apply後のoutput値を設定する
      - id: auth
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: projects/[PROJECT_NUMBER]/locations/global/workloadIdentityPools/github-pool/providers/github-provider  # output: workload_identity_provider
          service_account: github-actions-sa@[PROJECT_ID].iam.gserviceaccount.com  # output: github_actions_sa_email
      
      # Actions用にterraformをsetup
      - uses: hashicorp/setup-terraform@v3

      # terraform init(毎回新たな環境となるので毎回initが必要)
      - name: Terraform Init
        working-directory: [APP_NAME]/terraform
        run: terraform init

      - name: Terraform Plan
        working-directory: [APP_NAME]/terraform
        run: terraform plan
      
      # yesの入力を自動化してapply
      - name: Terraform Apply
        working-directory: [APP_NAME]/terraform
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: terraform apply -auto-approve

以上で、githubのmainブランチにpushすると、自動でterraform applyが走るようになる。

感想

  • 上記の体制を確立できると、terraformコードをmainブランチにpushするだけで自動でGCPのインフラが更新されるようになるので、非常に便利
  • 一度localからapplyするのは少し面倒だが、これはWorkload Identity Federation自体を作るためにWorkload Identity Federationが必要という鶏卵問題
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?