BLOG

ブログ

Terraform Cloudを利用した、CI/CD戦略と最適なterraformディレクトリについて考えてみた

初めに

 terraformで書いたクラウドリソースファイルのapplyをgithub Repositoryと連携、 正確には、github Actionsを利用して行えることがわかりました。 (TerraformとGitHub Actions(CI/CD)を用いたGitHub repositoryの管理) 今回は、「Terraform Cloud」をgithubと連携してより簡単に、CI/CDを実現して行きたいと思います。 Terraform CloudはほぼノーコードでCI/CDを実現できるのでかなり便利です。
以下のような方を想定読者として考えています。

  • terraformのActionsを試した方。
  • terraformの他のCI/CDを検討されてる方。

Terraform Cloud

 HachiCorp社が提供する、TerraformのCI/CDに特化したサービス。プロジェクトの状態と履歴などを追跡して、変更の承認プロセスなどを自動化できます。 Terraform Couldは、AWS・Azure・GCP・Cloudflare・herokuなどのクラウドプロバイダーなどをサポートしています。

https://app.terraform.io/app/organizations

価格について

 terraformのプレミアムサポートはつかないのと、「5ユーザー」までOrganizationで無料利用可能ぐらいで、 ほぼそれ以外の必要機能は利用できます。詳細はこちらよりご覧ください。

本記事で構築するアーキテクチャと技術

 まず、github repositoryとterraform cloudのprojectを作成して監視するブランチを決めて連携します。 次に、開発者がtfファイルをgithub repositoryにpushしてTerraformがファイルの変更を検知し クラウドリソースに変更を加えます。今回は、クラウドプロバイダーにGCPを使用して、GCPのVMインスタンスをterraform Cloudから deployしていこうと思います。

開発ブランチ設計

 ブランチと対応させる環境名について明示しておきます。branch:mainは本番環境(production)と対応させます。 branch:developは開発環境(development)と対応させます。

branch環境名
main本番環境(production)
develop開発環境(development)

Let’s try

今回はprodとdevの環境作成は繰り返しなのでproduction環境で記述します。

Terraformディレクトリ設計について

terraformの管理方法には、各開発環境のディレクトリの中に、クラウドプロバイダーが提供するサービスごとに分けて各リソース事をDRYにして管理するやり方など ありますが、その分tf.stateのfile数が増えたりするデメリットもあったりします。

.
└── dev
    └── gcp
        ├── gar
           ├── main.tf
           └── variable.tf
        └── sql
            ├── main.tf
            └── variable.tf

私は以下のディレクトリ構成が一番、開発者が一瞬でプロジェクトでどこに何がapplyされてるかを 把握できやすく、tfstateは各環境1ファイルのみの管理で運用しやすいかなと思っています。。

.
└── terraform
    ├── entrypoint #terraformを実際にapplyする対象ディレクトリ
       └── gcp #terraform applyするクラウドプロバイダーごとに分ける
           ├── dev #環境別に分ける
              ├── main.tf
              └── variable.tf
           └── prod
               ├── main.tf
               └── variable.tf
    └── modules #entrypointから呼び出す、terraformリソースのmoduleを定義する
        └── gcp #クラウドプロバイダーごとに分けて上げる
            └── compute_instance
                ├── main.tf
                └── variable.tf

/entrypoint

実際にterraform applyを行うディレクトリで、Terraform Cloudで参照するディレクトリです。 entrypoint/<クラウドプロバイダ>/<環境名>/main.tfとして定義します。

  • terraform/gcp/prod/main.tf
terraform {
  backend "remote" {
    organization = "mamushi" #自身で作成するOrganization名を記述

    workspaces {
      name = "terraform_cloud_exer_prod" #Organizationの中で作成するWorkSpace名を記述
    }
  }
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "4.50.0"
    }
  }
}

provider "google" {
  credentials = var.gcp_credentials # terraform cloudの環境変数で入力する
  project     = var.gcp_project
  region      = var.region
  zone        = var.zone
}

module "instance" {
  source = "../../../modules/gcp/compute_instance"
  machine_type = var.machine_type
  name = var.gcp_project
}
  • terraform/gcp/prod/variable.tf
variable "gcp_credentials" {
  description = "gcp credential"
}

variable "gcp_project" {
  type = string
}

variable "region" {
  type = string
}

variable "zone" {
  type = string
}

variable "machine_type" {
  type = string
  description = "vm instance of machine type"
}

/module

