🏗️
K8s・ECSのIaC管理(Terraform・Helm・GitOps)
マニフェスト直接管理・Helm・Terraform・Kustomize・GitOpsの全体像と使い分け。TerraformによるECS/K8sリソース管理の実例付き。
IaC全体像:何を使って何を管理するか
┌─────────────────────────────────────────────────────────────────┐
│ インフラ層(VPC / EKS Cluster / RDS / IAM) │
│ → Terraform が担当 │
├─────────────────────────────────────────────────────────────────┤
│ K8sリソース層(Deployment / Service / Ingress) │
│ → Helm / Kustomize / kubectl apply が担当 │
├─────────────────────────────────────────────────────────────────┤
│ 継続的デリバリー層(誰がいつ apply するか) │
│ → GitOps (ArgoCD / Flux) が担当 │
└─────────────────────────────────────────────────────────────────┘
※ Terraform の kubernetes/helm provider を使えば K8s リソースも
Terraform で管理できるが、後述のトレードオフがある
Terraform で ECS を管理する
基本構成
# クラスター
resource "aws_ecs_cluster" "main" {
name = "production"
setting {
name = "containerInsights"
value = "enabled" # CloudWatch Container Insights
}
}
# タスク定義(コンテナの設計図)
resource "aws_ecs_task_definition" "app" {
family = "my-app"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = "512" # 0.5 vCPU
memory = "1024" # 1 GB
# タスクが AWS API を呼ぶための IAM ロール
task_role_arn = aws_iam_role.task_role.arn
# ECS エージェントがイメージをプルするための IAM ロール
execution_role_arn = aws_iam_role.execution_role.arn
container_definitions = jsonencode([
{
name = "app"
image = "${aws_ecr_repository.app.repository_url}:${var.image_tag}"
essential = true
portMappings = [
{ containerPort = 8080, protocol = "tcp" }
]
environment = [
{ name = "ENV", value = "production" }
]
# Secrets Manager からシークレットを注入
secrets = [
{
name = "DB_PASSWORD"
valueFrom = "arn:aws:secretsmanager:ap-northeast-1:123456789:secret:db-password"
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = "/ecs/my-app"
"awslogs-region" = "ap-northeast-1"
"awslogs-stream-prefix" = "ecs"
}
}
healthCheck = {
command = ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval = 30
timeout = 5
retries = 3
startPeriod = 60
}
}
])
}
# サービス(タスクを何台動かすか・どうアップデートするか)
resource "aws_ecs_service" "app" {
name = "my-app"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = 3
launch_type = "FARGATE"
network_configuration {
subnets = var.private_subnet_ids
security_groups = [aws_security_group.app.id]
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.app.arn
container_name = "app"
container_port = 8080
}
deployment_circuit_breaker {
enable = true # デプロイ失敗時に自動ロールバック
rollback = true
}
lifecycle {
ignore_changes = [desired_count] # オートスケールで変更される値は無視
}
}
Terraform で K8s リソースを管理する
kubernetes provider
terraform {
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.0"
}
}
}
provider "kubernetes" {
host = module.eks.cluster_endpoint
cluster_ca_certificate = base64decode(module.eks.cluster_ca_certificate)
token = data.aws_eks_cluster_auth.main.token
}
# Namespace
resource "kubernetes_namespace" "production" {
metadata {
name = "production"
labels = {
environment = "production"
}
}
}
# Deployment
resource "kubernetes_deployment" "app" {
metadata {
name = "my-app"
namespace = kubernetes_namespace.production.metadata[0].name
}
spec {
replicas = 3
selector {
match_labels = { app = "my-app" }
}
template {
metadata {
labels = { app = "my-app" }
}
spec {
container {
name = "app"
image = "my-app:1.2.0"
port { container_port = 8080 }
resources {
requests = { cpu = "250m", memory = "256Mi" }
limits = { cpu = "500m", memory = "512Mi" }
}
}
}
}
}
}
kubernetes provider のトレードオフ
利点:
✓ インフラ(EKSクラスター)と K8sリソースを同じ tfstate で管理
✓ Terraform の変数・モジュール機能を使える
✓ CI/CD パイプラインを1本化できる
欠点:
✗ K8s のローリングアップデートが遅い(Terraform は変更を待つ)
✗ kubectl apply の柔軟さがない(helm rollback 相当がない)
✗ 複雑なK8sリソース(CRD等)の表現が難しい
✗ Terraform の state と K8s の実態がずれやすい
推奨:
VPC / EKSクラスター / IAM / RDS → Terraform
Deployment / Service / Ingress → Helm または Kustomize + GitOps
Helm:K8sのパッケージマネージャー
Chart・Values・Release の構造
my-app/ ← Chart ディレクトリ
├── Chart.yaml ← Chartのメタ情報(name, version, description)
├── values.yaml ← デフォルト値
├── values-prod.yaml ← 本番環境の上書き値(任意)
└── templates/ ← テンプレートディレクトリ
├── deployment.yaml
├── service.yaml
├── ingress.yaml
├── configmap.yaml
└── _helpers.tpl ← 共通のテンプレート関数定義
values.yaml と templates の関係
# values.yaml
replicaCount: 3
image:
repository: my-app
tag: "1.2.0"
resources:
requests:
cpu: 250m
memory: 256Mi
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
spec:
replicas: {{ .Values.replicaCount }} # values から参照
template:
spec:
containers:
- image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
resources:
requests:
cpu: {{ .Values.resources.requests.cpu }}
Helm コマンド
# 生成されるマニフェストを確認(apply前の確認に必須)
helm template my-release ./my-app -f values-prod.yaml
# インストール
helm install my-release ./my-app \
-n production \
-f values-prod.yaml \
--set image.tag=1.3.0 # コマンドラインで値を上書き
# アップグレード(リリースが存在しなければインストール)
helm upgrade --install my-release ./my-app \
-n production \
-f values-prod.yaml \
--set image.tag=1.3.0
# ロールバック
helm rollback my-release 2 # リビジョン2に戻す
helm history my-release # リビジョン履歴を確認
# リリース一覧
helm list -n production
# Artifact Hub からパブリックチャートをインストール
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx
Terraform の helm provider
provider "helm" {
kubernetes {
host = module.eks.cluster_endpoint
token = data.aws_eks_cluster_auth.main.token
cluster_ca_certificate = base64decode(module.eks.cluster_ca_certificate)
}
}
# Helm Release を Terraform で管理
resource "helm_release" "ingress_nginx" {
name = "ingress-nginx"
repository = "https://kubernetes.github.io/ingress-nginx"
chart = "ingress-nginx"
version = "4.10.0"
namespace = "ingress-nginx"
create_namespace = true
set {
name = "controller.replicaCount"
value = "2"
}
}
Kustomize:YAMLの差分管理
Helm のようなテンプレートエンジンを使わず、パッチ(差分) で環境差異を管理する。
k8s/
├── base/ ← 共通のマニフェスト
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ └── service.yaml
└── overlays/
├── staging/ ← ステージング環境の差分
│ ├── kustomization.yaml
│ └── replica-patch.yaml
└── production/ ← 本番環境の差分
├── kustomization.yaml
└── resource-patch.yaml
# base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
# overlays/production/kustomization.yaml
bases:
- ../../base
patchesStrategicMerge:
- resource-patch.yaml
images:
- name: my-app
newTag: "1.3.0" # イメージタグだけ上書き
# overlays/production/resource-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 5 # 本番は5台に上書き
template:
spec:
containers:
- name: app
resources:
requests:
cpu: "500m" # 本番はリソースを多く
# 生成されるマニフェストを確認
kubectl kustomize overlays/production/
# 適用
kubectl apply -k overlays/production/
GitOps:ArgoCD / Flux
「Gitリポジトリの状態 = クラスターの状態」を維持する仕組み。
【従来の Push 型CI/CD】
開発者 → push → CI/CD Pipeline → kubectl apply → K8s Cluster
↑ CI/CDがクラスターへの書き込み権限を持つ(セキュリティリスク)
【GitOps(Pull 型)】
開発者 → push → Git Repository
↑ ArgoCD/Flux がリポジトリを監視
K8s Cluster ← ArgoCD/Flux が変更を検知して apply
↑ クラスター側が引っ張る。CI/CDはクラスターに触らない
ArgoCD の概念
Application(ArgoCDのリソース):
source: Gitリポジトリのパス(マニフェスト or Helm Chart)
destination: 適用先のクラスター・Namespace
syncPolicy: 自動同期するかどうか
状態:
Synced = GitとClusterが一致している
OutOfSync = GitとClusterが乖離している(手動変更された等)
Healthy = 全リソースが正常動作中
Degraded = 一部リソースが異常
# Application マニフェスト
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/my-org/k8s-manifests
targetRevision: main
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Git から消えたリソースを自動削除
selfHeal: true # 手動変更を自動で元に戻す
syncOptions:
- CreateNamespace=true
選択基準:何をいつ使うか
┌──────────────────────────────────────────────────────────────────┐
│ インフラ(VPC/クラスター/DB/IAM) │
│ → Terraform(状態管理・依存解決が必要なリソース) │
├──────────────────────────────────────────────────────────────────┤
│ サードパーティの K8s アドオン(ingress-nginx, cert-manager等) │
│ → Helm(公式Chartが配布されているため) │
│ → Terraform helm_release でバージョン固定管理も可 │
├──────────────────────────────────────────────────────────────────┤
│ 自社アプリの K8s マニフェスト │
│ 環境差分が少ない → kubectl apply / Kustomize │
│ テンプレートが必要 → Helm(独自Chart) │
│ 継続的デリバリー → GitOps(ArgoCD/Flux)と組み合わせる │
└──────────────────────────────────────────────────────────────────┘
組み合わせの王道パターン:
Terraform クラスター・RDS・IAM を管理
Helm ingress-nginx, cert-manager, prometheus-stack を管理
Kustomize 自社アプリのマニフェストを環境別に管理
ArgoCD Git push → 自動デプロイのパイプラインを担う
アンチパターン
✗ Terraform で全 K8sリソースを管理する
→ terraform apply のたびに Deployment のローリングアップデートを待つ
→ K8sリソースは kubectl / helm / argocd に任せる
✗ kubectl apply を手動でしか行わない
→ 環境間の差分が属人化・ドリフトが発生
→ Git管理 + GitOps で宣言的に管理する
✗ Helm values.yaml に直接シークレットを書く
→ Gitにシークレットが残る
→ SOPS + Helm Secrets / External Secrets Operator を使う - 1. 📡TCP/IPパケット通信の全過程
- 2. 🔌物理層・データリンク層詳解
- 3. 🌐ネットワーク層(IP)詳解
- 4. 🔄TCPの全機能詳解
- 5. ⚡UDP詳解とユースケース
- 6. 🌍アプリケーション層詳解(DNS・HTTP・TLS)
- 7. 🔧ソケットAPIとI/Oモデル詳解
- 8. ☁️コンテナ・クラウドネットワーキング詳解
- 9. ⚖️ロードバランサー・リバースプロキシ詳解
- 10. 🕸️Kubernetesネットワーク全体図
- 11. ⚔️K8s vs ECS:設計思想とアナロジーで理解する
- 12. 📋Kubernetesマニフェスト読解ガイド
- 13. 🏗️K8s・ECSのIaC管理(Terraform・Helm・GitOps)