2026/04/06
はじめに
ざっくりイメージだけまとめると下記の通り
Terraform:インフラを管理する
→ 「どんなサーバー・ネットワークを用意するか」
Kubernetes:コンテナを管理する
→ 「どのようにアプリを動かし続けるか」
Terraform とは
概要
IaC(Infrastructure as Code)ツール
AWS/GCP/Azure などのクラウドリソースをHCL(HashiCorp Configuration Language)というコードで定義・管理する
手動によるインフラ構築
- GUI でポチポチ作った設定は再現性がない
- 「どんな設定で作ったか」が記録に残らない
- 本番と同じ環境を開発・ステージングに再現するのが難しい
- チームでの変更管理ができない
Terraform によるインフラ構築
- インフラの定義をコードとして Git で管理できる
applyコマンド一発で同じ環境を何度でも再現できる- 変更の差分が
planコマンドで事前に確認できる - チームでレビュー・承認フローを組み込める
基本構文(HCL)
下記一例
# main.tf
# プロバイダー設定(AWS を使用する場合)
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
# 状態ファイル(tfstate)をリモートで管理する
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "ap-northeast-1"
}
}
provider "aws" {
region = "ap-northeast-1"
}
# 変数定義
variable "environment" {
type = string
description = "デプロイ環境"
default = "production"
}
variable "app_name" {
type = string
default = "myapp"
}
# ローカル変数
locals {
common_tags = {
Environment = var.environment
ManagedBy = "Terraform"
App = var.app_name
}
}
# リソース定義(VPC)
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = merge(local.common_tags, {
Name = "${var.app_name}-vpc"
})
}
# サブネット定義
resource "aws_subnet" "public" {
count = 2 # 2つ作成(AZ 冗長化)
vpc_id = aws_vpc.main.id # 上で作った VPC を参照
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = merge(local.common_tags, {
Name = "${var.app_name}-public-${count.index + 1}"
})
}
# データソース
data "aws_availability_zones" "available" {
state = "available"
}
# アウトプット(他の設定から参照できる値)
output "vpc_id" {
value = aws_vpc.main.id
description = "作成した VPC の ID"
}
output "public_subnet_ids" {
value = aws_subnet.public[*].id
}
- https://developer.hashicorp.com/terraform/language/block/terraform#required_providers
- https://developer.hashicorp.com/terraform/language/backend
- https://developer.hashicorp.com/terraform/language/values/variables
- https://developer.hashicorp.com/terraform/language/block/locals
- https://developer.hashicorp.com/terraform/language/block/resource
基本コマンド
terraformコマンドを使用する
# 初期化
terraform init
# 構文チェック・フォーマット
terraform fmt # コードを整形する
terraform validate # 構文を検証する
# 変更のプレビュー(何が変わるか事前確認)
terraform plan
terraform plan -out=tfplan # プランをファイルに保存する
# 変更の適用
terraform apply
terraform apply tfplan # 保存したプランを適用する
# 現在の状態を確認
terraform show
terraform state list # 管理しているリソース一覧
# リソースの削除
terraform destroy # 全削除
terraform destroy -target=aws_db_instance.main # 特定のリソースのみ削除
# State の操作
terraform state mv # リソースの移動・名前変更
terraform state rm # State からリソースを除外
terraform import # 既存リソースを Terraform 管理下に取り込む
ディレクトリ構成
下記一例
infrastructure/
├── modules/ ← 再利用可能なモジュール
│ ├── vpc/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── ecs/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ └── rds/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
│
├── environments/ ← 環境ごとの設定
│ ├── production/
│ │ ├── main.tf ← モジュールを呼び出す
│ │ ├── variables.tf
│ │ ├── terraform.tfvars ← 環境ごとの変数値
│ │ └── backend.tf
│ └── staging/
│ ├── main.tf
│ └── terraform.tfvars
# environments/production/main.tf
# モジュールを呼び出す
module "vpc" {
source = "../../modules/vpc"
environment = var.environment
app_name = var.app_name
cidr_block = "10.0.0.0/16"
}
module "rds" {
source = "../../modules/rds"
environment = var.environment
vpc_id = module.vpc.vpc_id # VPC モジュールのアウトプットを渡す
subnet_ids = module.vpc.private_subnet_ids
db_password = var.db_password
}
Kubernetes(k8s)とは
概要
Google が開発し OSS 化したコンテナオーケストレーションシステム
複数のコンテナを自動でデプロイ・スケール・復旧・管理する
コンテナを手動で管理する場合
- コンテナが落ちたとき手動で再起動が必要
- トラフィックが増えたとき手動でコンテナを増やす必要がある
- 複数サーバーへのコンテナ配置を手動で管理するのが困難
- デプロイ時のダウンタイムを避けるのが難しい
コンテナを Kubernetes で管理する場合
- コンテナが落ちると自動で再起動する(自己修復)
- CPU 使用率などに応じて自動でスケールアウト/イン
- 複数ノードへのコンテナ配置を自動で最適化
- ローリングアップデートでダウンタイムなしデプロイ
クラスター構成
コントロールプレーン(管理層、Control Plane)
├── API Server ← kubectl などからの操作を受け付ける
├── Scheduler ← Pod をどのノードに配置するか決定する
├── Controller Manager ← 状態の監視・自己修復
└── etcd ← クラスターの全状態を保存する KV ストア
ワーカーノード(実行層、Worker Node)× N 台
├── kubelet ← API Server と通信・コンテナの管理
├── kube-proxy ← ネットワークルールの管理
└── Container Runtime(containerd / Docker)
└── Pod(コンテナの実行単位)
主要コンポーネント
Pod
コンテナの最小デプロイ単位
通常は直接 Pod を作らず Deployment 経由で管理する
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.0.0
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-secrets # Secret から参照
key: database_url
resources:
requests:
memory: "128Mi"
cpu: "100m" # 0.1 vCPU
limits:
memory: "256Mi"
cpu: "500m" # 0.5 vCPU
livenessProbe: # 生存確認(失敗したら再起動)
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe: # 準備完了確認(失敗したらトラフィックを送らない)
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
Deployment
Pod の宣言的な管理、ローリングアップデート
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
namespace: production
spec:
replicas: 3 # 常に 3つの Pod を維持する
selector:
matchLabels:
app: myapp
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 更新中に最大 1つ余分に起動してよい
maxUnavailable: 0 # 更新中に停止する Pod は 0(ダウンタイムなし)
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.2.0
ports:
- containerPort: 3000
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health
port: 3000
failureThreshold: 3
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
failureThreshold: 3
periodSeconds: 5
Service
Pod へのネットワークアクセスを提供する
Pod は再起動のたびに IP が変わるため Service を経由してアクセスする
# ClusterIP(クラスター内部からのみアクセス)
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: production
spec:
type: ClusterIP
selector:
app: myapp # このラベルを持つ Pod に転送する
ports:
- port: 80 # Service のポート
targetPort: 3000 # Pod のポート
---
# LoadBalancer(外部からアクセス・クラウドの LB を自動作成)
apiVersion: v1
kind: Service
metadata:
name: myapp-lb
spec:
type: LoadBalancer
selector:
app: myapp
ports:
- port: 80
targetPort: 3000
Ingress
HTTP/HTTPS のルーティングを定義する
ホスト名・パスに応じて複数の Service に振り分ける
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod # TLS 自動発行
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
tls:
- hosts:
- app.example.com
secretName: myapp-tls
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
ConfigMap
機密でない設定値を管理する
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
APP_ENV: "production"
LOG_LEVEL: "info"
REDIS_HOST: "redis-service"
Secret
機密情報を管理する(Base64 エンコード)
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
type: Opaque
stringData: # stringData は自動で Base64 エンコードされる
DATABASE_URL: "postgresql://user:pass@db-host:5432/myapp"
JWT_SECRET: "your-secret-key"
# ※ 実際には外部シークレット管理(AWS Secrets Manager 等)との連携を推奨
HPA(Horizontal Pod Autoscaler)
負荷に応じて Pod 数を自動でスケールする
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp-deployment
minReplicas: 2 # 最小 Pod 数
maxReplicas: 20 # 最大 Pod 数
metrics:
- type: Resource
resource:
name: cpu
target:
type: AverageUtilization
averageUtilization: 70 # CPU 使用率が 70% を超えたらスケールアウト
- type: Resource
resource:
name: memory
target:
type: AverageValue
averageValue: 400Mi # メモリ使用量が 400Mi を超えたらスケールアウト
基本コマンド
kubectlコマンドを使用する
# リソースの確認
kubectl get pods -n production # Pod 一覧
kubectl get deployments -n production # Deployment 一覧
kubectl get services -n production # Service 一覧
kubectl get all -n production # 全リソース
# 詳細確認
kubectl describe pod myapp-pod-xxxxx -n production # Pod の詳細
kubectl logs myapp-pod-xxxxx -n production # ログの確認
kubectl logs -f myapp-pod-xxxxx # ログをリアルタイムで追う
# デプロイ操作
kubectl apply -f deployment.yaml # マニフェストを適用する
kubectl rollout status deployment/myapp # デプロイの進捗確認
kubectl rollout history deployment/myapp # デプロイ履歴
kubectl rollout undo deployment/myapp # 1つ前のバージョンに戻す
# スケール操作
kubectl scale deployment myapp --replicas=5 # Pod 数を 5 に変更する
# デバッグ
kubectl exec -it myapp-pod-xxxxx -- /bin/sh # Pod の中に入る
2つの違い・役割分担
Terraform の役割
「インフラの箱」を作る
- VPC・サブネット・セキュリティグループ
- EKS クラスター本体(ノードグループ・IAM)
- RDS・ElastiCache・S3
- Route53・ACM(TLS 証明書)
- CloudFront・WAF
- ECR(コンテナイメージレジストリ)
Kubernetes の役割
箱の中でアプリを”安定/持続的”に動かす
- アプリのデプロイ(Deployment)
- ネットワーク(Service・Ingress)
- 設定・シークレット(ConfigMap・Secret)
- 自動スケール(HPA)
- バッチ処理(Job・CronJob)
- Pod のヘルスチェック・自己修復
併用例
① Terraform で EKS クラスターを作成する
→ VPC・サブネット・EKS・ノードグループ・IAM を定義
② Terraform で周辺インフラを作成する
→ RDS・ElastiCache・S3・ALB・Route53
③ Terraform でアプリが使う IAM ロールを作成する
→ IRSA(IAM Roles for Service Accounts)
④ Kubernetes でアプリをデプロイする
→ Deployment・Service・Ingress・HPA を apply
⑤ CI/CD で自動化する
→ インフラ変更:terraform plan → apply
→ アプリデプロイ:docker build → push → kubectl apply
選定基準
Terraform を使うタイミング
- 新しいクラウドインフラを構築するとき
- 既存の手動構築環境を IaC に移行するとき
- 本番と同じ構成のステージング環境を作るとき
- マルチクラウド・マルチアカウント管理が必要なとき
- インフラの変更履歴を Git で管理したいとき
Kubernetes を使うタイミング
- 複数のマイクロサービスを運用するとき
- 自動スケールアウトが必要なとき
- ダウンタイムなしのデプロイが必要なとき
- 複数チームが独立したデプロイを行うとき
- バッチ処理をコンテナで定期実行したいとき
注意点
Terraform
tfstateの破損・競合
- 複数人が同時に
applyするとステートが競合する - DynamoDB でのステートロックを設定する
- CI/CD 経由でのみ
applyを実行するルールにする
本番環境への誤destroy
destroyは全リソースを削除する- 本番環境では
deletion_protectionを有効化する - CI/CD の権限で
destroyを実行できないようにする planの確認を必須化する
シークレットの管理
- AWS Secrets Manager / Vault から動的に取得する
tfstateのバケットを暗号化・アクセス制限する
ドリフト(実態とコードのズレ)
- 手動でコンソールからリソースを変更すると
tfstateと実態がズレる - コンソールでの手動変更を禁止するルールを作る
planを定期実行してドリフトを検知する
Kubernetes
リソース制限の未設定
resources.requests/limitsを設定しないと、1つの Pod がノードのリソースを使い切ってしまう- 必ず
requests/limitsを設定する LimitRangeでネームスペース単位のデフォルト値を設定する
ヘルスチェックの未設定
livenessProbe/readinessProbeを設定しないと、起動中の Pod にトラフィックが流れてエラーになる- 必ず両方設定する
/health(生存確認)と/ready(準備確認)は別エンドポイントにする
Secrets の管理
applyでシークレットを YAML ファイルで管理すると、Base64 エンコードされただけで Git に上がってしまう- External Secrets Operator + AWS Secrets Manager を使う
- Sealed Secrets で暗号化して Git に保存する
1つの Namespace に全リソースを詰め込む
- default namespace に全リソースを入れると管理が困難になる
- サービス・環境ごとに namespace を分ける
- 例:production / staging / monitoring
PodDisruptionBudget の未設定
- ノードのメンテナンス時に Pod が全台同時に落ちる可能性がある
PodDisruptionBudgetを設定して同時停止 Pod 数を制限する- https://kubernetes.io/docs/tasks/run-application/configure-pdb/
まとめ
| Terraform | Kubernetes | |
|---|---|---|
| 役割 | インフラ管理 | コンテナ管理 |
| 言語 | HCL | YAML |
| 操作対象 | クラウドリソース全般 | コンテナ・Pod・Service 等 |
| 状態管理 | tfstate | etcd |
参考
- Terraform:https://developer.hashicorp.com/terraform/docs
- Kubernetes:https://kubernetes.io/docs/home/
- EKS Best Practices:https://aws.github.io/aws-eks-best-practices/
- AWS Terraform と Kubernetes の違い:https://aws.amazon.com/jp/compare/the-difference-between-terraform-and-kubernetes/