4、Terraform模块化设计SLI配置:创建可复用的SLI模块、输入变量与输出值设计、模块版本管理
在SLI配置管理中,直接编写扁平化的Terraform配置会导致大量重复代码、维护困难且难以统一口径。模块化设计是解决这些问题的核心手段。本章将深入讲解如何将SLI配置抽象为可复用的Terraform模块,涵盖模块结构设计、输入输出变量规范以及版本管理策略。
4.1 SLI模块化设计原则
SLI模块的核心目标是封装“指标口径定义”与“告警规则绑定”的逻辑,使其在不同服务、不同环境间可复用。设计时应遵循以下原则:
- 单一职责:一个模块只负责一种SLI类型(如延迟、错误率、饱和度)的配置。
- 参数化口径:将SLI的阈值、窗口大小、聚合方式等作为输入变量暴露。
- 输出标准化:模块输出SLI的ID、URN或监控端点,供上层调用方消费。
- 无状态:模块不依赖外部状态文件,所有依赖通过变量传递。
最佳实践:将SLI模块与基础设施模块(如Kubernetes、数据库)解耦。SLI模块只关心“如何计算指标”,不关心“指标来自哪个具体资源”。
4.2 创建可复用的SLI模块
一个标准的SLI模块目录结构如下:
modules/
└── sli-http-latency/
├── main.tf # 核心资源定义
├── variables.tf # 输入变量声明
├── outputs.tf # 输出值定义
├── README.md # 模块使用文档
└── versions.tf # Terraform与Provider版本约束
main.tf 示例:定义一个基于Prometheus的HTTP延迟SLI:
# modules/sli-http-latency/main.tf
resource "prometheus_rule_group" "sli" {
name = var.rule_group_name
interval = var.evaluation_interval
rule {
record = "${var.sli_name}:latency:p99"
expr = <
4.3 输入变量与输出值设计
输入变量是模块的“接口”,必须清晰定义类型、描述与默认值。输出值则是模块的“契约”,供其他配置引用。
4.3.1 输入变量设计规范
| 变量名 | 类型 | 描述 | 是否必填 | 默认值示例 |
|---|---|---|---|---|
sli_name |
string |
SLI唯一标识名称 | 是 | api-checkout |
job_name |
string |
Prometheus job标签值 | 是 | checkout-service |
route_pattern |
string |
匹配的路由正则 | 否 | "/api/v1/checkout/.*" |
threshold_seconds |
number |
P99延迟告警阈值 | 是 | 0.5 |
window |
string |
指标聚合窗口 | 否 | "5m" |
severity |
string |
告警严重级别 | 否 | "critical" |
variables.tf 声明示例:
# modules/sli-http-latency/variables.tf
variable "sli_name" {
description = "Unique name for this SLI"
type = string
}
variable "job_name" {
description = "Prometheus job label to filter metrics"
type = string
}
variable "threshold_seconds" {
description = "P99 latency threshold in seconds"
type = number
validation {
condition = var.threshold_seconds > 0 && var.threshold_seconds <= 10
error_message = "Threshold must be between 0 and 10 seconds."
}
}
variable "window" {
description = "Evaluation window for rate calculation"
type = string
default = "5m"
}
variable "severity" {
description = "Alert severity level"
type = string
default = "warning"
validation {
condition = contains(["critical", "warning", "info"], var.severity)
error_message = "Severity must be one of: critical, warning, info."
}
}
4.3.2 输出值设计
输出值应包含模块创建的核心资源标识,便于上层模块或根配置进行关联:
# modules/sli-http-latency/outputs.tf
output "sli_rule_group_name" {
description = "Name of the created Prometheus rule group"
value = prometheus_rule_group.sli.name
}
output "sli_alert_name" {
description = "Name of the high latency alert rule"
value = "${var.sli_name}_HighLatency"
}
output "sli_metric_name" {
description = "Recorded SLI metric name"
value = "${var.sli_name}:latency:p99"
}
设计要点:输出值尽量使用
description字段说明用途,方便模块消费者通过terraform-docs自动生成文档。
4.4 模块版本管理
当SLI模块被多个团队或环境引用时,版本管理至关重要。Terraform Registry原生支持语义化版本控制(SemVer)。
4.4.1 版本约束策略
| 场景 | 版本约束写法 | 说明 |
|---|---|---|
| 锁定主版本 | version = "~> 1.0" |
允许1.x范围内的任何升级,禁止2.0 |
| 精确锁定 | version = "= 1.2.0" |
仅使用该精确版本,用于生产环境 |
| 允许补丁升级 | version = "~> 1.2.0" |
允许1.2.1、1.2.2,但不允许1.3.0 |
| 最低版本要求 | version = ">= 1.2.0, < 2.0.0" |
确保功能可用,同时避免大版本破坏 |
4.4.2 模块发布与版本标记
在Git仓库中管理模块代码,通过Git Tag进行版本标记:
# 标记版本 v1.2.0
git tag v1.2.0
git push origin v1.2.0
# 标记预发布版本
git tag v1.3.0-rc.1
git push origin v1.3.0-rc.1
在根配置中引用模块时,通过source参数指定版本:
# environments/production/main.tf
module "checkout_sli" {
source = "git::https://gitlab.example.com/sre/sli-modules.git//sli-http-latency?ref=v1.2.0"
sli_name = "checkout-api"
job_name = "checkout-service"
threshold_seconds = 0.5
severity = "critical"
}
注意:使用Git源时,
ref参数必须指向具体的Tag或Commit SHA,避免使用main或master分支,否则会导致不可控的配置漂移。
4.5 SLI模块调用关系图
下图展示了根配置如何通过模块化设计,将不同SLI口径组合到同一服务监控中:
4.6 模块版本生命周期管理
在实际团队协作中,SLI模块的版本演进应遵循清晰的流程:
- 开发阶段:在feature分支上修改模块代码,使用
ref=feature-branch进行测试。 - 预发布:合并到main分支后,打
v1.2.0-rc.1标签,集成到预发布环境验证。 - 正式发布:验证通过后,打
v1.2.0标签,更新生产环境配置。 - 废弃与迁移:当模块需要重大变更时,发布
v2.0.0,并在旧版本README中标注废弃说明。
CI/CD集成提示:在CI流水线中,可以自动检测模块目录的变更,当
modules/sli-*/有提交时,自动执行terraform validate并推送新版本Tag。这将在第7章详细展开。
4.7 本章小结
通过模块化设计,SLI配置从“硬编码的脚本”转变为“可组合的乐高积木”。关键收获包括:
- 使用
variables.tf定义清晰的口径参数,使用outputs.tf暴露标准化接口。 - 通过Git Tag和语义化版本控制,确保不同环境使用一致的模块版本。
- 模块的单一职责原则让每个SLI类型独立演进,降低变更风险。