実際にGCPなどのCloudプロバイダのリソースを定義する場所です。module/<クラウドプロバイダ>/<クラウドプロバイダで提供されるリソース>/main.tfと定義します。 今回はGCPのVMインスタンスを立ち上げるので、こちらを参考にしてリソースを定義します。

  • terraform/gcp/compute_instance/main.tf
resource google_compute_instance default {
  name         = var.name //moduleの中で変数化しておき、entrypointのmain.tfから変数を入れる
  machine_type = var.machine_type
  zone         = var.zone

  boot_disk {
    initialize_params {
      image  = "debian-cloud/debian-11"
      labels = {
        my_label = "value"
      }
    }
  }

  network_interface {
    network = "default"
  }
}

  • /gcp/compute_instance/variable.tf
variable "name" {
  type = string
}

variable "machine_type" {
  type = string
}

variable "zone" {
  type = string
  default = "asia-northeast1-a"
}

これらを記述したらgithubでディレクトリを作成してコードをpushします。

作成したリポジトリ

terraform cloudの設定

workspaceを立ち上げ&githubとリポジトリ接続

上記で作成したgithubのリポジトリとterraform Cloudのworkspaceをリンクさせます。

  • New→workspaceを選択
  • github連携
  • workspace名を決定しworkspaceを作成

github repositoryの監視branchの設定

左バーからworkspace→settings→version controlで設定を行います。 今回production環境を適応するのでmainブランチを監視対象とします。

github repositoryの監視ディレクトリの設定

左バーからworkspace→settings→Generalで設定を行います。Terraform Working Directoryで監視するgithubリポジトリのパスを入力します。 Apply Methodはいつのタイミングでterraform applyするか、Auto applyのブランチに変更が入ったタイミングで動く方が 管理がしやすいです。設定したら保存するのをお忘れずに。

variable(変数)の設定

左バーからworkspace→variablesterraform/gcp/prod/variable.tfの変数の中身を定義してあげます。

  • 実際の環境変数の追加

gcp_projectはGCPのProjectIDを入力、gcp_credentialsは、GCPのサービスアカウントから作成する秘密鍵のjsonをそのままコピペします。

  • サービスアカウント及び、credential(json)

※この時、IAMアカウントも作成して、サービスアカウントと紐付けて下さい。この時、権限不足だとterraform applyした時に、権限不足でapplyできなくなります。

  • サービスアカウントからcredential(json)の作成

ダウンロードされたjsonをそのまま、環境変数のgcp_credentialsにコピペする

workspaceを跨いでtfstateを共有したい場合

左バーのworkspace→settings→GeneralRemote state sharingから別のworkspaceのtfstateを指定できます。 VPCなどをdevelopmentとproduction共通にしたい場合などに使用します。

実際にterraform applyをする

※この時、GCP側で、VMインスタンスのAPIを有効化しておかないとエラーがでます。 branch:mainにpushすると、リソースが作成され始めます。

実際に作成されたVMインスタンス

developmentは上記のworkspaceをdevelopment用に作り直して接続すればokです。

まとめ

 今回、Terraform Cloudでの紹介をしました。Terraform Cloudを使用すれば、 コードを書く事なく、簡単にterraformで定義したコードをクラウドプロバイダーに適応できることがわかりました。 実際にはdeployに関することはTerraform Cloudに任せて、terraformのlintや、テストケース、ドキュメント作成に関してはgithub Actionsで動かしています。 ActionsとTerraform Cloudの使い分けは、terraformの純機能(terraform apply,terraform planなど)のものは、Terraform Cloudで行い、サードパーティ製のものは、Actionsで行うといった形を取っています。

サービス環境名
Terraform Cloudterraformの純機能
Github Actionsterraformのサードパーティ製品のモジュール

今回テストでGCPをターゲットにapplyをしました。AWSとGCPなどのクラウドプロバイダを選ぶ選定基準はこちらをご覧ください。

参考リンク

  • tfsec:静的セキュリティスキャンとして使う
  • terratest:terraformのテストケースをかける。kubernetesのテストケースもかける。
  • terraform-docs:設定したterraform moduleのドキュメントを自動生成してくれる。

SinkCapitalではデータに関する支援を行っています

弊社はスペシャリスト人材が多く在籍するデータ組織です。 データ分析や分析基盤の設計などでお困りの方がいらっしゃれば、 まずは無料で、こちらから各分野のスペシャリストに直接相談出来ます。