Ansible剧本与变量实战全攻略
Ansible剧本与变量实战全攻略
一、概述
本文聚焦Ansible核心进阶功能,全面讲解Playbook剧本的编写规范、实操案例、流程控制(handlers/when/loop)、剧本调试技巧、Jinja2模板高级用法、文件包含(include)、模块化部署(Roles)、敏感信息加密(Vault)、第三方Roles共享(Galaxy) 及Ansible性能与安全优化,覆盖Ansible从基础到高阶的全量核心知识点。所有内容均结合实际运维场景设计案例,适配运维日常应急响应、项目维护、架构巡检、服务标准化部署等核心工作场景。本文从基础语法到实战部署层层递进,知识点全覆盖且实操性强,助力快速掌握集群批量部署的剧本化、变量化、模块化、安全化管理能力,完成Ansible自动化运维的全技能闭环。
二、核心知识点
2.1 Ansible Playbook剧本核心知识点
- 核心概念:Playbook(剧本)是Ansible用于持久化保存批量管理、部署、维护操作的YAML格式文件,存放模块与操作步骤,可重复调用,是Ansible实现自动化部署的核心。
- 与Ad-hoc模式的对比:二者均基于Ansible模块实现批量管理,核心差异在于复用性和应用场景,具体对比如下:
| 特性 | Ansible Playbook | Ansible Ad-hoc |
|---|---|---|
| 复用性 | 可重复调用、持久化保存 | 临时执行,不易重复使用 |
| 操作类型 | 多步骤、复杂服务部署 | 单步骤、临时测试/操作 |
| 应用场景 | 集群服务部署、标准化运维 | 模块测试、临时批量执行命令 |
- YAML格式规范:Playbook的核心书写规则,直接决定剧本是否能正常执行:
- 以
.yml或.yaml为后缀; - 同一层级内容左对齐,不同层级通过2个空格缩进区分;
- 禁止使用Tab键缩进;
- 键值对以
键: 值形式书写,冒号后需加1个空格。
- 以
- 核心结构:单个Playbook由一个或多个
Play组成,每个Play包含核心要素:hosts:指定要管理的主机/主机组(对应主机清单);vars/vars_files:定义/加载变量(可选);tasks:任务列表,由多个task组成,每个task对应一个Ansible模块的执行操作;handlers:触发器任务,由普通任务触发执行;roles:加载模块化Roles(新增);gather_facts:是否收集主机Facts信息(默认开启,可选关闭)。
- 剧本执行核心流程:先收集主机Facts信息(默认),再按
tasks中的顺序依次执行每个任务,若任务触发handlers则在当前Play的所有任务执行完成后运行触发器,所有操作完成后返回执行结果(成功/失败/跳过)。
2.2 Ansible变量核心知识点
- 核心作用:类似于Shell脚本中的变量,用于统一管理剧本中的可变参数(如路径、IP、用户名),提升剧本的通用性和可维护性,支持在剧本多位置定义、多场景调用。
- 定义与加载位置:按使用场景和生效范围划分,常用定义位置及说明如下,星标为运维高频使用:
| 定义位置 | 生效范围 | 适用场景 | 优先级 |
|---|---|---|---|
剧本内vars⭐⭐ |
仅当前Play | 单剧本、少量可变参数 | 中 |
独立变量文件vars_files |
仅当前Play | 单剧本、多可变参数 | 中 |
主机组变量group_vars⭐⭐⭐⭐⭐ |
指定主机组/所有主机 | 多剧本共用参数、标准化部署 | 高 |
| Roles内置变量(新增) | 仅当前Role | 模块化部署中Role专属参数 | 中 |
| Facts变量⭐⭐ | 所有Play(对应主机) | 获取主机基础信息(IP、CPU、系统) | 内置 |
| Register注册变量⭐⭐ | 仅当前Play | 捕获命令执行结果、实现脚本反引号功能 | 中 |
| 主机清单中定义 | 指定主机/主机组 | 批量修改主机参数(极少用) | 低 |
| 命令行定义 | 单次剧本执行 | 临时修改参数(几乎不用) | 最高 |
- 变量引用规则:使用
{{ 变量名 }}引用,若变量为选项开头,必须加双引号;变量非开头,可省略引号。 - 核心变量工具:
setup模块:手动获取主机Facts变量信息;template模块:解析Jinja2模板文件中的变量并分发(区别于仅做文件拷贝的copy模块);debug模块:测试/输出变量内容,是变量调试的核心模块。
2.3 Ansible流程控制核心知识点⭐⭐⭐⭐
流程控制是Ansible实现条件化执行、批量执行、触发式执行的核心,包含handlers(触发器)、when(条件判断)、loop/with_items(循环)三类核心功能,是提升剧本灵活性的关键,核心信息如下:
| 流程控制功能 | 核心概念 | 核心应用场景 | 关键规则 |
|---|---|---|---|
handlers触发器 |
由普通任务触发执行的特殊任务,仅当触发任务的状态为changed时才运行 |
分发配置文件后,配置变化则重启服务,无变化则不执行 | 1. 需通过notify指定触发名称;2. 需写在剧本最后;3. 触发名称与handlers任务名一致 |
when条件判断 |
为任务设置执行条件,满足条件则执行,不满足则跳过 | 1. 按系统版本(CentOS/Ubuntu)执行不同安装命令;2. 按主机名/IP执行指定操作;3. 结合变量做逻辑判断 | 1. 与Facts/Register变量配合使用;2. 支持and/or/is match/!=等逻辑/匹配符号;3. 多条件需用括号包裹 |
loop/with_items循环 |
实现任务的批量迭代执行,替代重复书写相同任务 | 1. 批量启动/重启服务;2. 批量创建用户/目录;3. 批量分发文件 | 1. loop为官方推荐用法,与with_items功能一致;2. 用{{ item }}引用循环变量;3. 支持单变量/键值对多变量循环 |
2.4 Ansible剧本调试核心知识点⭐⭐⭐
剧本调试是解决Playbook执行失败、优化剧本的关键,Ansible提供语法检查、单步执行、tag标签、忽略错误四类核心调试技巧,适配不同调试场景,核心信息如下:
| 调试技巧 | 核心命令/参数 | 应用场景 | 关键说明 |
|---|---|---|---|
| 语法检查 | ansible-playbook --syntax-check 剧本.yml |
检查剧本YAML语法、模块参数错误 | 仅检查语法,不实际执行任务 |
| 模拟运行 | ansible-playbook -C 剧本.yml |
预览任务执行结果,不修改被控端文件/配置 | 部分变量可能报错,因未实际执行任务 |
| 单步执行 | 无专属命令,执行时手动选择y/n/c |
逐任务调试,定位具体失败的任务 | y执行当前任务,n跳过,c自动执行后续所有任务 |
tag标签 |
任务中定义tags: 标签名,执行时-t 标签名/--skip-tags 标签名 |
1. 仅执行剧本中的指定任务;2. 跳过剧本中的指定任务 | 多标签用逗号分隔,支持查看剧本所有标签 |
| 忽略错误 | 任务中添加ignore_errors: true |
忽略任务执行的非致命错误(如目录已存在、用户已创建),保证剧本继续运行 | 仅适用于重复执行导致的非真实错误,不可全局使用 |
2.5 Jinja2模板核心知识点⭐⭐⭐⭐
Jinja2是Ansible的模板引擎,与template模块配合使用,可在文件中实现变量引用、条件判断、循环迭代,是实现配置文件动态生成的核心,适配Nginx/Keepalived/NFS等服务的个性化配置分发,核心规则如下:
- 模板文件后缀:必须以
.j2为后缀,template模块默认解析该格式文件; - 核心语法标签:
{{ 变量名 }}:变量引用标签,与Ansible变量引用规则一致;{% 逻辑语句 %}:逻辑控制标签,用于书写if判断、for循环,需以{% endif %}/{% endfor %}结尾;{# 注释内容 #}:注释标签,模板渲染时会忽略该内容,区别于文件原生注释;
- 核心功能:支持变量解析、多条件判断、多类型循环(列表/数字范围/键值对),渲染后再分发至被控端;
- 与copy模块的区别:
copy仅做文件纯拷贝,不解析任何内容;template先渲染模板(解析变量/逻辑),再将渲染后的文件分发至被控端。
2.6 Ansible文件包含(include_tasks)核心知识点⭐⭐
- 核心概念:
include_tasks是Ansible用于拆分大型Playbook的功能,将一个复杂的多Play/多任务剧本拆分为多个小型子剧本,通过include_tasks在主剧本中加载,实现剧本的模块化拆分与合并。 - 核心应用场景:
- 大型服务部署剧本(如同时包含服务端、客户端、监控配置),拆分后便于维护和调试;
- 多个剧本共用相同的任务序列(如环境初始化任务),抽离为子剧本后重复引用;
- 关键规则:
- 子剧本需遵循标准YAML格式,仅包含
tasks列表(无需hosts等Play级配置); - 主剧本通过
- include_tasks: 子剧本.yml加载子任务,加载后与主剧本的tasks按顺序执行; - 子剧本中可使用主剧本定义的变量,变量作用域贯通。
- 子剧本需遵循标准YAML格式,仅包含
2.7 Ansible Roles核心知识点⭐⭐⭐⭐⭐(运维高频)
- 核心概念:Roles是Ansible对剧本目录结构的标准化规范,本质是按功能模块划分的目录集合,将剧本、变量、配置文件、模板、触发器等按固定目录分类存储,实现剧本的高度模块化、可复用、易维护,是企业级Ansible自动化运维的核心方式。
- 核心目录结构:Roles的目录规范为固定格式,新建Role时需按以下结构创建,Ansible会自动识别加载对应目录下的文件:
role名称/ # Role核心目录(如nfs-server/、rsync-server/)
├── files/ # 存放无需解析的静态文件(如普通配置文件、脚本),copy模块直接引用
├── templates/ # 存放Jinja2模板文件(.j2后缀),template模块自动读取
├── tasks/ # 核心任务目录,必须包含main.yml(任务入口文件),按顺序执行
├── handlers/ # 触发器目录,必须包含main.yml(触发器入口文件)
├── vars/ # Role专属变量目录,main.yml中定义变量,仅当前Role生效
└── defaults/ # 默认变量目录(可选),优先级低于vars,用于定义默认参数
- 核心执行逻辑:
- 通过入口剧本(如top.yml)指定
roles: - role: Role名称,加载对应的Role; - Ansible自动按
tasks/main.yml→handlers/main.yml的顺序执行,自动读取files/、templates/目录下的文件; - Role变量可通过
group_vars/、vars/或命令行传入,实现参数灵活配置。
- 通过入口剧本(如top.yml)指定
- 核心优势:
- 目录结构标准化,团队协作时无需熟悉整体剧本,仅关注单个Role的功能;
- 可复用性极强,一个Role可在多个项目、多个环境中直接引用;
- 变量与代码分离,通过变量文件即可适配不同环境(开发/测试/生产)。
2.8 Ansible Vault核心知识点⭐⭐⭐(安全必备)
- 核心概念:Vault是Ansible用于加密敏感信息的工具,可对变量文件、主机清单、剧本中的敏感数据(如密码、密钥、端口)进行加密,避免敏感信息明文存储,提升Ansible运维的安全性。
- 支持加密的文件类型:
- 变量文件(如vars.yml、group_vars/all/vars.yml);
- 主机清单文件(hosts);
- 包含敏感信息的Playbook剧本文件;
- 核心操作命令:
| 操作类型 | 命令 | 说明 |
|---|---|---|
| 加密文件 | ansible-vault encrypt 文件名 |
对指定文件进行加密,需设置加密密码 |
| 解密文件 | ansible-vault decrypt 文件名 |
对加密文件彻底解密,需输入加密密码 |
| 查看加密文件 | ansible-vault view 文件名 |
不解密文件,直接查看内容,需输入密码 |
| 编辑加密文件 | ansible-vault edit 文件名 |
临时解密并编辑文件,保存后自动重新加密 |
| 批量加密 | ansible-vault encrypt 文件1 文件2 |
同时加密多个文件,共用一个加密密码 |
- 执行加密剧本:加密后的文件无法直接执行,需在执行时通过以下方式提供密码:
- 交互式输入:
ansible-playbook --ask-vault-pass 加密剧本.yml,执行时手动输入密码; - 密码文件:
ansible-playbook --vault-password-file 密码文件 加密剧本.yml,密码文件需设置600权限。
- 交互式输入:
2.9 Ansible Galaxy核心知识点⭐⭐
- 核心概念:Galaxy是Ansible的第三方Roles共享平台,类似于Python的PyPI、JavaScript的npm,提供海量现成的Roles(如Nginx、MySQL、Docker部署),用户可直接下载使用,无需从零编写剧本,大幅提升运维效率。
- 核心操作命令:
- 安装第三方Role:
ansible-galaxy collection install 作者.角色名(如ansible-galaxy collection install nginxinc.nginx_core,安装Nginx官方Role); - 搜索Role:
ansible-galaxy search 关键词(如ansible-galaxy search nginx,搜索Nginx相关Role); - 列出已安装Role:
ansible-galaxy collection list; - 删除Role:
ansible-galaxy collection remove 作者.角色名;
- 安装第三方Role:
- 核心应用场景:标准化服务部署(如Nginx、MySQL、K8s),直接复用成熟Role,减少重复开发,降低脚本编写错误风险。
2.10 Ansible优化核心知识点⭐⭐⭐
Ansible优化聚焦性能提升和安全加固,通过配置调整、环境优化等方式,适配大规模集群管理和高安全需求场景,核心优化方向如下:
| 优化方向 | 核心措施 | 优化效果 | 关键配置/命令 |
|---|---|---|---|
| SSH连接速度 | 关闭被控端UseDNS、GSSAPIAuthentication | 减少SSH连接耗时,提升批量执行速度 | 被控端/etc/ssh/sshd_config:UseDNS no、GSSAPIAuthentication no,重启sshd |
| 并发执行数量 | 调整Ansible并发数(forks) | 支持大规模集群批量执行,提升效率 | 配置文件ansible.cfg:forks=20(默认5,根据主控端性能调整),执行时-f 20临时调整 |
| Facts收集优化 | 关闭无需的Facts收集 | 减少主机信息收集耗时,加速剧本执行 | 剧本中gather_facts: no,配置文件gathering = explicit |
| 本地YUM源 | 搭建本地YUM仓库,批量安装时优先使用 | 避免外网依赖,提升软件安装速度 | 被控端配置本地YUM源,剧本中yum模块正常使用 |
| 安全加固 | 配置sudo免密用户、关闭主机密钥检查 | 提升运维安全性,避免root直接登录 | 被控端/etc/sudoers:ans ALL=(ALL) NOPASSWD: ALL;主控端ansible.cfg:host_key_checking = False |
| 缓存优化 | 缓存Facts变量(如Redis) | 重复执行剧本时无需重新收集Facts | 配置文件启用Facts缓存,指定Redis地址 |
三、步骤/命令
3.1 Ansible Playbook剧本基础操作与实战案例
通用执行命令:⭐️ansible-playbook 剧本名.yml(使用默认主机清单);⭐️ansible-playbook -i 自定义主机清单 剧本名.yml(使用自定义主机清单)
效果验证:执行后会输出每个Play、Task的执行结果,最终通过PLAY RECAP汇总各主机执行状态(ok/changed/failed/unreachable)。
场景1:剧本基础语法示例(入门必学)
操作场景:编写基础剧本,实现多步骤简单命令执行,熟悉Playbook核心结构与YAML格式。
# 编写剧本:01.show.yml
- hosts: all # 管理所有主机
tasks: # 任务列表
- name: 01 打开冰箱门 # 任务名称,便于排查
shell: echo 01 >> /tmp/bingxiang.log
- name: 02 把大象放入冰箱
shell: echo 02 >> /tmp/bingxiang.log
- name: 03 关上冰箱的门
shell: echo 03 >> /tmp/bingxiang.log
# 执行剧本
ansible-playbook 01.show.yml
# 效果验证:查看被控端文件
ansible all -a 'cat /tmp/bingxiang.log'
注释:执行后若出现奶牛图标,可修改/etc/ansible/ansible.cfg,将nocows = 1取消注释关闭。
场景2:创建目录并分发文件(基础运维操作)
操作场景:通过剧本实现批量创建目录+分发/etc/hosts文件,掌握file/copy模块在剧本中的两种书写格式。
# 编写剧本:02.dist_file.yml(专业键值对格式,推荐)
- hosts: all
tasks:
- name: 01 创建目录
file:
path: /server/files/
state: directory
- name: 02 分发文件
copy:
src: /etc/hosts
dest: /server/files/
# 执行剧本
ansible-playbook 02.dist_file.yml
# 效果验证:检查目录和文件是否存在
ansible all -a 'ls -l /server/files/'
补充:模块也可使用单行简写格式(file: path=/server/files/ state=directory),适合简单操作,复杂操作推荐专业键值对格式。
场景3:分发并安装zabbix-agent(软件部署实战)
操作场景:通过剧本实现批量下载zabbix-agent包、安装、启动并设置开机自启,掌握get_url/yum/systemd模块的剧本组合使用。
# 编写剧本:03.install_zabbix-agent.yml
- hosts: centos # 管理centos主机组
tasks:
- name: 01 下载zabbix-agent包到/tmp
get_url:
url: https://mirrors.tuna.tsinghua.edu.cn/zabbix/zabbix/6.0/rhel/7/x86_64/zabbix-agent-6.0.7-1.el7.x86_64.rpm
validate_certs: no # 关闭证书验证
dest: /tmp/
- name: 02 安装zabbix-agent包
yum:
name: /tmp/zabbix-agent-6.0.7-1.el7.x86_64.rpm
state: present
- name: 03 启动zabbix-agent并开机自启
systemd:
name: zabbix-agent
enabled: yes
state: started
# 执行剧本
ansible-playbook 03.install_zabbix-agent.yml
# 效果验证:检查服务状态
ansible centos -a 'systemctl status zabbix-agent'
场景4:部署NFS服务端+客户端(集群服务部署实战)
操作场景:通过单剧本实现NFS服务端(backup组)和客户端(web组)的全量部署,服务端共享/backup-nfs目录,客户端永久挂载至/ans-upload,掌握多Play剧本编写、lineinfile/mount模块的实战用法。
# 编写剧本:04.deploy_nfs.yml
# Play1:部署NFS服务端(backup组)
- hosts: backup
tasks:
- name: 01 安装nfs-utils和rpcbind
yum:
name: nfs-utils,rpcbind
state: present
- name: 02 修改NFS配置文件/etc/exports
lineinfile:
path: /etc/exports
line: "/backup-nfs 172.16.1.0/24(rw,all_squash)"
create: true # 配置文件不存在则创建
- name: 03 创建共享目录并修改属主为nfsnobody
file:
path: /backup-nfs
owner: nfsnobody
group: nfsnobody
state: directory
- name: 04 启动rpcbind并开机自启(先启动)
systemd:
name: rpcbind
enabled: yes
state: started
- name: 05 启动nfs并开机自启(后启动)
systemd:
name: nfs
enabled: yes
state: started
# Play2:部署NFS客户端(web组)
- hosts: web
tasks:
- name: 01 安装nfs-utils客户端
yum:
name: nfs-utils
state: present
- name: 02 永久挂载NFS共享目录
mount:
src: 172.16.1.41:/backup-nfs
path: /ans-upload
fstype: nfs
state: mounted # 挂载并修改/etc/fstab
# 执行剧本
ansible-playbook 04.deploy_nfs.yml
# 效果验证:服务端检查共享,客户端检查挂载
ansible backup -a 'showmount -e'
ansible web -a 'df -h /ans-upload/'
3.2 Ansible各类变量的定义与使用
通用调试命令:⭐️debug: msg="{{ 变量名 }}"(输出单个变量);⭐️debug: msg="变量内容 {{ 变量1 }} {{ 变量2 }}"(输出多个变量)
核心前提:变量引用遵循{{ 变量名 }}规则,变量开头必加双引号。
类型1:剧本内变量(vars)
操作场景:在剧本内直接定义变量,用于单剧本的可变参数管理,如统一定义目录路径。
# 编写剧本:05.vars_inside.yml
- hosts: all
vars: # 定义剧本内变量
dir: /oldboy/lidao/upload/ # 变量名:dir,变量值:路径
tasks:
- name: 批量创建目录(引用变量)
file:
path: "{{ dir }}" # 变量为开头,加双引号
state: directory
# 执行剧本
ansible-playbook 05.vars_inside.yml
# 效果验证:检查目录是否创建
ansible all -a 'ls -ld /oldboy/lidao/upload/'
类型2:独立变量文件(vars_files)
操作场景:将变量单独写在独立YAML文件中,通过vars_files加载,适用于单剧本多变量的场景,便于统一管理。
# 1. 创建独立变量文件:vars.yml
cat > vars.yml << EOF
dir: /tmp/
user: lidao996
file: lidao.txt
EOF
# 2. 编写剧本:06.vars_file.yml(加载变量文件)
- hosts: all
vars_files: ./vars.yml # 加载当前目录的vars.yml
tasks:
- name: 批量创建文件(引用多个变量)
file:
path: "{{ dir }}/{{ user }}-{{ file }}" # 组合变量
state: touch
# 执行剧本
ansible-playbook 06.vars_file.yml
# 效果验证:检查文件是否创建
ansible all -a 'ls -l /tmp/lidao996-lidao.txt'
类型3:主机组变量(group_vars)高频使用
操作场景:按主机清单的分组创建变量目录和文件,实现多剧本共用变量,是标准化运维的核心方式,默认加载group_vars目录下的变量文件。
# 1. 创建group_vars目录及全机组变量文件(推荐使用all组,所有主机共用)
mkdir -p group_vars/all
cat > group_vars/all/vars.yml << EOF
user: www
nfs_dir: /nfs_backup
web_mount_dir: /web_nfs
nfs_server: 172.16.1.41
rsync_pass: 1
EOF
# 2. 编写剧本:07.group_vars.yml(测试组变量)
- hosts: all
tasks:
- name: 测试所有主机的组变量
debug:
msg: "全机组变量:{{ user }} {{ rsync_pass }} {{ nfs_server }}"
- hosts: web
tasks:
- name: 测试web组的组变量
debug:
msg: "web组识别的变量:{{ user }} {{ web_mount_dir }}"
# 执行剧本(自动加载group_vars目录下的变量)
ansible-playbook 07.group_vars.yml
# 效果验证:执行后直接输出变量内容,确认加载成功
注释:可按主机组创建子目录(如group_vars/web/group_vars/backup),实现不同主机组的专属变量。
类型4:Facts变量(内置主机信息)
操作场景:使用Ansible内置的Facts变量获取主机基础信息(IP、CPU、主机名、系统版本),通过template模块解析变量并修改系统文件/etc/motd,掌握template与copy的核心区别。
# 1. 创建templates目录(template模块默认读取该目录)及模板文件:motd.j2
mkdir -p templates
cat > templates/motd.j2 << EOF
welcome to oldboy linux system
操作需谨慎,删根弹指间!
主机名:{{ ansible_hostname }}
IP地址:{{ ansible_default_ipv4.address }}
内存大小:{{ ansible_memtotal_mb }} MB
CPU数量:{{ ansible_processor_vcpus }}
发行版本:{{ ansible_distribution }}
EOF
# 2. 编写剧本:08.facts_vars.yml(分发模板文件)
- hosts: all
# gather_facts: no # 若不需要Facts变量,可关闭加速执行
tasks:
- name: 分发模板文件并解析变量(备份原文件)
template:
src: templates/motd.j2
dest: /etc/motd
backup: yes
# 执行剧本
ansible-playbook 08.facts_vars.yml
# 效果验证:远程登录被控端,直接查看motd输出
ssh [email protected]
# 手动获取Facts变量(调试用)
ansible all -m setup
类型5:Register注册变量(捕获命令输出)
操作场景:使用register捕获命令执行结果,实现Shell脚本的反引号/$(command) 功能,掌握从注册变量中提取有效输出(stdout)的方法。
# 编写剧本:09.register_vars.yml
- hosts: all
tasks:
- name: 执行date命令并捕获结果
shell: date +%F
register: result # 将命令结果注册到变量result中
- name: 输出注册变量的内容(调试)
debug:
msg: | # | 表示多行输出
命令执行的标准输出:{{ result.stdout }}
命令执行的返回码:{{ result.rc }}
命令执行的错误输出:{{ result.stderr }}
# 执行剧本
ansible-playbook 09.register_vars.yml
# 效果验证:执行后输出注册变量的各项内容,核心取result.stdout
核心说明:注册变量为JSON格式,核心有效字段:
stdout:命令标准输出(最常用);rc:命令返回码(0为成功,非0为失败);stderr:命令错误输出;stdout_lines:按行分割的标准输出。
3.3 Ansible流程控制实操核心进阶
场景1:handlers触发器(NFS配置分发后重启服务)
操作场景:分发NFS配置文件/etc/exports,仅当配置文件变化时重启NFS服务,无变化则不执行重启,掌握notify与handlers的配合使用。
# 编写剧本:10.handlers_nfs.yml
- hosts: backup
gather_facts: no
tasks:
- name: 01 分发NFS配置文件
copy:
src: files/exports
dest: /etc/exports
backup: yes
notify: 重启NFS服务 # 触发handlers,名称需与handlers任务一致
handlers: # 写在剧本最后,特殊任务列表
- name: 重启NFS服务 # 触发名称与notify一致
systemd:
name: nfs
state: reloaded
# 首次执行(配置文件新增,触发重启)
ansible-playbook 10.handlers_nfs.yml
# 再次执行(配置文件无变化,不触发重启)
ansible-playbook 10.handlers_nfs.yml
# 效果验证:查看NFS服务状态
ansible backup -a 'systemctl status nfs'
场景2:when条件判断(按系统版本安装不同软件)
操作场景:为所有主机执行安装操作,CentOS系统安装sl、cowsay,Ubuntu系统安装cmatrix,结合Facts变量ansible_distribution实现条件判断。
# 编写剧本:11.when_sys.yml
- hosts: all
tasks:
- name: CentOS系统安装sl、cowsay
yum:
name: sl,cowsay
state: installed
when: ansible_distribution == "CentOS" # 条件判断:系统为CentOS
- name: Ubuntu系统安装cmatrix
apt:
name: cmatrix
state: present
when: ansible_distribution == "Ubuntu" # 条件判断:系统为Ubuntu
# 执行剧本
ansible-playbook 11.when_sys.yml
# 效果验证:CentOS查看安装包,Ubuntu查看安装包
ansible centos -a 'rpm -qa sl cowsay'
ansible ubuntu -a 'dpkg -l cmatrix'
补充:多条件判断示例(CentOS且主机名为web/backup)
- name: CentOS+web/backup安装tree
yum:
name: tree
state: present
when:
- ansible_distribution == "CentOS"
- ansible_hostname is match("web|backup") # 正则匹配主机名
场景3:loop循环(批量启动服务+批量创建用户)
操作场景1:批量重启NFS相关服务(rpcbind→nfs),使用单变量循环,掌握{{ item }}的基本使用。
# 编写剧本:12.loop_service.yml
- hosts: backup
gather_facts: no
tasks:
- name: 批量重启rpcbind、nfs服务
systemd:
name: "{{ item }}" # 引用循环变量
state: restarted
loop: # 循环列表
- rpcbind
- nfs
操作场景2:批量创建用户(指定用户名+UID),使用键值对多变量循环,掌握复杂循环的写法。
# 编写剧本:13.loop_user.yml
- hosts: all
gather_facts: no
tasks:
- name: 批量创建用户(指定UID)
user:
name: "{{ item.name }}" # 引用键值对变量name
uid: "{{ item.uid }}" # 引用键值对变量uid
state: present
loop: # 键值对循环列表
- { name: 'oldboy', uid: 2020 }
- { name: 'lidao', uid: 2021 }
- { name: 'lidao996', uid: 2022 }
# 执行两个循环剧本
ansible-playbook 12.loop_service.yml
ansible-playbook 13.loop_user.yml
# 效果验证:检查服务状态、检查用户
ansible backup -a 'systemctl status rpcbind nfs'
ansible all -a 'id oldboy lidao lidao996'
3.4 Ansible剧本调试实操实战必备
以NFS部署剧本为例,结合tag标签、语法检查、模拟运行、忽略错误实现全流程调试。
# 编写带tag标签的NFS部署剧本:14.deploy_nfs_tag.yml
- hosts: backup
tasks:
- name: 01 安装nfs-utils、rpcbind
yum:
name: nfs-utils,rpcbind
state: present
tags: 01.install # 定义标签
- name: 02 修改NFS配置文件
lineinfile:
path: /etc/exports
line: "/backup-nfs 172.16.1.0/24(rw,all_squash)"
create: true
tags: 02.conf # 定义标签
notify: 重启NFS服务
- name: 03 创建共享目录
file:
path: /backup-nfs
owner: nfsnobody
group: nfsnobody
state: directory
tags: 03.dir # 定义标签
ignore_errors: true # 忽略目录已存在的错误
- name: 04 批量启动服务
systemd:
name: "{{ item }}"
enabled: yes
state: started
loop:
- rpcbind
- nfs
tags: 04.start_srv # 定义标签
handlers:
- name: 重启NFS服务
systemd:
name: nfs
state: reloaded
# 1. 语法检查(核心第一步)
⭐️ansible-playbook --syntax-check 14.deploy_nfs_tag.yml
# 2. 模拟运行(预览结果,不修改被控端)
⭐️ansible-playbook -C 14.deploy_nfs_tag.yml
# 3. 仅执行指定标签的任务(仅启动服务)
⭐️ansible-playbook -t 04.start_srv 14.deploy_nfs_tag.yml
# 4. 跳过指定标签的任务(跳过安装)
ansible-playbook --skip-tags 01.install 14.deploy_nfs_tag.yml
# 5. 查看剧本所有标签
ansible-playbook --list-tags 14.deploy_nfs_tag.yml
3.5 Jinja2模板实操动态配置核心
场景1:Jinja2基本使用(分发带变量的motd文件)
已在3.2 类型4 Facts变量中实现,核心为template模块解析motd.j2中的Facts变量,此处不再赘述。
场景2:Jinja2条件判断(动态生成Keepalived配置)
操作场景:根据主机名动态生成Keepalived配置文件,web01为主节点(MASTER),backup为备节点(BACKUP),实现个性化配置分发。
# 1. 创建Jinja2模板文件:templates/keepalived.conf.j2
cat > templates/keepalived.conf.j2 << EOF
# Keepalived动态配置文件 - 由Jinja2生成
global_defs {
router_id {{ ansible_hostname }} # 引用主机名变量
}
vrrp_instance VI_1 {
{% if ansible_hostname == "web01" %} # 条件判断:主机名为web01
state MASTER # 主节点
{% elif ansible_hostname == "backup" %} # 条件判断:主机名为backup
state BACKUP # 备节点
{% endif %} # 结束判断
interface eth0
virtual_router_id 51
}
EOF
# 2. 编写剧本:15.jinja2_if.yml
- hosts: all
tasks:
- name: 分发Keepalived动态配置文件
template:
src: templates/keepalived.conf.j2
dest: /tmp/keepalived.conf
# 执行剧本
ansible-playbook 15.jinja2_if.yml
# 效果验证:查看web01和backup的配置文件差异
ansible web01 -a 'cat /tmp/keepalived.conf'
ansible backup -a 'cat /tmp/keepalived.conf'
场景3:Jinja2循环(批量生成IP列表配置)
操作场景:通过Jinja2的for循环,在/tmp/server.conf中批量生成10.0.0.2~10.0.0.10的IP列表,掌握数字范围、列表循环的写法。
# 1. 创建Jinja2模板文件:templates/server.conf.j2
cat > templates/server.conf.j2 << EOF
# 批量生成IP列表 - 数字范围循环(2-10)
{% for ip in range(2,11) %}
10.0.0.{{ ip }}
{% endfor %}
# 批量生成主机名 - 列表循环
{% for name in ["web01","web02","backup01","nfs01"] %}
{{ name }}
{% endfor %}
EOF
# 2. 编写剧本:16.jinja2_for.yml
- hosts: all
tasks:
- name: 分发批量IP列表配置文件
template:
src: templates/server.conf.j2
dest: /tmp/server.conf
# 执行剧本
ansible-playbook 16.jinja2_for.yml
# 效果验证:查看生成的配置文件
ansible all -a 'cat /tmp/server.conf'
3.6 Ansible文件包含(include_tasks)实操
操作场景:将NFS部署剧本拆分为服务端子剧本、客户端子剧本,通过主剧本加载合并,实现剧本模块化拆分。
# 1. 创建子剧本:nfs_server_tasks.yml(服务端任务)
cat > nfs_server_tasks.yml << EOF
- name: 01 安装nfs-utils、rpcbind
yum:
name: nfs-utils,rpcbind
state: present
- name: 02 修改NFS配置文件
copy:
src: files/exports
dest: /etc/exports
backup: yes
notify: 重启NFS服务
- name: 03 创建共享目录
file:
path: /backup-nfs
owner: nfsnobody
group: nfsnobody
state: directory
EOF
# 2. 创建子剧本:nfs_client_tasks.yml(客户端任务)
cat > nfs_client_tasks.yml << EOF
- name: 01 安装nfs-utils
yum:
name: nfs-utils
state: present
- name: 02 永久挂载NFS
mount:
src: 172.16.1.41:/backup-nfs
path: /ans-upload
fstype: nfs
state: mounted
EOF
# 3. 创建主剧本:nfs_main.yml(加载子剧本)
cat > nfs_main.yml << EOF
- hosts: backup
tasks:
- include_tasks: nfs_server_tasks.yml # 加载服务端子剧本
handlers:
- name: 重启NFS服务
systemd:
name: nfs
state: reloaded
- hosts: web
tasks:
- include_tasks: nfs_client_tasks.yml # 加载客户端子剧本
EOF
# 执行主剧本
ansible-playbook nfs_main.yml
# 效果验证:检查服务端共享和客户端挂载
ansible backup -a 'showmount -e'
ansible web -a 'df -h /ans-upload/'
3.7 Ansible Roles实操(NFS服务端部署)高频实战
场景1:创建Roles目录结构与核心文件
# 1. 创建Roles项目目录结构
mkdir -p /server/ansible/roles-project/{group_vars/all,nfs-server/{files,templates,tasks,handlers},roles}
cd /server/ansible/roles-project
# 2. 创建主机清单文件:hosts
cat > hosts << EOF
[backup]
172.16.1.41
[web]
172.16.1.7
EOF
# 3. 创建group_vars全局变量文件:group_vars/all/main.yml
cat > group_vars/all/main.yml << EOF
nfs_share_dir: /backup-nfs-v3 # NFS共享目录
nfs_user: nfsnobody # 共享目录属主
nfs_user_id: 65534 # 匿名用户UID/GID
nfs_network: 172.16.1.0/24 # 允许访问的网段
EOF
# 4. 创建nfs-server Role的files目录静态文件:nfs-server/files/exports(备用,后续用模板替代)
cat > nfs-server/files/exports << EOF
/backup-nfs 172.16.1.0/24(rw,all_squash)
EOF
# 5. 创建nfs-server Role的templates目录模板文件:nfs-server/templates/exports.j2(动态配置)
cat > nfs-server/templates/exports.j2 << EOF
{{ nfs_share_dir }} {{ nfs_network }}(rw,all_squash,anonuid={{ nfs_user_id }},anongid={{ nfs_user_id }})
EOF
# 6. 创建nfs-server Role的tasks任务文件:nfs-server/tasks/main.yml(核心入口)
cat > nfs-server/tasks/main.yml << EOF
- name: 01 安装nfs-utils和rpcbind
yum:
name: nfs-utils,rpcbind
state: installed
tags: 01-install-nfs
- name: 02 分发NFS动态配置文件(模板)
template:
src: exports.j2
dest: /etc/exports
backup: yes
tags: 02-conf
notify: restart nfs server
- name: 03 创建NFS共享目录
file:
path: "{{ nfs_share_dir }}"
owner: "{{ nfs_user }}"
group: "{{ nfs_user }}"
state: directory
tags: 03-mkdir
- name: 04 批量启动rpcbind和nfs服务
systemd:
name: "{{ item }}"
enabled: yes
state: started
loop:
- rpcbind
- nfs
tags: 04-start-service
- name: 05 分发motd模板文件(示例)
template:
src: motd.j2
dest: /etc/motd
backup: yes
tags: 05-motd
EOF
# 7. 创建nfs-server Role的handlers触发器文件:nfs-server/handlers/main.yml
cat > nfs-server/handlers/main.yml << EOF
- name: restart nfs server
systemd:
name: nfs
state: reloaded
EOF
# 8. 创建Roles入口剧本:top.yml(加载nfs-server Role)
cat > top.yml << EOF
- hosts: backup
roles:
- role: nfs-server # 加载nfs-server Role
EOF
场景2:执行Roles并验证
# 1. 语法检查
ansible-playbook -i hosts --syntax-check top.yml
# 2. 执行Roles剧本(指定主机清单和入口剧本)
ansible-playbook -i hosts top.yml
# 3. 仅执行指定tag的任务(如仅启动服务)
ansible-playbook -i hosts -t 04-start-service top.yml
# 4. 效果验证
# 检查NFS配置文件(是否为动态变量生成)
ansible backup -a 'cat /etc/exports'
# 检查共享目录
ansible backup -a 'ls -ld {{ nfs_share_dir }}'
# 检查服务状态
ansible backup -a 'systemctl status rpcbind nfs'
# 检查共享
ansible backup -a 'showmount -e'
3.8 Ansible Vault实操(敏感信息加密)
场景1:加密变量文件
# 1. 创建包含敏感信息的变量文件:secrets.yml
cat > secrets.yml << EOF
db_password: "Oldboy@123"
ssh_port: 2222
root_password: "Admin@456"
EOF
# 2. 加密该文件(设置加密密码,需牢记)
⭐️ansible-vault encrypt secrets.yml
# 执行后提示输入加密密码,确认密码,文件变为加密状态
# 3. 查看加密文件内容(无需解密)
ansible-vault view secrets.yml
# 输入加密密码后即可查看明文
# 4. 编辑加密文件
ansible-vault edit secrets.yml
# 输入密码后进入编辑模式,保存后自动重新加密
# 5. 解密文件(彻底解密,谨慎使用)
ansible-vault decrypt secrets.yml
# 输入密码后文件恢复为明文
场景2:执行包含加密文件的剧本
# 1. 创建剧本:use_secrets.yml(加载加密变量文件)
cat > use_secrets.yml << EOF
- hosts: all
vars_files: ./secrets.yml # 加载加密的变量文件
tasks:
- name: 输出敏感变量(调试用)
debug:
msg: "数据库密码:{{ db_password }}, SSH端口:{{ ssh_port }}"
EOF
# 2. 执行剧本(需提供Vault密码)
# 方式1:交互式输入密码
⭐️ansible-playbook --ask-vault-pass use_secrets.yml
# 方式2:通过密码文件提供密码(推荐生产环境)
# 先创建密码文件(权限600)
echo "MyVaultPass123" > vault_pass.txt
chmod 600 vault_pass.txt
# 执行剧本
ansible-playbook --vault-password-file vault_pass.txt use_secrets.yml
3.9 Ansible Galaxy实操(安装第三方Roles)
# 1. 搜索Nginx相关Roles
ansible-galaxy search nginx
# 2. 安装Nginx官方Role(nginxinc.nginx_core)
⭐️ansible-galaxy collection install nginxinc.nginx_core
# 3. 列出已安装的Roles/Collections
ansible-galaxy collection list
# 4. 使用安装的Role(参考官方文档编写剧本)
cat > install_nginx.yml << EOF
- hosts: web
collections:
- nginxinc.nginx_core
tasks:
- name: Install Nginx
nginx_core.nginx:
state: present
EOF
# 5. 执行剧本安装Nginx
ansible-playbook install_nginx.yml
3.10 Ansible优化实操
场景1:性能优化(SSH连接+并发数)
# 1. 优化被控端SSH配置(加速连接)
ansible all -m lineinfile -a 'path=/etc/ssh/sshd_config line="UseDNS no" state=present'
ansible all -m lineinfile -a 'path=/etc/ssh/sshd_config line="GSSAPIAuthentication no" state=present'
ansible all -m systemd -a 'name=sshd state=reloaded'
# 2. 调整Ansible并发数(临时调整)
ansible-playbook -f 20 nfs_main.yml # -f指定并发数为20(默认5)
# 3. 永久调整并发数(修改配置文件)
sed -i 's/^#forks = 5/forks = 20/' /etc/ansible/ansible.cfg
# 4. 关闭Facts收集(加速剧本执行)
# 在剧本中添加gather_facts: no
cat > fast_playbook.yml << EOF
- hosts: all
gather_facts: no # 关闭Facts收集
tasks:
- name: 批量创建目录
file:
path: /tmp/optimize
state: directory
EOF
场景2:安全优化(sudo免密用户)
# 1. 主控端创建普通用户ans,并配置密钥认证
useradd ans
echo "ans:123456" | chpasswd
ssh-keygen -t rsa -f /home/ans/.ssh/id_rsa -P ''
chown -R ans:ans /home/ans/.ssh/
# 2. 批量在被控端创建ans用户,并配置sudo免密
ansible all -m user -a 'name=ans password={{ "123456" | password_hash("sha512", "salt") }} state=present'
ansible all -m lineinfile -a 'path=/etc/sudoers line="ans ALL=(ALL) NOPASSWD: ALL" state=present'
# 3. 主控端ans用户分发密钥到被控端
su - ans
ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]
ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]
# 4. 修改主控端Ansible配置文件(使用ans用户登录)
cat >> /etc/ansible/ansible.cfg << EOF
[defaults]
remote_user = ans # 被控端登录用户
remote_port = 22
host_key_checking = False
log_path = /var/log/ansible.log
[privilege_escalation]
become = True # 开启sudo
become_method = sudo # 使用sudo
become_user = root # 切换为root
EOF
# 5. 测试(无需root用户,通过ans用户sudo执行)
ansible all -m command -a 'whoami' # 输出root
四、原理说明
4.1 Ansible Playbook执行原理
Playbook是Ansible将多个Ad-hoc操作按业务流程封装后的YAML文件,其底层执行仍基于SSH协议,核心执行流程为:
- 加载配置与主机清单:Ansible读取
ansible.cfg配置文件,加载指定的主机清单,解析Playbook中的hosts字段,确定待管理的主机/主机组; - 收集Facts信息(默认):执行内置的
setup模块,收集待管理主机的基础信息(生成Facts变量),并将信息缓存至主控端; - 解析Play与Task:按Playbook中的顺序解析每个
Play,再按从上到下的顺序解析Play中的每个Task,将每个Task转换为对应的模块调用指令; - 批量执行模块:通过SSH连接插件与被控端建立连接,将模块指令发送至被控端临时目录,依次执行每个Task的模块指令,若任务状态为
changed则记录触发的handlers; - 执行触发器任务:当前Play的所有普通任务执行完成后,若有已记录的
handlers,则按顺序执行触发器任务; - 结果收集与反馈:实时收集每个被控端的模块执行结果(成功/失败/改变/跳过),执行完成后按
Play和主机进行汇总,通过PLAY RECAP输出最终执行状态。
补充Roles执行逻辑:
- 若Play中包含
roles,Ansible会先加载Role的tasks/main.yml,按顺序执行所有任务;任务执行过程中若触发notify,则记录对应的触发器; - 所有Role任务执行完成后,执行Role的
handlers/main.yml中的触发器任务; - Role中的
files/、templates/目录由对应模块自动识别,无需指定路径,仅需填写文件名即可。
4.2 Ansible变量生效与加载原理
Ansible变量采用按场景加载、按优先级覆盖的生效规则,底层通过变量解析引擎实现引用替换,核心原理为:
- 变量加载顺序:按「内置Facts变量→主机清单变量→组变量
group_vars→变量文件vars_files→剧本内vars→命令行变量」的顺序加载,后加载的变量会覆盖先加载的同名变量; - 变量引用解析:剧本执行时,解析引擎会扫描所有
{{ 变量名 }}的内容,从已加载的变量池中匹配对应的变量值,完成替换后再执行模块指令;若变量为选项开头,需加双引号避免YAML格式解析错误; - 特殊变量生效:
- Facts变量:由
setup模块收集主机信息后动态生成,与对应主机绑定,仅对该主机生效; - Register变量:执行命令模块后,将命令的执行结果(返回码、输出、错误)封装为JSON格式存入指定变量,仅在当前Play中生效;
- 组变量:Ansible会自动扫描当前执行目录下的
group_vars目录,按主机组名匹配加载对应的变量文件,all组的变量对所有主机生效。
- Facts变量:由
补充Roles变量优先级:
- Roles变量的加载顺序:
group_vars/all→Role的vars/main.yml→Role的defaults/main.yml,后加载的变量覆盖先加载的同名变量; - 命令行传入的变量优先级最高,可覆盖所有Role和全局变量。
4.3 Ansible流程控制执行原理
Ansible流程控制基于任务执行状态和条件解析引擎实现,底层与Playbook执行流程深度融合,核心原理为:
- handlers触发器原理:
handlers是特殊的任务列表,默认不执行,仅当普通任务中notify指定的名称与handlers任务名一致,且普通任务的执行状态为changed时,才会在当前Play的所有普通任务执行完成后触发;若任务状态为ok(无变化),则不会触发。 - when条件判断原理:Ansible通过条件解析引擎解析
when后的逻辑语句,先从变量池中获取语句中的变量值,再执行逻辑判断(等于/匹配/与/或);判断结果为True则执行当前任务,结果为False则跳过任务,标记为skipped。 - loop循环执行原理:Ansible将
loop后的列表/键值对视为迭代数据源,对当前任务进行批量复制,每次迭代将item变量替换为数据源中的一个值,依次执行迭代后的任务;loop与with_items底层迭代引擎一致,loop为官方推荐的新一代语法。
4.4 Jinja2模板解析原理
Jinja2模板是Ansible实现动态配置的核心,其解析过程分为模板渲染和文件分发两步,底层基于Jinja2引擎与Ansible变量池联动,核心原理为:
- 模板加载:
template模块读取指定路径的.j2模板文件,加载至主控端的内存中; - 变量与逻辑解析:Jinja2引擎扫描模板中的
{{ 变量 }}和{% 逻辑 %}标签,从Ansible的变量池中获取变量值,执行条件判断/循环迭代等逻辑,生成无模板标签的纯文本文件(该过程称为渲染); - 文件分发:将渲染后的纯文本文件通过SSH协议分发至被控端的指定路径,完成动态配置文件的部署;
- 核心特性:模板渲染过程在主控端完成,仅将最终的纯文本文件发送至被控端,减少被控端的资源消耗。
4.5 Ansible Roles执行原理
Roles的核心是目录规范驱动的自动化加载,底层执行逻辑与Playbook一致,但增加了目录解析步骤:
- 入口剧本
top.yml指定hosts和role后,Ansible先定位Role目录(如nfs-server/); - 自动读取
tasks/main.yml,按顺序解析每个任务,转换为模块调用指令; - 任务中使用
copy模块时,自动从files/目录查找文件;使用template模块时,自动从templates/目录查找.j2文件; - 任务执行过程中若触发
notify,则从handlers/main.yml中匹配对应的触发器任务,待所有任务执行完成后执行; - 变量从
group_vars/、Role的vars/等位置加载,按优先级替换任务和模板中的{{ 变量名 }}。
4.6 Ansible Vault加密原理
Vault基于对称加密算法实现,核心原理为:
- 加密时,用户输入的密码作为密钥,通过AES算法对文件内容进行加密,加密后的文件仅包含密文和加密元数据(无明文信息);
- 解密/执行时,用户提供的密码被用于生成密钥,对密文进行解密,解密过程在内存中完成,不写入磁盘,避免敏感信息泄露;
- 加密文件的扩展名不变,但文件头部会添加
$ANSIBLE_VAULT;1.1;AES256标识,Ansible通过该标识识别加密文件。
4.7 Ansible优化原理
- SSH连接优化:关闭
UseDNS可避免SSH连接时的DNS反向解析耗时,关闭GSSAPIAuthentication可禁用Kerberos认证,减少连接握手步骤; - 并发数优化:
forks参数控制Ansible同时与多少个被控端建立连接,默认5个,调整为20-50(根据主控端CPU/内存性能)可大幅提升大规模集群的执行效率; - Facts收集优化:Facts收集需执行
setup模块,获取主机CPU、内存、网络等信息,关闭后可减少一次SSH连接和模块执行的耗时; - 安全优化:配置sudo免密用户可避免root用户直接暴露,关闭
host_key_checking可避免首次连接时的交互确认,提升自动化程度。
五、注意事项
5.1 Playbook剧本编写注意事项
- 剧本文件必须以
.yml/.yaml为后缀,Ansible仅识别该格式的Playbook文件; - YAML格式对空格缩进严格敏感,同一层级必须左对齐,不同层级用2个空格缩进,绝对禁止使用Tab键,否则会直接执行失败;
- 编写多步骤服务部署剧本时,需严格遵循服务启动顺序(如NFS需先启动rpcbind,再启动nfs),否则会导致服务部署失败;
- 剧本中每个
Task建议添加有意义的name,便于执行失败后快速定位问题节点; - 执行服务部署剧本前,建议对被控端拍摄快照,分步测试每个Task,测试通过后再整体执行,避免批量部署故障;
- 使用
get_url模块下载外网文件时,若目标站点无有效证书,需添加validate_certs: no关闭证书验证,否则下载失败; handlers触发器需写在剧本最后,若写在tasks中间,会导致后续普通任务被识别为触发器任务,无法正常执行。
5.2 Ansible变量使用注意事项
- 变量引用必须使用
{{ 变量名 }},若变量为模块选项的开头字符,必须加双引号,非开头可省略引号; - 组变量建议统一放在
group_vars/all目录下,实现所有剧本、所有主机的变量共用,减少重复定义; - 若剧本中不需要使用Facts变量,需在
Play中添加gather_facts: no,关闭主机信息收集,大幅提升剧本执行速度; template模块仅能解析.j2后缀的模板文件,且默认读取当前执行目录下的templates目录,需按规范创建目录和文件;- Register注册变量仅能捕获命令类模块(shell/command/script)的执行结果,无法捕获文件/服务类模块的结果;
- 多剧本共用变量时,优先使用组变量而非独立变量文件,避免变量文件分散,提升可维护性。
5.3 通用运维注意事项
- 剧本中执行删除/修改系统配置的操作时,需添加
backup: yes参数(支持的模块),对原文件进行备份,便于故障回滚; - 批量部署软件时,优先使用本地下载包+yum本地安装,避免被控端因网络问题导致下载失败;
- 剧本执行完成后,通过
ansible <主机组> -a <验证命令>批量验证执行结果,确保所有被控端操作一致; - 标准化运维中,建议将Playbook、变量文件、模板文件按目录规范管理(如
playbook//group_vars//templates//files/),便于团队协作; - Ubuntu系统默认禁用root远程登录,若需管理Ubuntu主机,需先修改
/etc/ssh/sshd_config,设置PermitRootLogin yes,并重启sshd服务、设置root密码。
5.4 流程控制使用注意事项
handlers的notify名称与handlers任务名必须完全一致(大小写敏感),否则无法触发;且一个任务可通过notify触发多个handlers任务,用逗号分隔即可;when条件判断中,使用正则匹配时需用is match("正则表达式"),取反用is not match,匹配多个值用|分隔;when多条件判断时,每个条件需单独一行并以-开头,默认是and关系;or关系需写在同一行;loop循环中,键值对变量的键名必须与模块参数一致(如item.name对应user模块的name参数),否则会提示变量未定义;- 避免在
loop中执行高风险操作(如state=absent删除目录),防止批量误操作; with_items为旧版循环语法,官方推荐使用loop,二者功能一致,可相互替换。
5.5 剧本调试与Jinja2模板注意事项
剧本调试
- 语法检查是剧本执行的核心第一步,所有剧本在实际执行前必须先做语法检查,排除基础语法错误;
tag标签建议按任务步骤/功能命名(如01.install/02.conf),便于识别和调用;ignore_errors: true仅适用于重复执行导致的非致命错误(如目录已存在、用户已创建),不可全局使用,否则会掩盖真实的执行错误;- 模拟运行(
-C)无法解决所有问题,部分依赖任务执行结果的变量会报错,需结合单步执行调试。
Jinja2模板
- Jinja2的逻辑标签(
{% %})必须成对出现(如{% if %}对应{% endif %},{% for %}对应{% endfor %}),缺少结束标签会导致模板渲染失败; - 模板中的变量引用与Ansible剧本中的变量引用规则一致,若变量为路径/端口等开头,需注意引号使用;
- 避免在模板中书写过于复杂的逻辑(如多层嵌套if/for),否则会降低剧本的可维护性,复杂逻辑建议在剧本中用
when实现; - 模板中的注释建议使用
{# 注释 #},而非文件原生注释,避免渲染后的文件出现无用注释,影响服务配置; copy与template模块不可混用,若文件中包含Jinja2标签,必须使用template模块,否则会将标签直接写入文件,导致服务配置错误。
5.6 文件包含(include_tasks)注意事项
- 子剧本仅需包含
tasks列表,无需hosts、vars等Play级配置,避免与主剧本冲突; - 子剧本的路径需正确,主剧本与子剧本在同一目录时直接写文件名,否则需写绝对路径或相对路径;
- 多个子剧本加载时,变量作用域贯通,主剧本的变量可在子剧本中直接使用,子剧本间也可共享变量;
- 子剧本中若触发
handlers,触发器需定义在主剧本中,子剧本无法单独定义handlers。
5.7 Ansible Roles注意事项高频重点
- Role目录结构必须严格遵循规范,
tasks/main.yml和handlers/main.yml为必填文件,其他目录可选; files/目录存放静态文件,templates/存放.j2模板文件,不可混淆使用,否则模块无法找到文件;- Role名称建议体现功能(如
nfs-server、rsync-client),目录命名统一,便于团队识别; - 变量优先使用
group_vars/all定义,实现多Role共用,Role专属变量放在vars/main.yml中,避免变量冲突; - 编写Role时建议分步测试,先测试单个任务,再整体执行,避免批量部署故障;
- Role可通过版本控制工具(如Git)管理,实现版本迭代和团队共享。
5.8 Ansible Vault注意事项
- 加密密码必须牢记,丢失后无法恢复加密文件的内容,建议记录在安全的密码管理工具中;
- 生产环境中建议使用密码文件(
--vault-password-file)提供密码,避免交互式输入,适配自动化执行(如定时任务); - 密码文件需设置600权限(
chmod 600 vault_pass.txt),仅允许当前用户读取,避免密码泄露; - 仅加密必要的敏感信息(如密码、密钥),无需加密所有文件,避免增加执行复杂度;
- 加密后的文件不可直接编辑,必须通过
ansible-vault edit编辑,否则会破坏加密结构。
5.9 Ansible Galaxy注意事项
- 安装第三方Role前,建议通过
ansible-galaxy search查看Role的评分和下载量,优先选择官方或高评分Role; - 第三方Role的使用需参考其官方文档,不同Role的参数和目录结构可能不同,避免盲目使用;
- 安装的Role默认存储在
~/.ansible/collections/目录下,可通过ANSIBLE_COLLECTIONS_PATH环境变量指定自定义存储路径; - 若第三方Role不符合需求,可基于其二次开发,或在本地创建同名Role覆盖(本地Role优先级高于第三方Role)。
5.10 Ansible优化注意事项
- 并发数(
forks)不可设置过大,需根据主控端性能调整,否则会导致主控端CPU/内存占用过高,影响执行稳定性; - 关闭Facts收集前需确认剧本中未使用任何Facts变量(如
ansible_hostname、ansible_default_ipv4.address),否则会提示变量未定义; - 搭建本地YUM源时,需确保所有被控端能访问该源,且源中的软件包版本与被控端系统匹配;
- 安全优化中,sudo免密用户仅授权必要的命令,避免授予
ALL=(ALL) NOPASSWD: ALL权限(生产环境根据需求最小化授权); - 调整SSH端口后,需在Ansible配置文件中指定
remote_port,否则会连接失败。
六、结尾
总结
本文核心覆盖Ansible自动化运维的十大核心模块,构建了从基础到高阶、从功能到安全的完整知识体系,是Ansible学习与实战的终极指南:
- Playbook剧本:掌握YAML规范、多Play编写,实现自动化部署的基础;
- Ansible变量:精通多场景变量定义与引用,提升剧本通用性;
- 流程控制:通过
handlers/when/loop实现灵活执行逻辑,适配复杂场景; - 剧本调试:掌握语法检查、tag标签等技巧,快速解决执行问题;
- Jinja2模板:实现动态配置文件生成,适配个性化部署;
- 文件包含:拆分大型剧本,提升可维护性;
- Roles模块化:标准化目录结构,实现剧本的高度复用与团队协作,是企业级运维的核心;
- Vault加密:保护敏感信息,提升运维安全性;
- Galaxy共享:复用第三方Role,减少重复开发;
- 性能与安全优化:适配大规模集群与高安全需求,提升运维效率与稳定性。
这十大模块贯穿Ansible自动化运维的全流程,从简单的批量命令执行到复杂的集群服务部署,从单人运维到团队协作,从功能实现到安全合规,均可通过Ansible高效完成,是运维工程师从初级晋升到中级、高级的必备核心技能。
避坑指南(坑点+解决方案)
基础剧本与变量类
- 坑点:Playbook执行提示
YAML syntax error语法错误;解决方案:① 检查是否使用Tab键缩进,替换为2个空格;② 检查键: 值冒号后是否加空格;③ 用ansible-playbook --syntax-check 剧本.yml做语法检查。 - 坑点:变量引用后执行失败,提示
undefined variable;解决方案:① 检查变量名是否拼写错误;② 检查变量是否在当前Play的生效范围内;③ 组变量确认group_vars目录与剧本同目录。
流程控制与模板类
- 坑点:
handlers触发器不执行;解决方案:① 检查notify名称与handlers任务名是否完全一致;② 检查触发任务的执行状态是否为changed;③ 确认handlers写在剧本最后。 - 坑点:Jinja2模板渲染失败,提示
template error;解决方案:① 检查逻辑标签是否成对出现;② 确认模板文件后缀为.j2;③ 检查变量引用格式是否正确。
Roles与文件包含类
- 坑点:Roles执行提示“文件找不到”;解决方案:① 检查文件是否放在对应目录(静态文件→
files/,模板→templates/);② 确认文件名称与剧本中引用的名称一致(大小写敏感);③ 检查Role目录结构是否符合规范。 - 坑点:include_tasks加载子剧本失败;解决方案:① 检查子剧本路径是否正确;② 子剧本仅包含
tasks列表,无hosts等Play级配置;③ 主剧本与子剧本的变量是否贯通。
Vault与Galaxy类
- 坑点:Vault加密后剧本执行失败,提示“需要Vault密码”;解决方案:① 执行时添加
--ask-vault-pass或--vault-password-file参数;② 确认密码文件权限为600;③ 确认密码输入正确。 - 坑点:第三方Role安装后无法使用;解决方案:① 检查Role名称是否正确(区分大小写);② 参考Role官方文档编写剧本参数;③ 确认Role存储路径在Ansible的搜索路径中。
优化与安全类
- 坑点:调整并发数后执行卡顿;解决方案:① 降低
forks参数(如从50调整为20);② 检查主控端CPU/内存占用,避免资源耗尽;③ 分批执行大规模集群(按主机组拆分)。 - 坑点:sudo免密用户执行剧本提示“权限不足”;解决方案:① 检查被控端
sudoers配置是否正确(ans ALL=(ALL) NOPASSWD: ALL);② 主控端Ansible配置文件开启become: True;③ 测试su - ans -c "sudo whoami"是否能切换为root。 - 坑点:关闭Facts后剧本提示“变量未定义”;解决方案:① 重新开启
gather_facts: yes;② 手动定义缺失的变量(如在group_vars中定义ansible_hostname);③ 替换Facts变量为固定值(适用于简单场景)。

浙公网安备 33010602011771号