4. 基础设施即代码入门
在 Confluent DataOps 的持续交付链路中,基础设施即代码(Infrastructure as Code,IaC)解决的是“环境与资源如何被可重复地创建、变更和销毁”的问题。Confluent Cloud 上的环境(Environment)、集群(Cluster)、服务账号(Service Account)、ACL、Topic 与 Schema 都是可被 API 管理的资源,Terraform 通过 Provider 把这些资源抽象为声明式配置:你描述“最终状态”,Terraform 负责计算差异并调用 Confluent Cloud API 完成收敛。
理解 Terraform 管理 Confluent Cloud 的关键,是分清两类对象:控制面资源(环境、集群、服务账号、角色绑定、ACL)和数据面资源(Topic、Schema、Schema 兼容性配置)。控制面资源决定“谁能在哪个集群上做什么”,数据面资源决定“数据以什么结构流动”。二者在 Terraform 中都属于同一份状态文件(state),但生命周期与变更风险不同:改 ACL 可能立即影响生产读写,改 Topic 分区数通常不可逆,改 Schema 兼容性可能阻断生产者。
terraform plan 的输出逐条确认。Provider 与认证:先让 Terraform 能“说话”
Confluent Cloud 的 Terraform Provider 需要两类凭据:Cloud API Key(管理组织级资源,如环境、集群、服务账号)和集群 API Key(管理集群内资源,如 Topic、ACL)。推荐做法是把 Cloud API Key 通过环境变量注入,而不是写进代码:
export CONFLUENT_CLOUD_API_KEY="CCLOUD_API_KEY_示例"
export CONFLUENT_CLOUD_API_SECRET="CCLOUD_API_SECRET_示例"
# provider.tf
terraform {
required_providers {
confluent = {
source = "confluentinc/confluent"
version = "~> 2.0"
}
}
}
provider "confluent" {
# 从环境变量读取,避免密钥入库
}
这里的环境变量名是示例约定,实际以 Provider 文档为准。核心原则是:密钥不进入 Git,不进入 state 的明文可读字段。state 中会保存资源属性,因此远程后端(如 S3 + DynamoDB 锁)应开启加密与访问控制。
用声明式配置描述一条最小可用链路
下面是一段教学示例,展示从环境到 Topic 的声明式定义。参数值均为示例,实际使用时需替换为你的组织 ID、区域与命名规范。
resource "confluent_environment" "dev" {
display_name = "dataops-dev"
}
resource "confluent_kafka_cluster" "standard" {
display_name = "dataops-dev-kafka"
availability = "SINGLE_ZONE"
cloud = "AWS"
region = "us-east-1"
standard {}
environment {
id = confluent_environment.dev.id
}
}
resource "confluent_service_account" "app_producer" {
display_name = "svc-order-producer"
description = "订单事件生产者"
}
resource "confluent_kafka_topic" "orders" {
kafka_cluster {
id = confluent_kafka_cluster.standard.id
}
topic_name = "orders.created.v1"
partitions_count = 6
rest_endpoint = confluent_kafka_cluster.standard.rest_endpoint
credentials {
key = var.cluster_api_key
secret = var.cluster_api_secret
}
}
这段配置体现了 Terraform 的依赖推导:confluent_kafka_cluster.standard.id 被 Topic 引用,Terraform 会先建集群再建 Topic。若把 partitions_count 从 6 改为 12,plan 会显示原地更新;若改为 3,Confluent Cloud 不允许缩减分区,plan 阶段或 apply 阶段会报错——这正是“声明式也要懂约束”的典型场景。
ACL 与 Schema:把治理也纳入版本控制
服务账号创建后本身没有权限,需要 ACL 或 RBAC 角色绑定。以 ACL 为例,生产者需要 WRITE 与 DESCRIBE,消费者需要 READ 与 DESCRIBE。用 Terraform 表达时,通常按“主体 + 资源 + 操作”三元组逐条声明:
resource "confluent_kafka_acl" "producer_write" {
kafka_cluster { id = confluent_kafka_cluster.standard.id }
resource_type = "TOPIC"
resource_name = "orders.created.v1"
pattern_type = "LITERAL"
principal = "User:${confluent_service_account.app_producer.id}"
host = "*"
operation = "WRITE"
permission = "ALLOW"
rest_endpoint = confluent_kafka_cluster.standard.rest_endpoint
credentials {
key = var.cluster_api_key
secret = var.cluster_api_secret
}
}
Schema 侧则通过 Schema Registry 资源声明 subject 与兼容性。例如为 orders.created.v1-value 注册 Avro Schema,并设置 BACKWARD 兼容性。这样,Schema 变更也走 MR/PR 评审,而不是在控制台里“手改一下”。
工作流:plan 是评审对象,apply 是执行动作
把上述资源放进同一目录后,标准工作流是 terraform init → terraform plan -out=tfplan → 人工评审 → terraform apply tfplan。在 CI 中,plan 输出可作为 MR 评论附件,让 Reviewer 看到“将新增 1 个 Topic、修改 2 条 ACL、无销毁”。若 plan 出现 destroy,必须显式确认,必要时用 prevent_destroy = true 保护集群与关键 Topic。
目录与工作区:让 dev / staging / prod 不互相污染
常见做法是按环境拆分目录,例如 envs/dev、envs/prod,各自引用 modules/confluent-kafka 模块。模块内定义变量 environment_name、cluster_name、topic_specs,环境目录只传参。这样 dev 与 prod 的差异被收敛到变量文件,而不是复制粘贴两份资源定义。若使用 Terraform Workspace,则要确保 state 隔离,避免误在 prod 工作区执行 dev 的 plan。
实操检查清单
| 检查项 | 判断依据 | 不通过时的处理 |
|---|---|---|
| 密钥是否入库 | Git 历史中无 API Key/Secret 明文 | 轮换密钥,改用环境变量或 Vault |
| plan 是否含 destroy | plan 输出中无 -/+ destroy 关键资源 | 加 prevent_destroy 或拆分变更 |
| Topic 分区是否可逆 | 新分区数 ≥ 当前值 | 不可缩减,需新建 Topic 迁移 |
| ACL 是否最小权限 | principal 仅绑定所需 operation | 移除通配 * 资源名 |
| Schema 兼容性 | 与现有消费者兼容策略一致 | 先评估再改,避免阻断生产 |
把上述流程固化到 CI 后,Confluent Cloud 的资源变更就具备了可审计、可回滚、可复现的基础。下一步才是把 Terraform 与 CI/CD 流水线结合,让 plan 成为合并请求的必过门禁。