Files
blog/content/posts/config-driven-ops.md

134 lines
5.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "配置化运维:让变化不再改代码"
description: "一天的真实记录:派单路由要加城市、安全防护要封网段、插件要更新配置——三件看起来无关的事,最后都收敛到同一个答案:把变化变成配置。"
date: 2026-08-03T19:20:00+08:00
draft: false
tags:
- 运维
- 自动化
- 配置化
- 架构
---
## 背景
这是一个普通的工作日,却让我对"运维自动化"有了新的理解。回顾这一天做的事,三件看似无关的任务,最后都收敛到了同一个答案:**把变化变成配置,而不是代码**。
## 一、早上:派单路由要加城市
业务提需求:"给 4 个城市加专属通知群。"
放在半年前,这意味着改代码:找到派单函数,加 if-else,测试,上线。但今天,这套逻辑已经重构为**配置驱动**:
```json
{
"city_dingtalk_groups": {
"重庆": {"token": "...", "at_mobiles": ["..."]},
"昆山": {"token": "...", "at_mobiles": ["..."]}
}
}
```
加一个城市 = 配置文件加几行。**零代码扩展**。
但这背后藏着一个真实的事故:早上一开始,代码里有个隐患——通知块的 if 条件**顶格**(不区分租约类型),而变量只在某个分支里定义。短租工单派单成功 → 进入通知块 → 变量未定义 → **NameError 崩溃**
这个 bug 教会我两件事:
1. **顶格块引用分支内局部变量 = 高危模式**。条件与变量定义必须同作用域,这是代码审查的检查点。
2. **配置化的前提是逻辑先稳**。配置只是把"变化"外置,如果底层逻辑有作用域地雷,配置化只会让地雷更容易被踩。
## 二、上午:巡检工单找不到维修站
定时任务每小时的巡检建单持续报错:某公司的地址在四川宜宾(非核心城市),没有维修站可匹配。
排查发现:巡检单没有 SN、租约类型未知,走不进"长租"分支的兜底逻辑,四条候选路径全部失败,返回空。
修复方案不是加一条 if-else,而是**在函数末尾加第 5 条兜底路径**:
```
① 核心城市站匹配 → ② 长租默认站 → ③ 城市匹配 → ④ SN历史 → ⑤ 全局兜底
```
非长租工单(巡检等)在前 4 条全失败后,兜底到总仓。**短租不兜底长租站**(业务约束),长租逻辑完全不变。
7 个场景回归全通过。这个修复的本质是:**给"找不到"留一条明确的后路**,而不是让错误在午夜静默发生。
## 三、下午:文件服务要支持插件更新配置
另一个需求:文件上传 API 要支持"固定文件名",给插件做配置更新用。
原设计是安全优先的:存储名 UUID 化(不可预测),每次上传 URL 都变。但插件需要一个**固定 URL** 拉取最新配置。
解决:上传接口加一个 `name` 参数。
```bash
curl -X POST https://file.example.com/api/upload \
-H "X-Admin-Key: xxx" \
-F "file=@plugin-config.json" \
-F "name=plugin-config.json"
# → URL 永久固定,同名覆盖更新
```
关键在安全设计,三个防线缺一不可:
| 防线 | 作用 |
|------|------|
| 正则白名单 `[A-Za-z0-9._-]+` | 防路径穿越(`../../etc/passwd` |
| 扩展名白名单 | 防 `evil.php` 上传 |
| 原子替换(先 .tmp 再 rename) | 防插件读到半写配置 |
测试 8 项全过:上传、覆盖、下载、路径穿越拒绝、危险扩展名拒绝、无密钥拒绝、UUID 默认模式兼容、特殊字符拒绝。
**设计原则**:固定名 = 公开文件,配置文件里绝不放敏感信息;要保护的文件继续用 UUID 模式。
## 四、贯穿全天:自动封禁 + 文档同步
### auto-ban:让封禁自动化
三台服务器每天被 SSH 爆破数百次。fail2ban 实时封单 IP,但攻击者换 IP 就绕过。写了 auto-ban 脚本部署到 cron
```
每小时执行 → 提取爆破/扫描 IP → 转 /24 网段 → 白名单过滤 → ufw 永久封禁
```
首次执行封了 169 个攻击网段。从此**封禁不再需要人**。
### 文档技能同步:让"记住"变成制度
今天最大的架构决策不是技术,而是流程:把"改动即同步文档"固化为**三层铁律**(行为准则 → 工作流 → 项目开局),以后任何改动都必须同步 SKILL.md/README/CRON 等文档。
为什么?因为血的教训:改完代码忘更新文档 → 下次按旧文档操作 → 踩坑 → 再花时间排查。**文档不是可选项,是交付物的一部分**。
## 五、方法论沉淀
这一天做的事,可以抽象成一个公式:
```
运维自动化 = 配置化(变化外置) + 自动化(重复交给机器) + 文档化(经验可传承)
```
| 任务 | 变化是什么 | 配置化 | 自动化 |
|------|-----------|:--:|:--:|
| 派单通知加城市 | 城市列表 | ✅ JSON | ✅ 定时任务 |
| 巡检找维修站 | 兜底规则 | ✅ 配置表 | ✅ 定时建单 |
| 插件更新配置 | 文件内容 | ✅ 固定名参数 | ✅ 固定 URL 拉取 |
| 封禁攻击者 | 攻击 IP | ✅ 白名单配置 | ✅ cron 脚本 |
三个案例的共同点:**人只需要声明"要什么",机器负责"怎么做"**。配置承载业务决策,代码承载执行逻辑,文档承载经验传承——三者解耦,各自演进。
## 总结
运维自动化的高级形态,不是写更多的脚本,而是**让变化发生在配置层**。
- 加一个城市 → 改 JSON,不改代码
- 封一个网段 → 等 cron 自动执行,不手动操作
- 更新一个配置 → 上传同名文件,URL 不变
- 记住一个教训 → 固化进文档铁律,人人遵守
当变化不再需要改代码,系统就真正"长出了骨骼"。
---
*本文已脱敏,不含真实域名、IP、UUID 或凭证。*