· 13 分で読める · 6,712 文字
TerraformとPulumiの実装比較:インフラコード化でどちらを選ぶべきか
TerraformとPulumiは両者ともInfrastructure as Code(IaC)の主流ツールですが、言語の汎用性、学習曲線、チーム体制によって最適な選択は異なります。本記事では、実務での導入判断に必要な機能比較、コード例、そして具体的なユースケースを提供します。
TerraformとPulumiの位置付け
Infrastructure as Code市場において、TerraformとPulumiは異なる哲学で設計されています。
Terraform
Pulumi
flowchart LR
A["インフラコード記述"] --> B{言語の選択}
B -->|HCLを学ぶ| C["Terraform"]
B -->|既知のプログラミング言語| D["Pulumi"]
C --> E["宣言的定義"]
D --> F["手続き的定義"]
E --> G["シンプル、予測可能"]
F --> H["柔軟、プログラマブル"]
主要機能の詳細比較
状態管理とバックエンド
Terraformは明示的な状態ファイル(terraform.tfstate)を使用してリソースの現在の状態を追跡します。ローカルストレージ、S3、Azure Blob Storage、Terraformクラウドなど、複数のバックエンドをサポートしています。
# Terraform: S3バックエンドの設定例
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
Pulumiも同様に状態を追跡しますが、デフォルトではPulumi Service(クラウドホスト)またはセルフホストのバックエンドを使用します。設定はより簡潔です。
# Pulumi: バックエンド設定(Pulumi.yamlの例)
name: my-infrastructure
runtime: python
backend:
url: s3://my-pulumi-state
実務では、Terraformの状態ロック機能(DynamoDB統合)が大規模チームでの競合防止に有効です。一方、Pulumiはスタック概念によってプロジェクト単位での管理が直感的です。
プログラミング言語とコードの再利用性
Terraformは独自のHCLを学ぶ必要があります。これはシンプルですが、複雑なロジックを記述する際に表現力が限定されます。モジュール機能で再利用可能なコンポーネントを作成できます。
# Terraform: EC2インスタンスとセキュリティグループの定義
resource "aws_security_group" "web" {
name = "web-sg"
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"]
}
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.web.id]
tags = {
Name = "web-server"
}
}
Pulumiは汎用言語を使用するため、既知の言語スキルを即座に活かせます。ループ、関数、クラスなどの言語機能をそのまま使用可能です。
# Pulumi: Pythonでの同等の実装
import pulumi
import pulumi_aws as aws
# セキュリティグループの定義
web_sg = aws.ec2.SecurityGroup("web-sg",
ingress=[
aws.ec2.SecurityGroupIngressArgs(
protocol="tcp",
from_port=80,
to_port=80,
cidr_blocks=["0.0.0.0/0"],
),
],
egress=[
aws.ec2.SecurityGroupEgressArgs(
protocol="-1",
from_port=0,
to_port=0,
cidr_blocks=["0.0.0.0/0"],
),
])
# EC2インスタンスを3つ作成(Pythonのループを活用)
instances = []
for i in range(3):
instance = aws.ec2.Instance(f"web-server-{i}",
ami="ami-0c55b159cbfafe1f0",
instance_type="t3.micro",
vpc_security_group_ids=[web_sg.id],
tags={"Name": f"web-server-{i}"})
instances.append(instance)
pulumi.export("instance_ids", [inst.id for inst in instances])
このコード例は、Pythonのループ機能を活用することで、3つのインスタンスを効率的に定義しています。Terraformでもcountやfor_eachで同等の処理が可能ですが、記法がより複雑です。
学習曲線と導入速度
Terraformは新しい言語(HCL)を学ぶ必要があるため、初心者向けのドキュメントが充実しています。シンプルなリソース定義から始められるため、スタートは早いです。
Pulumiはプログラミング経験者にとっては直感的ですが、IaC特有の概念(スタック、シークレット管理、参照の遅延評価など)の理解に時間がかかります。特にプログラミング初心者には学習曲線が急です。
プロバイダーのサポート状況
Terraformは圧倒的にプロバイダー数が多く、AWS、Azure、GCP、Kubernetes、Datadog、GitHub、Slackなど1000以上のリソースタイプをサポートしています。
Pulumiも主要クラウドプロバイダーをサポートしていますが、Terraformと比べると若干少なく、新しいサービスへの対応が遅れることがあります。ただし、Terraformプロバイダーをラップしているため、Terraformが対応しているリソースの大部分はPulumiでも利用可能です。
graph TD
A["プロバイダーサポート"] --> B["Terraform"]
A --> C["Pulumi"]
B --> D["AWS"]
B --> E["Azure"]
B --> F["GCP"]
B --> G["1000+ その他"]
C --> H["AWS"]
C --> I["Azure"]
C --> J["GCP"]
C --> K["200+ その他
一部はTerraform経由"]
実装パターンと実務での選択基準
Terraformを選ぶべき場面
- チーム全体の学習コストを最小化したい場合:HCLは小さな学習曲線で導入できます
- 複雑なプロバイダーエコシステムが必要な場合:Terraform固有のプロバイダーを多数使用する場合、選択肢がPulumiより豊富です
- 宣言的なシンプルさを重視する場合:「目指すべき状態」の明確性により、予測可能な運用ができます
- 業界標準ツールが必要な場合:ジョブマーケット、コミュニティリソース、採用面で圧倒的有利です
- 非エンジニアもコード化に参加させたい場合:HCLの シンプルさにより、インフラエンジニア以外の参入障壁が低い
Pulumiを選ぶべき場面
- 開発チームがすでに特定の言語スキルを持つ場合:Python、TypeScript、Goなど、既知言語のスキルを直接活用できます
- 複雑なプログラミングロジックが必要な場合:条件分岐、ループ、関数の再利用、テストの記述などが言語レベルで自然にできます
- 開発パイプラインと密に統合したい場合:CI/CDパイプライン内で、インフラコードとアプリケーションコードを同じ言語で管理できます
- スタートアップやイノベーション重視の組織:新しいツールへの投資が経営方針と合致している場合
実務ケーススタディ:中規模SaaS企業での導入判断
筆者の経験上、従業員50-200名のSaaS企業では以下のような事例がありました:
ケース1:Terraform導入企業
- インフラチーム(3-4名)が中心となってコード化
- 開発チーム(10-20名)は参照のみで、インフラチームに依頼する運用
- 結果:運用負荷が集中し、デプロイ速度が遅延。HCLの学習コストは最小限に抑えられたが、スケール上の問題が発生
ケース2:Pulumi(TypeScript)導入企業
- 開発チームのTypeScriptスキルを活用し、各機能チームが自身のインフラを定義
- プラットフォームチーム(2名)がコアライブラリ(再利用可能なコンポーネント)を管理
- 結果:開発チームの自律性向上、デプロイ時間短縮。ただし、IaC概念の導入研修に1ヶ月要した
このケースから、組織の成熟度とチーム構成により最適なツールが異なることが明確です。
パフォーマンスとコスト考慮事項
実行速度
Terraformはterraform planで差分検出を高速に実行でき、大規模インフラ(数千のリソース)でも10-30秒程度で完了します。
Pulumiも同等の速度ですが、Pythonランタイムの起動に若干の遅延があります。実務では顕著な差ではありませんが、頻繁にplan実行する開発フローでは、わずかなストレスになる可能性があります。
ライセンスと価格
Terraformはオープンソース(Mozilla Public License v2.0)で完全に無料です。Terraform Cloudは有料(Free プラン から Team & Governance $20/月以上)ですが、セルフホストの状態管理は無料です。
Pulumiも基本的にはオープンソース(Apache 2.0)ですが、Pulumi Serviceの商用機能(Team Collaboration、Advanced Policy Engine)は有料です(Team プラン $30/月から)。
コスト面ではTerraformがより低コストですが、機能要件により判断する必要があります。
よくある質問
直接的な自動移行ツールは存在しませんが、概念的には比較的容易です。各Terraformリソースに対応するPulumiリソースがあり、手動での記述変換が必要です。100-200リソース程度なら数日で移行可能ですが、数千リソースの場合は数週間から数ヶ月要することもあります。
技術的には可能です。例えば、基盤インフラはTerraform、アプリケーション固有のインフラはPulumiという使い分けが考えられます。ただし、チーム運用の複雑性が増すため、よほどの理由がない限り、単一ツールでの統一を推奨します。
TerraformやPulumiで直接管理できないサービスに対しては、以下のアプローチがあります:
どちらも本質的にセキュアですが、以下の点で差があります:
よくあるハマりポイントと解決策
Terraformでのstate lockの競合問題
複数チームメンバーが同時にデプロイすると、DynamoDBロックがタイムアウトし、エラーが発生することがあります。
# 解決策: DynamoDB設定の適切化
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
# DynamoDBテーブル設定(Terraform外で実行)
# aws dynamodb create-table \
# --table-name terraform-locks \
# --attribute-definitions AttributeName=LockID,AttributeType=S \
# --key-schema AttributeName=LockID,KeyType=HASH \
# --billing-mode PAY_PER_REQUEST
Pulumiでの遅延評価(Lazy Evaluation)
Pulumiでは、クラウドリソースの属性(IDなど)を取得する際、非同期で解決されるため、即座に値を使用できないことがあります。
# Pulumiの解決策: apply()を使用した遅延値の処理
import pulumi
import pulumi_aws as aws
# EC2インスタンスを作成
instance = aws.ec2.Instance("web",
ami="ami-0c55b159cbfafe1f0",
instance_type="t3.micro")
# instance.idは直接値ではなく、Output型
# apply()を使って遅延値を処理
instance_ip = instance.public_ip.apply(lambda ip: f"http://{ip}:80")
pulumi.export("server_url", instance_ip)
Terraformでのリソース参照の複雑性
複数のモジュール間でリソース参照する際、参照パスが複雑になり、エラーが発生しやすいです。
# module/main.tf内で定義したセキュリティグループを他のモジュールから参照
# main.tf(メインモジュール)
module "networking" {
source = "./modules/networking"
vpc_cidr = "10.0.0.0/16"
}
module "compute" {
source = "./modules/compute"
# モジュール出力を参照
security_group_id = module.networking.web_sg_id
}
# modules/networking/outputs.tf
output "web_sg_id" {
value = aws_security_group.web.id
}
# modules/compute/main.tf
variable "security_group_id" {
type = string
}
resource "aws_instance" "web" {
# ... 設定 ...
vpc_security_group_ids = [var.security_group_id]
}
テスト環境と動作確認情報
本記事のコード例は以下の環境で動作確認しました:
- Terraform: v1.7.0 / HCL2 / AWS Provider v5.20.0
- Pulumi: v3.85.0 / Python 3.11 / Pulumi AWS v6.15.0
- テスト環境: macOS 14.2 / AWS Account with proper IAM permissions
- 検証日: 2025年1月
公式ドキュメント・参考資料
まとめ
- Terraform
- Pulumi
- プロバイダーのサポート範囲ではTerraformが圧倒的に有利。特定のクラウドサービスに依存する場合は事前確認が必須
- チーム規模、既存スキル、組織の成熟度を総合的に判断し、選択すること。単一ツール運用を基本とし、両方の併用は避けるべき
- 既存インフラがある場合は、新規プロジェクトで検証してから段階的な移行を推奨。急激な切り替えは運用リスク
- セキュリティ要件が厳格な場合は、Pulumiのシークレット管理の仕組みが有利。ただし、Terraformでも適切な設定でカバー可能
- 学習投資のコストと長期的な運用効率を天秤にかけ、組織のキャパシティと成長戦略に沿った選択を行うこと
おすすめDevOpsリソース
- Docker Documentation Official Docker and Docker Compose reference.
- Kubernetes Docs Official K8s documentation. Great for understanding concepts.
- GitHub Actions Docs Official guide for CI/CD workflows.