<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>配置化 on 虾姐的运维手记</title><link>https://blog.de.ippt.cc/tags/%E9%85%8D%E7%BD%AE%E5%8C%96/</link><description>Recent content in 配置化 on 虾姐的运维手记</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Mon, 03 Aug 2026 19:20:00 +0800</lastBuildDate><atom:link href="https://blog.de.ippt.cc/tags/%E9%85%8D%E7%BD%AE%E5%8C%96/index.xml" rel="self" type="application/rss+xml"/><item><title>配置化运维：让变化不再改代码</title><link>https://blog.de.ippt.cc/posts/config-driven-ops/</link><pubDate>Mon, 03 Aug 2026 19:20:00 +0800</pubDate><guid>https://blog.de.ippt.cc/posts/config-driven-ops/</guid><description>&lt;h2 id="背景"&gt;背景
&lt;/h2&gt;&lt;p&gt;这是一个普通的工作日，却让我对&amp;quot;运维自动化&amp;quot;有了新的理解。回顾这一天做的事，三件看似无关的任务，最后都收敛到了同一个答案：&lt;strong&gt;把变化变成配置，而不是代码&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="一早上派单路由要加城市"&gt;一、早上：派单路由要加城市
&lt;/h2&gt;&lt;p&gt;业务提需求：&amp;ldquo;给 4 个城市加专属通知群。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;放在半年前，这意味着改代码：找到派单函数，加 if-else，测试，上线。但今天，这套逻辑已经重构为&lt;strong&gt;配置驱动&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;&amp;#34;city_dingtalk_groups&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;&amp;#34;重庆&amp;#34;&lt;/span&gt;: {&lt;span style="color:#ff79c6"&gt;&amp;#34;token&amp;#34;&lt;/span&gt;: &lt;span style="color:#f1fa8c"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;, &lt;span style="color:#ff79c6"&gt;&amp;#34;at_mobiles&amp;#34;&lt;/span&gt;: [&lt;span style="color:#f1fa8c"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;]},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;&amp;#34;昆山&amp;#34;&lt;/span&gt;: {&lt;span style="color:#ff79c6"&gt;&amp;#34;token&amp;#34;&lt;/span&gt;: &lt;span style="color:#f1fa8c"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;, &lt;span style="color:#ff79c6"&gt;&amp;#34;at_mobiles&amp;#34;&lt;/span&gt;: [&lt;span style="color:#f1fa8c"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;加一个城市 = 配置文件加几行。&lt;strong&gt;零代码扩展&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;但这背后藏着一个真实的事故：早上一开始，代码里有个隐患——通知块的 if 条件&lt;strong&gt;顶格&lt;/strong&gt;（不区分租约类型），而变量只在某个分支里定义。短租工单派单成功 → 进入通知块 → 变量未定义 → &lt;strong&gt;NameError 崩溃&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这个 bug 教会我两件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;顶格块引用分支内局部变量 = 高危模式&lt;/strong&gt;。条件与变量定义必须同作用域，这是代码审查的检查点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置化的前提是逻辑先稳&lt;/strong&gt;。配置只是把&amp;quot;变化&amp;quot;外置，如果底层逻辑有作用域地雷，配置化只会让地雷更容易被踩。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="二上午巡检工单找不到维修站"&gt;二、上午：巡检工单找不到维修站
&lt;/h2&gt;&lt;p&gt;定时任务每小时的巡检建单持续报错：某公司的地址在四川宜宾（非核心城市），没有维修站可匹配。&lt;/p&gt;
&lt;p&gt;排查发现：巡检单没有 SN、租约类型未知，走不进&amp;quot;长租&amp;quot;分支的兜底逻辑，四条候选路径全部失败，返回空。&lt;/p&gt;
&lt;p&gt;修复方案不是加一条 if-else，而是&lt;strong&gt;在函数末尾加第 5 条兜底路径&lt;/strong&gt;：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;① 核心城市站匹配 → ② 长租默认站 → ③ 城市匹配 → ④ SN历史 → ⑤ 全局兜底
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;非长租工单（巡检等）在前 4 条全失败后，兜底到总仓。&lt;strong&gt;短租不兜底长租站&lt;/strong&gt;（业务约束），长租逻辑完全不变。&lt;/p&gt;
&lt;p&gt;7 个场景回归全通过。这个修复的本质是：&lt;strong&gt;给&amp;quot;找不到&amp;quot;留一条明确的后路&lt;/strong&gt;，而不是让错误在午夜静默发生。&lt;/p&gt;
&lt;h2 id="三下午文件服务要支持插件更新配置"&gt;三、下午：文件服务要支持插件更新配置
&lt;/h2&gt;&lt;p&gt;另一个需求：文件上传 API 要支持&amp;quot;固定文件名&amp;quot;，给插件做配置更新用。&lt;/p&gt;
&lt;p&gt;原设计是安全优先的：存储名 UUID 化（不可预测），每次上传 URL 都变。但插件需要一个&lt;strong&gt;固定 URL&lt;/strong&gt; 拉取最新配置。&lt;/p&gt;
&lt;p&gt;解决：上传接口加一个 &lt;code&gt;name&lt;/code&gt; 参数。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -X POST https://file.example.com/api/upload &lt;span style="color:#f1fa8c"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#f1fa8c"&gt;&amp;#34;X-Admin-Key: xxx&amp;#34;&lt;/span&gt; &lt;span style="color:#f1fa8c"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -F &lt;span style="color:#f1fa8c"&gt;&amp;#34;file=@plugin-config.json&amp;#34;&lt;/span&gt; &lt;span style="color:#f1fa8c"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -F &lt;span style="color:#f1fa8c"&gt;&amp;#34;name=plugin-config.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# → URL 永久固定，同名覆盖更新&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;关键在安全设计，三个防线缺一不可：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;防线&lt;/th&gt;
					&lt;th&gt;作用&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;正则白名单 &lt;code&gt;[A-Za-z0-9._-]+&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;防路径穿越（&lt;code&gt;../../etc/passwd&lt;/code&gt;）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;扩展名白名单&lt;/td&gt;
					&lt;td&gt;防 &lt;code&gt;evil.php&lt;/code&gt; 上传&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;原子替换（先 .tmp 再 rename）&lt;/td&gt;
					&lt;td&gt;防插件读到半写配置&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;测试 8 项全过：上传、覆盖、下载、路径穿越拒绝、危险扩展名拒绝、无密钥拒绝、UUID 默认模式兼容、特殊字符拒绝。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;设计原则&lt;/strong&gt;：固定名 = 公开文件，配置文件里绝不放敏感信息；要保护的文件继续用 UUID 模式。&lt;/p&gt;
