在 OpenTofu 中使用 OneUptime Provider:引擎差异、配置实战与可复用监控模块
【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime
OneUptime 的官方 Terraform Provider 同时发布到 OpenTofu Registry 与 Terraform Registry,因此tofu与terraform可以使用完全相同的配置管理 OneUptime 的监控资源。本文以仓库文档 App/FeatureSet/Docs/Content/en/terraform/opentofu.md 为骨架,结合仓库内的可运行示例、CI 工作流与代码生成器源码,完整讲解在 OpenTofu 下声明 Provider、理解两引擎差异、选择版本、落地可复用监控模块以及离线镜像的全部要点,读完后可直接用tofu完成一次从 init 到 apply 的完整实践。
为什么 OpenTofu 是"受测试"的路径,而非"兼容假设"
很多 Terraform Provider 声称支持 OpenTofu,只是因为二者共享 HCL 语言与插件协议。OneUptime 的态度更进一步:OpenTofu 是被 CI 强制把关的受支持路径。
仓库中的端到端工作流 .github/workflows/terraform-provider-e2e.yml 在每次 Pull Request 和主分支推送时,会先后以TF_CLI: terraform与TF_CLI: tofu两次运行完全相同的 fixtures(见该工作流中Run Terraform E2E tests与Run OpenTofu E2E tests两个步骤)。工作流注释明确写道:"这是 OneUptime 对 OpenTofu 支持声明背后的门禁——Provider 发布到 OpenTofu Registry,所以tofu必须保持为受测试路径,而不是从 Terraform 兼容性继承来的假设。"一旦tofu下出现失败,构建即失败。
几个值得注意的实现细节:
- 工作流同时安装了 Terraform 1.9.8 与 OpenTofu 1.12.5 两个二进制,且刻意绕开官方 setup action(其包装脚本会缓冲 stdout,破坏
plan -detailed-exitcode的处理)。 - OpenTofu 测试复用 Terraform 那轮已经启动的 OneUptime 服务栈与测试项目,而非重新拉起整套基础设施;之所以安全,是因为每个测试都会销毁自己创建的资源,删除失败即判定构建失败。
- 套件末尾还有一道静态覆盖率门禁
coverage-report.sh,保证被测试的 Provider 资源类型数量不低于基线文件 E2E/Terraform/e2e-tests/scripts/coverage-baseline.txt。
对读者而言,这意味着"用tofu驱动 OneUptime Provider"不是一个碰运气的兼容动作,而是一个持续被端到端验证的官方路径。
使用 Provider 与 OpenTofu:配置不变,命令换成 tofu
在原文档给出的结论是:你的配置什么都不用改,按 Terraform 的方式声明 Provider,然后用tofu驱动即可。下面是文档中的最小声明(已按仓库 Examples/opentofu/quickstart/main.tf 的注释补全说明):
terraform { required_providers { oneuptime = { source = "oneuptime/oneuptime" version = "~> 11.0" } } } provider "oneuptime" { # api_key 从 ONEUPTIME_API_KEY 环境变量读取。 # oneuptime_url 默认为 https://oneuptime.com —— 仅自托管时才需要显式设置。 }命令行侧,把terraform换成tofu:
export ONEUPTIME_API_KEY="your-project-api-key" tofu init tofu plan tofu applyAPI Key 的硬性要求:必须是项目级 API Key
从 App/FeatureSet/Docs/Content/en/terraform/quick-start.md 可知,Provider 使用**项目作用域(project-scoped)**的 API Key 认证,需要在 OneUptime 控制台的Project Settings > API Keys中创建,并授予计划管理的每种资源类型的Create、Read、Update(Edit)、Delete权限。仓库示例 Examples/opentofu/README.md 特别警告:使用用户 Key 或自托管 Master Key 会以ProjectId required错误失败。
环境变量方式让配置可以在云端与自托管之间无缝迁移:
export ONEUPTIME_URL="https://oneuptime.example.com" export ONEUPTIME_API_KEY="your-project-api-key"为什么 source 地址不写 registry 主机名
source = "oneuptime/oneuptime"是一个不含主机名的裸地址。根据 OpenTofu 与 Terraform 各自的解析规则,它会被解析到各引擎的默认注册中心:
- OpenTofu →
registry.opentofu.org - Terraform →
registry.terraform.io
由于 Provider 同时发布到这两个注册中心,一行裸地址即可覆盖两个引擎。这一点在 Examples/opentofu/modules/monitoring-and-incident-response/versions.tf 的注释中被反复强调:"Do not hard-code a registry hostname here"(不要在这里硬编码注册中心主机名)。
反之,如果写成source = "registry.terraform.io/oneuptime/oneuptime",配置就被钉死在 Terraform Registry 上:在离线或注册中心受限的环境中,OpenTofu 将直接失败。原文档的结论很干脆:把主机名去掉。
仓库对这条约定有专门的自动化保障:契约测试 Scripts/TerraformProvider/Tests/OpenTofuContent.test.ts 扫描Examples/opentofu/下的全部.tf文件,断言其中不存在registry.terraform.io或registry.opentofu.org字样(正则REGISTRY_HOST),确保发布出去的 OpenTofu 内容不会意外带上主机名而破坏跨引擎可用性。该测试还校验source =行必须存在、api_key不得硬编码进文件(正则API_KEY_ASSIGNMENT要求.tf中不出现api_key =赋值),这些都在每次 PR 时静默守护着配置的可移植性。
值得知道的引擎差异
原文档用一张表总结了两个引擎之间真正有意义的差异,这里完整继承并补充实现层面的说明:
| 主题 | 行为 |
|---|---|
terraform块 | 保持叫terraform。它是语言关键字,不是对 Terraform CLI 的引用,OpenTofu 原样读取。quickstart 示例 Examples/opentofu/quickstart/main.tf 的注释也专门解释了这一点。 |
.tfvs.tofu文件 | .tf在两个引擎下都可用。OpenTofu 额外读取.tofu文件,并且会忽略任何存在.tofu同名兄弟文件的.tf文件——这是为"仅 OpenTofu 可见"的配置准备的逃生舱,代价是牺牲 Terraform 兼容性。 |
required_version | OpenTofu 的版本序列从 1.6.0 起步,所以为 Terraform 写的约束(如>= 1.5.0)会被每一个OpenTofu 版本满足。 |
| 锁文件 | 两个引擎都写.terraform.lock.hcl,但锁文件会记录它解析时使用的注册中心——一个引擎的锁文件无法满足另一个引擎。请提交 CI 实际使用的那份,切换引擎后运行tofu init -upgrade重新生成。 |
| 状态文件 | 格式与文件名完全一致,已有状态可在两引擎间免转换直接迁移。 |
| CLI 配置文件 | OpenTofu 读取~/.tofurc(回退到~/.terraformrc);两者都尊重TF_CLI_CONFIG_FILE环境变量。 |
| 变量 | OpenTofu 既读TF_VAR_*也读TOFU_VAR_*,已有工具链和 CI 无需改动。 |
值得补充的是锁文件细节:由于.terraform.lock.hcl内部记录了 provider 来源的注册中心标识,混用两引擎时最常见的报错就是锁文件校验失败。切换引擎后的标准动线是tofu init -upgrade(或terraform init -upgrade),让它重新解析约束并重写锁文件。
版本选择:云端与自托管的统一规则
Provider 版本与 OneUptime 平台版本一一对应(11.x 的 Provider 由 OneUptime 11.x 生成并测试),这个规则与引擎无关,详见 App/FeatureSet/Docs/Content/en/terraform/registry.md:
- OneUptime Cloud:
version = "~> 11.0"。云上始终运行最新平台,因此最新 Provider 总是正确。 - 自托管:选择小于或等于你的平台版本的最新已发布 Provider 版本。更详细的规则见 App/FeatureSet/Docs/Content/en/terraform/self-hosted.md:更"新"的 Provider 可能引用你旧平台尚不存在的 API 字段。
两条来自文档的硬性约束:
- 不要锁定精确的 patch 版本(如
= 11.0.7)——并非每个平台 patch 都会发布到注册中心,精确锁定极易遇到no matching version found。悲观约束~> 11.0总能解析到真实存在的发布版本。 - 自托管场景建议用有界约束表达"不超过平台版本"规则。例如平台运行 11.2.x 时:
version = ">= 11.0, <= 11.2"升级顺序也务必遵守:先升级 OneUptime 平台,再提高 Provider 约束并运行tofu init -upgrade。
这条版本规则同样适用于 OpenTofu Registry,因为两个注册中心服务的是同一批发布版本——原文档在版本选择一节末尾专门做了这个声明。
仓库内的可运行示例与可复用模块
原文档指向的示例全部存在于仓库的 Examples/opentofu/ 目录,共三个部分:
| 目录 | 内容 |
|---|---|
| Examples/opentofu/quickstart/ | 最小可用配置——一个标签、一个监控、一个状态页 |
| Examples/opentofu/monitoring-and-incident-response/ | 通过下方模块串联起来的两个服务 |
| Examples/opentofu/modules/monitoring-and-incident-response/ | 可复用模块:监控、值班轮换(on-call)、状态页 |
最小示例:一个标签 + 一个监控 + 一个状态页
quickstart/main.tf 是"最小有用配置"的完整形态,除了上一节展示的terraform块与provider块,还包含三个资源。其中监控部分展示了 OneUptime Provider 特色的嵌套monitor_steps结构(详细参考见 App/FeatureSet/Docs/Content/en/terraform/monitor-steps.md):
resource "oneuptime_label" "quickstart" { name = "opentofu-quickstart" description = "Created by the OneUptime OpenTofu quickstart." color = "#4287f5" } resource "oneuptime_monitor" "homepage" { name = "Homepage" description = "Checks that ${var.website_url} responds." monitor_type = "Website" monitoring_interval = "Every 5 minutes" labels = [oneuptime_label.quickstart.id] monitor_steps = [{ monitor_destination = var.website_url monitor_destination_type = "URL" request_type = "GET" criteria = [ { name = "Online" description = "Responds successfully." filter_condition = "All" filters = [ { check_on = "Is Online" filter_type = "True" } ] } ] }] } resource "oneuptime_status_page" "quickstart" { name = "OpenTofu Quickstart Status" description = "Status page created by the OneUptime OpenTofu quickstart." page_title = "Service Status" page_description = "Live status of our services." is_public_status_page = false enable_email_subscribers = false enable_sms_subscribers = false labels = [oneuptime_label.quickstart.id] }配套的 variables.tf 提供两个带默认值的变量:oneuptime_url(默认https://oneuptime.com,自托管时覆盖)与website_url(默认https://example.com,即监控探针的目标)。outputs.tf 导出monitor_id、monitor_slug、status_page_id、label_id四个输出,便于在 apply 后立即拿到资源标识。
运行方式(与 README 一致):
export ONEUPTIME_API_KEY="<project api key>" cd Examples/opentofu/quickstart tofu init tofu plan tofu apply可复用模块:monitoring-and-incident-response
模块的调用方式与原文档完全一致,source指向已发布的 Provider 仓库(而非主 monorepo),因此tofu init只克隆一个小仓库而不是整个代码库:
module "storefront" { source = "github.com/OneUptime/terraform-provider-oneuptime//modules/monitoring-and-incident-response?ref=v11.7.4" service_name = "storefront" status_page_is_public = true monitors = { homepage = { url = "https://example.com" } checkout = { url = "https://example.com/checkout" } api = { url = "https://api.example.com/health", expected_status_code = "204" } } }模块会为服务创建:一个标签、每个监控项一个 HTTP 监控、一个可选的带单条升级规则的值班策略(on-call policy),以及一个列出这些监控的可选状态页;监控掉线时翻转状态、按配置的严重级别开启事件并呼叫值班策略。这些资源在 module main.tf 中的实际构成如下:
- 查找而非创建项目内置的分类数据:
data "oneuptime_monitor_status" "operational"/"offline"与data "oneuptime_incident_severity" "incident"按名字查找。这是原文档强调的设计决策——OneUptime 按项目预置这些状态与严重级别,模块若每次实例化都创建一份,会在每个服务上产生重复集合。若项目重命名了它们,可通过operational_monitor_status_name、offline_monitor_status_name、incident_severity_name覆盖(默认值分别为Operational、Offline、Critical Incident,见 module variables.tf)。 - 每个监控的
monitor_steps包含 "Online" 与 "Offline" 两个 criteria,分别通过Is Online(True/False)与Response Status Code(Equal To / Not Equal To)两类过滤器判定,并使用数据源查到的状态 ID 执行状态切换。 - incidents 的"省略即关闭"约定:当
open_incident_on_down = false时,incidents字段被整体省略(置null)而不是设为[]——因为 API 会拒绝空的占位列表,"不存在"才是表达"未设置"的唯一方式。 - 状态页相关资源
oneuptime_status_page、oneuptime_status_page_group、oneuptime_status_page_resource通过for_each与本地变量status_page_monitors联动,只把声明了show_on_status_page = true的监控挂到状态页上。
模块变量表(全部来自 variables.tf):
| 变量 | 类型/默认值 | 说明 |
|---|---|---|
service_name | string(必填) | 服务名,用于命名标签、值班策略与状态页,含非空校验 |
monitors | map(必填) | 每个 HTTP 端点一条记录,键会进入 Terraform 地址(改名即替换监控),含 URL 前缀校验 |
monitoring_interval | "Every 1 minute" | 未单独指定间隔的监控的默认探针间隔 |
label_color | "#4287f5" | 标签颜色 |
operational_monitor_status_name | "Operational" | 健康状态名(按名查找) |
offline_monitor_status_name | "Offline" | 掉线状态名(按名查找) |
incident_severity_name | "Critical Incident" | 事件严重级别名(按名查找) |
create_on_call_policy | true | 是否创建带单条升级规则的值班策略 |
escalate_after_in_minutes | 5 | 事件未确认多少分钟后触发升级,含大于零校验 |
create_status_page | true | 是否创建状态页 |
status_page_is_public | false | 状态页是否公开 |
auto_resolve_incidents | true | 监控恢复时事件是否自动关闭 |
模块在 outputs.tf 中导出了label_id、monitor_ids、monitor_slugs、on_call_policy_id、escalation_rule_id、status_page_id、status_page_group_id。模块的 versions.tf 声明required_version = ">= 1.5.0"与source = "oneuptime/oneuptime"(无主机名)——这正是前文"一行地址覆盖两引擎"的实践样本。
两个服务如何通过模块串联
Examples/opentofu/monitoring-and-incident-response/main.tf 展示了模块的两种典型用法:
storefront(面向客户):公开状态页、5 分钟升级、三个监控(homepage / checkout / api,其中 api 期望204状态码);internal_tools(内部):create_status_page = false、create_on_call_policy = false、5 分钟探针间隔、紫色标签,ci监控显式open_incident_on_down = false。
同一个模块通过布尔开关即可在"完整告警链路"与"仅监控、不告警、不对外"之间切换,这是该模块设计上的核心可复用点。模块本身是引擎无关的纯 HCL,在 Terraform 下同样可用。
离线(Air-gapped)环境:用 tofu providers mirror 镜像 Provider
在无法访问公共注册中心的网络里,tofu providers mirror与terraform providers mirror行为一致,都是在内部镜像 Provider:
mkdir -p /srv/terraform-mirror cd /path/to/your/opentofu/config # 一个 required_providers 包含 oneuptime 的目录 tofu providers mirror /srv/terraform-mirror该命令会按你的版本约束把 Provider 发布版本下载到 Terraform/OpenTofu 能理解的目录布局中。随后把目录搬到内网(HTTPS 文件服务器或共享文件系统均可),并在 CLI 配置(~/.terraformrc或~/.tofurc)中指向它:
provider_installation { filesystem_mirror { path = "/srv/terraform-mirror" include = ["registry.terraform.io/oneuptime/oneuptime"] } direct { exclude = ["registry.terraform.io/oneuptime/oneuptime"] } }tofu init此时会从镜像安装 OneUptime Provider,其余 Provider 仍按默认方式获取(删除direct块可强制仅用镜像)。每次提高版本约束后记得重跑一次 mirror 命令。完整走读见 App/FeatureSet/Docs/Content/en/terraform/self-hosted.md,把其中的terraform换成tofu即可;由于tofu也会读取~/.terraformrc作为回退,镜像配置在 OpenTofu 下同样生效。
Provider 从哪来:OpenAPI 生成的构建产物
原文档在支持一节给出了 Provider 的来源:它由主仓库中的 OneUptime OpenAPI 规范生成,发布的 Provider 仓库只是只读的构建输出。这一点在仓库中有完整的生成器实现:
- 生成入口 Scripts/TerraformProvider/GenerateProvider.ts;
- 核心生成器目录 Scripts/TerraformProvider/Core/,其中 OpenAPIParser.ts 解析 OpenAPI 规范,ResourceGenerator.ts 与 DataSourceGenerator.ts 生成资源与数据源代码,DocumentationGenerator.ts 还会把
Examples/opentofu/modules/复制进已发布的 Provider 仓库,使模块源码与文档保持同步; - 发布脚本 Scripts/TerraformProvider/publish-terraform-provider.sh,以及本地安装脚本 Scripts/TerraformProvider/install-terraform-provider-locally.sh。
CI 工作流中的npm run generate-terraform-provider步骤(见 .github/workflows/terraform-provider-e2e.yml)会在每次测试前重新生成 Provider 并执行go vet与go test,随后才启动 OneUptime 服务栈跑双引擎 E2E——生成、单元测试、端到端测试形成一条完整链路。因此,你在 OpenTofu 或 Terraform 下遇到的任何 Provider 行为问题,都应反馈到主仓库的 issue 跟踪器,而不是发布用的只读仓库。
小结
在 OneUptime 的语境下,"使用 OpenTofu"不是一套特殊的配置方言,而是同一份配置换一个 CLI:Provider 同时发布到两个注册中心,裸source地址让引擎自行解析;terraform块、.tf文件、状态文件全部通用,只有锁文件因记录注册中心而需要按引擎分别维护。配合仓库自带的 quickstart 与 可复用模块,加上每轮 CI 对tofu的端到端把关,从零到一套"监控 + 值班 + 状态页"的落地成本被降到了最低。
【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考