Kubernetesの運用では、既存のYAML資産とTerraformのHCLによる一貫管理の両立がよく課題になります。本稿では、Terraform Kubernetes ProviderにおけるYAMLとの関係、実務での制限、そして試験で問われやすい判断ポイントをまとめます。
前提として、TerraformはKubernetes APIに対しCRUDを行うツールであり、kubectl applyの挙動(特にサーバーサイド適用のフィールド所有や競合解決)と同一ではありません。公式の安定仕様と一般的なパターンを中心に、バージョン差異が影響しやすい点は注意書きに留めます。
TerraformはHCLで記述したリソースをプロバイダ経由でKubernetes APIに反映します。一方、kube yamlはkubectl等のクライアントから適用されます。HelmはYAMLテンプレートをレンダリングし、最終的にはKubernetes APIにYAML(もしくは同等のJSON)を流し込みます。
つまり、最終的に到達する先は同じでも、経路と状態管理の思想が異なります。Terraformは状態管理と計画(plan)に強みがあり、kubectl/HelmはKubernetesの文脈での適用柔軟性に強みがあります。
Kubernetesへの適用経路(概念)
最小構成のKubernetesプロバイダ(kubeconfigを利用)
terraform {
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = ">= 2.0"
}
}
}
provider "kubernetes" {
config_path = var.kubeconfig_path
config_context = var.kube_context
}
variable "kubeconfig_path" { type = string }
variable "kube_context" { type = string }Kubernetes Providerの型付きリソース(例: kubernetes_deployment_v1, kubernetes_service_v1)は、スキーマに沿った事前検証が効き、plan時点での差分可視化が明瞭です。Terraformの参照やfor_eachとの相性も良く、資格試験でも基本パターンとして押さえるべきです。
一方で、すべてのKubernetes APIやCRDが型付きでカバーされるわけではありません。Admission/Mutating Webhookやコントローラによるサーバー側のデフォルト付与・書き換えにより、Terraformが意図しない差分を検出する場合があります。特に、Podテンプレートの注釈やサイドカー自動注入などはplanで毎回差分になりがちです。その場合は、Terraformのlifecycle.ignore_changesで特定フィールドを無視する運用が一般的です。
DeploymentをHCLで管理し、注釈差分を抑制する例
resource "kubernetes_deployment_v1" "web" {
metadata {
name = "web"
namespace = "default"
labels = { app = "web" }
# Admissionやコントローラで変わりやすい注釈は無視
annotations = { managed-by = "terraform" }
}
spec {
replicas = 2
selector { match_labels = { app = "web" } }
template {
metadata {
labels = { app = "web" }
annotations = { sidecar.istio.io/inject = "false" }
}
spec {
container {
name = "nginx"
image = "nginx:1.25"
port { container_port = 80 }
}
}
}
}
lifecycle {
ignore_changes = [
metadata[0].annotations,
spec[0].template[0].metadata[0].annotations
]
}
}既存のYAMLをそのまま適用したい場合、Terraformのyamldecode関数でHCLのオブジェクトに変換し、汎用マニフェスト系のリソース(例: kubernetes_manifest など、提供可否や名称はプロバイダのバージョンに依存)に渡す方法があります。この方法はCRDや型未対応のAPIにも適用できるため、現実的な落としどころです。
注意点として、YAMLのマルチドキュメントを一括適用する際は分割し、for_eachで安定キー(kind/name/namespace)を付けて管理します。また、CRDとCRは明示的に順序制御(depends_on)してください。
YAMLを読み込み、汎用マニフェストに渡す(単一ドキュメント)
locals {
deploy = yamldecode(file("${path.module}/manifests/deploy.yaml"))
}
# プロバイダが提供する汎用マニフェスト系リソース名はバージョンで異なる場合があります
resource "kubernetes_manifest" "deploy" {
manifest = local.deploy
}
Helm Providerのhelm_releaseは、Chartの配布物をTerraformのライフサイクルで管理します。values.yamlはそのまま文字列として渡すか、HCLからyamlencodeで生成して渡せます。ChartがCRDを含む場合、CR適用の前にCRDが存在するよう順序を設計します。
helm_releaseはKubernetesオブジェクトを個別リソースとしてTerraform状態に保持するのではなく、リリース単位での管理になります。個々のオブジェクト単位の詳細な差分管理が必要なら、Helmとマニフェスト系リソースを役割分担する設計が適しています。
helm_releaseでChartを適用し、values.yamlを渡す
terraform {
required_providers {
helm = {
source = "hashicorp/helm"
version = ">= 2.9"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = ">= 2.0"
}
}
}
provider "helm" {
kubernetes {
config_path = var.kubeconfig_path
config_context = var.kube_context
}
}
resource "helm_release" "nginx" {
name = "nginx"
repository = "https://charts.bitnami.com/bitnami"
chart = "nginx"
namespace = "web"
create_namespace = true
values = [file("${path.module}/values.yaml")]
}
Kubernetes APIサーバーや各種コントローラは、受け取ったオブジェクトにデフォルト値を付与したり、注釈やフィールドを書き換えます。kubectl apply(特にサーバーサイド適用)とTerraformの適用はフィールド所有や競合解決の挙動が異なるため、混在運用はドリフトの原因になります。基本はどちらかに統一し、やむを得ず併用する場合も責務分離を明確にしてください。
CRDとCRの整合性、イベントチュアルコンシステンシ(適用直後に参照すると未反映のことがある)、リスト順序や生成フィールドの差分は、Terraformのdepends_on、time_sleep(必要な場合)、ignore_changes、for_eachの安定キー化などで抑制します。
CRD→CRの順序保証(HelmでCRD、YAMLでCRを適用)
locals {
cr_files = fileset("${path.module}/crs", "*.yaml")
cr_objs = [for f in local.cr_files : yamldecode(file("${path.module}/crs/${f}"))]
cr_map = { for m in local.cr_objs : "${m["kind"]}/${m["metadata"]["namespace"]}/${m["metadata"]["name"]}" => m }
}
# 例: CRDを含むChart
resource "helm_release" "crds" {
name = "operator-crds"
repository = var.operator_repo
chart = var.operator_crd_chart
namespace = "operators"
}
# CRをYAMLから適用(汎用マニフェスト系リソース想定)
resource "kubernetes_manifest" "cr" {
for_each = local.cr_map
manifest = each.value
depends_on = [helm_release.crds]
}
variable "operator_repo" { type = string }
variable "operator_crd_chart" { type = string }Associate/Pro試験では、HCLでの型付き管理・YAML取り込み・Helmの使い分け、そしてドリフト抑制の判断が頻出です。実務でも、CRDや既存YAML資産との共存、Secretの取り扱い、順序制御が成功の鍵になります。
以下の比較表で、アプローチごとの特徴を整理します。
| アプローチ | YAMLの扱い | 適合ユースケース | 主な制限 |
|---|---|---|---|
| Kubernetes型付きリソース(HCL) | 不要(HCLで宣言) | Deployment/Serviceなど主要リソースを厳密に管理 | CRDや未対応APIは対象外。Webhook変異で差分が出やすい |
| 汎用マニフェスト適用(yamldecode + manifest系) | YAMLをそのまま適用(HCLにデコード) | CRD/CRや型未対応API、既存YAML資産の流用 | プロバイダ機能・バージョン差異に注意。フィールド単位の厳密検証は弱め |
| Helm(helm_release) | values.yamlでテンプレートへ注入 | 複数オブジェクトをChartとして一括配布・更新 | 状態はリリース単位。オブジェクト単位の微細制御は弱い |
| kubectlをlocal-execで実行(参考) | YAMLを直接kubectl apply | 最小限の移行や一時的対応 | Terraformの計画・状態管理と乖離。再現性・ドリフトに弱い |
マルチドキュメントYAMLを分割してfor_each適用するスニペット
locals {
# --- 区切りで分割(改行を含む区切りを考慮)。空要素は除外
raw = file("${path.module}/bundle.yaml")
docs = [for d in split("\n---\n", local.raw) : d if trimspace(d) != ""]
objs = [for d in local.docs : yamldecode(d)]
mp = { for m in local.objs : "${m["kind"]}/${coalesce(try(m["metadata"]["namespace"], null), "default")}/${m["metadata"]["name"]}" => m }
}
resource "kubernetes_manifest" "multi" {
for_each = local.mp
manifest = each.value
}
Associate / Pro
問題 1
既存のYAMLで定義された複数のCustomResource(CR)をTerraformで管理したい。CRDはHelm Chartで提供され、まずCRDを適用し、その後CRを適用する必要がある。ドリフトを最小化し、計画可能性を確保するパターンとして最も適切なのはどれか。
正解: A
CRDはhelm_releaseで先に適用し、CRはYAMLをyamldecodeで読み込み汎用マニフェスト系リソースで管理、さらにdepends_onで順序を保証するのがTerraform的に計画可能でドリフトを抑えやすい。local-execでのkubectlは状態管理がなく不適。型付きへの無理なマッピングは非現実的。CRをvaluesに埋める方式は依存順制御や個別差分の可視性が弱い。
Terraformはkubectl apply(サーバーサイド適用)と同じ挙動ですか?
いいえ。TerraformはKubernetes APIに対してCRUDを行います。kubectlのサーバーサイド適用が持つフィールド所有や競合解決の挙動とは異なります。プロバイダの実装・バージョンによっては近いモードを提供する場合もありますが、前提にせず公式ドキュメントを確認してください。混在運用はドリフトの原因になります。
マルチドキュメントのYAML(---区切り)はTerraformでそのまま扱えますか?
そのまま1つのオブジェクトとしては扱えません。fileで読み込み、---で分割してからyamldecodeでオブジェクト化し、for_eachで個別に適用するのが一般的です。安定したキー(kind/namespace/name)を作ると計画と差分が安定します。
SecretをTerraformで管理しても安全ですか?
Terraformの状態ファイルにSecretの値が保存される可能性があります。Terraform Cloud/Enterpriseや暗号化されたリモートバックエンドの利用、またはVaultやExternal Secretsなど外部シークレット管理と組み合わせて、Terraform側には参照や束ねだけを任せる設計を推奨します。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Terraform HCL 構文の基礎:Block / Attribute / Expression を正しく使い分ける
Terraform Associate で頻出の HCL 構文を、ブロック・属性・式の3視点で整理。実務で迷いがちな書き...
Terraform Authoring & Ops Pro: 上位資格の範囲と対策
上位レベルを想定したTerraformの設計・運用ドメインを整理し、実務で通用する対策を提示。モジュール設計、ステート運...
Terraform Providers の基本: プラグイン型アーキテクチャを正しく使いこなす
Associate レベルで押さえるべき Provider の基礎、インストール、バージョニング、認証、エイリアス運用を...
Terraform Resourceブロック徹底ガイド: 最小単位のリソース定義
Associateレベルで押さえるべきResourceブロックの構造、依存関係、メタ引数、ライフサイクル制御を実務目線で...
Terraform Data Source徹底理解:既存リソースの参照で壊さず足す
Terraform Associate向けに、Data Sourceを用いた既存リソース参照の基本、選択基準、評価順序、...