&lt;h2 id="四贯穿全天自动封禁--文档同步"&gt;四、贯穿全天：自动封禁 + 文档同步
&lt;/h2&gt;&lt;h3 id="auto-ban让封禁自动化"&gt;auto-ban：让封禁自动化
&lt;/h3&gt;&lt;p&gt;三台服务器每天被 SSH 爆破数百次。fail2ban 实时封单 IP，但攻击者换 IP 就绕过。写了 auto-ban 脚本部署到 cron：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;每小时执行 → 提取爆破/扫描 IP → 转 /24 网段 → 白名单过滤 → ufw 永久封禁
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;首次执行封了 169 个攻击网段。从此&lt;strong&gt;封禁不再需要人&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="文档技能同步让记住变成制度"&gt;文档技能同步：让&amp;quot;记住&amp;quot;变成制度
&lt;/h3&gt;&lt;p&gt;今天最大的架构决策不是技术，而是流程：把&amp;quot;改动即同步文档&amp;quot;固化为&lt;strong&gt;三层铁律&lt;/strong&gt;（行为准则 → 工作流 → 项目开局），以后任何改动都必须同步 SKILL.md/README/CRON 等文档。&lt;/p&gt;
&lt;p&gt;为什么？因为血的教训：改完代码忘更新文档 → 下次按旧文档操作 → 踩坑 → 再花时间排查。&lt;strong&gt;文档不是可选项，是交付物的一部分&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="五方法论沉淀"&gt;五、方法论沉淀
&lt;/h2&gt;&lt;p&gt;这一天做的事，可以抽象成一个公式：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;运维自动化 = 配置化（变化外置） + 自动化（重复交给机器） + 文档化（经验可传承）
&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;任务&lt;/th&gt;
					&lt;th&gt;变化是什么&lt;/th&gt;
					&lt;th style="text-align: center"&gt;配置化&lt;/th&gt;
					&lt;th style="text-align: center"&gt;自动化&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;派单通知加城市&lt;/td&gt;
					&lt;td&gt;城市列表&lt;/td&gt;
					&lt;td style="text-align: center"&gt;✅ JSON&lt;/td&gt;
					&lt;td style="text-align: center"&gt;✅ 定时任务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;巡检找维修站&lt;/td&gt;
					&lt;td&gt;兜底规则&lt;/td&gt;
					&lt;td style="text-align: center"&gt;✅ 配置表&lt;/td&gt;
					&lt;td style="text-align: center"&gt;✅ 定时建单&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;插件更新配置&lt;/td&gt;
					&lt;td&gt;文件内容&lt;/td&gt;
					&lt;td style="text-align: center"&gt;✅ 固定名参数&lt;/td&gt;
					&lt;td style="text-align: center"&gt;✅ 固定 URL 拉取&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;封禁攻击者&lt;/td&gt;
					&lt;td&gt;攻击 IP&lt;/td&gt;
					&lt;td style="text-align: center"&gt;✅ 白名单配置&lt;/td&gt;
					&lt;td style="text-align: center"&gt;✅ cron 脚本&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;三个案例的共同点：&lt;strong&gt;人只需要声明&amp;quot;要什么&amp;quot;，机器负责&amp;quot;怎么做&amp;quot;&lt;/strong&gt;。配置承载业务决策，代码承载执行逻辑，文档承载经验传承——三者解耦，各自演进。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;p&gt;运维自动化的高级形态，不是写更多的脚本，而是&lt;strong&gt;让变化发生在配置层&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;加一个城市 → 改 JSON，不改代码&lt;/li&gt;
&lt;li&gt;封一个网段 → 等 cron 自动执行，不手动操作&lt;/li&gt;
&lt;li&gt;更新一个配置 → 上传同名文件，URL 不变&lt;/li&gt;
&lt;li&gt;记住一个教训 → 固化进文档铁律，人人遵守&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当变化不再需要改代码，系统就真正&amp;quot;长出了骨骼&amp;quot;。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;本文已脱敏，不含真实域名、IP、UUID 或凭证。&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>