Ansible 从入门到精通实战运维手册
适用版本:ansible-core 2.16+(多数内容同样适用于更新版本) 典型环境:Linux 控制节点,通过 SSH 管理 Linux 主机 原则:先保证幂等、可审计、可回滚,再追求执行速度
目录
认识 Ansible
安装与实验环境
配置文件与项目结构
Inventory 主机清单
Ad-hoc 临时命令
Playbook 基础
变量、Facts 与优先级
条件、循环、注册变量与重试
文件、模板与 Jinja2
Handlers、Blocks 与错误控制
Role 工程化
Collections 与 Galaxy
Vault 与敏感信息
Privilege Escalation 与安全
批次、委派与滚动发布
Tag、Check Mode 与 Diff
生产实战一:部署 Nginx
生产实战二:滚动发布应用
生产实战三:系统基线与补丁
测试、质量门禁与 CI
性能优化
调试与故障排查
AWX、Automation Controller 与执行环境
生产规范与检查清单
常用命令速查
进阶路线与练习
1. 认识 Ansible Ansible 是无代理自动化工具。控制节点读取 Inventory 和 Playbook,通过 SSH/WinRM 连接受管节点,传输并执行模块,然后收集结果。
1 2 3 4 5 6 7 8 9 控制节点 ├── Inventory:管理谁 ├── Playbook:做什么 ├── Variables:不同环境的参数 ├── Roles/Collections:复用单元 └── Vault:敏感数据 │ SSH / WinRM ▼ 受管节点:Linux、Windows、网络设备、云服务
核心特性:
Agentless :受管 Linux 一般只需 SSH 与 Python。
声明式与幂等 :描述目标状态,重复执行不应产生额外变化。
Push 模式 :由控制端主动执行,便于集中审计。
模块化 :优先使用模块,不把自动化退化为 Shell 脚本集合。
术语:
术语
含义
Control node
安装并运行 Ansible 的控制节点
Managed node
被管理主机
Inventory
主机及分组清单
Module
执行具体动作的单元,如 package、service
Task
一次模块调用
Play
对一组主机执行的一组任务
Playbook
一个或多个 Play 构成的 YAML 文件
Role
按约定目录组织的可复用自动化单元
Collection
模块、插件、Role 等内容的发布包
1.1 Ansible 适合与不适合的场景 适合:配置管理、批量变更、应用部署、补丁升级、账户与密钥管理、云资源编排、网络自动化、灾备演练。
不宜直接承担:毫秒级实时控制、持续高频状态同步、大规模数据搬运、复杂业务工作流引擎。此类场景应由专用系统负责,Ansible 用于安装、配置和编排它们。
2. 安装与实验环境 2.1 控制节点要求
Linux 或 WSL;Windows 原生环境通常使用 WSL。
Python 3.10+。
能通过 SSH 到达受管主机。
受管 Linux 需可用的 Python;少数初始化任务可用 raw。
2.2 使用虚拟环境安装 1 2 3 4 5 6 7 8 python3 -m venv .venvsource .venv/bin/activate python -m pip install --upgrade pip python -m pip install "ansible-core>=2.16,<2.18" ansible-lint ansible --version ansible-playbook --version ansible-lint --version
生产环境应固定版本:
1 2 3 # requirements.txt ansible-core==2.17.7 ansible-lint==24.12.2
1 python -m pip install -r requirements.txt
不要在同一项目中依赖“机器上碰巧安装的版本”。控制节点、CI 和 AWX 应使用一致版本。
2.3 SSH 准备 1 2 3 ssh-keygen -t ed25519 -C "ansible-control" ssh-copy-id ops@192.168.56.11 ssh ops@192.168.56.11
推荐为自动化使用独立账户,并通过最小化的 sudo 规则授权。不要把个人账户和自动化身份混用。
2.4 首次连通 创建最小 Inventory:
1 2 3 4 5 6 7 8 [web] web01 ansible_host =192.168 .56.11 web02 ansible_host =192.168 .56.12 [all:vars] ansible_user =opsansible_python_interpreter =/usr/bin/python3
测试:
1 ansible -i inventory.ini all -m ansible.builtin.ping
ping 模块不是 ICMP Ping;它验证 SSH、Python 和 Ansible 模块执行链路。网络连通只能说明目标端口可达,不能替代该测试。
受管机尚无 Python 时:
1 2 3 ansible -i inventory.ini all -m ansible.builtin.raw \ -a 'test -e /usr/bin/python3 || (apt-get update && apt-get install -y python3)' \ --become
3. 配置文件与项目结构 3.1 配置查找顺序 Ansible 使用找到的第一个配置文件:
ANSIBLE_CONFIG 环境变量指定的文件
当前目录的 ansible.cfg
~/.ansible.cfg
/etc/ansible/ansible.cfg
检查实际配置:
1 2 3 ansible --version ansible-config dump --only-changed ansible-config view
3.2 推荐项目结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ansible-project/ ├── ansible.cfg ├── requirements.yml ├── inventories/ │ ├── dev/ │ │ ├── hosts.yml │ │ ├── group_vars/ │ │ │ ├── all.yml │ │ │ └── web.yml │ │ └── host_vars/ │ └── prod/ │ ├── hosts.yml │ ├── group_vars/ │ └── host_vars/ ├── playbooks/ │ ├── site.yml │ ├── deploy.yml │ └── patch.yml ├── roles/ │ ├── common/ │ └── nginx/ ├── files/ ├── templates/ └── .ansible-lint
示例 ansible.cfg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [defaults] inventory = inventories/dev/hosts.ymlroles_path = rolescollections_path = collectionshost_key_checking = True retry_files_enabled = False forks = 20 timeout = 20 interpreter_python = auto_silentstdout_callback = default[privilege_escalation] become = False [ssh_connection] pipelining = True ssh_args = -o ControlMaster=auto -o ControlPersist=60 s
安全提醒:
生产环境不应为了方便关闭 host_key_checking。
预先维护 known_hosts,避免首次连接被中间人攻击。
pipelining=True 可提高速度,但要验证目标机 sudo 策略兼容。
4. Inventory 主机清单 4.1 YAML Inventory 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 all: children: web: hosts: web01: ansible_host: 10.10 .1 .11 web02: ansible_host: 10.10 .1 .12 db: hosts: db01: ansible_host: 10.10 .2 .11 prod: children: web: db: vars: ansible_user: ops ansible_python_interpreter: /usr/bin/python3
4.2 INI Inventory 1 2 3 4 5 6 7 8 9 10 11 12 [web] web[01:02] ansible_host =10.10 .1 .[11 :12 ][db] db01 ansible_host =10.10 .2.11 [prod:children] web db[prod:vars] ansible_user =ops
INI 中复杂变量容易产生类型歧义,生产项目更推荐 YAML 与 group_vars。
4.3 分组变量和主机变量 1 2 3 4 5 timezone: Asia/Shanghai ntp_servers: - ntp1.example.com - ntp2.example.com
1 2 3 nginx_worker_connections: 4096 app_port: 8080
1 2 nginx_worker_connections: 8192
原则:
Inventory 只描述环境差异,不存放任务逻辑。
主机名使用稳定资产标识,连接地址放 ansible_host。
密码、Token、私钥等必须通过 Vault 或外部密钥系统管理。
4.4 主机模式 1 2 3 4 5 6 ansible all --list-hosts ansible web --list-hosts ansible 'web:&prod' --list-hosts ansible 'prod:!db' --list-hosts ansible 'web[0]' --list-hosts ansible 'web01,db01' --list-hosts
在执行变更前先用 --list-hosts 验证范围,尤其是 --limit 与复杂模式。
4.5 动态 Inventory 云环境应使用 Inventory 插件,避免手工维护易过期 IP。以 AWS 为例:
1 2 3 4 5 6 7 8 9 10 11 plugin: amazon.aws.aws_ec2 regions: - cn-north-1 filters: instance-state-name: running keyed_groups: - key: tags.Role prefix: role compose: ansible_host: private_ip_address
1 2 3 ansible-galaxy collection install amazon.aws ansible-inventory -i inventories/prod/aws_ec2.yml --graph ansible-inventory -i inventories/prod/aws_ec2.yml --host i-xxxxxxxx
动态 Inventory 插件配置文件名通常必须符合插件要求,例如以 aws_ec2.yml 结尾。
5. Ad-hoc 临时命令 Ad-hoc 适合查询、诊断和一次性低风险操作;需要审计、复用或回滚的变更应写入 Playbook。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ansible all -m ansible.builtin.ping ansible all -m ansible.builtin.setup -a 'filter=ansible_distribution*' ansible web -m ansible.builtin.command -a 'uptime' ansible web -m ansible.builtin.shell -a 'df -h | sort -k5 -h' ansible web -m ansible.builtin.copy \ -a 'src=files/banner dest=/etc/motd owner=root group=root mode=0644' \ --become ansible web -m ansible.builtin.package -a 'name=nginx state=present' --become ansible web -m ansible.builtin.service \ -a 'name=nginx state=started enabled=true' --become ansible all -m ansible.builtin.command -a 'uptime' -f 30 -T 20
command 与 shell 的选择:
默认使用 command,参数不会交给 Shell 解释。
只有确实需要管道、重定向、通配符、变量展开时才用 shell。
shell 输入中包含变量时必须正确引用;不要拼接不可信输入。
系统存在对应模块时优先使用模块,如 user、package、service、uri。
6. Playbook 基础 6.1 第一个 Playbook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 --- - name: Configure web servers hosts: web become: true gather_facts: true tasks: - name: Install Nginx ansible.builtin.package: name: nginx state: present - name: Start and enable Nginx ansible.builtin.service: name: nginx state: started enabled: true
执行:
1 2 3 ansible-playbook playbooks/site.yml --syntax-check ansible-playbook playbooks/site.yml --check --diff ansible-playbook playbooks/site.yml
6.2 YAML 要点
使用空格缩进,禁止 Tab。
布尔值统一写 true / false。
权限模式写成字符串,如 '0644',避免 YAML 数值解析差异。
每个 Play 和 Task 使用清晰的 name。
模块名使用完全限定集合名(FQCN),如 ansible.builtin.copy。
6.3 幂等性 幂等意味着执行一次和重复执行多次得到相同目标状态。第二次运行理想结果应为 changed=0。
不推荐:
1 2 - name: Add configuration line ansible.builtin.shell: echo 'vm.swappiness=10' >> /etc/sysctl.conf
推荐:
1 2 3 4 5 6 7 - name: Configure swappiness ansible.posix.sysctl: name: vm.swappiness value: '10' state: present sysctl_set: true reload: true
如果不得不执行命令,使用 creates、removes、changed_when 等准确描述变化:
1 2 3 4 - name: Initialize application database ansible.builtin.command: cmd: /opt/app/bin/init-db creates: /var/lib/app/.initialized
7. 变量、Facts 与优先级 7.1 定义和引用变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - name: Variable example hosts: web vars: app_name: demo app_port: 8080 app_config: log_level: info workers: 4 tasks: - name: Show application settings ansible.builtin.debug: msg: "{{ app_name }} listens on {{ app_port }} " - name: Show worker count ansible.builtin.debug: var: app_config.workers
仅由变量构成的值应保留原始类型:
1 enabled: "{{ feature_enabled }} "
不要为了“看起来像字符串”在变量内部再次嵌套 {{ }}。
7.2 常见变量来源
Role defaults:roles/x/defaults/main.yml,适合可被覆盖的默认值。
Inventory 的 group_vars、host_vars。
Play 的 vars、vars_files。
Role vars:roles/x/vars/main.yml,优先级高,不适合普通默认配置。
Task/block vars。
注册变量和 set_fact。
命令行 -e 额外变量,优先级最高。
完整优先级规则较多,遇到冲突应以当前版本官方文档和下面命令为准:
1 2 ansible-inventory -i inventories/prod/hosts.yml --host web01 ansible-config dump
实践建议:同一个业务变量尽量只在 Role defaults、环境 group vars、主机特例三个层次定义。不要依赖复杂优先级“技巧”。
7.3 Facts 1 2 3 4 5 6 - name: Print selected facts ansible.builtin.debug: msg: >- {{ inventory_hostname }} runs {{ ansible_facts.distribution }} {{ ansible_facts.distribution_version }}
查看 Facts:
1 2 ansible vm101 -m ansible.builtin.setup ansible vm101 -m ansible.builtin.setup -a 'filter=ansible_default_ipv4'
Facts 成本较高。不需要时可关闭:
1 2 3 - name: API-only operation hosts: localhost gather_facts: false
自定义本地 Facts 可放在受管机 /etc/ansible/facts.d/*.fact,通过 ansible_local 访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 一、两种格式本地 Facts 路径标准:/etc/ansible/facts.d /,目录不存在需要手动创建 1)静态文件(JSON 格式,.fact ) 文件:/etc/ansible/facts.d /app_info.fact json { "env" : "prod" , "zone" : "yun-nan" , "service_name" : "gateway" , "version" : "2.1.0" } 2)可执行脚本(Shell /Python,输出 JSON,.fact ) ansible 会自动执行脚本,捕获 stdout JSON 作为 fact 。 示例 /etc/ansible/facts.d /disk_status.fact bash #!/bin/bashcat <<EOF { "root_used_pct" : $(df -P / | awk 'NR>1 {print $5} ' | sed 's/% "hostname" : "$(hostname)" } EOF 授权: bash chmod +x /etc/ansible/facts.d /disk_status.fact ⚠️ 重要约束 脚本必须输出标准 JSON,不能有多余日志; 必须有执行权限; 文件名后缀必须 .fact 。 二、如何读取自定义 facts 所有本地 facts 会收纳在变量:ansible_local 查看验证命令 bash # 抓取所有local facts ansible test -node -m setup -a 'filter=ansible_local' 假设文件 app_info.fact ,取值方式: jinja2 # jinja2模板 / playbook中 {{ ansible_local.app_info.env }} {{ ansible_local.app_info.service_name }} # 脚本fact disk_status.fact {{ ansible_local.disk_status.root_used_pct }}
7.4 默认值、必填值和省略参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - name: Use safe defaults ansible.builtin.debug: msg: "{{ optional_name | default('unknown') }} " - name: Require a value ansible.builtin.assert: that: - app_version is defined - app_version | length > 0 fail_msg: app_version must be set - name: Optionally set shell ansible.builtin.user: name: "{{ user_name }} " shell: "{{ user_shell | default(omit) }} "
8. 条件、循环、注册变量与重试 8.1 条件 1 2 3 4 5 6 7 8 9 10 11 12 - name: Install package on Debian family ansible.builtin.apt: name: nginx state: present update_cache: true when: ansible_facts.os_family == 'Debian' - name: Install package on Red Hat family ansible.builtin.dnf: name: nginx state: present when: ansible_facts.os_family == 'RedHat'
when 表达式不加 {{ }}。
8.2 循环 1 2 3 4 5 6 7 8 9 - name: Install base packages ansible.builtin.package: name: "{{ base_packages }} " state: present vars: base_packages: - curl - jq - vim
模块支持列表时,一次传列表通常比逐项循环更快。
复杂循环:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - name: Create service users ansible.builtin.user: name: "{{ account.name }} " uid: "{{ account.uid }} " groups: "{{ account.groups | join(',') }} " append: true state: present loop: - name: app uid: 2101 groups: [app , logs ] - name: deploy uid: 2102 groups: [app ] loop_control: loop_var: account label: "{{ account.name }} "
8.3 注册结果 1 2 3 4 5 6 7 8 9 - name: Query service status ansible.builtin.command: systemctl is-active myapp register: app_status changed_when: false failed_when: app_status.rc not in [0 , 3 ]- name: Show status ansible.builtin.debug: var: app_status.stdout
常见返回字段包括 rc、stdout、stderr、changed、failed。循环注册结果位于 result.results。
8.4 等待与重试 1 2 3 4 5 6 7 8 9 10 11 - name: Wait for application health check ansible.builtin.uri: url: "http://127.0.0.1:{{ app_port }} /health" status_code: 200 return_content: true register: health retries: 12 delay: 5 until: - health.status == 200 - health.content is search('UP')
等待端口:
1 2 3 4 5 6 - name: Wait for TCP port ansible.builtin.wait_for: host: 127.0 .0 .1 port: "{{ app_port }} " delay: 2 timeout: 60
9. 文件、模板与 Jinja2 9.1 文件管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 - name: Create application directory ansible.builtin.file: path: /etc/myapp state: directory owner: root group: myapp mode: '0750' - name: Copy static file ansible.builtin.copy: src: myapp.service dest: /etc/systemd/system/myapp.service owner: root group: root mode: '0644' validate: /usr/bin/systemd-analyze verify %s notify: - Reload systemd - Restart myapp
copy 的 src 默认在控制端;remote_src: true 表示源文件已经在受管机。
9.2 Jinja2 模板 模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # templates/nginx.conf.j2 # Managed by Ansible. Manual changes will be overwritten. user {{ nginx_user }}; worker_processes {{ nginx_worker_processes | default('auto') }}; events { worker_connections {{ nginx_worker_connections }}; } http { {% for backend in app_backends %} upstream {{ backend.name }} { {% for server in backend.servers %} server {{ server.host }}:{{ server.port }}; {% endfor %} } {% endfor %} }
任务:
1 2 3 4 5 6 7 8 9 10 - name: Render Nginx configuration ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644' backup: true validate: /usr/sbin/nginx -t -c %s notify: Reload Nginx
validate 会在替换正式文件前校验临时文件,是配置变更的重要防线。
9.3 常用过滤器 1 2 3 4 5 6 normalized_name: "{{ app_name | lower | replace(' ', '-') }} " unique_ports: "{{ ports | unique | sort }} " encoded: "{{ secret_value | b64encode }} " json_text: "{{ payload | to_nice_json }} " yaml_text: "{{ config | to_nice_yaml(indent=2) }} " selected_users: "{{ users | selectattr('enabled', 'equalto', true) | list }} "
处理未受信数据时,不要把模板输出直接拼接进 shell 命令。
10. Handlers、Blocks 与错误控制 10.1 Handlers Handler 仅在收到通知且任务发生变化时执行 ,同名 Handler 每台主机每个 Play 通常只运行一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 tasks: - name: Render application configuration ansible.builtin.template: src: app.yml.j2 dest: /etc/myapp/app.yml mode: '0640' notify: Restart myapp handlers: - name: Restart myapp ansible.builtin.service: name: myapp state: restarted
需要立即执行已通知的 Handler(不需要等待所有的task都执行完毕采取执行handler):
1 2 - name: Flush handlers before verification ansible.builtin.meta: flush_handlers
10.2 Block、Rescue、Always 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 - name: Deploy with recovery block: - name: Install new package ansible.builtin.package: name: "myapp-{{ app_version }} " state: present - name: Verify service ansible.builtin.uri: url: http://127.0.0.1:8080/health status_code: 200 rescue: - name: Restore previous symlink ansible.builtin.file: src: "releases/{{ previous_version }} " dest: /opt/myapp/current state: link force: true - name: Stop deployment after rollback ansible.builtin.fail: msg: "Deployment failed on {{ inventory_hostname }} ; rollback applied" always: - name: Record deployment attempt ansible.builtin.debug: msg: "Deployment attempt finished on {{ inventory_hostname }} "
rescue 处理任务失败,不处理语法错误、主机不可达等所有异常。回滚逻辑必须主动演练。
10.3 失败和变化判定 1 2 3 4 5 6 7 - name: Run application migration check ansible.builtin.command: /opt/myapp/bin/check-migration register: migration changed_when: "'migration required' in migration.stdout" failed_when: - migration.rc != 0 - "'already current' not in migration.stderr"
不要滥用:
它容易掩盖真正故障。应明确允许哪些返回码,或在 rescue 中处理恢复动作。
10.4 全局失败控制 1 2 3 4 5 - name: Critical cluster change hosts: app serial: 20 % any_errors_fatal: true max_fail_percentage: 10
any_errors_fatal:任一主机失败后终止整个 Play。
max_fail_percentage:当前批次失败比例超过阈值时终止。
两者会显著改变发布行为,需在测试环境验证。
11. Role 工程化 11.1 创建 Role 1 ansible-galaxy role init roles/nginx
1 2 3 4 5 6 7 8 9 roles/nginx/ ├── defaults/main.yml # 可覆盖默认值 ├── files/ # 静态文件 ├── handlers/main.yml # Handlers 任务通知执行 ├── meta/main.yml # 元数据和依赖 ├── tasks/main.yml # 任务入口 ├── templates/ # Jinja2 模板 ├── tests/ # 简单测试入口 └── vars/main.yml # 高优先级内部变量
11.2 最小 Role 1 2 3 4 nginx_package_name: nginx nginx_service_name: nginx nginx_worker_connections: 2048
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 --- - name: Install Nginx ansible.builtin.package: name: "{{ nginx_package_name }} " state: present - name: Render Nginx configuration ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644' validate: /usr/sbin/nginx -t -c %s notify: Reload Nginx - name: Enable and start Nginx ansible.builtin.service: name: "{{ nginx_service_name }} " state: started enabled: true
1 2 3 4 5 6 --- - name: Reload Nginx ansible.builtin.service: name: "{{ nginx_service_name }} " state: reloaded
引用 Role:
1 2 3 4 5 6 - name: Configure web tier hosts: web become: true roles: - role: nginx tags: [nginx ]
11.3 Role 设计原则
一个 Role 负责一个清晰职责。
用户可配置项放 defaults/main.yml,内部常量才放 vars/main.yml。
变量加 Role 前缀,如 nginx_worker_connections,避免冲突。
Role 不应偷偷依赖 Inventory 中未说明的魔法变量。
在 README 中记录输入变量、默认值、支持平台、依赖和示例。
一个很短且只使用一次的 Playbook 不必强行拆成 Role。
11.4 include 与 import 1 2 3 4 5 - name: Import static task list ansible.builtin.import_tasks: install.yml - name: Include task list dynamically ansible.builtin.include_tasks: "{{ ansible_facts.os_family | lower }} .yml"
import_* 在解析阶段静态展开,适合结构固定的内容。
include_* 在执行阶段动态加载,可基于运行时变量选择。
Tag 和条件在两者上的传播行为不同,使用前应以小例验证。
12. Collections 与 Galaxy Collection 提供模块、插件、Role 和 Playbook。依赖应进入版本控制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 community.general:通用大量模块(files、command、sysctl、各种零散模块) community.docker:docker 相关模块 kubernetes.core:K8s 模块 ansible.posix:posix 系统模块(mount、firewalld、sysctl 等) --- collections: - name: ansible.posix version: 1.6 .2 - name: community.general version: 9.5 .0 roles: - name: geerlingguy.docker version: 7.4 .3
安装:
1 2 3 ansible-galaxy collection install -r requirements.yml ansible-galaxy role install -r requirements.yml ansible-galaxy collection list
查看文档:
1 2 3 ansible-doc ansible.builtin.copy ansible-doc -l ansible-doc -t inventory amazon.aws.aws_ec2
生产要求:
固定依赖版本并评审升级变更。
内网环境使用私有 Automation Hub/Galaxy 镜像或离线包。
外部 Role 在引入前做源码、安全和许可证审查。
Playbook 中使用 FQCN,避免同名模块冲突。
13. Vault 与敏感信息 13.1 加密变量文件 1 2 3 4 ansible-vault create inventories/prod/group_vars/all/vault.yml ansible-vault edit inventories/prod/group_vars/all/vault.yml ansible-vault view inventories/prod/group_vars/all/vault.yml ansible-vault rekey inventories/prod/group_vars/all/vault.yml
文件内容在解密后可为:
1 2 vault_db_password: change-me vault_api_token: change-me
普通变量文件映射业务变量:
1 2 3 db_password: "{{ vault_db_password }} " api_token: "{{ vault_api_token }} "
执行:
1 ansible-playbook playbooks/site.yml --ask-vault-pass
13.2 Vault ID 多个环境或密钥来源:
1 2 3 4 5 6 ansible-vault encrypt --vault-id dev@prompt inventories/dev/group_vars/all/vault.yml ansible-vault encrypt --vault-id prod@prompt inventories/prod/group_vars/all/vault.yml ansible-playbook playbooks/site.yml \ --vault-id dev@prompt \ --vault-id prod@/secure/path/prod-vault-password
13.3 防止日志泄密 1 2 3 4 5 6 7 8 - name: Configure database password ansible.builtin.template: src: database.yml.j2 dest: /etc/myapp/database.yml owner: root group: myapp mode: '0640' no_log: true
no_log 会降低排障信息,应只用于真正包含敏感数据的任务。Vault 保护的是静态文件,变量解密后仍可能进入日志、命令行参数、临时文件或目标配置。
更成熟的环境应使用 HashiCorp Vault、云 Secret Manager、CyberArk 等外部密钥系统,通过 lookup 插件按需读取并实施短期凭据与轮换。
14. Privilege Escalation 与安全 14.1 become 1 2 3 4 5 6 7 8 9 - name: Configure system service hosts: app become: true become_user: root tasks: - name: Install package ansible.builtin.package: name: myapp state: present
命令行:
1 ansible-playbook playbooks/site.yml --become --ask-become-pass
不要在整个 Play 上启用 become,除非所有任务确实需要。应用 API 调用、健康检查等可用普通身份执行。
14.2 最小权限 sudoers 示例 1 2 3 4 5 6 # /etc/sudoers.d/ansible-ops Defaults:ops !requiretty ops ALL=(root) /usr/bin/systemctl restart myapp, \ /usr/bin/systemctl reload nginx, \ /usr/bin/apt-get, \ /usr/bin/dnf
实际模块可能调用临时脚本或其他程序,严格命令白名单需结合执行模型验证。更常见的生产做法是限制控制节点来源、SSH 密钥、目标主机范围和自动化入口,同时审计 sudo。
14.3 安全基线
自动化账号禁用密码登录,使用专用短期证书或受管 SSH 密钥。
使用 known_hosts 校验服务端身份。
CI 密钥不落盘,不输出到日志。
Inventory、Vault 密文、Playbook 全部纳入版本控制;Vault 密码不能入库。
控制节点限制登录,执行日志集中留存。
第三方 Collection 固定版本并进行供应链审查。
对危险变量使用 assert 建立前置条件。
1 2 3 4 5 6 - name: Validate deployment target ansible.builtin.assert: that: - deploy_environment in ['dev' , 'staging' , 'prod' ] - app_version is match('^[0-9]+\\.[0-9]+\\.[0-9]+$') fail_msg: Invalid deployment parameters
15. 批次、委派与滚动发布 15.1 serial 1 2 3 4 5 6 7 8 - name: Rolling update hosts: app serial: 2 max_fail_percentage: 0 tasks: - name: Update application ansible.builtin.include_role: name: myapp
逐步扩大批次:
1 2 3 4 5 serial: - 1 - 10 % - 25 % - 100 %
先发布一台金丝雀,再逐步扩大范围。批次大小应小于负载均衡和容量允许的同时离线节点数。
15.2 delegate_to 1 2 3 4 5 6 7 8 9 - name: Remove node from load balancer ansible.builtin.uri: url: "https://lb-api.example.com/nodes/{{ inventory_hostname }} " method: DELETE headers: Authorization: "Bearer {{ lb_api_token }} " status_code: [200 , 204 , 404 ] delegate_to: localhost no_log: true
委派(把当前的task换到另外的主机上去执行 )任务仍处于当前 Inventory 主机上下文。变量来自谁、结果注册到谁必须明确。
15.3 run_once 1 2 3 4 - name: Run database migration once ansible.builtin.command: /opt/myapp/bin/migrate run_once: true delegate_to: "{{ groups['app'][0] }} "
注意:配合 serial 时,run_once 可能对每个批次执行一次。真正全局只运行一次的迁移更适合独立 Play,并明确指定执行节点。
15.4 strategy 与 throttle 1 2 3 4 5 6 7 8 9 10 11 - name: Independent checks hosts: all strategy: free tasks: - name: Collect service status ansible.builtin.service_facts: - name: Call rate-limited API ansible.builtin.uri: url: https://api.example.com/check throttle: 3
默认 linear:同一批主机大致按任务同步推进。
free:每台主机独立推进,适合互不依赖的任务。
throttle:限制某任务/Block 的并发数。
有共享状态、集群仲裁或严格发布顺序时,不要随意使用 free。
16. Tag、Check Mode 与 Diff 1 2 3 4 5 6 7 - name: Install packages ansible.builtin.import_tasks: install.yml tags: [install ]- name: Configure service ansible.builtin.import_tasks: configure.yml tags: [config ]
1 2 3 ansible-playbook playbooks/site.yml --list-tags ansible-playbook playbooks/site.yml --tags config ansible-playbook playbooks/site.yml --skip-tags install
保留标签:
always:默认总执行,除非显式跳过。
never:默认不执行,只有显式选择时执行,适合高风险维护动作。
16.2 Check Mode 1 ansible-playbook playbooks/site.yml --check --diff --limit web01
Check Mode 是预测,不是事务:
部分模块不支持或支持不完整。
依赖前序任务实际产物的后续任务可能失败。
外部 API、命令和脚本未必能可靠预测变化。
--diff 可能暴露敏感配置,CI 日志需谨慎。
个别任务控制:
1 2 3 4 - name: Read-only check should always run ansible.builtin.command: /opt/myapp/bin/status check_mode: false changed_when: false
生产发布推荐顺序:
--syntax-check
ansible-lint
测试环境执行
生产 --check --diff --limit 小范围预演
生产金丝雀实际执行
扩大批次并观察监控
17. 生产实战一:部署 Nginx 17.1 变量 1 2 3 4 5 6 7 8 9 nginx_user: www-data nginx_worker_processes: auto nginx_worker_connections: 4096 nginx_server_name: app.example.com nginx_listen_port: 80 app_upstream_servers: - 10.10 .3 .11 :8080 - 10.10 .3 .12 :8080
17.2 站点模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 # roles/nginx/templates/nginx.conf.j2 user {{ nginx_user }}; worker_processes {{ nginx_worker_processes }}; events { worker_connections {{ nginx_worker_connections }}; } http { include /etc/nginx/mime.types; upstream app_backend { {% for server in app_upstream_servers %} server {{ server }} max_fails=3 fail_timeout=10s; {% endfor %} keepalive 32; } server { listen {{ nginx_listen_port }}; server_name {{ nginx_server_name }}; access_log /var/log/nginx/app.access.log; error_log /var/log/nginx/app.error.log warn; location /healthz { access_log off; return 200 "ok\n"; } location / { proxy_pass http://app_backend; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Connection ""; } } }
17.3 Role 任务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 --- - name: Install Nginx ansible.builtin.package: name: nginx state: present - name: Deploy Nginx configuration ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644' backup: true validate: /usr/sbin/nginx -t -c %s notify: Reload Nginx - name: Enable and start Nginx ansible.builtin.service: name: nginx enabled: true state: started - name: Flush configuration handler ansible.builtin.meta: flush_handlers - name: Verify local health endpoint ansible.builtin.uri: url: "http://127.0.0.1:{{ nginx_listen_port }} /healthz" status_code: 200 return_content: true register: nginx_health failed_when: nginx_health.status != 200 or nginx_health.content != 'ok\n'
17.4 Handler 1 2 3 4 5 6 --- - name: Reload Nginx ansible.builtin.service: name: nginx state: reloaded
17.5 Playbook 1 2 3 4 5 6 7 8 9 --- - name: Configure Nginx servers hosts: web become: true serial: 25 % max_fail_percentage: 0 roles: - nginx
验证:
1 2 3 4 5 6 ansible-playbook playbooks/nginx.yml --syntax-check ansible-lint playbooks/nginx.yml roles/nginx ansible-playbook playbooks/nginx.yml --check --diff --limit web01 ansible-playbook playbooks/nginx.yml --limit web01 ansible-playbook playbooks/nginx.yml ansible-playbook playbooks/nginx.yml
18. 生产实战二:滚动发布应用 假设制品已由 CI 构建并发布到制品库;Ansible 负责下载、校验、切换软链接、重启和验证,不在生产主机现场编译。
18.1 发布变量 1 2 3 4 5 6 7 app_name: myapp app_version: 2.4 .1 app_root: /opt/myapp app_release_dir: "{{ app_root }} /releases/{{ app_version }} " app_artifact_url: "https://artifacts.example.com/myapp/{{ app_version }} /myapp.tar.gz" app_artifact_checksum: "sha256:0123456789abcdef..." app_health_url: http://127.0.0.1:8080/health
版本号和校验值应来自已批准的发布单或 CI 产物元数据,不能使用不稳定的 latest。
18.2 滚动发布 Playbook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 --- - name: Rolling deploy application hosts: app become: true serial: - 1 - 25 % - 100 % max_fail_percentage: 0 pre_tasks: - name: Validate release inputs ansible.builtin.assert: that: - app_version is match('^[0-9]+\\.[0-9]+\\.[0-9]+$') - app_artifact_checksum is match('^sha256:[0-9a-f]{64}$') - name: Disable node in load balancer ansible.builtin.uri: url: "{{ lb_api_url }} /nodes/{{ inventory_hostname }} /disable" method: POST headers: Authorization: "Bearer {{ lb_api_token }} " status_code: [200 , 204 ] delegate_to: localhost become: false no_log: true - name: Wait for load balancer connections to drain ansible.builtin.uri: url: "{{ lb_api_url }} /nodes/{{ inventory_hostname }} /status" method: GET headers: Authorization: "Bearer {{ lb_api_token }} " status_code: 200 return_content: true register: lb_node_status retries: 24 delay: 5 until: lb_node_status.json.active_connections | int == 0 delegate_to: localhost become: false no_log: true tasks: - name: Read current release link ansible.builtin.stat: path: "{{ app_root }} /current" register: current_link - name: Remember previous release ansible.builtin.set_fact: previous_release: "{{ current_link.stat.lnk_source | default('') }} " - name: Deploy release block: - name: Create release directory ansible.builtin.file: path: "{{ app_release_dir }} " state: directory owner: myapp group: myapp mode: '0750' - name: Download verified artifact ansible.builtin.get_url: url: "{{ app_artifact_url }} " dest: "{{ app_release_dir }} /myapp.tar.gz" checksum: "{{ app_artifact_checksum }} " owner: myapp group: myapp mode: '0640' - name: Extract artifact ansible.builtin.unarchive: src: "{{ app_release_dir }} /myapp.tar.gz" dest: "{{ app_release_dir }} " remote_src: true owner: myapp group: myapp creates: "{{ app_release_dir }} /bin/myapp" - name: Point current link to new release ansible.builtin.file: src: "{{ app_release_dir }} " dest: "{{ app_root }} /current" state: link force: true notify: Restart myapp - name: Apply service restart now ansible.builtin.meta: flush_handlers - name: Wait for healthy application ansible.builtin.uri: url: "{{ app_health_url }} " status_code: 200 register: health retries: 18 delay: 5 until: health.status == 200 rescue: - name: Restore previous release link ansible.builtin.file: src: "{{ previous_release }} " dest: "{{ app_root }} /current" state: link force: true when: previous_release | length > 0 - name: Restart previous release ansible.builtin.service: name: myapp state: restarted when: previous_release | length > 0 - name: Fail this deployment batch ansible.builtin.fail: msg: "Release {{ app_version }} failed on {{ inventory_hostname }} and was rolled back" post_tasks: - name: Enable node in load balancer ansible.builtin.uri: url: "{{ lb_api_url }} /nodes/{{ inventory_hostname }} /enable" method: POST headers: Authorization: "Bearer {{ lb_api_token }} " status_code: [200 , 204 ] delegate_to: localhost become: false no_log: true handlers: - name: Restart myapp ansible.builtin.service: name: myapp state: restarted
重要边界:如果部署在 rescue 后失败,普通 post_tasks 不一定按预期完成所有恢复流程。生产版本应将“重新加入负载均衡”设计为经过健康检查的显式恢复步骤,并测试 Ansible 进程中断、目标失联和控制节点故障。
18.3 数据库迁移 数据库迁移不要隐式混在每台应用主机的 Role 中。推荐独立 Play:
1 2 3 4 5 6 7 8 9 10 11 - name: Run approved database migration hosts: migration_runner become: true serial: 1 tasks: - name: Apply migration ansible.builtin.command: cmd: "/opt/myapp/releases/{{ app_version }} /bin/migrate" register: migration changed_when: "'applied' in migration.stdout" tags: [never , migrate ]
执行时显式启用:
1 ansible-playbook playbooks/deploy.yml --tags migrate -e app_version=2.4.1
迁移应具备备份、兼容窗口、锁策略和独立回滚方案。应用二进制回滚不等于数据库结构回滚。
19. 生产实战三:系统基线与补丁 19.1 基线配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 --- - name: Apply Linux baseline hosts: linux become: true tasks: - name: Set timezone community.general.timezone: name: Asia/Shanghai - name: Install baseline packages ansible.builtin.package: name: - curl - rsync - chrony state: present - name: Enable time synchronization ansible.builtin.service: name: chronyd enabled: true state: started when: ansible_facts.os_family == 'RedHat' - name: Configure file descriptor limit community.general.pam_limits: domain: '*' limit_type: soft limit_item: nofile value: '65535' - name: Configure kernel settings ansible.posix.sysctl: name: "{{ item.name }} " value: "{{ item.value }} " state: present sysctl_set: true reload: true loop: - name: net.ipv4.tcp_syncookies value: '1' - name: fs.file-max value: '2097152'
基线参数必须结合内核版本、业务负载和安全标准评审,不能机械复制。
19.2 Debian/Ubuntu 安全更新 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 - name: Apply security updates on Debian family hosts: debian become: true serial: 10 % tasks: - name: Update APT cache ansible.builtin.apt: update_cache: true cache_valid_time: 3600 - name: Upgrade installed packages ansible.builtin.apt: upgrade: safe - name: Check whether reboot is required ansible.builtin.stat: path: /var/run/reboot-required register: reboot_required - name: Reboot if required ansible.builtin.reboot: reboot_timeout: 900 when: reboot_required.stat.exists
19.3 RHEL 系安全更新 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 - name: Apply security updates on Red Hat family hosts: redhat become: true serial: 10 % tasks: - name: Install security updates ansible.builtin.dnf: name: '*' state: latest security: true - name: Check whether reboot is required ansible.builtin.command: dnf needs-restarting -r register: reboot_check changed_when: false failed_when: reboot_check.rc not in [0 , 1 ] - name: Reboot if required ansible.builtin.reboot: reboot_timeout: 900 when: reboot_check.rc == 1
补丁流程至少包含:维护窗口、业务摘流、备份/快照、分批、重启、健康检查、监控观察、失败终止和回滚决策。虚拟机快照不是数据库一致性备份的替代品。
20. 测试、质量门禁与 CI 20.1 本地检查 1 2 3 4 yamllint . ansible-playbook playbooks/site.yml --syntax-check ansible-lint ansible-inventory -i inventories/dev/hosts.yml --graph
.ansible-lint 示例:
1 2 3 4 5 6 --- profile: production exclude_paths: - .cache/ - .venv/ mock_roles: []
ansible-lint 规则不应被大面积跳过。确需跳过时,在最小范围注明原因。
20.2 幂等性测试 1 2 ansible-playbook -i inventories/test/hosts.yml playbooks/site.yml ansible-playbook -i inventories/test/hosts.yml playbooks/site.yml
第二次运行应无变化。若必须每次变化,应明确记录原因,例如生成时间戳或轮换一次性凭据。
20.3 Molecule 思路 Molecule 可创建临时实例、执行 Role、验证结果并销毁环境。典型阶段:
1 dependency → create → prepare → converge → idempotence → verify → destroy
验证不应只检查 Playbook “没有报错”,还应检查目标状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 --- - name: Verify Nginx role hosts: all gather_facts: false tasks: - name: Request health endpoint ansible.builtin.uri: url: http://127.0.0.1/healthz status_code: 200 - name: Read service facts ansible.builtin.service_facts: - name: Assert Nginx is running ansible.builtin.assert: that: - ansible_facts.services['nginx.service'].state == 'running'
20.4 CI 流程 建议门禁:
1 2 3 4 5 6 7 8 9 10 提交 → YAML/语法检查 → ansible-lint → 单元/模板测试 → Molecule converge + idempotence + verify → 测试环境部署 → 人工审批生产参数 → 金丝雀发布 → 指标观察 → 分批扩大发布
CI 中必须固定 Python、Ansible、Collection 和 Role 版本,并对实际使用的 Inventory/变量组合做验证。
21. 性能优化 21.1 首先测量 启用耗时回调(需确认当前版本与 Collection 提供情况),找出慢任务,而不是盲目调大并发。
常见瓶颈:SSH 建连、Facts 收集、包仓库、逐项循环、文件传输、串行 API、目标机 I/O。
21.2 优化手段
SSH 复用与 pipelining
1 2 3 [ssh_connection] pipelining = True ssh_args = -o ControlMaster=auto -o ControlPersist=60 s
合理 forks
1 2 3 4 5 [defaults] forks = 30
并发受控制节点 CPU/内存、网络、目标资源和外部 API 限流共同约束。
减少 Facts
或仅收集需要的子集:
1 2 3 4 5 gather_facts: true gather_subset: - '!all' - min - network
批量传参而非逐项循环 (前提是模块支持批量传递)
1 2 3 4 - name: Install packages in one transaction ansible.builtin.package: name: "{{ package_list }} " state: present
缓存 Facts
1 2 3 4 5 [defaults] gathering = smartfact_caching = jsonfilefact_caching_connection = .cache/factsfact_caching_timeout = 3600
缓存会变旧,不要让关键安全决策依赖过期 Facts。
异步任务
1 2 3 4 5 6 7 8 9 10 11 12 13 - name: Start long-running package update ansible.builtin.command: /usr/local/sbin/long-update async: 1800 poll: 0 register: update_job - name: Wait for update ansible.builtin.async_status: jid: "{{ update_job.ansible_job_id }} " register: update_result until: update_result.finished retries: 180 delay: 10
异步并不自动降低目标系统负载。数据库、包仓库和共享存储仍需限流。
22. 调试与故障排查 22.1 分层定位 按以下顺序排查:
范围 :Inventory 是否选中了正确主机?
网络 :DNS、路由、TCP 22/WinRM 是否可达?
SSH :用户、密钥、Host Key、跳板机是否正确?
Python :解释器是否存在、版本是否兼容?
权限 :普通用户和 become 是否有权操作目标?
变量 :实际合并后的变量值是什么?
模块 :模块参数、返回值和远端依赖是否正确?
业务状态 :配置校验、服务日志、端口和健康检查是否正常?
22.2 常用诊断命令 1 2 3 4 5 6 7 8 9 ansible-inventory -i inventories/prod/hosts.yml --graph ansible-inventory -i inventories/prod/hosts.yml --host web01 ansible web01 -m ansible.builtin.ping -vvvv ansible web01 -m ansible.builtin.setup -a 'filter=ansible_python*' ansible-playbook playbooks/site.yml --syntax-check ansible-playbook playbooks/site.yml --list-hosts ansible-playbook playbooks/site.yml --list-tasks ansible-playbook playbooks/site.yml --step ansible-playbook playbooks/site.yml --start-at-task 'Render Nginx configuration'
-vvvv 可能打印连接细节和敏感上下文,日志共享前先脱敏。
22.3 debug、assert 与 fail 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - name: Show safe deployment context ansible.builtin.debug: msg: host: "{{ inventory_hostname }} " version: "{{ app_version }} " environment: "{{ deploy_environment }} " - name: Ensure enough free space ansible.builtin.assert: that: - ansible_facts.mounts | selectattr('mount', 'equalto' , '/opt' ) | map(attribute='size_available') | first > 1073741824 fail_msg: At least 1 GiB free space is required on /opt
22.4 常见故障
现象
常见原因
处理
UNREACHABLE
SSH 用户/密钥、路由、Host Key
先手工 SSH,再用 -vvvv
Python not found
目标无 Python 或路径不同
用 raw 安装,设置解释器
Missing sudo password
become 配置不完整
配置 sudo 或使用 --ask-become-pass
变量未定义
分组错误、文件位置错误、拼写错误
用 ansible-inventory --host 查看
YAML 解析失败
缩进、冒号、引号错误
yamllint + --syntax-check
每次都 changed
命令不幂等、模板内容波动
使用状态模块或准确的 changed_when
Handler 未执行
通知任务未 changed 或前面失败
查看 changed 状态,必要时 flush_handlers
模块找不到
Collection 未安装或名称错误
安装依赖并使用 FQCN
Check Mode 结果异常
模块不完整支持预演
查看模块文档,在隔离环境实测
模板替换后服务失败
未做语法校验
使用 validate,再通知 reload/restart
22.5 不可达主机恢复 1 2 - name: Retry after transient network recovery ansible.builtin.meta: clear_host_errors
这只清除 Ansible 的失败状态,不会修复网络。应仅在恢复动作确实可能让主机重新可达时使用。
23. AWX、Automation Controller 与执行环境 当团队需要 RBAC、审批、凭据托管、调度、Webhook、审计和统一执行环境时,可使用 AWX 或 Red Hat Ansible Automation Platform 的 Automation Controller。
核心对象:
对象
用途
Organization/Team/User
多租户与权限
Project
Git 中的自动化代码
Inventory
静态或动态主机来源
Credential
SSH、Vault、云凭据等
Job Template
Playbook、Inventory、凭据和参数组合
Workflow
多个 Job 的编排、审批和分支
Execution Environment
固定 Ansible 与依赖的容器镜像
23.1 执行环境 执行环境解决“本地能跑、平台不能跑”的依赖漂移问题。镜像中应固定:
ansible-core 版本
Collections
Python 包
系统级依赖
必要的 CLI 工具
execution-environment.yml 示例:
1 2 3 4 5 6 7 8 9 --- version: 3 dependencies: galaxy: requirements.yml python: requirements.txt system: bindep.txt images: base_image: name: quay.io/ansible/ansible-runner:stable-2.17-latest
镜像标签应在正式环境进一步固定到不可变摘要,并进行漏洞扫描和签名验证。
23.2 平台化原则
普通用户只能启动批准的模板,不能读取原始凭据。
生产 Inventory、凭据与 Job Template 分权管理。
高风险模板加入审批节点与变更单号。
Survey/extra vars 建立选项约束,不接受任意命令文本。
日志发送到集中平台,设置保留周期和敏感信息策略。
同一份代码通过 Inventory 和变量区分环境,不复制多份 Playbook。
24. 生产规范与检查清单 24.1 编码规范
24.2 发布前
24.3 发布中
24.4 发布后
25. 常用命令速查 Inventory 1 2 3 4 ansible-inventory --graph ansible-inventory --list ansible-inventory --host web01 ansible all --list-hosts
连通与信息 1 2 3 ansible all -m ansible.builtin.ping ansible all -m ansible.builtin.setup ansible all -m ansible.builtin.command -a 'uptime'
Playbook 1 2 3 4 5 6 7 8 9 ansible-playbook playbooks/site.yml --syntax-check ansible-playbook playbooks/site.yml --list-hosts ansible-playbook playbooks/site.yml --list-tasks ansible-playbook playbooks/site.yml --check --diff ansible-playbook playbooks/site.yml --limit web01 ansible-playbook playbooks/site.yml --tags config ansible-playbook playbooks/site.yml --skip-tags reboot ansible-playbook playbooks/site.yml -e app_version=2.4.1 ansible-playbook playbooks/site.yml -vvv
Vault 1 2 3 4 5 6 ansible-vault create secrets.yml ansible-vault edit secrets.yml ansible-vault view secrets.yml ansible-vault encrypt plain.yml ansible-vault decrypt encrypted.yml ansible-vault rekey secrets.yml
Galaxy 与文档 1 2 3 4 5 ansible-galaxy collection install -r requirements.yml ansible-galaxy role install -r requirements.yml ansible-galaxy collection list ansible-doc ansible.builtin.template ansible-doc -t lookup ansible.builtin.env
配置与质量 1 2 3 4 5 ansible --version ansible-config dump --only-changed ansible-config list yamllint . ansible-lint
26. 进阶路线与练习 阶段一:入门 目标:独立管理 3 台实验主机。
配置 SSH 密钥和 Inventory。
使用 ping、setup、command、package、service。
编写安装 Nginx 的 Playbook。
加入变量、模板和 Handler。
验证第二次运行 changed=0。
阶段二:熟练 目标:形成可复用、可测试的项目。
将 Nginx Playbook 重构为 Role。
创建 dev/staging/prod 三套 Inventory。
用 Vault 管理测试密码。
使用 assert、block/rescue 和配置 validate。
接入 yamllint、ansible-lint 和 Molecule。
阶段三:生产 目标:完成可靠的滚动发布。
设计负载均衡摘流与恢复流量任务。
使用 serial 实现金丝雀和分批发布。
下载带 SHA-256 校验的固定版本制品。
实现软链接切换、健康检查和失败回滚。
演练目标失联、服务启动失败、制品损坏和控制节点中断。
阶段四:精通 目标:把自动化建设为团队平台能力。
设计 Role/Collection 边界和版本策略。
建立执行环境,固定全部依赖。
使用 AWX/Controller 建立 RBAC、审批、调度和审计。
对接 CMDB 动态 Inventory 和企业密钥系统。
建立 SLO:执行成功率、平均耗时、变更失败率、回滚耗时。
通过故障注入和恢复演练验证自动化,而不只验证成功路径。
结语 掌握 Ansible 的关键不在于记住多少模块,而在于形成一套可靠的变更方法:
1 2 3 4 5 6 7 8 9 明确目标状态 → 缩小执行范围 → 验证输入和前置条件 → 使用幂等模块实施变更 → 配置校验与健康检查 → 分批扩大范围 → 失败即停止并按预案恢复 → 留下可追溯记录 → 再次执行验证幂等性
任何进入生产的 Playbook,都应被视为软件:需要版本控制、代码评审、自动测试、依赖锁定、发布审批、监控和持续维护。