こんにちは!インフラエンジニアの世界へようこそ。
ターミナルで `terraform apply` を実行する瞬間、あの「本当に実行して大丈夫かな…?」という独特の緊張感、経験したことはありませんか?
特に、本番環境のリソースが 「Destroy(削除)」 される計画が表示されたときの冷や汗は、誰もが一度は通る道です。
通常、Terraformは既存のリソースを置き換える(Replace)際、「旧リソースを削除(Destroy)してから、新リソースを作成(Create)する」 という挙動をデフォルトで行います。しかし、本番運用中のデータベースやWebサーバー、セキュリティグループでこれが起きるとどうなるでしょうか?
そうです。サービスに「停止時間(ダウンタイム)」が発生してしまうのです。
今回は、そんな恐怖からあなたを解放し、「ゼロダウンタイム(無停止)でのリソース更新」 と 「うっかり削除の完全防止」 を実現するTerraformの強力な機能、`lifecycle` ブロックの活用術を優しく丁寧にお伝えします!
これをマスターすれば、毎日のインフラ運用が劇的に安全に、そして楽になりますよ。一緒に一歩ずつ進めていきましょう!
—
1. なぜ `lifecycle` が必要なのか?
Terraformは「コードで宣言した通りの状態(To Be)」と「実際のクラウドの状態(As Is)」を一致させる素晴らしいツールです。
しかし、設定の変更内容によっては、クラウド側の仕様上「起動したまま変更(In-place update)」ができず、リソースの「再作成(Replacement)」が必要になるケースがあります。
デフォルトの挙動(ダウンタイム発生)
1. 旧リソースを 削除(Destroy)
2. (この間、サービスが停止する!!)
3. 新リソースを 作成(Create)
`create_before_destroy` を使った挙動(ゼロダウンタイム)
1. 先に新リソースを 作成(Create)
2. トラフィックの参照先や依存関係を新リソースに切り替え
3. 役目を終えた旧リソースを 削除(Destroy)
この「順番を逆転させる魔法」をかけるのが、`lifecycle` ブロックの中にある `create_before_destroy = true` です。
さらに、絶対に消してはいけないデータベースなどを守る `prevent_destroy = true` も合わせて学ぶことで、あなたのIaCコードは本番レベルの堅牢性を手に入れることができます。
—
2. 実験環境の準備(セットアップ)
まずは手元で動かして試してみましょう!
今回は最も安全で分かりやすい例として、AWSのセキュリティグループ(Security Group)を使って解説します。
前提条件
- Terraform CLI がインストールされていること(`v1.0.0` 以上推奨)
- AWS CLI がセットアップされ、適切なIAM権限を持っていること(または評価用のアカウントがあること)
まだTerraformをインストールしていない方は、Macなら `brew install hashicorp/tap/terraform`、Windowsなら `winget install HashiCorp.Terraform` で一瞬でセットアップできます。
—
3. ハンズオン:ゼロダウンタイムを体験する「Hello World」
適当な空ディレクトリ(例: `terraform-lifecycle-demo`)を作成し、その中に `main.tf` というファイルを作成してください。
`main.tf` のコード
以下のコードをコピーして貼り付けてみましょう。初心者の方でも意味が追えるよう、コメントを豊富に記載しています。
——————————————————————-
プロバイダーの設定(AWSを使用)
——————————————————————-
terraform {
required_version = “>= 1.0.0”
required_providers {
aws = {
source = “hashicorp/aws”
version = “~> 5.0”
}
}
}
provider “aws” {
region = “ap-northeast-1” # 東京リージョン
}
——————————————————————-
実験用VPC(セキュリティグループの配置先)
——————————————————————-
resource “aws_vpc” “demo_vpc” {
cidr_block = “10.0.0.0/16”
enable_dns_hostnames = true
tags = {
Name = “lifecycle-demo-vpc”
}
}
——————————————————————-
ゼロダウンタイムを実現するセキュリティグループの定義
——————————————————————-
resource “aws_security_group” “demo_sg” {
# ★プロの極意ポイント1:
# `name` ではなく `name_prefix` を使うことで、一時的に新旧2つのSGが同名で衝突するのを防ぎます。
name_prefix = “demo-app-sg-”
description = “Security Group with zero-downtime lifecycle”
vpc_id = aws_vpc.demo_vpc.id
# インバウンドルール(例:80番ポートを許可)
ingress {
from_port = 80
to_port = 80
protocol = “tcp”
cidr_blocks = [“0.0.0.0/0”]
}
# アウトバウンドルール(全解放)
egress {
from_port = 0
to_port = 0
protocol = “-1”
cidr_blocks = [“0.0.0.0/0”]
}
# —————————————————————–
# ★心臓部:lifecycle ブロック
# —————————————————————–
lifecycle {
# 1. 新しいリソースを作成してから、古いリソースを削除する(ゼロダウンタイム)
create_before_destroy = true
# 2. 万が一 terraform destroy が呼ばれても、このリソースの削除をブロックするガード機能
# (実験のために最初はコメントアウトしておきます。後ほど試します!)
# prevent_destroy = true
}
tags = {
Environment = “Dev”
}
}
—
4. 動作確認ステップ:魔法が起きる瞬間を目撃しよう!
ステップ 1: 初期化と作成
ターミナルを開き、以下のコマンドを実行します。
プロバイダープラグインのダウンロード
terraform init
リソースの作成
terraform apply
確認プロンプトが出たら `yes` と入力します。これでVPCとセキュリティグループが作られました。
—
ステップ 2: 「再作成」が必要な変更を加える
セキュリティグループの `description`(説明文)を変更してみましょう。AWSの仕様上、`description` の変更はセキュリティグループの再作成(Replacement)を伴います。
`main.tf` の `description` を書き換えます。
# 変更前
# description = “Security Group with zero-downtime lifecycle”
# 変更後
description = “Updated Security Group with zero-downtime”
—
ステップ 3: 実行計画(plan)を確認する
ここが運命の分かれ道です!以下のコマンドを実行してください。
terraform plan
ターミナルの出力結果をよく見てみてください。次のような表示があるはずです。
aws_security_group.demo_sg must be replaced
+/- resource “aws_security_group” “demo_sg” {
~ description = “Security Group with zero-downtime lifecycle” -> “Updated Security Group with zero-downtime” # forces replacement
}
Plan: 1 to add, 0 to change, 1 to destroy.
そして、注目すべきは 実行順序の注記 です!
出力の中に `(must be created before destroy)` という記述があるはずです。
これはTerraformが 「先に新しいセキュリティグループを作成し、成功した後に古い方を削除します」 と宣言している証拠です!
もし `create_before_destroy = true` を書いていなかったら、古いセキュリティグループが先に消され、そこに紐づいていたEC2インスタンスなどの通信が一瞬(あるいは新作成が失敗した場合は永続的に)切断されてしまうところでした。
実際に `terraform apply` を実行すると、ログに以下の順番で出力されるのがわかります。
1. `aws_security_group.demo_sg: Creating…` (新作成)
2. `aws_security_group.demo_sg: Creation complete…` (完了)
3. `aws_security_group.demo_sg: Destroying…` (旧削除)
4. `aws_security_group.demo_sg: Destruction complete` (完了)
見ごとに「作成 → 削除」の順序で処理されましたね!
—
5. 誤削除を防ぐ最強の盾 `prevent_destroy`
もうひとつの守護神、`prevent_destroy` も試してみましょう。
これは「操作ミスでDBや重要リソースを消してしまう事故」を物理的に防ぐ設定です。
`main.tf` のコメントアウトを解除します。
lifecycle {
create_before_destroy = true
prevent_destroy = true # ★コメントアウトを解除!
}
この状態で、誤って全リソースを削除するコマンドを打ったとしましょう。
terraform destroy
すると、Terraformは削除処理を実行する前にエラーを吐いて即座に処理を中断してくれます!
Error: Instance cannot be destroyed
on main.tf line 26, in resource “aws_security_group” “demo_sg”:
26: resource “aws_security_group” “demo_sg” {
Resource aws_security_group.demo_sg has lifecycle.prevent_destroy set to true, so it cannot be destroyed.
どんなに焦って操作ミスをしても、Terraform自身が「ダメですよ!このリソースは削除禁止に指定されています!」とガードしてくれるのです。本番環境のRDS(データベース)やS3バケットには、必ず書いておきたいお守り設定ですね。
—
6. 先輩エンジニアからの伝承:現場でハマらないための「罠」と対策
ここまでは基礎編です。最後に、現場のSRE/インフラエンジニアだけが知っている「`create_before_destroy` の罠」 とその解決策をお話ししますね。
罠①: 名前の一意性制約(Name Collision)
AWSのリソースには「同じ名前を重複して作れない」ものが多数あります(例: IAMロール名、S3バケット名、固定のセキュリティグループ名)。
もし `name = “my-app-sg”` とハードコードした状態で `create_before_destroy = true` を使うとどうなるでしょうか?
1. 新リソースを `”my-app-sg”` という名前で作ろうとする
2. AWS「同名のリソースが既に存在するのでエラーです!」 と言われて失敗する
3. 新しいリソースが作れないので、古いリソースも削除されず止まる
【解決策】
名前を固定する `name` 引数は使わず、ランダムなサフィックスを自動付与してくれる `name_prefix` を使いましょう!
今回のコードで `name_prefix = “demo-app-sg-“` と書いたのは、まさにこのトラブルを未然に防ぐためのプロの設計テクニックだったのです。
罠②: 依存関係の連鎖(Cascading Effect)
リソースAがリソースBに依存している場合、リソースBに `create_before_destroy = true` を設定すると、リソースAにも `create_before_destroy = true` を伝播させなければならない場合があります。
計画(`terraform plan`)を実行した際、「依存関係のライフサイクルが整合していません」というエラーが出たら、依存元(親)のリソースにも `create_before_destroy` を追加してあげてくださいね。
—
7. まとめ
今回学んだ要点を整理しましょう!
| 設定値 | 役割 | 主なユースケース |
| :— | :— | :— |
| `create_before_destroy = true` | 「新作成 → 旧削除」の順序に変更し、ゼロダウンタイムを実現する | Webサーバー、セキュリティグループ、ロードバランサーの設定変更 |
| `prevent_destroy = true` | リソースの削除処理を強制拒否し、誤削除事故を防ぐ | 本番DB(RDS)、ステートフルなストレージ(S3, EBS)、VPC |
| `name_prefix` の活用 | 新旧リソースが一時的に共存する際の名前衝突を防ぐ | `create_before_destroy` を使うすべてのリソース |
Terraformの `lifecycle` ブロックは、単なる文法ではなく「無事故で本番運用を続けるためのエンジニアの知恵」が詰まった機能です。
これからコードを書くときは、ぜひ「このリソースが再作成されたらサービスは止まらないかな?」「間違えて消えたら困るものはどれかな?」と一歩立ち止まって、`lifecycle` を添えてあげてください。
その小さな配慮が、チームとサービスを未来の障害から救うことになりますよ!
応援しています。それでは、快適なIaCライフを!