4.2 Ansible 生产级实战——Role 设计、执行策略与大规模管理
预计阅读时间:14 分钟
📖 目录
4.1:Ansible 自动化 介绍了 Ansible 的入门用法:inventory、ad-hoc、playbook 和 role。生产环境中 Ansible 需要面对更复杂的场景——50+ 节点的分批执行、role 复用、标签过滤、变量覆盖和性能调优。本文覆盖这些工程实践。
学习目标
- 掌握生产级 Ansible 项目的目录结构与 Role 设计原则(单一职责、幂等、可配置)
- 理解
serial分批滚动与 linear/free 等执行策略的适用场景 - 能使用 Tags 体系实现 playbook 的细粒度按需执行
- 掌握 Jinja2 过滤器与模板在动态生成配置中的应用
- 能部署 AWX 并利用作业模板、RBAC 实现团队协作管理
- 能针对 100+ 节点场景完成 SSH 连接与执行性能调优
前置知识
- 4.1:Ansible 自动化 Ansible 自动化配置管理入门——inventory、ad-hoc、playbook 与 role 基础
- 2.1:Shell 脚本入门 Shell 脚本入门——命令式脚本与声明式配置的差异对比
- 4.4:Shell 自动化实战 Shell 自动化实战——批量操作与任务脚本化
- 4.13:服务器初始化规范 服务器初始化规范——标准化初始化流程可配合 Ansible 实现自动化
1. Role 组织结构
生产环境的 Role 应遵循 Ansible Galaxy 的推荐结构和团队的约定:
ansible-project/
├── ansible.cfg # 全局配置
├── inventory/
│ ├── production/
│ │ ├── hosts # 生产主机
│ │ └── group_vars/ # 生产变量
│ ├── staging/
│ │ ├── hosts
│ │ └── group_vars/
│ └── development/
├── roles/
│ ├── common/ # 通用角色
│ │ ├── tasks/main.yml
│ │ ├── handlers/main.yml
│ │ ├── defaults/main.yml
│ │ ├── vars/main.yml
│ │ └── meta/main.yml # 依赖关系
│ ├── nginx/
│ │ ├── tasks/
│ │ ├── templates/
│ │ ├── handlers/
│ │ └── defaults/
│ ├── postgres/
│ └── monitoring/
├── playbooks/
│ ├── site.yml # 全量部署
│ ├── deploy-web.yml # Web 服务更新
│ └── security-patch.yml # 安全更新
├── group_vars/
│ ├── all.yml
│ └── db.yml
└── host_vars/
└── db-master.yml
Role 设计原则
- 单一职责:每个 Role 只做一件事(安装 Nginx 的 Role 不配置防火墙)
- 幂等:重复执行playbook 不改变已正确的状态
- 可配置:所有可变参数通过
defaults/main.yml暴露,不在 tasks 中硬编码 - 依赖显式:通过
meta/main.yml中dependencies声明依赖
多环境变量分层
Ansible 变量按层级覆盖:role defaults(最低)→ group_vars → host_vars → play 内 vars → -e(最高)。生产项目按此组织:
inventory/
├── production/
│ ├── hosts
│ ├── group_vars/
│ │ ├── all.yml # 所有生产主机共享(NTP 服务器、镜像源)
│ │ ├── web.yml # Web 组专用
│ │ └── db.yml # 数据库组专用
│ └── host_vars/
│ └── db-master-01.yml # 单台主机专属(如主从标记)
# production/group_vars/all.yml
ntp_servers: ["ntp.internal.example"]
yum_mirror: "mirrors.internal.example/rocky"
app_env: production
ssl_cert_dir: /etc/pki/tls/certs
# production/group_vars/db.yml
pg_version: "16"
pg_max_connections: 500
pg_listen_addresses: "10.0.0.0/8"
# production/host_vars/db-master-01.yml
pg_role: primary
# 同一 playbook 跑不同环境,取值自动切换:
ansible-playbook -i inventory/production deploy-db.yml
ansible-playbook -i inventory/staging deploy-db.yml
# 排查变量来源(覆盖问题定位)
ansible-inventory -i inventory/production --host db-master-01 --vars
defaults/main.yml(可被任何上层覆盖);环境差异放 group_vars/;单机差异放 host_vars/;临时覆盖用 -e。禁止把环境特定值写死在 Role 的 vars/main.yml——该层优先级高于 group_vars,写死就无法覆盖。2. 执行策略与批次控制
serial——逐批滚动
---
- name: Rolling update web servers
hosts: web
serial: 2 # 每次同时更新 2 台
# serial: "20%" # 或按百分比
# serial: [1, 5, 10] # 第一批 1 台,第二批 5 台,后续 10 台
tasks:
- name: Update nginx config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: reload nginx
- name: Verify health
ansible.builtin.uri:
url: "http://{{ inventory_hostname }}/health"
status_code: 200
strategy——执行模式
| 策略 | 行为 | 适用 |
|---|---|---|
linear(默认) | 每台执行完当前 task 后再执行下一 task | 通用场景 |
free | 各主机独立执行,快的先跑下一 task | 无依赖的批量任务 |
free 策略 | 各主机独立推进 task,快的先跑 | 无依赖任务(重启、收集事实) |
# ansible.cfg
[defaults]
strategy = linear # 无依赖任务可改 free 提速
forks = 20 # 并行进程数(默认 5)
# 大规模优化
[ssh_connection]
pipelining = True # 减少 SSH 连接次数
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
control_path = /tmp/ansible-%%h-%%p-%%r
批次失败处理
---
- name: Rolling update with failure control
hosts: web
serial: "20%"
any_errors_fatal: true # 任一批内有主机失败,立即终止后续批次
# max_fail_percentage: 10 # 或允许 10% 主机失败不中断(二选一)
tasks:
- name: Drain node from LB
ansible.builtin.shell: "{{ lb_drain_cmd }} {{ inventory_hostname }}"
- name: Deploy new version
ansible.builtin.copy:
src: "app-{{ version }}.tar.gz"
dest: /opt/app/
- name: Restart service
ansible.builtin.systemd:
name: myapp
state: restarted
- name: Health check
ansible.builtin.uri:
url: "http://{{ inventory_hostname }}/health"
status_code: 200
- name: Rejoin node to LB
ansible.builtin.shell: "{{ lb_join_cmd }} {{ inventory_hostname }}"
# 只有健康检查通过才会执行到这里;失败批次自动回滚(重新挂载旧版本)
strategy: free 模式下各主机独立推进任务,适合「不要求整齐划一」的批量操作(清理日志、批量打补丁);配合 --step 参数可逐任务确认,新 playbook 首次执行或演练时很有用。
安全排练:check 模式与任务断点
变更越大越要先"排练":--check 模拟执行、--diff 展示差异、--start-at-task 从指定任务续跑、--limit 先打一台试点。组合使用形成发布前固定动作:
# 发布前排练(先看差异,不落盘)
ansible-playbook deploy-web.yml --check --diff --limit web-01
# 试点执行(只动一台)
ansible-playbook deploy-web.yml --limit web-01
# 全量执行中途失败:修好后从失败任务续跑,不重放已成功的任务
ansible-playbook deploy-web.yml --start-at-task "Restart service"
# 全程逐任务确认模式(新 playbook 演练推荐)
ansible-playbook deploy-web.yml --step
# 执行前先看影响范围
ansible-playbook deploy-web.yml --list-hosts --list-tasks
3. Tags 体系
---
- name: Full server setup
hosts: all
roles:
- role: common
tags: [always] # 始终执行
- role: nginx
tags: [web, nginx]
- role: postgres
tags: [db, postgres]
- role: monitoring
tags: [monitoring, prometheus]
tasks:
- name: Reboot for kernel update
ansible.builtin.reboot:
reboot_timeout: 300
tags: [reboot, never] # 默认不执行
# 用法
ansible-playbook site.yml --tags nginx # 只执行 nginx 相关
ansible-playbook site.yml --skip-tags monitoring # 跳过监控
ansible-playbook site.yml --tags reboot # 手动触发重启
# 常用 tag 规范:web / db / monitoring / security / common / reboot
标签命名约定
| 类型 | 命名示例 | 说明 |
|---|---|---|
| 组件级 | web db monitoring | 按部署单元划分,与 Role 名对应 |
| 动作级 | install configure restart upgrade | 按操作类型划分,只执行某类动作 |
| 生命周期级 | always never | always 不可跳过(如基础软件源);never 显式触发(重启) |
| 组合标签 | web-install db-upgrade | 动作 + 组件组合,一个 task 可打多个标签 |
# 常见用法组合
ansible-playbook site.yml --tags web-install # 只装 Web 组件
ansible-playbook site.yml --tags upgrade --skip-tags db # 升级除数据库外全部
ansible-playbook site.yml --list-tags # 先查看 playbook 的标签清单
ansible-playbook site.yml --tags all --skip-tags never # 等价于全部默认任务
4. Jinja2 过滤器与宏
# Nginx 配置模板 nginx.conf.j2
upstream backend {
{% for host in groups['web'] %}
server {{ host }}:{{ nginx_port | default(8080) }};
{% endfor %}
}
server {
listen 80;
server_name {{ nginx_server_name }};
location / {
# 用 Jinja2 过滤器处理变量
proxy_set_header Host $host;
proxy_pass http://backend;
}
}
# 多环境变量模板
# group_vars/production/hosts
nginx_server_name: "example.com"
nginx_ssl_enabled: true
# group_vars/staging/hosts
nginx_server_name: "staging.example.com"
nginx_ssl_enabled: false
常用过滤器
| 过滤器 | 示例 | 输出 |
|---|---|---|
| to_json | {"a": 1} | to_json | {"a": 1} |
| combine | dict1 | combine(dict2) | 合并字典 |
| default("value") | var | default("fallback") | fallback |
| ternary | is_prod | ternary(8080, 8081) | 条件表达式 |
| ipaddr | "192.168.1.0/24" | ipaddr('network') | IP 地址计算 |
条件渲染与控制结构
{# 条件渲染:根据环境生成不同配置 #}
{% if app_env == "production" %}
worker_processes {{ ansible_processor_vcpus }};
{% elif app_env == "staging" %}
worker_processes 2;
{% else %}
worker_processes 1;
{% endif %}
{# 循环 + 条件过滤:只渲染存活且非备用的 web 组主机 #}
upstream backend {
{% for host in groups['web'] if host not in groups.get('web_standby', []) %}
server {{ hostvars[host]['ansible_default_ipv4']['address'] }}:8080;
{% endfor %}
}
{# 宏(macro)——模板内可复用的片段 #}
{% macro tls_server(domain, cert) %}
server {
listen 443 ssl;
server_name {{ domain }};
ssl_certificate {{ cert }}.crt;
ssl_certificate_key {{ cert }}.key;
}
{% endmacro %}
{{ tls_server("api.example.com", "/etc/ssl/api") }}
{# include:按变量决定是否引入额外配置片段 #}
{% include "extra/" ~ nginx_flavor ~ ".conf.j2" if nginx_flavor is defined %}
{% extends %}/{% block %} 支持有限(模板上下文作用域问题),生产项目更常用 {% include %} 与宏组合复用片段。发现模板难以维护时优先拆文件,而不是硬套继承。模板渲染调试:先看渲染结果再落盘
模板出错的成本很高(生成错误配置 → 服务起不来),生产上先渲染、后落盘:
# 1. 本地渲染验证(不连目标机):用 debug 模块打印渲染结果
ansible localhost -m debug -a "msg={{ lookup('template', 'templates/nginx.conf.j2') }}"
# 2. 或先渲染到临时目录对比差异
ansible-playbook deploy-web.yml --check --diff --limit web-01
# --check 只模拟不执行,--diff 显示模板变更前后的差异
# 3. 模板中从变量文件/环境/文件取值(lookup 插件)
{{ lookup('env', 'NGINX_TAG') | default('latest') }}
{{ lookup('file', '/etc/secret_token') }}
{{ lookup('ansible.builtin.vars', 'groups') }}
# 4. 敏感值进模板:用 vault 加密变量文件,渲染时自动解密
ansible-vault encrypt group_vars/production/vault.yml
# 模板中直接引用 {{ vault_db_password }}
# 执行时加 --ask-vault-pass 或指定 vault 密钥文件
--check(干跑)、--diff(看变更)、--limit(先打一台)。任何新 playbook 都先在这三个参数下跑一遍再上规模,比事后看执行日志省一个量级的时间。5. AWX 图形化管理
AWX 是 Ansible 的 Web UI 和管理平台(Red Hat Ansible Automation Platform 的上游开源版):
# 部署 AWX(基于 K8s 或 Docker Compose)
# 官方推荐 K8s 部署
git clone https://github.com/ansible/awx-operator.git
cd awx-operator
make deploy
# 创建 AWX 实例
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: awx-demo
spec:
service_type: nodeport
admin_user: admin
admin_password_secret: awx-admin-password
AWX 提供:作业模板、SCM 同步、RBAC 权限、REST API 触发、作业调度、通知集成。适合运维团队协作管理。
项目、作业模板与作业执行
# 概念链路:项目(Project) → 作业模板(Job Template) → 作业(Job)
# 项目:指向 Git 仓库,AWX 自动拉取 playbook
# 作业模板:项目 + inventory + playbook + 变量 + 执行策略 的固定组合
# 作业:模板的一次实际执行
# 通过 AWX REST API 创建作业模板并触发执行
curl -k -H "Authorization: Bearer $AWX_TOKEN" \
-H "Content-Type: application/json" \
-X POST https://awx.example.com/api/v2/job_templates/ \
-d '{
"name": "deploy-web-prod",
"project": 5,
"inventory": 3,
"playbook": "playbooks/deploy-web.yml",
"job_type": "run",
"forks": 30,
"extra_vars": {"version": "2.4.1", "app_env": "production"}
}'
# 触发运行
curl -k -H "Authorization: Bearer $AWX_TOKEN" \
-X POST https://awx.example.com/api/v2/job_templates/12/launch/
# 查询作业执行历史
curl -k -H "Authorization: Bearer $AWX_TOKEN" \
https://awx.example.com/api/v2/jobs/?job_template=12
# RBAC 划分:
# - 运维组:可创建作业模板、审批作业
# - 开发组:只能对指定 inventory 执行只读模板(禁止生产 inventory)
6. 大规模优化 Checklist
| 优化项 | 配置 | 效果 |
|---|---|---|
| SSH pipelining | pipelining = True | 减少 30-50% SSH 连接开销 |
| SSH multiplexing | ControlMaster=auto + ControlPersist | 复用已有连接 |
| 增大 forks | forks = 20-50 | 并行执行更多主机 |
| 并行策略 | strategy = free | 无依赖任务跨主机并行提速 |
| Facts 缓存 | gathering = smart + fact_caching = redis | 跳过已缓存的 facts 收集 |
| 缩短 gather_timeout | gather_timeout = 10 | 避免慢主机拖慢整体 |
# ansible.cfg 生产推荐配置
[defaults]
forks = 30
host_key_checking = False
gathering = smart
fact_caching = redis
fact_caching_connection = localhost:6379:0
fact_caching_timeout = 86400
stdout_callback = yaml
callback_whitelist = profile_tasks
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no
control_path = /tmp/ansible-%%h-%%p-%%r
100+ 节点参数调优速查
| 参数 | 默认 | 100+ 节点建议 | 说明 |
|---|---|---|---|
forks | 5 | 50-100 | 并行主机数;注意控制节点内存(每 fork 约 20-50MB) |
pipelining | False | True | 合并 SSH 会话,减少 30-50% 连接数 |
ControlPersist | — | 60s-300s | 复用连接,任务间免重连 |
gathering | implicit | smart | 有缓存时跳过 facts 重复收集 |
fact_caching | — | redis / jsonfile | 跨 playbook 复用 facts |
ssh_timeout | 10 | 30 | 避免网络抖动误判 UNREACHABLE |
host_key_checking | True | False + known_hosts 预置 | 关闭后配合堡垒机与密钥管控 |
实测经验:200 节点纯配置任务,forks=5 约 40 分钟;调优到 forks=80 + pipelining + facts 缓存 后可压到 6-8 分钟。瓶颈通常在控制节点 CPU 与 SSH 连接建立,而不是目标机。
7. 动态 Inventory 管理
云环境主机频繁伸缩,静态 hosts 文件维护成本高。Ansible 通过 inventory 插件或脚本直接查询云厂商 API:
方式一:inventory 插件(推荐,如 aws_ec2)
# inventory/aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- ap-northeast-1
filters:
tag:Environment: production
instance-state-name: running
keyed_groups:
- key: tags.Name
prefix: tag_name_
- key: tags.Role
prefix: role_
compose:
ansible_host: public_ip_address
ansible_user: ec2-user
# 使用:自动发现所有符合过滤条件的实例
ansible-playbook -i inventory/aws_ec2.yml site.yml --list-hosts
ansible-playbook -i inventory/aws_ec2.yml site.yml --limit role_web
方式二:自写动态 inventory 脚本
#!/usr/bin/env python3
# inventory/cloud_hosts.py——输出 JSON(支持 --list / --host <name>)
import json, subprocess
def list_hosts():
out = subprocess.check_output(
["openstack", "server", "list", "-f", "json"])
servers = json.loads(out)
hosts = {}
for s in servers:
name = s["Name"]
hosts[name] = {
"hosts": [name],
"vars": {"ansible_host": s["Networks"]}
}
return {"_meta": {"hostvars": {
s["Name"]: {"ansible_host": s["Networks"]} for s in servers}}}
if __name__ == "__main__":
import sys
if len(sys.argv) > 1 and sys.argv[1] == "--host":
print(json.dumps({}))
else:
print(json.dumps(list_hosts()))
# 使用
ansible-playbook -i inventory/cloud_hosts.py site.yml
ansible-playbook -i inventory/aws_ec2.yml -i inventory/production/hosts site.yml 可同时使用动态与静态 inventory,把裸机节点与云节点放进同一个 playbook。常见错误
UNREACHABLE连接失败——检查 inventory 主机 IP、SSH 密钥权限(chmod 600)、防火墙规则和ansible_port配置- Playbook 非幂等导致重复执行出错——确保 task 使用声明式模块(如
ansible.builtin.file而非shell: mkdir),避免命令式操作 - 变量覆盖优先级混乱——Ansible 变量有 22 层优先级,记住
extra vars (-e)最高,defaults最低;调试时用ansible-inventory --graph检查变量来源 - forks 过大导致控制节点 OOM——
forks调到 50+ 时注意控制节点内存,建议配合fact_caching和gathering = smart减少开销 - Handler 未触发——Handler 只在 notify 且 task 状态为 changed 时执行,确认
notify拼写正确且 handler 名称在handlers/main.yml中定义 - facts 缓存配置报错——
fact_caching = redis需要控制节点安装 python redis 库并启动 Redis,否则报 "Could not set fact cache"。轻量替代:fact_caching = jsonfile+fact_caching_connection = /tmp/ansible_facts - vault 解密失败——playbook 引用 vault 变量但未传
--ask-vault-pass或密钥文件路径错误;先用ansible-vault view file.yml单独验证加密文件本身可用
最佳实践
- Role 单一职责——每个 Role 只做一件事,安装 Nginx 的 Role 不配置防火墙,便于复用和测试
- 使用
serial滚动更新——生产环境批量部署时设置serial(如serial: "20%"),逐批验证后再继续,避免全量故障 - 所有可变参数放
defaults/main.yml——不在 tasks 中硬编码值,方便不同环境覆盖配置 - 开启 SSH pipelining——
pipelining = True可减少 30-50% 的 SSH 连接开销,大规模场景效果显著 - 使用 Tags 细粒度控制——为 task 和 role 打标签(如
web、db、security),支持按需执行部分操作 - 发布前固定排练动作——新 playbook 一律
--check --diff干跑 +--limit试点 +--start-at-task续跑,形成固定三步再上规模 - 模板渲染先验证——用 debug 模块或 check+diff 先看渲染结果,避免错误配置直接落盘到生产
练习题
- 编写一个 Nginx 部署 Role,要求:支持多环境变量覆盖(production/staging)、包含 Jinja2 模板生成配置、附带 handler 实现配置变更后自动 reload,并用
--check --diff验证幂等性。 - 对 10 台模拟 Web 服务器执行滚动更新:配置
serial: 2,每批更新后自动调用健康检查接口,失败时停止后续批次。记录每批的执行时间和状态变化。 - 搭建一个包含 common、nginx、postgres 三个 Role 的 Ansible 项目,使用 Tags 实现:只部署 Web 层(
--tags web)、跳过数据库(--skip-tags db)、仅执行安全补丁(--tags security)。 - 用
lookup插件从环境变量与文件读取值渲染模板,分别用ansible localhost -m debug与--check --diff验证渲染结果一致。 - 给 playbook 配置
fact_caching(redis 与 jsonfile 各试一次),对比二次执行时跳过 facts 收集的耗时差异。
学习检查点
学完本章后,请检验自己是否掌握以下内容:
| 检查项 | 自测问题 | 验证方法 |
|---|---|---|
| 概念理解 | 能用自己的话解释 Ansible 在生产环境中的最佳实践 | 尝试向他人讲解 |
| 命令操作 | 能不查文档完成 Ansible 项目结构组织、Role 开发 | 在终端实际执行 |
| 原理掌握 | 能说出 Ansible 的并发执行机制和性能优化方法 | 画出流程图 |
| 故障排查 | 能独立排查 Ansible 执行超时、连接不稳定、Playbook 调试 | 模拟故障并修复 |
| 最佳实践 | 能说明为什么需要使用 Ansible Galaxy 和版本控制 | 对比不同方案 |
本章总结
Ansible 生产化的核心是结构化与可控:通过 Role 单一职责和 defaults 参数化保证复用与幂等,借助 serial 分批与 Tags 缩小变更爆炸半径,用 SSH pipelining、facts 缓存和 Mitogen 解决大规模执行性能问题。整个体系遵循"小批量、可验证、可追溯"的原则,最终通过 AWX 沉淀为团队协作的标准操作流程。
延伸阅读
- 4.1:Ansible 自动化 Ansible 自动化配置管理入门
- 4.13:服务器初始化规范 Linux 服务器初始化标准流程(可配合 Ansible 实现自动化初始化)