Terraformの門を叩く
私は以前、AWS SAMとgithub ActionsでCD環境を構築しました。
その時に、「AWS SAMはある程度理解したから、次はCloudFormationやな...」
と思っていましたが、上司からTerraformはAWSだけじゃなくほかのクラウドでも使えるから使ってみたらと勧められまして、今に至ります。
Terraformとは
HashiCorpという会社が作成したIaaCサービスです
HCLという独自の言語で記述するようですが、習得が難しいという全くなかったです。
ブロックと引数で記述する感じでした。
ブロックとは
provider "aws" {
region = "us-east-1"
access_key = "hogehoge"
secret_key = "fugafuga"
}
resource "aws_s3_bucket" "hoge" {
bucket = "my-unique-bucket-name-12345"
acl = "private"
}
module "vpc" {
source = "./vpc"
}
ブロックはあらかじめ定義されており、いくつか存在します。上記の例は
resourceブロック
providerブロック
moduleブロック
を使用しています。
ブロックごとに宣言したときの意味が違います。例に挙げた三つは下記のとおりです
-
resourceブロック
resourceブロックで宣言されたリソースを作成します。
上記の例だとs3バケットが作成されます。 -
providerブロック
どのクラウドにリソースを作成するかを決めます -
moduleブロック
分割したファイルを読み込んだり、公式が用意したファイルをそのまま使用したりできます。
ブロックの後に記述している文字列はファイル内で参照するときに使用される名前です。
変数名に近いです。
module "vpc" {#この"vpc"が参照される名前
source = "./vpc"
}
resourceブロックでは"hoge"が参照されます。
"aws_s3_bucket"はどのリソースを作成するか決めています。
resource "aws_s3_bucket" "hoge" {#hogeが参照される名前
}
引数とは
HCLでいう引数とはブロック内で宣言されている変数みたいなやつです。
ここで渡した値を使用してリソース作ったり、モジュールを使用したりしてくれるわけです。
provider "aws" {
region = "us-east-1"#これ
access_key = "hogehoge"#これ
secret_key = "fugafuga"#これ
}
resource "aws_s3_bucket" "hoge" {
bucket = "my-unique-bucket-name-12345"#これ
acl = "private"#これ
}
module "vpc" {
source = "./vpc"#これ
}
基本的な三つのファイル
HCLでメインで記述するファイルは3つに分ける慣習があるようです。
-
main.tf
リソースの定義やプロバイダーの宣言などメインの記述はココ -
variables.tf
main.tfで使用する変数を定義する。 -
outputs.tf
main.tfではなく別のtfファイルで変数を参照する際に記述する
全て一つのファイルに書いても問題なく動作する。
またファイル名も適当でよく、決まったものはない。
基本的にterraformコマンドを使用すると、そのディレクトリのtfファイルはすべて読み込まれるため、ファイル名は上記以外でも問題ない
terraform {
required_version = ">= 0.12"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0.0"
}
}
}
provider "aws" {
region = var.region
access_key = var.aws_access_key
secret_key = var.aws_secret_key
}
#GitHub Actions用のIAMロールを作成
resource "aws_iam_role" "github_actions_deploy_role" {
name = "github-actions-deploy-role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Federated = "arn:aws:iam::${var.aws_account_id}:oidc-provider/token.actions.githubusercontent.com"
},
Action = "sts:AssumeRoleWithWebIdentity",
Condition = {
StringLike = {
"token.actions.githubusercontent.com:sub" : [
"repo:${var.deploy-repository[0]}",
"repo:${var.deploy-repository[1]}"
]
}
}
}
]
})
}
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "region" {}
variable "aws_account_id" {
default = 940723107657
}
variable "deploy-repository" {
description = "GitHub repository for deployment"
default = ["sugiYutaka/utinoko:ref:refs/heads/stage", "sugiYutaka/utinoko:ref:refs/heads/prod"]
}
variables.tfで定義した変数はvar.変数名で参照できる。
variables.tfではデフォルト値を設定できるほか、親のtfファイルから引数として受け取ることも可能
また同じ階層に.auto.tfvarsを作成することで変数を渡すことも可能。
// ステージ環境用の変数
aws_access_key = "******"
aws_secret_key = "******"
region = "us-east-1"
tfvarsファイルは.gitignoreに設定しておくことを推奨する
構成
今回のディレクトリ構成はこんな感じになっており
project/
├── frontend/ # Next.js (React) フロントエンド
├── backend/ # PHP Laravel バックエンドAPI
|
└── terraform/ # AWS インフラ構成管理
├── vpc/ # VPC・ネットワーク設定
├── rds/ # RDS MySQL データベース
├── rdsProxy/ # RDS Proxy(コネクション管理)
├── lambda/ # Lambda関数(Laravel API)
├── apiGateway/ # API Gateway(REST API)
├── s3/ # S3(フロントエンドホスティング)
├── cloudfront/ # CloudFront(CDN)
└── main.tf # メインの構成定義
今回はすべてモジュールを使用して構築しました
使用したモジュールはこれです
https://github.com/terraform-aws-modules
最初はすべて手書きでやっていたんですが、moduleが便利そうだったので、乗り換えました。
モジュールを簡単に使用、作成したりできるのはCloudFormationにはない良い点だと思います。
デプロイ
まずはterraformディレクトリで下記のコマンドを実行します。
実行することで.terraformディレクトリが作成され、ここにmoduleなどがダウンロードされます。
terraform init
つぎは今回のデプロイで何が作成されるのかを確認します。
terraform plan
...
Plan: 1 to add, 2 to change, 1 to destroy.
1つのリソースの追加と
2ふたつのリソースの変更
1つのリソースの削除があることが確認できます。
問題ないのでデプロイします。
#.auto.tfvarsファイルを読み込む場合
terraform apply
#任意の.tfvarsファイルを読み込む場合
terraform apply -vars-file=./stage.tfvars
使ってみた感想
ファイルの分割が簡単で、きれいにコードが書けるので、気持ちよく記述できました。
再利用もしやすいモジュールを作成することもできて、別のプロジェクトでもつかえそうな感じがします。
いいところのほうが多いように感じましたが、
めんどくさいと思うことがありました。
s3にNextJsのbuildしたディレクトリをアップロードしようと思ったのですが、for文でループしてアップロードするような方法しかありませんでした。リソースのプロビジョニングには適していますが、CDはCloudFormationのほうが構築しやすいです。とはいえS3でホスティングするならAmplifyを使うようにAWS公式が言っているので、Amplifyを使いましょう。
再利用しやすいですし、私はCloufFormationよりTerraformを推していきたいです