同一套剧本跑不同发行版
Linux 发行版之间,包名、服务名、配置路径、默认参数都可能不同。让一份剧本同时适配多发行版,靠的不是到处写条件判断,而是把差异集中成数据:用事实里的发行版信息去查一张取值表,任务逻辑保持不变。这样新增一个发行版只是往表里加一行,而不是再改一遍剧本。
基本信息
| 判断依据 | 事实中的发行版家族与发行版名称字段(采集后可用) |
|---|---|
| 数据组织 | 角色变量里按家族分组的取值表,兜底值单独列一项 |
| 通用模块 | 优先用软件包、服务管理的通用模块,由它适配具体系统 |
| 常见差异 | 包名、服务单元名、配置路径、仓库配置方式、安全机制 |
| 验证方式 | 在每种目标系统上跑检查模式,并连跑两次验证幂等 |
| 版本边界 | 同一发行版的不同大版本行为也可能不同,以官方文档为准 |
典型使用场景
- 企业内混合环境:既有主流商业发行版也有社区发行版,共用一套基线角色
- 逐步迁移:老系统尚未下线、新系统已上线,只维护一份配置逻辑
- 容器与虚拟机组合:镜像基线与裸机基线共用同一套角色
- 第三方交付:角色交给不同客户环境执行,需要适配对方系统
- 云上多镜像:同一云厂商的不同基础镜像也常来自不同家族
- 回归验证:新增一种目标系统时,把验证结果固化成测试矩阵
差异用数据表达
差异用数据表达
- name: 按发行版家族取包名(映射表放角色变量里)
ansible.builtin.package:
name: "{{ pkg_map[ansible_os_family] | default(pkg_map['default']) }}"
state: present
vars:
pkg_map:
Debian: apache2 # 同一个软件在不同家族里叫法不同
RedHat: httpd
Suse: apache2
default: httpd
- name: 配置目录同样走映射表,不在任务里写条件
ansible.builtin.template:
src: app.conf.j2
dest: "{{ conf_map[ansible_os_family] | default(conf_map['default']) }}/app.conf"
vars:
conf_map:
Debian: /etc/apache2
RedHat: /etc/httpd
default: /etc/apache2
- name: 服务名也走变量,避免写死在任务里
ansible.builtin.service:
name: "{{ svc_name }}"
state: started
enabled: true常见差异维度
| 维度 | 表现 | 处理方式 |
|---|---|---|
| 包名 | 同名软件在不同发行版里叫法不同 | 映射表按家族取值,保留兜底项 |
| 包管理器 | 命令与模块不同 | 优先用通用包模块,必要时按家族分支 |
| 服务单元名 | 服务名与单元文件位置不同 | 服务名变量化,不要写死在任务里 |
| 配置路径 | 同一软件的配置目录布局不同 | 路径变量化或按家族取值 |
| 安全机制 | 强制访问控制与防火墙工具不同 | 差异封在独立任务文件里按家族引入 |
| 初始化系统 | 新旧系统初始化方式不同 | 先确认目标系统实际情况再决定写法 |
“通用模块优先”是降低差异维护量的关键:软件包与服务管理这类模块本身就会适配目标系统,只有模块覆盖不到的地方才需要按家族分支。把分支控制在少数几个取值点,比在任务里到处判断更易验证。
按发行版分支的逻辑必须有兜底或显式失败:遇到未覆盖的家族时,宁可任务立刻报错,也不要静默套用默认值,否则会在某台机器上以看似成功的方式写入错误配置,事后极难发现。
验证与维护
每支持一种新系统,都跑一遍完整验证:检查模式看预演结果、连跑两次看幂等、抽样核对配置文件的实际内容。把这些验证固化成一份测试矩阵(哪种系统、哪个版本、覆盖哪些角色),后续改角色时按矩阵回归,才不会出现“修好了新系统、弄坏了老系统”。