Python 写一个配置巡检小工具:别等线上启动失败才发现 YAML 写错
先说问题
我以前对配置文件有点过分信任。只要服务能在本地启动,application.yml、config.yaml、.env 这些文件基本就算过关。后来有一次灰度发布,容器起来了,业务接口却一直 500。查了半天才发现不是代码问题,是一个配置项从 timeout_ms 写成了 timeout,默认值又刚好把请求拖到了超时。
这种问题很尴尬。它不像语法错误那么明显,也不像单测失败那么直接。配置文件通常散在不同目录里,开发、测试、预发各一份,人工看一遍容易漏。更麻烦的是,有些配置项不是“有没有”的问题,而是“类型对不对、值域合不合适、环境变量有没有补齐”的问题。
所以我后来给项目加了一个很小的巡检脚本。它不负责替代配置中心,也不想做成大而全的平台,只做三件事:检查必填项、检查类型、检查明显危险的值。脚本不长,但对中小项目挺有用。
我希望它解决什么
先定边界。这个工具面对的是本地仓库里的 YAML 配置文件,典型目录长这样:
config/
dev.yaml
test.yaml
prod.yaml
我希望执行下面这条命令后,能直接看到哪些配置不对:
python config_audit.py config/prod.yaml
校验规则单独放在脚本里,后面也可以拆出去。比如数据库地址必须是字符串,端口必须是整数,线程池大小不能小于 1,生产环境的 debug 不能打开。
一个可跑的版本
先安装依赖:
pip install pyyaml
下面是完整脚本,我一般会放在 tools/config_audit.py:
import sys
from pathlib import Path
from typing import Any
import yaml
RULES = {
"app.name": {"type": str, "required": True},
"app.debug": {"type": bool, "required": True, "forbid_prod_true": True},
"server.port": {"type": int, "required": True, "min": 1, "max": 65535},
"database.host": {"type": str, "required": True},
"database.port": {"type": int, "required": True, "min": 1, "max": 65535},
"pool.worker_count": {"type": int, "required": True, "min": 1, "max": 128},
}
def load_yaml(path: Path) -> dict[str, Any]:
if not path.exists():
raise FileNotFoundError(f"配置文件不存在:{path}")
with path.open("r", encoding="utf-8") as f:
data = yaml.safe_load(f) or {}
if not isinstance(data, dict):
raise ValueError("配置文件顶层必须是对象")
return data
def get_by_path(data: dict[str, Any], key_path: str) -> Any:
current: Any = data
for part in key_path.split("."):
if not isinstance(current, dict) or part not in current:
return None
current = current[part]
return current
def audit(data: dict[str, Any], env: str) -> list[str]:
errors: list[str] = []
for key_path, rule in RULES.items():
value = get_by_path(data, key_path)
if value is None:
if rule.get("required"):
errors.append(f"缺少必填项:{key_path}")
continue
expected_type = rule.get("type")
if expected_type and not isinstance(value, expected_type):
errors.append(
f"类型错误:{key_path} 期望 {expected_type.__name__},实际是 {type(value).__name__}"
)
continue
if "min" in rule and value < rule["min"]:
errors.append(f"取值过小:{key_path}={value},最小值是 {rule['min']}")
if "max" in rule and value > rule["max"]:
errors.append(f"取值过大:{key_path}={value},最大值是 {rule['max']}")
if env == "prod" and rule.get("forbid_prod_true") and value is True:
errors.append(f"生产环境禁止开启:{key_path}=true")
return errors
def guess_env(path: Path) -> str:
name = path.stem.lower()
if "prod" in name:
return "prod"
if "test" in name:
return "test"
return "dev"
def main() -> int:
if len(sys.argv) != 2:
print("用法:python config_audit.py <config.yaml>")
return 2
path = Path(sys.argv[1])
data = load_yaml(path)
env = guess_env(path)
errors = audit(data, env)
if not errors:
print(f"OK:{path} 配置检查通过")
return 0
print(f"发现 {len(errors)} 个配置问题:")
for index, error in enumerate(errors, start=1):
print(f"{index}. {error}")
return 1
if __name__ == "__main__":
raise SystemExit(main())
拿一份有问题的配置试一下:
app:
name: order-service
debug: true
server:
port: 8080
database:
host: db.internal
port: "5432"
pool:
worker_count: 0
文件名如果是 prod.yaml,输出大概会是这样:
发现 3 个配置问题:
1. 生产环境禁止开启:app.debug=true
2. 类型错误:database.port 期望 int,实际是 str
3. 取值过小:pool.worker_count=0,最小值是 1
这个反馈比“服务启动失败”要早很多。尤其是在提交前或者 CI 里跑一次,能把不少低级错误挡住。
这里有几个细节别偷懒
第一个细节是不要用 yaml.load。很多旧文章里还在这么写,但普通配置读取用 safe_load 就够了。配置文件不是可信代码,不该让它有机会构造奇怪的对象。
第二个细节是类型判断要谨慎。Python 里 bool 是 int 的子类,所以如果你写了很宽松的判断,true 可能会被当成数字过关。上面的脚本先按配置项明确类型检查,够简单,也不容易误放。
第三个细节是错误信息要能让人直接改文件。只说“配置不合法”没意义,最好把路径、实际值和期望值都打出来。半夜排障时,少一次猜测就是少一次火气。
第四个细节是规则别一开始就抽象过度。我见过有人一上来写一套规则 DSL,结果配置校验工具本身也需要文档。小团队可以先把规则写在 Python 字典里,等规则变多了再拆成 JSON Schema 或 Pydantic 模型。
接到 CI 里
如果项目用 GitHub Actions,可以加一个很薄的检查:
name: config-audit
on: [push, pull_request]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install pyyaml
- run: python tools/config_audit.py config/prod.yaml
公司内部 GitLab CI、Jenkins 也是同一个思路:把脚本返回码交给流水线。返回 0 就继续,返回 1 就拦住。
后面可以怎么扩展
这个脚本只是起点。配置越来越多后,可以继续加几类检查:
• 检查环境变量占位符,比如 `${DB_PASSWORD}` 是否真的存在;
• 检查不同环境的配置项是否一致,避免 dev 有、prod 没有;
• 检查敏感字段,防止密码、token 明文进仓库;
• 把校验结果输出成 Markdown,直接贴到流水线报告里。
我现在更倾向于把这类工具做得小一点。它不需要华丽界面,也不需要复杂平台。只要能在提交代码之前告诉我“这个配置会炸”,它就已经值回票价了。
更多推荐



所有评论(0)