【Day 74 】Ansible-playbook剧本-角色
一、ansible playbook
Playbook 是 Ansible 实现自动化部署的核心组件,便于功能的重用,本质上是遵循 YAML 语法的文本文件,通常以 xxxx.yml
或 xxxx.yaml
为后缀。核心价值体现在:
- 状态化管理:通过模块确保目标主机始终处于 “期望状态”(如软件已安装、服务已启动),而非单纯执行命令。
- 幂等性保障:重复执行剧本不会导致异常(如重复创建用户、重复启动服务),这是 Ansible 区别于 Shell 脚本的核心优势。
- 团队协作:标准化的 YAML 格式可纳入版本控制(如 Git),支持多人协作开发和迭代。
(一)YAML 语法
1、基础规则
YAML 有两个最基础且必须遵守的规则,违反会直接导致解析错误:
(1)大小写敏感:name
和 Name
是两个不同的键(key)。
(2)缩进表示层级:
- 必须使用 空格 缩进(不能用 Tab 键,不同解析器对 Tab 处理不一致)。
- 缩进的空格数不强制(通常 2 或 4 个),但同一层级必须保持一致。
- 子层级需比父层级多缩进(例如父级缩进 2 空格,子级缩进 4 空格)。
示例(正确缩进):
# 正确:同一层级缩进一致,子层级多缩进
server:port: 8080 # 子层级(比 server 多 2 空格)host: localhostconfig:timeout: 30 # 孙子层级(比 config 多 2 空格)
示例(错误缩进):
# 错误:同一层级缩进不一致(port 2 空格,host 4 空格)
server:port: 8080host: localhost
2、数据结构
YAML 支持三种基本数据结构,所有复杂配置都基于这三种类型组合。
(1) 键值对(映射 / Mapping):
用 键: 值
(冒号后必须跟 1 个空格)表示键值关系,键值对可嵌套形成层级结构。
基础示例:
# 简单键值对
name: Alice
age: 25
is_student: false # 布尔值:false/True(大小写不严格,但建议统一小写)# 嵌套键值对(层级结构)
user:name: Bobcontact:email: bob@example.comphone: 123456789
注意:
- 键(key)无需加引号(除非包含特殊字符,如空格、冒号);
- 值(value)若为字符串且不含特殊字符,也无需加引号(如
name: Alice
)。
(2)列表(序列 / Sequence):类似数组(List)
用 - (短横线 + 空格)表示列表中的每个元素,元素可横向排列(用 []
包裹),也可纵向排列(更常用,易读)。
基础示例:
# 纵向列表(推荐,易读)
fruits:- apple- banana- orange# 横向列表(简洁,适合短列表)
colors: [red, green, blue]# 列表嵌套(元素可是键值对或另一个列表)
students:- name: Aliceage: 20- name: Bobage: 21- hobbies: [reading, sports] # 列表作为元素
注意:
- 纵向列表中,
-
后的空格必须存在; - 列表元素类型可混合(如字符串、键值对、列表)。
(3)标量(Scalar):单个值
表示单个数据(如字符串、数字、布尔值、日期),是键值对的 “值” 或列表的 “元素” 的基础类型。
标量类型 | 示例 | 说明 |
---|---|---|
字符串 | title: "Hello YAML" | 含特殊字符(空格、冒号)时需加引号 |
整数 | count: 100 | 直接写数字,无需引号 |
浮点数 | price: 99.9 | 支持小数、科学计数法(如 1e3 表示 1000) |
布尔值 | is_valid: true | 取值:true/false 或 True/False |
日期时间 | birthday: 2000-01-01 | 日期默认格式:YYYY-MM-DD |
空值 | address: null 或 address: ~ | 表示 “无值”,~ 是 YAML 特有的空值符号 |
字符串特殊场景示例:
# 含特殊字符的字符串(必须加引号)
message: "Hello: World" # 含冒号
path: '/home/user/my folder' # 含空格(单引号、双引号均可)# 多行字符串(| 保留换行,> 折叠换行)
description: | # 保留所有换行和空格This is a multi-line string.It preserves line breaks.And spaces.summary: > # 折叠换行(将换行转为空格,仅保留最后一个换行)This is also a multi-line string.But it collapses line breaks into spaces.
3、特殊语法:注释、引用、锚点
(1)注释
用 # 开头表示注释,注释内容会被解析器忽略,支持单行注释(不支持多行注释)。
# 这是一个配置文件(单行注释)
server:port: 8080 # 服务端口(行尾注释)# host: localhost # 被注释的配置(暂不生效)
(2)锚点与引用:避免重复
YAML 支持用 &
定义 “锚点”(重复数据的源头),用 *
引用锚点内容,减少重复代码(适合配置中多次出现的相同参数)。
# 定义锚点:&common_config 标记重复的配置
common_config: &common_configtimeout: 30retries: 3# 引用锚点:*common_config 复用上述配置
server1:<<: *common_config # << 表示“合并锚点的键值对”host: server1.example.comserver2:<<: *common_confighost: server2.example.comtimeout: 60 # 覆盖锚点的 timeout(优先级更高)
解析结果(等效于):
server1:timeout: 30retries: 3host: server1.example.comserver2:timeout: 60 # 覆盖后的值retries: 3host: server2.example.com
注意:<<
是 “合并键”,用于将锚点的键值对 “展开” 到当前层级,若有同名键,当前层级的键会覆盖锚点的键。
4、YAML 与 JSON 的关系
所有合法的 JSON 都可以被 YAML 解析器正确解析,但 YAML 语法更简洁(如省略引号、大括号、方括号)。
对比示例:
// JSON 格式(必须加引号、大括号、方括号)
{"name": "Alice","age": 25,"hobbies": ["reading", "sports"]
}
# 等效的 YAML 格式(更简洁)
name: Alice
age: 25
hobbies:- reading- sports
5、注意事项
YAML 语法的核心是 “缩进表层级、键值对表配置、列表表集合”。
常见错误:
- 缩进错误:用 Tab 键缩进、同一层级缩进不一致 → 直接解析失败,需统一用空格缩进(建议 2 空格)。
- 冒号后无空格:
name:Alice
(错误)→ 正确:name: Alice
(冒号后必须 1 个空格)。 - 字符串特殊字符未加引号:
path: /home/user/my folder
(错误,含空格)→ 正确:path: "/home/user/my folder"
。 - 布尔值误写:
is_ok: yes
(不推荐,部分解析器可能识别为字符串)→ 推荐用true/false
。 - 列表元素缺少空格:
-apple
(错误)→ 正确:- apple
(-
后必须 1 个空格)。
(1)一个键对应一个值时,冒号后必须有空格,示例:
key: value
(2)一个键对应多个值时,需换行缩进展示,示例:
key:value1value2
key:value3value4
(3)同逻辑的代码缩进必须一致,建议使用 4 个空格(避免使用 Tab 键,不同编辑器 Tab 解析可能不同)。
(4)字符串与注释规则
- 含特殊字符(如
:
、空格
、$
)的字符串必须加引号。 - 例如
password: "P@ssw0rd$123"
;纯数字或布尔值(true
/false
)若需按字符串处理,也需加引号,例如port: "8080"
(避免被解析为数字)。 - 仅支持单行注释,以
#
开头,且#
前需加空格,示例:
tasks:- name: 创建用户 # 这是规范的单行注释,#前需空格user:name: hadoop
- 列表简写:当列表元素较简单时,可使用方括号简写,例如
user_list: ["userA", "userB", "userC"]
,等同于多行列表格式。
(二)结构、流程
1、playbook (剧本)的结构
- name: 部署 MySQL 服务 # 新增:Play 名称,便于日志识别hosts: db # 被管理机(来自主机清单)gather_facts: true # 新增:是否收集主机 Facts 信息(默认 true)become: true # 新增:是否切换为 root 权限(默认 false,需特权操作时开启)become_method: sudo # 新增:提权方式(sudo/su,默认 sudo)tasks:- name: 任务名称1模块名称1:参数1-1: 值1-1参数1-2: 值1-2- name: 任务名称2模块名称2:参数2-1: 值2-1参数2-2: 值2-2
2、playbook 的使用流程
(1)编写剧本
以创建 hadoop
用户为例,剧本内容如下:
[root@zabbix_server work]# cat user.yaml
- hosts: dbtasks:- name: create user named hadoopuser:name: hadoopshell: /sbin/nologinstate: present
(2)检测语法
# 先检查语法(确认无错)
ansible-playbook --syntax-check /opt/data/nginx26.yaml# 再执行剧本
ansible-playbook /opt/data/nginx26.yaml
(3)执行剧本
在控制机执行剧本,查看执行过程与结果,同时补充常用参数:
[root@zabbix_server work]# ansible-playbook user.yaml PLAY [db] ****************************************************************************************************************************TASK [Gathering Facts] ***************************************************************************************************************
ok: [192.168.140.11]
ok: [192.168.140.12]
ok: [192.168.140.13]TASK [create user named hadoop] ******************************************************************************************************
changed: [192.168.140.11]
changed: [192.168.140.12]
changed: [192.168.140.13]PLAY RECAP ***************************************************************************************************************************
192.168.140.11 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.140.12 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.140.13 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
补充:常用执行参数
参数 | 作用场景 | 示例 |
---|---|---|
-K /--ask-become-pass | 当 become: true 时,提示输入 sudo 密码(避免硬编码密码) | ansible-playbook -K user.yaml |
--limit | 仅执行部分主机(适合灰度发布) | ansible-playbook --limit 192.168.140.11 user.yaml |
-C /--check | 模拟执行(不实际修改主机,用于预检查) | ansible-playbook -C user.yaml |
-v /-vvv | 显示详细日志(-v 基本日志,-vvv debug 级日志,排查问题时常用) | ansible-playbook -vvv user.yaml |
3、setup 模块的使用
ansible 执行剧本时,会自动执行一个名为 “Gathering Facts” 的任务,该任务会调用 setup 模块搜集被管理机的状态数据(IP、主机名、CPU、系统版本等),这些数据统称为 Facts 变量,可在剧本中直接引用。
(1)在控制机执行以下命令,可查看某台被管理机的完整 Facts 信息:
[root@zabbix_server work]# ansible 192.168.140.12 -m setup
192.168.140.12 | SUCCESS => {"ansible_facts": {"ansible_all_ipv4_addresses": ["192.168.140.12"], "ansible_all_ipv6_addresses": [], "ansible_apparmor": {"status": "disabled"}, "ansible_architecture": "x86_64", "ansible_bios_date": "07/22/2020", "ansible_bios_version": "6.00", "ansible_cmdline": {"BOOT_IMAGE": "/vmlinuz-3.10.0-1160.el7.x86_64", "LANG": "en_US.UTF-8", "crashkernel": "auto", "quiet": true, "rd.lvm.lv": "centos/swap", "rhgb": true, "ro": true, "root": "/dev/mapper/centos-root", "spectre_v2": "retpoline"}, # 此处省略部分 Facts 内容..."ansible_virtualization_type": "VMware", "discovered_interpreter_python": "/usr/bin/python", "gather_subset": ["all"], "module_setup": true}, "changed": false
}
(2)取消 ansible 调用 setup 行为:
当无需 Facts 数据时,可通过 gather_facts: false
取消自动搜集,提升剧本执行效率:
- hosts: dbgather_facts: false tasks:- name: create user named hadoopuser:name: hadoopshell: /sbin/nologinstate: present
补充:Facts 优化收集默认 gather_facts: true
会收集所有 Facts,耗时较长。若仅需部分数据,可通过 gather_subset
过滤:
- hosts: dbgather_facts: truegather_subset: ["network", "hardware"] # 仅收集网络和硬件信息
(3)调用Ansible Facts变量
Facts 变量以 ansible_ 为前缀,在 Playbook 中通过 Jinja2 模板语法 {{ 变量名 }} 直接调用。
补充:手动触发 Facts 收集
若关闭了自动收集,但某任务需要 Facts,可手动调用 setup
模块:
- hosts: allgather_facts: notasks:# 手动收集 Facts- name: 收集主机信息ansible.builtin.setup:# 之后即可使用 Facts 变量- name: 显示主机内存ansible.builtin.debug:msg: "总内存:{{ ansible_memtotal_mb }} MB"
三、playbook 变量的使用
1、支持调用变量
在 Playbook 中,通过 {{ 变量名称 }}
格式引用变量,例如引用 Facts 变量或自定义变量:
tasks:- name: 打印主机 IPdebug:msg: "被管理机 IP: {{ ansible_ens33.ipv4.address }}" # 引用 Facts 变量
2、自定义变量
(1)使用 vars 关键字定义变量
在剧本内通过 vars
关键字直接定义变量,适用于简单场景:
[root@zabbix_server work]# cat user.yaml
- hosts: dbgather_facts: falsevars:- user_name: "AA"- sh_name: "/bin/false"tasks:- name: create useruser:name: "{{ user_name }}"shell: "{{ sh_name }}"state: present
(2)主机清单文件定义变量
在主机清单中为单个主机或主机组定义变量,变量仅对对应主机 / 主机组生效。
① 为单个主机定义变量
[db]
192.168.140.11 user_name="c1" sh_name="/bin/bash"
192.168.140.12 user_name="c2" sh_name="/sbin/nologin"
192.168.140.13 ansible_ssh_user="root" ansible_ssh_pass="redhat" ansible_ssh_port=22 user_name="c3" sh_name="/bin/false"
// 变量的作用域是单个主机,每个主机的变量值独立生效,即使变量名相同,不同主机的取值也互不影响。Ansible 会根据目标主机匹配对应的变量值进行操作。
② 为主机组定义变量
[db:vars]
user_name="BB"
sh_name="/sbin/nologin"
(3)在外部文件中定义变量
将变量存储在外部文件中,通过 vars_files
引入剧本,便于变量统一管理与复用:
[root@zabbix_server work]# cat userInfo
user_name: "D1"
sh_name: "/bin/bash"[root@zabbix_server work]# cat user.yaml
- hosts: dbgather_facts: falsevars_files:- /opt/work/userInfo # 引入外部变量文件tasks:- name: create useruser:name: "{{ user_name }}"shell: "{{ sh_name }}"state: present
3、变量加解密
(1)加密
① 变量文件进行加密
对于存储密码、密钥等敏感信息的变量文件,需通过 ansible-vault
加密,防止信息泄露:
[root@zabbix_server work] ansible-vault encrypt userInfo
# New Vault password:
# Confirm New Vault password:
# Encryption successful[root@zabbix_server work] cat userInfo
# $ANSIBLE_VAULT;1.1;AES256.....
执行加密变量文件的剧本时,需输入解密密码:
[root@zabbix_server work] ansible-playbook --ask-vault-pass user.yaml
# Vault password:
② 直接创建加密文件
无需先写明文,直接通过 ansible-vault create
创建加密文件,命令执行后会自动打开默认编辑器(如 Vi),在编辑器中写入敏感变量,保存退出后文件即被加密:
ansible-vault create vars/secrets.yml
操作需求 | 命令 | 说明 |
---|---|---|
查看加密文件 | ansible-vault view vars/secrets.yml | 输入 Vault 密码后,以明文形式显示文件内容(不会解密文件本身) |
编辑加密文件 | ansible-vault edit vars/secrets.yml | 输入 Vault 密码后,打开编辑器修改明文内容,保存后自动重新加密文件 |
重新加密文件 | ansible-vault rekey vars/secrets.yml | 更换 Vault 密码(先输入旧密码验证,再设置新密码,适用于密码泄露场景) |
补充:批量加密与密码文件
- 批量加密多个文件:
ansible-vault encrypt userInfo db_secrets.yml
- 使用密码文件避免重复输入:创建
vault_pass.txt
(权限设为 0600),执行时指定:ansible-playbook --vault-password-file vault_pass.txt user.yaml
(2)解密
① 变量文件进行解密
若需修改加密变量文件,可先解密再编辑:
# 解密文件(会生成明文文件,原加密文件保留)
ansible-vault decrypt vars/secrets.yml --output vars/secrets_plain.yml# 若需直接覆盖原加密文件为明文(谨慎!)
ansible-vault decrypt vars/secrets.yml[root@zabbix_server work] ansible-vault decrypt userInfo
# Vault password:
# Decryption successful[root@zabbix_server work]# cat userInfo
user_name: "D1"
sh_name: "/bin/bash"
② 交互式输入 Vault 密码
执行 Playbook 时,通过 --ask-vault-pass
(或缩写 -Kv
)参数,-e
是 --extra-vars
的缩写,用于向 Ansible Playbook 传递额外变量。@vars/secrets.yml
表示 “从指定路径的文件中读取变量”(@
符号是 Ansible 的语法,用于指定 “从文件加载变量”)。
Ansible 会提示输入 Vault 密码,验证通过后加载加密文件中的变量:
# 示例:执行 Playbook,引用加密的变量文件
ansible-playbook site.yml -e @vars/secrets.yml --ask-vault-pass
③ 通过密码文件自动加载(适用于自动化场景)
若需在脚本 / CI 中自动执行(无需人工输入密码),可将 Vault 密码存入一个 权限严格的文件(如 ~/.vault_pass
),再通过 --vault-password-file
参数指定该文件:
创建密码文件并设置权限(仅当前用户可读写,防止泄露):
echo "MyVaultPass123" > ~/.vault_pass
chmod 600 ~/.vault_pass # 关键:限制权限,避免其他用户读取
执行 Playbook 时引用密码文件:
ansible-playbook site.yml -e @vars/secrets.yml --vault-password-file ~/.vault_pass
补充:变量优先级当多个位置定义同名变量时,优先级从高到低为:
- 命令行
-e
传递的变量(--extra-vars
) - 剧本内
vars
定义的变量 - 外部变量文件
vars_files
- 主机清单中主机组变量(
[db:vars]
) - 主机清单中单个主机变量
- 角色默认变量(
roles/defaults/main.yml
)
4、示例使用:部署 MySQL
结合变量与模板,实现 MySQL 配置的动态生成.根据不同主机设置 server_id
和端口:
① Inventory 主机清单
# 数据库服务器组:包含3台主机,定义主机变量和组变量
[db]
192.168.140.11 db_id=11 # 主机1:仅定义db_id(数据库服务器ID)
192.168.140.12 db_id=12 # 主机2:仅定义db_id
192.168.140.13 ansible_ssh_user="root" ansible_ssh_pass="redhat" ansible_ssh_port=22 db_id=13 # 主机3:完整定义SSH连接参数和db_id# db组的公共变量:所有db组主机共享
[db:vars]
db_port=3307 # MySQL/MariaDB监听端口
② MySQL 配置模板
# MySQL/MariaDB主配置文件(Jinja2模板,后缀.j2)
[mysqld]
# 1. 绑定当前主机的ens33网卡IP(调用Ansible Facts变量,确保正确性)
bind-address={{ ansible_ens33["ipv4"]["address"] | default('0.0.0.0') }} # 补充默认值,避免变量不存在时报错
# 2. 监听端口(引用db组变量)
port={{ db_port }}
# 3. 服务器ID(引用主机变量,确保主从复制时唯一)
server_id={{ db_id }}
# 4. 启用二进制日志(用于主从复制)
log_bin=master
# 5. 补充必要基础配置(避免默认配置缺失导致启动失败)
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid[mysqld_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid
③ 基础 MySQL 部署剧本
- hosts: db # 目标主机组:db组gather_facts: true # 启用Facts收集,确保能获取ansible_ens33等变量tasks:# 补充1:检测Facts是否正常收集(避免变量调用失败)- name: Check if Ansible Facts are collectedfail:msg: "错误:无法收集主机Facts,无法获取网卡IP等关键变量"when: ansible_ens33 is undefined # 若ens33网卡变量不存在,终止执行# 1. 安装MariaDB服务(MySQL的分支,CentOS7默认)- name: Install MariaDB serveryum:name: mariadb-serverstate: presentupdate_cache: yes # 补充:安装前更新yum缓存,避免依赖下载失败# 2. 推送MySQL配置文件(使用template模块触发变量替换)- name: Push MySQL config file (Jinja2 template)template:src: /opt/work/my.cnf.j2 # 源模板文件(需确保路径存在)dest: /etc/my.cnf # 目标路径(覆盖默认配置)mode: 0644 # 补充:设置文件权限,符合系统规范owner: root # 补充:设置文件属主group: root # 补充:设置文件属组notify: Restart MariaDB # 配置文件变化时,触发handlers重启服务# 3. 启动MariaDB服务并设置开机自启- name: Start and enable MariaDB daemonservice:name: mariadbstate: startedenabled: yes# handlers组件:配置文件变化时才执行(避免无意义重启)handlers:- name: Restart MariaDBservice:name: mariadbstate: restarted
四、条件判断
(一)条件判断(when)用法
- hosts: dbgather_facts: true # 需收集主机名变量ansible_nodenametasks:# 1. 仅在主机名为"node01.linux.com"时创建用户u1- name: Create user u1 on node01user:name: u1state: presentshell: /bin/bash # 补充:设置默认shell,避免使用系统默认的nologincreatehome: yes # 补充:创建用户家目录when: ansible_nodename == "node01.linux.com"# 2. 仅在主机名为"node02.linux.com"时创建用户u2- name: Create user u2 on node02user:name: u2state: presentshell: /bin/bashcreatehome: yeswhen: ansible_nodename == "node02.linux.com"# 3. 仅在主机名为"node03.linux.com"时创建用户u3- name: Create user u3 on node03user:name: u3state: presentshell: /bin/bashcreatehome: yeswhen: ansible_nodename == "node03.linux.com"# 补充:若主机名不匹配任何条件,输出提示(避免无任务执行的困惑)- name: Print message if no user is createddebug:msg: "当前主机名{{ ansible_nodename }}不匹配任何创建用户的条件,未创建用户"when: ansible_nodename != "node01.linux.com" andansible_nodename != "node02.linux.com" andansible_nodename != "node03.linux.com"
(二)register+when 配合使用
- hosts: dbtasks:# 1. 检测dhcp服务是否已安装(忽略执行错误,避免未安装时剧本中断)- name: Check if DHCP is installedshell: rpm -q dhcpregister: result # 注册命令结果到变量resultignore_errors: true # 关键:即使rpm -q失败(未安装),剧本继续执行# 2. 调试输出result变量内容(方便排查问题)- name: Show DHCP check resultdebug:var: result # 输出result的所有属性(如rc、stdout、stderr)# 3. 仅当dhcp已安装(result.rc == 0)时,执行清理/tmp操作- name: Clean /tmp directory if DHCP is installedshell: rm -rf /tmp/*when: result.rc == 0 # rc=0表示命令执行成功(dhcp已安装)# 补充:dhcp未安装时,输出提示- name: Print message if DHCP is not installeddebug:msg: "DHCP服务未安装(rpm -q返回状态码{{ result.rc }}),跳过/tmp清理"when: result.rc != 0 # rc≠0表示命令执行失败(dhcp未安装)
五、循环(loop)用法
# 普通列表循环:创建3个基础用户
- hosts: dbtasks:- name: Create basic users (list loop)user:name: "{{ item }}" # item为循环列表中的每个元素state: presentshell: /bin/bashcreatehome: yesgroups: users # 补充:将用户加入users组loop:- "userA"- "userB"- "userC"loop_control: # 补充:优化循环输出,显示当前创建的用户名label: "正在创建用户: {{ item }}"# 字典循环:创建带自定义shell的用户
- hosts: dbtasks:- name: Create users with custom shell (dict loop)user:name: "{{ item.u_name }}" # 引用字典的u_name键shell: "{{ item.u_shell }}" # 引用字典的u_shell键state: presentcreatehome: yespassword: "!" # 补充:设置密码为"!"(禁止直接登录,增强安全)loop:- {"u_name": "user01", "u_shell": "/bin/bash"}- {"u_name": "user02", "u_shell": "/sbin/nologin"}- {"u_name": "user03", "u_shell": "/bin/false"}loop_control:label: "正在创建用户: {{ item.u_name }} (shell: {{ item.u_shell }})"
六、Ansible 角色(Role)
(一)ansible角色 role
// 本质上就是个目录,一个需求对应一个角色
1、 创建 Nginx 角色
# 1. 切换到Ansible角色默认目录(/etc/ansible/roles)
cd /etc/ansible/roles# 2. 使用ansible-galaxy创建nginx角色(自动生成标准目录结构)
ansible-galaxy init nginx# 3. 补充:设置角色目录权限(避免后续文件读写权限问题)
chmod -R 755 /etc/ansible/roles/nginx角色目录结构
[root@zabbix_server roles]# tree nginx/
nginx/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars└── main.yml8 directories, 8 files
2、 Zabbix-Agent 角色配置
① xxx.conf.j2 Zabbix-Agent 配置模板(templates/zabbix_agentd.conf.j2)
# Zabbix-Agent配置模板(Jinja2格式)
Server=192.168.140.10 # Zabbix Server IP(固定)
ServerActive=192.168.140.10 # Zabbix Server主动监控IP
Hostname={{ ansible_fqdn }} # 引用主机的完全限定域名(确保唯一)
ListenPort=10050 # 补充:Zabbix-Agent默认监听端口
ListenIP=0.0.0.0 # 补充:允许所有IP连接(根据需求调整)
LogFile=/var/log/zabbix/zabbix_agentd.log # 补充:日志路径
LogFileSize=0 # 补充:日志不轮转(或根据需求设置大小)
② xxx/tasks/main.yml 编写剧本:Zabbix-Agent 角色任务文件
---
# tasks file for zabbixAgent: Zabbix-Agent部署任务# 补充1:检测Zabbix Server是否可达(避免Agent无法连接Server)
- name: Check Zabbix Server connectivity (192.168.140.10:10051)wait_for:host: 192.168.140.10port: 10051 # Zabbix Server默认端口timeout: 10 # 超时时间10秒state: startedignore_errors: trueregister: zabbix_server_check- name: Fail if Zabbix Server is unreachablefail:msg: "错误:Zabbix Server(192.168.140.10:10051)不可达,请检查Server状态"when: zabbix_server_check.failed# 1. 添加Zabbix官方YUM源(阿里云镜像,速度更快)
- name: Add Zabbix YUM repositoryyum_repository:name: zabbixdescription: Zabbix Official Repository - RHEL/CentOS 7file: zabbixbaseurl: https://mirrors.aliyun.com/zabbix/zabbix/4.4/rhel/7/x86_64/gpgcheck: no # 阿里云镜像已验证,关闭GPG检查加速enabled: yesgpgkey: https://mirrors.aliyun.com/zabbix/RPM-GPG-KEY-ZABBIX # 补充:GPG密钥(可选启用)# 2. 安装Zabbix-Agent
- name: Install Zabbix-Agent packageyum:name: zabbix-agentstate: presentupdate_cache: yes # 安装前更新缓存# 3. 推送Zabbix-Agent配置文件(模板替换)
- name: Push Zabbix-Agent config filetemplate:src: zabbix_agentd.conf.j2 # 角色内的模板路径(templates/目录下)dest: /etc/zabbix/zabbix_agentd.conf # 目标路径mode: 0644owner: zabbixgroup: zabbixnotify: Restart zabbix-agent # 配置变化时重启服务# 4. 启动Zabbix-Agent并设置开机自启
- name: Start and enable Zabbix-Agentservice:name: zabbix-agentstate: startedenabled: yes# 补充2:验证Zabbix-Agent是否正常运行
- name: Verify Zabbix-Agent statusservice_facts: # 收集服务状态Facts
- name: Fail if Zabbix-Agent is not runningfail:msg: "错误:Zabbix-Agent启动失败,请检查日志(/var/log/zabbix/zabbix_agentd.log)"when: "'zabbix-agent' not in ansible_facts.services or ansible_facts.services['zabbix-agent']['state'] != 'running'"
③ xxxx/handlers/main.yml Zabbix-Agent 角色 handlers 文件
[root@zabbix_server roles]# cat /etc/ansible/roles/zabbixAgent/handlers/main.yml
---
# handlers file for zabbixAgent: 触发式任务(配置变化时重启)
- name: Restart zabbix-agentservice:name: zabbix-agentstate: restartedenabled: yes # 补充:确保重启后仍开机自启
④ xxxx.yaml 执行 Zabbix-Agent 角色的剧本
[root@zabbix_server roles] cat /opt/work/installZabbixAgent.yaml
- hosts: db # 目标主机组:db组(也可改为zabbix-agent组,根据需求调整)gather_facts: true # 需收集服务状态、主机名等变量roles:- zabbixAgent # 引用zabbixAgent角色(角色名需与目录名一致)[root@zabbix_server roles] ansible-playbook /opt/work/installZabbixAgent.yaml
(二)部署分布式zabbix
代码仓库:martinwjc/playbook
1、zabbixproxy
① 角色结构
[root@zabbix_server roles]# tree zabbixProxy/
zabbixProxy/
├── defaults
│ └── main.yml
├── files
│ └── my.cnf
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
│ └── zabbix_proxy.conf.j2
├── tests
│ ├── inventory
│ └── test.yml
└── vars└── main.yml8 directories, 10 files
②zabbixProxy task/main.yml
[root@zabbix_server roles]# cat zabbixProxy/tasks/main.yml
---
# tasks file for zabbixProxy- name: add epel reposhell: wget -O /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo- name: add zabbix yum repoyum_repository:name: zabbixdescription: zabbixfile: zabbixbaseurl: https://mirrors.aliyun.com/zabbix/zabbix/4.4/rhel/7/x86_64/gpgcheck: noenabled: yes- name: install zabbix-proxyyum:name: "{{ item }}"loop:- mariadb-server- zabbix-proxy-mysql- python2-PyMySQL- name: push mysql configcopy:src: my.cnfdest: /etc/my.cnf- name: start MySQL daemonservice:name: mariadbstate: startedenabled: yes- name: create zabbix proxy databasemysql_db:name: zabbixproxystate: present- name: create zabbix proxy user connect to mysql mysql_user:name: puserpassword: 'redhat'host: localhostpriv: 'zabbixproxy.*:ALL'state: present- name: load zabbix proxy tableshell: zcat /usr/share/doc/zabbix-proxy-mysql-4.4.10/schema.sql.gz | mysql -uroot zabbixproxy- name: push zabbix proxy configtemplate:src: zabbix_proxy.conf.j2dest: /etc/zabbix/zabbix_proxy.conf- name: start zabbix proxy daemonservice:name: zabbix-proxystate: startedenabled: yes
2、zabbixAgentByProxy
① 角色结构
[root@zabbix_server roles]# tree zabbixAgentByProxy/
zabbixAgentByProxy/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
│ └── zabbix_agentd.conf.j2
├── tests
│ ├── inventory
│ └── test.yml
└── vars└── main.yml
② zabbixAgentByProxy tasks/main.yml
---
# tasks file for zabbixProxy: Zabbix-Proxy部署任务# 补充1:检测依赖命令(如zcat)是否存在
- name: Check if required commands exist (zcat, mysql)command: which {{ item }}loop:- zcat- mysqlregister: cmd_checkignore_errors: true- name: Fail if required commands are missingfail:msg: "错误:缺少必要命令{{ item.item }},请手动安装"when: item.failedloop: "{{ cmd_check.results }}"# 1. 添加EPEL源(Zabbix-Proxy依赖可能在EPEL中)
- name: Add EPEL YUM repositoryyum_repository:name: epeldescription: Extra Packages for Enterprise Linux 7 - $basearchfile: epelbaseurl: https://mirrors.aliyun.com/epel/7/$basearch/gpgcheck: noenabled: yes# 替代原shell命令:避免wget依赖,使用yum_repository模块更可靠# 2. 添加Zabbix YUM源(同Agent)
- name: Add Zabbix YUM repositoryyum_repository:name: zabbixdescription: Zabbix Official Repository - RHEL/CentOS 7file: zabbixbaseurl: https://mirrors.aliyun.com/zabbix/zabbix/4.4/rhel/7/x86_64/gpgcheck: noenabled: yes# 3. 安装Zabbix-Proxy及依赖(MariaDB、PyMySQL)
- name: Install Zabbix-Proxy and dependenciesyum:name: "{{ item }}"state: presentupdate_cache: yesloop:- mariadb-server- zabbix-proxy-mysql- python2-PyMySQL # Ansible操作MySQL的Python依赖loop_control:label: "正在安装: {{ item }}"# 4. 推送MySQL配置文件(用于Proxy的数据库)
- name: Push MySQL config for Zabbix-Proxycopy:src: my.cnf # 角色files目录下的my.cnf文件dest: /etc/my.cnfmode: 0644owner: rootgroup: rootnotify: Restart MariaDB# 5. 启动MariaDB并设置开机自启
- name: Start and enable MariaDB for Proxyservice:name: mariadbstate: startedenabled: yes# 6. 创建Zabbix-Proxy数据库(避免重复创建)
- name: Create Zabbix-Proxy database (zabbixproxy)mysql_db:name: zabbixproxystate: presentlogin_unix_socket: /var/lib/mysql/mysql.sock # 本地Socket登录(无需密码)login_user: root # 登录用户(默认root)# 7. 创建Proxy数据库用户并授权
- name: Create MySQL user for Zabbix-Proxy (puser)mysql_user:name: puserpassword: 'redhat'host: localhost # 仅允许本地连接(安全)priv: 'zabbixproxy.*:ALL' # 授予zabbixproxy库的所有权限state: presentlogin_unix_socket: /var/lib/mysql/mysql.socklogin_user: root# 8. 导入Zabbix-Proxy数据库表结构(关键:避免Proxy无法连接数据库)
- name: Import Zabbix-Proxy database schemashell: zcat /usr/share/doc/zabbix-proxy-mysql-4.4.10/schema.sql.gz | mysql -uroot -S /var/lib/mysql/mysql.sock zabbixproxyargs:creates: /var/lib/mysql/zabbixproxy/history.ibd # 标记文件:若表已存在,跳过导入(幂等性)# 后续任务(推送Proxy配置、启动服务)保持原逻辑,补充权限和状态检测...
七、 剧本示例
(一)验证网络连通性
- name: Check internet connectivityhosts: all # 或指定目标主机组,如 web、db 等gather_facts: falsetasks:- name: Check default gateway connectivitycommand: ping -c 2 {{ default_gateway }} vars:default_gateway: "192.168.140.2" register: gateway_checkchanged_when: falseignore_errors: true - name: Fail if default gateway is unreachablefail:msg: "错误:目标主机无法连接默认网关 {{ default_gateway }},请检查网关配置或网络链路"when: gateway_check.rc != 0 - name: Install bind-utils (for nslookup command)yum:name: bind-utilsstate: presentbecome: true # 确保以root权限安装- name: Check external network connectivity (ping 223.5.5.5)command: ping -c 2 223.5.5.5register: external_net_checkchanged_when: falseignore_errors: true- name: Fail if external network is unreachablefail:msg: "错误:目标主机无法访问外网(223.5.5.5),请检查网关路由或防火墙规则"when: external_net_check.rc != 0- name: Check DNS resolution (mirrors.aliyun.com)command: nslookup mirrors.aliyun.com register: dns_checkchanged_when: falseignore_errors: true- name: Fail if DNS resolution failsfail:msg: "错误:目标主机无法解析 mirrors.aliyun.com,请检查DNS配置(建议使用223.5.5.5/223.6.6.6)"when: dns_check.rc != 0
(二)部署 MySQL 剧本
基础部署剧本可实现 MySQL 安装、配置推送与服务启动,此处补充安全初始化步骤,形成生产级剧本:
[root@zabbix_server work]# cat installMySQL.yaml
- hosts: dbbecome: true # 新增:提权为 root 执行vars_files:- ./vars/db_secrets.yml # 新增:外部加密变量文件(存储 root 密码)handlers: # 新增:触发式任务(配置变更后重启服务)- name: restart mysqlservice:name: mariadbstate: restartedtasks:- name: install MySQLyum:name: mariadb-serverstate: present- name: push MySQL configcopy:src: /opt/work/my.cnfdest: /etc/my.cnfmode: 0644 # 新增:设置配置文件权限,避免非法修改owner: rootgroup: rootnotify: restart mysql # 新增:配置变更时触发重启- name: start MySQL daemonservice:name: mariadbstate: startedenabled: yes# 新增:MySQL 安全初始化(删除匿名用户、设置 root 密码)- name: 删除 MySQL 匿名用户mysql_user:name: ""state: absentlogin_unix_socket: /var/lib/mysql/mysql.sock- name: 设置 root 本地密码mysql_user:name: roothost: localhostpassword: "{{ db_root_password }}" # 从加密变量文件读取密码priv: "*.*:ALL,GRANT"login_unix_socket: /var/lib/mysql/mysql.sock