简介面向 CentOS 7 / RHEL 7 系统管理员与运维工程师提供 Ansible 2.9.27 完整离线部署包适合在无外网或内网隔离环境中快速搭建自动化运维平台。压缩包共 29 个文件以 RPM 安装包为主包含 ansible 主程序及 python-paramiko、PyYAML、sshpass 等关键依赖另有 gz/bz2 格式的 yum 仓库元数据与 repomd.xml 索引整体仅 19.29MB便于传输和离线安装。该版本延续 Ansible 无代理特性通过 SSH 协议即可管理大量主机支持用 YAML 编写 Playbook 统一完成系统配置、软件部署、服务启停等任务内置的 copy、yum、template、service 等常用模块能满足批量化日常运维需求。包中对 RHEL7 系列依赖链做了整理可避免因缺少依赖导致的安装失败企业用户可直接将本地目录配置为 yum 源进行批量安装。已有 635 人学习下载既适合初学者系统了解自动化运维也可作为生产环境快速部署的实用工具包。1. ansible-2.9.27它为什么还在生产环境服役登录某生产环境的控制节点输入ansible --version屏幕返回ansible 2.9.27——这个画面在很多一线运维手里并不陌生。明明 Ansible 已经发到更高的版本为什么 2.9.27 还在大批服务器上稳稳跑着答案很现实它足够稳定、边界清楚、资料好查而且很多存量 Playbook 就是照着这一代语法写的。这个标题讲的就是把版本锁死在 ansible-2.9.27 之后的完整打法怎么装、怎么配置、怎么写 Playbook、遇到玄学错误怎么排查以及它到底还能不能扛住新需求。适合批量管理几十到几千台主机、正在维护老 Playbook、又不想被升级折腾的运维和开发。2. 为什么选 2.9.27兼容性边界与三条判断2.1 2.9 系列在 Ansible 迭代里的位置Ansible 2.9 是模块体系拆分前最后一条完整主线。2.9 之后模块慢慢被拆进 collectionPlaybook 写法开始出现“要不要带 fully qualified collection name”的争论依赖关系也变复杂。反过来看 2.9它的模块是整包随发行版提供的ansible-doc -l一敲就是全量模块列表不用额外装 collection这对内网离线环境极其友好。2.9.27 是 2.9 分支的收尾修复版。它不是新功能版而是把前面十几轮暴露的问题集中修掉后的“最终形态”。对运维来说版本号末尾的修复版本往往才是最值得长期用的功能不再膨胀行为已经固定网上能搜到的报错样本最全。很多团队的线上 Ansible 停留在 2.9.27不是因为懒而是因为升级到 2.10 意味着每个模块都要重新回归收益却只是用不上几个新特性。2.2 控制端与被管端的 Python 兼容矩阵决定 2.9.27 能不能跑先看两端的 Python。控制端需要 Python 2.7 或 3.5 及以上也就是说现在用 Python 3.9、3.10 装它没问题被管端情况复杂一些基础模块要 Python 2.6 以上但有相当一部分模块在 2.6 上表现不完整官方实际上更推荐 2.7 或 3.5 及以上。位置常见可用 Python需要重点关注的配置控制端Python 2.7 / 3.5用虚拟环境锁版本避免系统 Python 被污染被管端Python 2.6模块多要求 2.73.5 通用ansible_python_interpreter/usr/bin/python3被管端无 Python只能跑raw模块做引导先用raw装 Python再走正常模块这一条直接影响后面所有 Playbook 的适配。例如在老的 CentOS 6 上/usr/bin/python指向 2.6而某个模块内部用了 2.7 才有的语法就会出现模块执行失败在只装了 Python 3 的新镜像上默认解释器路径又可能指向不存在的/usr/bin/python。所以 2.9.27 的 inventory 里“显式指定解释器”通常排在第一位。2.3 什么时候坚持 2.9.27什么时候换新版三条判断供参考。第一现有 Playbook 是否大量基于 2.9 语法、是否依赖未拆分的核心模块如果是留在 2.9.27 是成本最低的选择。第二是否明确需要 collection 分发的新模块、是否要对接新版生态如果不是就没有升级的动力。第三团队是否有多套环境需要统一版本多数情况下各环境拉到同一版本号 2.9.27比追新版本更容易保持一致。我见过一个模拟项目X控制端还是老 Python 环境被管端从 CentOS 6 一直混到新版云镜像整套 Playbook 全是yum、service、template这类传统模块。换成新版后要逐个模块回归最后结论仍是留在 2.9.27。反过来如果项目一开始就要用新模块体系里的功能比如某些定向发布的插件那就别硬用 2.9.27应该评估直接上新主线。3. 把 2.9.27 跑起来安装、cfg 与最小 inventory3.1 用虚拟环境安装 ansible-2.9.27安装 Ansible 最常见也最可控的方式是拉一个独立的 Python 虚拟环境然后把版本钉死在 2.9.27。不要图省事直接pip install ansible装进系统 Python全局安装会把权限、依赖、版本全搅在一起后面想换版本很被动。python3 -m venv /opt/ansible-2.9.27 source /opt/ansible-2.9.27/bin/activate pip install --upgrade pip pip install ansible2.9.27 ansible --version逻辑说明先建虚拟环境激活后所有 Python 包都落在/opt/ansible-2.9.27下不污染系统解释器。升级 pip 是为了避免老 pip 拉取依赖时出错。最后一条命令验证安装结果输出里会显示ansible 2.9.27。参数说明--upgrade pip不是必须的但在 CentOS 7 这类自带 pip 较老的系统上建议执行ansible2.9.27把版本精确锁定避免 pip 顺手装了更新版本导致行为不一致。安装完成后建议把PATH写进.bashrc或直接使用绝对路径调用。我一般会加一行export PATH/opt/ansible-2.9.27/bin:$PATH这样任何终端登录控制节点都能直接执行ansible。只要依赖的 Python 解释器在 3.5 以上这个安装方式在绝大多数系统上都能顺利走通。安装过程经常卡在编译某个依赖上比如装了旧版 openssl 导致 cryptography 构建失败把编译工具链补全再重装即可。3.2 ansible.cfg先写对再动手控制节点第一次跑不起来十有八九是没配ansible.cfg。Ansible 查找配置文件的顺序是当前目录、用户家目录、/etc/ansible/ansible.cfg我习惯把ansible.cfg放在每个项目目录里随 Playbook 一起走避免不同项目互相干扰。[defaults] inventory hosts host_key_checking False retry_files_enabled True remote_user ops become True become_method sudo timeout 30 [ssh_connection] pipelining True ssh_args -o ControlMasterauto -o ControlPersist60s逻辑说明inventory指定同目录下的主机清单文件host_key_checking False跳过首次 SSH 指纹确认省得批量管新主机时卡在交互确认上remote_user指定默认登录用户become True让 Playbook 默认启用提权省去每个任务重复写become。timeout控制 SSH 连接等待时限避免连不上的时候长时间挂起。参数说明pipelining True会把模块执行和传输合并成一个 SSH 通道大批量执行时提速明显但要求被管端目录/tmp可写ControlPersist60s保留空闲连接 60 秒连续执行多个任务时复用连接。如果被管端 sudo 需要密码这里还要在 inventory 或运行时指定密码不建议写死在 config 文件里。host_key_checking False是一个典型的安全权衡内网批量环境收益大于风险但公网环境应改为预先在控制端.ssh/known_hosts里写入主机指纹而不是关掉校验。另外retry_files_enabled会在 Playbook 失败时生成.retry文件方便重跑指定失败主机。3.3 最小 inventory三种写法与验证命令Inventory 决定 Ansible 认识哪些主机常见写法是 INI 格式按组划分。以下是最小可用的 hosts 文件。[web] 192.0.2.10 ansible_userops ansible_becomeyes 192.0.2.11 [db] 192.0.2.20 [all:vars] ansible_python_interpreter/usr/bin/python3逻辑说明[web]和[db]是两个组组名在 Playbook 里可以直接作为 hosts 目标使用。第二行没有写主机变量会走[all:vars]里的默认解释器配置。参数说明ansible_user指定 SSH 用户ansible_becomeyes表示该主机默认提权ansible_python_interpreter是 2.9 系列里最重要的一个变量直接决定被管端用哪个 Python 跑模块。写好后先摸一遍连通性再验证被管端事实收集是否正常。ansible -i hosts web -m ping ansible -i hosts all -m setup -a filteransible_distribution*第一条命令用ping模块检查连接与登录是否正常能返回pong说明 SSH、用户、提权链路都通。第二条命令调用setup模块收集系统事实filteransible_distribution*只保留发行版相关信息用来确认被管端 Python 解释器是否生效。如果第二行报module failed多半是解释了器路径错了。另一种 inventory 写法是把变量拆到目录里group_vars/all.yml、group_vars/web.yml适合变量多、需要版本管理的场景。目录方式比把全部变量塞进 hosts 文件更清晰且同名变量优先级按 主机变量 组变量 all 变量 的顺序覆盖。4. 写第一个 Playbook模板、执行策略与三层校验4.1 目录结构把配置和代码分开Playbook 不是单一 YAML 文件我习惯按项目建目录。一个典型结构如下。web-ops/ ├── ansible.cfg ├── hosts ├── group_vars/ │ └── web.yml ├── templates/ │ └── nginx_reverse_proxy.conf.j2 └── site.yml逻辑说明group_vars/web.yml存 web 组专用的变量templates放 Jinja2 模板site.yml是入口 Playbook。这样做的价值是后续加服务器时不用翻 YAML 找变量模板和任务分离也让非 Python 背景的同事能看懂改动点。2.9.27 对这个结构的支持很完整不需要额外配置。4.2 用模板下发 Nginx 反向代理配置模板的作用是把“可变的部分”留成占位符由变量注入。下面是一个最小 Nginx 反向代理模板。server { listen 80; server_name {{ nginx_server_name }}; location / { proxy_pass http://{{ nginx_upstream }}; proxy_set_header Host $host; } }逻辑说明{{ nginx_server_name }}和{{ nginx_upstream }}是 Jinja2 变量渲染时由 Ansible 从 变量源取值。$host是 Nginx 自身的变量不会被 Ansible 模板引擎解析。参数说明nginx_server_name填写对外域名nginx_upstream填写后端服务地址比如127.0.0.1:8080。对应的 Playbook 如下。--- - name: deploy nginx reverse proxy hosts: web become: yes vars: nginx_server_name: example.com nginx_upstream: 127.0.0.1:8080 tasks: - name: install nginx yum: name: nginx state: present - name: template reverse proxy config template: src: nginx_reverse_proxy.conf.j2 dest: /etc/nginx/conf.d/reverse_proxy.conf notify: - restart nginx handlers: - name: restart nginx systemd: name: nginx state: restarted daemon_reload: yes逻辑说明hosts: web表示只在这个组执行become: yes提权vars是任务内的临时变量。第一个任务用yum模块装 Nginx第二个任务把模板渲染到目标路径notify触发同名 handler。参数说明template模块的src是模板文件相对路径dest是目标机绝对路径notify只有当配置文件内容发生变化时才会触发restart nginx内容没变不会重启服务这是幂等设计的关键。如果目标机是 Debian 系把yum换成apt即可。2.9.27 的yum/apt模块在使用上都很稳定主要坑在于被管端默认 Python 环境前面配置文件里已通过ansible_python_interpreter解决。4.3 执行策略serial、forks 与 become 的配合批量执行时最怕一组挂掉拖垮全部。2.9.27 用serial控制每批次执行的机器数生产环境建议设为 2 或 3先在少量机器上验证再滚动到全部。- name: rolling deploy hosts: web become: yes serial: 2 forks: 5 tasks: - name: reload nginx systemd: name: nginx state: reloaded逻辑说明serial: 2表示每批只拿 2 台机器执行一批成功后才进入下一批forks: 5表示批次内最多并行跑 5 个任务节点。become: yes指定默认提权。参数说明当被管端用户需要 sudo 密码时可以额外指定-K参数或通过 inventory 里的ansible_become_pass传密码后者不建议明文写在 hosts 文件里。serial和forks不是同一个概念前者控制批次大小后者控制同一批里的并行度。如果机器数量少、网络环境差可以把forks调低避免并发 SSH 把控制端或交换机打满。我在模拟项目X里就遇到过forks20时控制端 CPU 突然飙高改成serial: 3, forks: 5后稳定完成滚动更新。4.4 三层校验语法检查、dry-run 与 diffPlaybook 写完后不要直接跑全量先用校验命令过三关。依次执行以下命令。ansible-playbook -i hosts site.yml --syntax-check ansible-playbook -i hosts --list-hosts site.yml ansible-playbook -i hosts site.yml --check --diff逻辑说明第一行只做 YAML 语法检查不连主机能快速暴露拼写和结构错误。第二行确认 Playbook 的目标主机范围避免把改动波及到不该动的组。第三行进入 dry-run--check让模块只预测变更不真正执行--diff显示文件前后差异。参数说明--check对纯配置类任务很有用但不是所有模块都支持 check 模式command类模块在 check 下也会执行--diff在任务涉及template、copy时输出文件差异方便 review。我执行 Playbook 的习惯是先把目标机用-l限定一台例如ansible-playbook -i hosts site.yml -l 192.0.2.10确认这台机器上渲染结果符合预期再去掉-l跑全组。这个习惯省下的后悔药比什么技巧都值钱。5. 2.9.27 排查五个高频踩坑与修复记录5.1 SSH 连接被 host key 弹窗卡死现象执行ansible web -m ping时任务长时间不返回手动 SSH 登录却要确认指纹通过被管端大量新增主机时尤其明显。原因默认host_key_checking开启首次连接未知主机时出现交互确认而 Ansible 是非交互执行无法响应确认导致挂起。解决在ansible.cfg里加host_key_checking False或预先用ssh-keyscan采集指纹写入known_hosts。讲真内网场景关掉它最常见但要意识到这是安全让步公网环境应改为预采集指纹。5.2 模块报错找不到 Python 解释器现象执行setup或任何模块任务时报类似FAILED! {msg: module (and its dependencies) is missing}或者后台日志显示/usr/bin/python不存在。原因新云镜像默认没有 Python 2而 Ansible 在 2.9 里默认找/usr/bin/python。解决在 inventory 的[all:vars]下固定ansible_python_interpreter/usr/bin/python3。另一种情况是被管端只有 Python 2.6此时基础模块能跑部分依赖 2.7 特性的模块仍会失败只能换路径或改用raw模块引导。5.3 提权后环境变量和 PATH 丢失现象Playbook 里通过become提权后执行command提示找不到某个命令或者部署的服务起不来直接 SSH 上去手动跑却没问题。原因sudo 默认重置环境变量PATH、HOME都变了Ansible 提权后拿到的不是原用户环境。解决在配置里加become_flags: -E保留环境或者在ansible.cfg的[privilege_escalation]段统一设置。注意-E可能引入安全风险生产环境更推荐把完整路径写进命令或把需要的变量显式传进 Playbook。5.4 YAML 语法检查报错冒号惹的祸现象Playbook 语法检查报Syntax Error while loading YAML定位到某一行看似正常的字符串。原因YAML 会把“冒号加空格”解析为字典比如msg: 状态: 正常里第二个冒号会触发解析错误。解决给带冒号的字符串加引号例如写成msg: 状态: 正常。路径里带冒号也一样比如dest: /etc/nginx/conf.d/rev: proxy.conf必须整体加引号。这种报错在 2.9.27 里经常被误判为模板问题排查时先看冒号。5.5 sshpass 未安装导致密码认证失败现象inventory 里配置了ansible_password或使用了-k参数执行时报to use the ssh connection type with passwords, you must install the sshpass program。原因控制端没有安装 sshpass 工具Ansible 无法通过 ssh 的密码认证方式连接。解决在控制端用系统包管理器安装例如基于红帽的是yum install -y sshpass基于 Debian 的是apt install -y sshpass。安装后重新执行即可。生产环境仍建议使用密钥认证密码方式只适合临时任务或切换认证期的过渡方案。这些坑的共同点是报错信息看着吓人但根因都不是 2.9.27 本身的问题而是环境配置没有对齐。遇到奇怪报错先检查三处——SSH 握手、Python 解释器、提权方式能省下大量时间。6. 进阶动态 inventory 与固定版本的自检习惯6.1 用一段脚本做动态 inventory主机清单不一定要写死在 hosts 文件里。当机器数量大、分组来自内部系统或需要频繁变化时可以用动态 inventory 脚本让 Ansible 每次执行时实时获取。Ansible 2.9.27 对动态 inventory 的协议很成熟脚本支持--list输出全量主机与分组支持--host输出指定主机变量返回 JSON 即可。#!/usr/bin/env python3 # -*- coding: utf-8 -*- import json import sys def build_inventory(): inventory { web: { hosts: [192.0.2.10, 192.0.2.11] }, db: { hosts: [192.0.2.20] }, _meta: { hostvars: { 192.0.2.10: {ansible_python_interpreter: /usr/bin/python3}, 192.0.2.11: {ansible_python_interpreter: /usr/bin/python3}, 192.0.2.20: {ansible_python_interpreter: /usr/bin/python3} } } } return inventory if __name__ __main__: if len(sys.argv) 1 and sys.argv[1] --host: print({}) else: print(json.dumps(build_inventory()))逻辑说明脚本输出web、db两个组和_meta里的主机变量--host分支返回空对象表示没有额外主机变量。参数说明脚本需要可执行权限chmod x inventory.py后在ansible.cfg把inventory hosts改成inventory ./inventory.py之后所有ansible、ansible-playbook命令都会自动调用脚本。动态 inventory 的坑在于脚本报错时 Ansible 给到的提示不够直观。可以先手动执行python3 inventory.py --list验证 JSON 合法性再接入 Ansible。真实环境里这个脚本可以换成读取内部 CMDB 接口或扫描云厂商 API 生成主机列表核心逻辑不变。6.2 固定版本的三条自检习惯我最后想补一个长期养成的习惯它比任何模块技巧都重要。第一控制节点上用虚拟环境钉死版本任何新人登录都不会意外升级全局包。第二每次换控制机或重建环境后先跑一遍基线任务——ansible all -m ping加一个--syntax-check的 Playbook——确认版本与库存行为一致再跑正式业务。第三Playbook 里尽量少用latest作为安装状态版本一旦漂移问题排查成本会指数级上升。以上这套组合拳背后是我的一次翻车教训某次新控制机顺手升级到了新版 Ansible原 Playbook 里的一批模块语法在新版下行为变化批量执行时把老服务的配置改出了偏差事后只能逐台回滚。从那以后我坚持一个原则自动化管机器的地方Ansible 自己的版本必须先被自动化管住。这套做法希望能替你把同样的一跤挡在门外希望帮到你。本文还有配套的精品资源点击获取