Iacツールの「Terraform」でAWSインフラをコード化して再現可能な環境を構築してみよう

- 1 はじめに
- 2 IaCとは
- 2.1 IaCのメリット
- 2.2 IaCの代表的なツール
- 3 Terraformとは
- 3.1 Terraformの特徴
- 3.2 Terraformのインストール
- 4 Terraformのベストプラクティス
- 4.1 コードフォーマットの統一
- 4.2 ディレクトリ構成の工夫
- 4.3 状態ファイルの管理
- 4.4 機密情報の扱い
- 4.5 リソースの命名規則
- 4.6 タグの活用
- 4.7 バージョン管理
- 5 TerraformでAWSにリソースを作成
- 5.1 前提条件
- 5.2 ステップ1: プロジェクトのセットアップ
- 5.3 ステップ2: プロバイダーの設定
- 5.4 ステップ3: 変数の定義
- 5.5 ステップ4: VPCの作成
- 5.6 ステップ5: IAMロールの作成
- 5.7 ステップ6: ECRリポジトリの作成
- 5.8 ステップ7: EKSクラスターの作成
- 5.9 ステップ8: 出力値の定義
- 5.10 ステップ9: Terraformの初期化
- 5.11 ステップ10: 実行計画の確認
- 5.12 ステップ11: リソースの作成
- 5.13 ステップ12: kubectlの設定
- 5.14 ステップ13: 状態の確認
- 6 後片付けと注意点
- 6.1 リソースの削除
- 6.2 状態ファイルのバックアップ
- 6.3 コストの管理
- 6.4 エラーが発生した場合
- 7 おわりに
はじめに
第13回と第14回では「AWS ECR」と「EKS」を使ってコンテナアプリケーションの実行基盤を構築しました。これらの環境構築では、AWSマネジメントコンソールから手動でリソースを作成してきました。
しかし、手動での環境構築にはいくつかの課題があります。
- 再現性の欠如
同じ環境を再度構築しようとしても、設定値を覚えていなかったり、手順を間違えたりして、完全に同じ環境を作るのが困難です。開発環境、ステージング環境、本番環境を別々に構築する場合、それぞれで微妙な差異が生まれてしまいます。
これは環境の削除にも同じことが言えます。削除手順や依存関係などが複雑に絡み合った環境では環境1つ破棄するのも困難です。環境構築は手順を間違ったとしても他の環境に影響を出しづらいですが、環境の削除は間違って別の環境を削除してしまう恐れもあります。狙った環境のみを削除できる再現性が重要となってきます。 - 変更履歴の管理が難しい
「誰が」「いつ」「何のために」インフラを変更したのかを追跡することが困難です。複数人でインフラを管理している場合は変更内容が属人化し、後から振り返ることができません。 - レビューができない
コンソール操作では、変更内容を事前にチームでレビューすることができません。アプリケーションコードのようにプルリクエストを出して、チームで確認してからデプロイするという運用が難しくなります。 - スケールしにくい
環境が増えるたびに手動で構築していては、時間がかかりすぎます。10個、20個の環境を管理することは現実的ではありません。
これらの課題を解決するのが「Infrastructure as Code(IaC)」の考え方です。そして、そのIaCを実現するための代表的なツールが「Terraform」です。
本記事では、第13回と第14回で手動構築したAWSリソース(ECR、EKS、VPC、IAMロールなど)をTerraformのコードで再現し、インフラをコードで管理する方法を学びます。
IaCとは
IaCとは、コードでインフラストラクチャを構築・管理する手法です。従来のように管理画面から手作業で設定するのではなく、設定内容をコードとして記述し、自動的にインフラを構築します。
IaCのメリット
- 再現性の確保
コードがあれば、いつでも同じ環境を再構築できます。開発、ステージング、本番環境を同じコードから作成することで、環境差異によるトラブルを防げます。 - バージョン管理
Gitなどのバージョン管理システムでインフラの変更履歴を管理できます。「誰が」「いつ」「なぜ」変更したのかが明確になり、問題が発生した際にも過去の状態に戻すことができます。 - レビュープロセスの導入
インフラの変更をプルリクエストとして提出し、チームでレビューしてからデプロイできます。これにより、設定ミスや意図しない変更を事前に防ぐことができます。 - ドキュメントとしての役割
コードそのものがインフラの設計書になります。どのようなリソースがどのように設定されているかが、コードを読めば一目瞭然です。 - 自動化の推進
CI/CDパイプラインと統合することで、インフラの構築・更新を完全に自動化できます。これにより、手作業によるミスを排除し、デプロイのスピードと信頼性を向上させます。
IaCの代表的なツール
IaCを実現するツールはいくつかありますが、代表的なものは以下の通りです。
- Terraform: HashiCorpが開発するクラウド非依存のIaCツール
- AWS CloudFormation: AWSが提供するAWS専用のIaCツール
- Pulumi: 汎用プログラミング言語でインフラを記述できるツール
- Ansible: 構成管理ツールとしても使われるが、IaCとしても利用可能
本記事では、最も広く使われているTerraformを採用します。
Terraformとは
Terraformは、HashiCorp社が開発したオープンソースのIaCツールです。HCL(HashiCorp Configuration Language)というDSL(ドメイン特化言語)でコードを記述し、AWS、Azure、Google Cloudなど、さまざまなクラウドプロバイダーに対応しています。
Terraformの特徴
- マルチクラウド対応
Terraformは単一のツールで複数のクラウドプロバイダーを管理できます。AWSとGoogle Cloudを併用している場合でも、同じツールとワークフローで管理できます。 - 宣言的な記述
「何を作るか」を記述するだけで、Terraformが自動的に「どうやって作るか」を判断します。これにより、複雑な依存関係も自動的に解決されます。 - 状態管理
Terraformは現在のインフラの状態を「stateファイル」として保存します。このファイルにより既存のリソースとコードの差分を検出し、必要な変更だけを適用できます。 - プランとアプライの分離
実際にリソースを変更する前に「何が変更されるか」をプレビューできます(terraform plan)。確認してから実行できるため、意図しない変更を防げます。 - モジュール化
再利用可能なモジュールを作成し、コードの重複を避けることができます。例えば、VPCの構成を1つのモジュールとして定義し、複数のプロジェクトで使い回すことが可能です。
なお、TerraformはAPIが公開されているクラウドリソースの管理に優れていますが、サーバー内部の設定管理(nginxのインストールや設定ファイルの配置など)はあまり得意ではありません。そのような操作には「Ansible」などの構成管理ツールを併用するのが一般的です。
Terraformのインストール
Terraformをインストールします。インストール方法は公式ドキュメントを参照してください。
●Terraformのインストール
https://developer.hashicorp.com/terraform/install
インストール後、以下のコマンドでバージョンを確認できます。
$ terraform version
執筆時点の最新版はv1.13.4でした。
Terraformのベストプラクティス
実際にコードを書く前に、Terraformを使う上で押さえておきたいベストプラクティスを紹介します。これらは「Terraform公式のスタイルガイド」に基づいています。
コードフォーマットの統一
Terraformには、コードを自動整形するterraform fmtコマンドが用意されています。
$ terraform fmt
このコマンドを実行することで、以下が自動的に整えられます。
- インデントは2スペース
- ブロック内の属性は
=で揃える - 空行の適切な配置
コミット前やCI/CDパイプラインで自動実行することにより、チーム全体でコードスタイルを統一できます。
ディレクトリ構成の工夫
プロジェクトの規模に応じて、適切なディレクトリ構成を選択します。
小規模プロジェクト向け(フラット構成)terraform/ ├── main.tf # メインの構成ファイル ├── variables.tf # 変数定義 ├── outputs.tf # 出力値定義 ├── terraform.tfvars # 変数の値 └── .gitignore大規模プロジェクト向け(モジュール構成)
terraform/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ └── terraform.tfvars
│ ├── staging/
│ │ ├── main.tf
│ │ └── terraform.tfvars
│ └── prod/
│ ├── main.tf
│ └── terraform.tfvars
└── modules/
├── vpc/
├── eks/
└── ecr/
本記事では、学習のしやすさを優先してフラット構成を採用します。
状態ファイルの管理
Terraformの状態ファイル(terraform.tfstate)には、管理しているリソースの情報が含まれています。このファイルの管理方法には注意が必要です。
ローカル管理(開発・学習用)
個人の学習や検証環境では、ローカルに状態ファイルを保存しても問題ありません。
リモート管理(チーム開発・本番環境)
チームで開発する場合や本番環境では、状態ファイルをS3などのリモートバックエンドに保存します。これには、以下のメリットがあります。
- 複数人で同じ状態を共有できる
- 状態ファイルのロック機能により同時実行を防げる
- バージョン管理により過去の状態に戻せる
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "eks-cluster/terraform.tfstate"
region = "ap-northeast-1"
}
}
本記事では学習のためローカル管理を使用しますが、本番環境では必ずリモートバックエンドを使用してください。
機密情報の扱い
APIキーやパスワードなどの機密情報はコードに直接書かず、環境変数や専用の管理サービスを使用します。
悪い例
variable "db_password" {
default = "password123" # ハードコーディングはNG
}
良い例
variable "db_password" {
description = "Database password"
type = string
sensitive = true # センシティブ情報としてマーク
# 値は環境変数やtfvarsファイルで指定
}
環境変数で渡す場合:
$ export TF_VAR_db_password="secure_password" $ terraform apply
リソースの命名規則
リソース名は一貫性のある命名規則に従います。公式スタイルガイドでは、リソース名にリソースタイプを繰り返さないことが推奨されています。
# 悪い例: リソースタイプの重複
resource "aws_vpc" "eks_vpc" {
# ...
}
resource "aws_security_group" "eks_cluster_sg" {
# ...
}
# 良い例: リソースタイプを繰り返さない
resource "aws_vpc" "main" {
# ...
}
resource "aws_security_group" "cluster" {
# ...
}
ただし、本記事では学習のしやすさを優先し、分かりやすさのため一部でリソースタイプを含めた命名を使用しています。
タグの活用
AWSリソースには必ずタグを付け、管理しやすくします。
tags = {
Name = "my-eks-cluster"
Environment = "production"
ManagedBy = "terraform"
Project = "devops-demo"
}
バージョン管理
Terraformのバージョンとプロバイダーのバージョンを明示的に指定します。
terraform {
required_version = ">= 1.13.4"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.16.0"
}
}
}
なぜバージョンを固定する必要があるのか
Terraformやプロバイダーのバージョンを固定しないと、以下のような問題が発生する可能性があります。
- 既存のコードが動かなくなる
プロバイダーのバージョンアップでリソースの属性名が変更されたり、必須パラメータが追加されたりすることがあります。バージョンを固定していないと、ある日突然terraform applyが失敗する可能性があります。 - チームメンバー間で挙動が異なる
チームメンバーがそれぞれ異なるバージョンのTerraformやプロバイダーを使用していると、同じコードでも実行結果が異なる場合があります。これにより、予期しないリソースの変更や削除が発生する危険性があります。 - 破壊的変更への対応
Terraformやプロバイダーはメジャーバージョンアップ時に破壊的変更を導入することがあります。バージョンを固定することで計画的にアップグレードを行い、必要なコード修正を事前に準備できます。
バージョン指定の演算子
バージョン指定には以下の演算子を使用できます。
>=: 指定したバージョン以上であればどのバージョンでも使用可能です。例えば、>= 1.3.4はバージョン1.3.4、1.3.5、1.4.0、2.0.0などすべてが許容されます。~>:「悲観的バージョン制約」と呼ばれ、右端のバージョン番号のみ増加を許可します。例えば、~> 6.16.0は6.16.0、6.16.5、6.16.10などパッチバージョンの増加は許容されますが、6.17.0のようにマイナーバージョンの増加は許可されません。これにより、セキュリティフィックスなどの修正は受け入れつつ、マイナーアップデートを防ぐことができます。
本番環境では、予期しない変更を防ぐため~>を使用することが推奨されます。
TerraformでAWSにリソースを作成
それでは、第13回と第14回で作成したAWSリソースをTerraformで再現していきましょう。
前提条件
- Terraformがインストール済み
- AWS CLIが設定済み(
aws configure完了) - 第13回、第14回で作成したリソースは削除済み
ステップ1: プロジェクトのセットアップ
作業用ディレクトリを作成します。
$ mkdir terraform-eks $ cd terraform-eks
ステップ2: プロバイダーの設定
provider.tfファイルを作成し、AWSプロバイダーを設定します。
terraform {
required_version = ">= 1.13.4"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.16.0"
}
}
}
provider "aws" {
region = var.aws_region
}
ステップ3: 変数の定義
variables.tfファイルを作成し、環境ごとに変わる値を変数として定義します。
variable "aws_region" {
description = "AWS region"
type = string
default = "ap-northeast-1"
}
variable "cluster_name" {
description = "EKS cluster name"
type = string
default = "my-cluster"
}
variable "ecr_repository_name" {
description = "ECR repository name"
type = string
default = "my-app"
}
variable "node_instance_type" {
description = "EC2 instance type for EKS nodes"
type = string
default = "t3.medium"
}
variable "node_desired_size" {
description = "Desired number of nodes"
type = number
default = 2
}
variable "node_min_size" {
description = "Minimum number of nodes"
type = number
default = 1
}
variable "node_max_size" {
description = "Maximum number of nodes"
type = number
default = 3
}
ステップ4: VPCの作成
vpc.tfファイルを作成し、EKS用のVPCを定義します。
# VPCの作成
resource "aws_vpc" "eks_vpc" {
cidr_block = "192.168.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.cluster_name}-vpc"
}
}
# インターネットゲートウェイ
resource "aws_internet_gateway" "eks_igw" {
vpc_id = aws_vpc.eks_vpc.id
tags = {
Name = "${var.cluster_name}-igw"
}
}
# パブリックサブネット
resource "aws_subnet" "public_subnet_1" {
vpc_id = aws_vpc.eks_vpc.id
cidr_block = "192.168.0.0/18"
availability_zone = "${var.aws_region}a"
map_public_ip_on_launch = true
tags = {
Name = "${var.cluster_name}-public-subnet-1"
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/elb" = "1"
}
}
resource "aws_subnet" "public_subnet_2" {
vpc_id = aws_vpc.eks_vpc.id
cidr_block = "192.168.64.0/18"
availability_zone = "${var.aws_region}c"
map_public_ip_on_launch = true
tags = {
Name = "${var.cluster_name}-public-subnet-2"
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/elb" = "1"
}
}
# プライベートサブネット
resource "aws_subnet" "private_subnet_1" {
vpc_id = aws_vpc.eks_vpc.id
cidr_block = "192.168.128.0/18"
availability_zone = "${var.aws_region}a"
tags = {
Name = "${var.cluster_name}-private-subnet-1"
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/internal-elb" = "1"
}
}
resource "aws_subnet" "private_subnet_2" {
vpc_id = aws_vpc.eks_vpc.id
cidr_block = "192.168.192.0/18"
availability_zone = "${var.aws_region}c"
tags = {
Name = "${var.cluster_name}-private-subnet-2"
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/internal-elb" = "1"
}
}
# NAT Gateway用のElastic IP
resource "aws_eip" "nat_eip_1" {
domain = "vpc"
tags = {
Name = "${var.cluster_name}-nat-eip-1"
}
}
resource "aws_eip" "nat_eip_2" {
domain = "vpc"
tags = {
Name = "${var.cluster_name}-nat-eip-2"
}
}
# NAT Gateway
resource "aws_nat_gateway" "nat_gateway_1" {
allocation_id = aws_eip.nat_eip_1.id
subnet_id = aws_subnet.public_subnet_1.id
tags = {
Name = "${var.cluster_name}-nat-gateway-1"
}
depends_on = [aws_internet_gateway.eks_igw]
}
resource "aws_nat_gateway" "nat_gateway_2" {
allocation_id = aws_eip.nat_eip_2.id
subnet_id = aws_subnet.public_subnet_2.id
tags = {
Name = "${var.cluster_name}-nat-gateway-2"
}
depends_on = [aws_internet_gateway.eks_igw]
}
# パブリックルートテーブル
resource "aws_route_table" "public_route_table" {
vpc_id = aws_vpc.eks_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.eks_igw.id
}
tags = {
Name = "${var.cluster_name}-public-route-table"
}
}
# パブリックサブネットへのルートテーブル関連付け
resource "aws_route_table_association" "public_subnet_1_association" {
subnet_id = aws_subnet.public_subnet_1.id
route_table_id = aws_route_table.public_route_table.id
}
resource "aws_route_table_association" "public_subnet_2_association" {
subnet_id = aws_subnet.public_subnet_2.id
route_table_id = aws_route_table.public_route_table.id
}
# プライベートルートテーブル
resource "aws_route_table" "private_route_table_1" {
vpc_id = aws_vpc.eks_vpc.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat_gateway_1.id
}
tags = {
Name = "${var.cluster_name}-private-route-table-1"
}
}
resource "aws_route_table" "private_route_table_2" {
vpc_id = aws_vpc.eks_vpc.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat_gateway_2.id
}
tags = {
Name = "${var.cluster_name}-private-route-table-2"
}
}
# プライベートサブネットへのルートテーブル関連付け
resource "aws_route_table_association" "private_subnet_1_association" {
subnet_id = aws_subnet.private_subnet_1.id
route_table_id = aws_route_table.private_route_table_1.id
}
resource "aws_route_table_association" "private_subnet_2_association" {
subnet_id = aws_subnet.private_subnet_2.id
route_table_id = aws_route_table.private_route_table_2.id
}
ステップ5: IAMロールの作成
iam.tfファイルを作成し、EKSクラスターとノードグループ用のIAMロールを定義します。
# EKSクラスター用IAMロール
resource "aws_iam_role" "eks_cluster_role" {
name = "${var.cluster_name}-cluster-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "eks.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "eks_cluster_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.eks_cluster_role.name
}
# EKSノードグループ用IAMロール
resource "aws_iam_role" "eks_node_role" {
name = "${var.cluster_name}-node-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "eks_worker_node_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
role = aws_iam_role.eks_node_role.name
}
resource "aws_iam_role_policy_attachment" "eks_cni_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
role = aws_iam_role.eks_node_role.name
}
resource "aws_iam_role_policy_attachment" "eks_container_registry_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
role = aws_iam_role.eks_node_role.name
}
ステップ6: ECRリポジトリの作成
ecr.tfファイルを作成し、コンテナイメージを保存するECRリポジトリを定義します。
resource "aws_ecr_repository" "app_repository" {
name = var.ecr_repository_name
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
tags = {
Name = var.ecr_repository_name
}
}
# ライフサイクルポリシー(古いイメージを自動削除)
resource "aws_ecr_lifecycle_policy" "app_repository_policy" {
repository = aws_ecr_repository.app_repository.name
policy = jsonencode({
rules = [
{
rulePriority = 1
description = "Keep last 10 images"
selection = {
tagStatus = "any"
countType = "imageCountMoreThan"
countNumber = 10
}
action = {
type = "expire"
}
}
]
})
}
ステップ7: EKSクラスターの作成
eks.tfファイルを作成し、EKSクラスターとノードグループを定義します。
# EKSクラスター
resource "aws_eks_cluster" "main" {
name = var.cluster_name
role_arn = aws_iam_role.eks_cluster_role.arn
version = "1.33"
vpc_config {
subnet_ids = [
aws_subnet.public_subnet_1.id,
aws_subnet.public_subnet_2.id,
aws_subnet.private_subnet_1.id,
aws_subnet.private_subnet_2.id
]
endpoint_private_access = true
endpoint_public_access = true
}
depends_on = [
aws_iam_role_policy_attachment.eks_cluster_policy
]
}
# EKSノードグループ
resource "aws_eks_node_group" "main" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "${var.cluster_name}-node-group"
node_role_arn = aws_iam_role.eks_node_role.arn
subnet_ids = [
aws_subnet.private_subnet_1.id,
aws_subnet.private_subnet_2.id
]
scaling_config {
desired_size = var.node_desired_size
min_size = var.node_min_size
max_size = var.node_max_size
}
instance_types = [var.node_instance_type]
disk_size = 20
depends_on = [
aws_iam_role_policy_attachment.eks_worker_node_policy,
aws_iam_role_policy_attachment.eks_cni_policy,
aws_iam_role_policy_attachment.eks_container_registry_policy
]
}
ステップ8: 出力値の定義
outputs.tfファイルを作成し、作成したリソースの情報を出力します。
output "cluster_name" {
description = "EKS cluster name"
value = aws_eks_cluster.main.name
}
output "cluster_endpoint" {
description = "EKS cluster endpoint"
value = aws_eks_cluster.main.endpoint
}
output "cluster_security_group_id" {
description = "Security group ID attached to the EKS cluster"
value = aws_eks_cluster.main.vpc_config[0].cluster_security_group_id
}
output "ecr_repository_url" {
description = "ECR repository URL"
value = aws_ecr_repository.app_repository.repository_url
}
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.eks_vpc.id
}
ステップ9: Terraformの初期化
作成したファイルをもとに、Terraformを初期化します。
$ terraform init
このコマンドで、AWSプロバイダーのプラグインがダウンロードされます。
ステップ10: 実行計画の確認
実際にリソースを作成する前に、何が作成されるかを確認します。
$ terraform plan
このコマンドで、作成されるリソースの一覧が表示されます。以下のような出力が表示されるはずです。
Plan: 27 to add, 0 to change, 0 to destroy.
約27個のリソースが作成されることが確認できます。
ステップ11: リソースの作成
実行計画に問題がなければ、実際にリソースを作成します。
$ terraform apply
確認メッセージが表示されるので、yesと入力して実行します。
Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes
リソースの作成には10〜15分程度かかります。完了すると、以下のような出力が表示されます。
Apply complete! Resources: 27 added, 0 changed, 0 destroyed. Outputs: cluster_endpoint = "https://XXXXX.gr7.ap-northeast-1.eks.amazonaws.com" cluster_name = "my-cluster" cluster_security_group_id = "sg-XXXXX" ecr_repository_url = "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-app" vpc_id = "vpc-XXXXX"
ステップ12: kubectlの設定
作成したEKSクラスターにアクセスできるよう、kubectlを設定します。
$ aws eks update-kubeconfig --region ap-northeast-1 --name my-cluster
接続を確認します。
$ kubectl get nodes
以下のような出力が表示されれば成功です。
NAME STATUS ROLES AGE VERSION ip-192-168-xxx-xxx.ap-northeast-1.compute.internal Ready <none> 2m v1.33.x ip-192-168-xxx-xxx.ap-northeast-1.compute.internal Ready <none> 2m v1.33.x
ステップ13: 状態の確認
Terraformが管理しているリソースの一覧を確認できます。
$ terraform state list
以下のようなリソース一覧が表示されます。
aws_ecr_lifecycle_policy.app_repository_policy aws_ecr_repository.app_repository aws_eip.nat_eip_1 aws_eip.nat_eip_2 aws_eks_cluster.main ...
特定のリソースの詳細を確認する場合は以下のコマンドを使います。
$ terraform state show aws_eks_cluster.main
後片付けと注意点
リソースの削除
Terraformで作成したリソースを削除する場合は、以下のコマンドを実行します。
$ terraform destroy
【重要】: この操作は取り消せません。本番環境では十分に注意してください。
また、Kubernetesで作成したLoadBalancer(ELB)などは、Terraformの管理外のため自動削除されません。事前に手動で削除するか、またはterraform destroy実行後に残っているリソースを手動で削除してください。
状態ファイルのバックアップ
terraform.tfstateファイルは非常に重要です。このファイルを失うと、Terraformがリソースを管理できなくなります。
.gitignoreにterraform.tfstateを追加(機密情報が含まれるため)- 定期的に別の場所にバックアップを取る
# .gitignore .terraform/ terraform.tfstate terraform.tfstate.backup *.tfvars
本番環境での対策
S3バックエンドを使用し、バージョニングを有効化します。
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "eks-cluster/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-lock"
}
}
コストの管理
クラウド上に作成したリソースは、使用していなくても課金されます。特に以下のリソースに注意してください。
- EKSクラスター: $0.10/時間
- EC2インスタンス(ノードグループ): インスタンスタイプに応じた料金
- NAT Gateway: $0.062/時間 + データ転送料
- Elastic IP(未使用時): $0.005/時間
検証が終わったら、必ずterraform destroyでリソースを削除してください。
エラーが発生した場合
- 権限エラー
IAMユーザーに必要な権限が不足している可能性があります。以下のポリシーがアタッチされているか確認してください。AmazonEC2FullAccessAmazonEKSClusterPolicyIAMFullAccessAmazonVPCFullAccess
- リソースの依存関係エラー
リソース間の依存関係が正しく解決されていない場合、depends_onを明示的に指定します。resource "aws_eks_cluster" "main" { # ... depends_on = [ aws_iam_role_policy_attachment.eks_cluster_policy ] } - タイムアウトエラー
EKSクラスターの作成には時間がかかるため、タイムアウトが発生することがあります。その場合は再度terraform applyを実行してください。Terraformは差分を検出し、作成が完了していないリソースのみを処理します。
おわりに
本記事では、第13回と第14回で手動構築したAWSリソースを、Terraformのコードで再現する方法を学びました。IaCを導入することで、以下のメリットが得られます。
- 再現性: 同じ環境をいつでも再構築できる
- バージョン管理: インフラの変更履歴を追跡できる
- レビュー: チームで変更内容を確認してからデプロイできる
- 自動化: CI/CDパイプラインと統合し、デプロイを自動化できる
- ドキュメント: コードそのものがインフラの設計書になる
最初は手動での構築に比べて手間がかかるように感じるかもしれませんが、環境が増えたり、変更が頻繁に発生したりする場合、IaCの効果は絶大です。
次回は、Terraformをチーム開発で使用する際のベストプラクティスについて解説します。リモートバックエンドの設定、モジュール化、CI/CDとの統合など、より実践的な内容に踏み込んでいきます。
今回作成したTerraformコードはそのまま本番環境でも使用できる品質ですが、環境に応じてカスタマイズが必要です。次回の記事を参考に、チーム開発に適した構成へと発展させていきましょう。
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- 「AWS EKS」でKubernetesクラスターを構築してコンテナアプリケーションをデプロイしてみよう
- 「Terraform」のコードを自分で書けるようになろう
- Policy as Codeでインフラのコンプライアンスを自動実現! 「Pulumi CrossGuard」を活用してみよう
- 「GitHub Actions」と「AWS ECR」を連携してセキュアなCI/CDパイプラインを構築してみよう
- SecretもPulumiで使いこなしたい! PulumiのSecurityを試してみよう
- インフラの構成管理を自動化するTerraform入門
- Pulumi Kubernetes Operatorを活用してPulumiのCI/CDを実現しよう
- 「Pulumi Stack」とは ー Pulumiによるマルチステージ環境の構築方法
- Oracle Cloud Hangout Cafe Season7 #2「IaC のベストプラクティス」(2023年7月5日開催)
- TerraformからPulumiへの移行









