目录
Ansible:Vault 加密、批量部署与 Zabbix 数据库 TiDB 替换踩坑全记录
这次实操主要啃了三块内容:Ansible Vault 加密敏感变量、用 host_vars/group_vars 做分层变量管理,以及把 Zabbix 的底层数据库从 MariaDB 替换成 TiDB 分布式数据库。前面 Ansible 部分还算顺畅,到 TiDB 对接 Zabbix 初始 SQL 的环节踩了一连串的坑,光触发器兼容问题就折腾了快四个小时,试了 sed、awk、perl 好几种文本处理方式都翻了车,最后用最朴素的强制导入方案才搞定。把完整的操作流程和踩坑细节都整理出来,给大家避避坑。
一、Ansible Vault 加密变量与批量用户创建
1.1 实验目标与前置准备
日常写 Playbook 时,密码、密钥这类敏感信息如果明文写在变量文件里,安全风险很高。Ansible Vault 是官方提供的加密工具,可以将整个变量文件加密,执行时再输入密码解密,非常适合生产环境管理敏感配置。
前置环境:控制端 server1 已完成 Ansible 安装,被管理端主机配置好免密登录与 sudo 权限,inventory 清单中已划分 db、webservers 等主机组。
1.2 Vault 加密变量文件的基础操作
首先创建存放用户列表的变量文件 userlist.yml,写入需要批量创建的用户名与密码,再通过 Vault 对文件进行加密。
userlist:
- {user: user1, pass: pass1}
- {user: user2, pass: pass2}
- {user: user3, pass: pass3}
加密、查看、编辑三个核心命令:
# 加密文件
ansible-vault encrypt userlist.yml
# 查看加密文件内容,输入密码后解密展示
ansible-vault view userlist.yml
# 编辑加密文件,自动完成加解密
ansible-vault edit userlist.yml
执行 view 命令输入密码后即可看到明文内容,效果如下。加密后的文件直接 cat 无法读取任何敏感信息,安全性有保障。


1.3 编写批量创建用户的 Playbook
接下来编写 user.yml 剧本,通过 vars_files 加载加密变量文件,配合 loop 循环批量创建用户;密码使用 password_hash 过滤器做 sha512 哈希处理,符合 Linux 系统密码存储规范。
- hosts: db
gather_facts: no
vars_files:
- userlist.yml
tasks:
- name: Add the users
ansible.builtin.user:
name: "{{ item.user }}"
password: "{{ item.pass | password_hash('sha512') }}"
state: present
loop: "{{ userlist }}"

1.4 首次执行的两个经典踩坑
写完直接执行剧本,上来就踩了两个坑,报错信息如下:
[WARNING]: Could not match supplied host pattern, ignoring: db
ERROR! Attempting to decrypt but no vault secrets found
第一个警告是 inventory 中没有匹配到 db 主机组,通常是主机组名拼写错误或清单文件路径不对,检查修正即可。
第二个报错是核心问题:使用了加密变量文件,但执行 playbook 时没有传入 Vault 密码,Ansible 无法解密变量。
解决方法:执行时追加 --ask-vault-pass 参数,运行时输入加密密码。
ansible-playbook user.yml --ask-vault-pass

1.5 进阶:用 host_vars 自动加载加密变量
每次手动编写 vars_files 加载变量略显繁琐,Ansible 支持按主机自动加载对应目录下的变量文件,也就是 host_vars 机制。
在 playbook 同级目录创建 host_vars 目录,再为 node1 主机创建独立子目录,将加密后的 userlist.yml 放入其中。
mkdir -p host_vars/node1
cd host_vars/node1
vim userlist.yml # 写入变量后执行vault加密
目录结构效果参考。

随后修改 user.yml,注释掉 vars_files 部分,无需手动加载变量,Ansible 执行到对应主机时会自动读取 host_vars 下的变量文件。
- hosts: db
gather_facts: no
# vars_files:
# - userlist.yml
tasks:
- name: Add the users
ansible.builtin.user:
name: "{{ item.user }}"
password: "{{ item.pass | password_hash('sha512') }}"
state: present
loop: "{{ userlist }}"
修改后的剧本参考。


执行时同样携带 --ask-vault-pass 参数,变量会自动加载生效,这种方式在主机数量多、变量差异化大的场景中实用性很强。
二、group_vars 组变量 + Apache 批量部署实战
2.1 group_vars 组变量管理
与 host_vars 对应,group_vars 是面向主机组的变量管理方案,同一组内的所有主机共享一套变量,适合批量部署同类型服务的场景。
创建 webservers 组的变量目录,在其中定义 Apache 监听端口等公共参数。
mkdir -p group_vars/webservers
cd group_vars/webservers
vim vars.yml
在变量文件中定义 http_port: 80,后续剧本中可直接引用该变量。

2.2 Apache 部署 Playbook
编写 Apache 部署剧本,为每个任务设置不同的 tag,后续可单独执行某一步操作,无需全量运行,大幅提升调试效率。
- hosts: lamp
become: yes
vars:
http_port: 80
tasks:
- name: Install the Apache
ansible.builtin.yum:
name: httpd
state: present
tags: t1
- name: Start service httpd, if not started
ansible.builtin.service:
name: httpd
state: started
enabled: yes
tags: t2
- name: create index.html
ansible.builtin.copy:
content: "{{ ansible_hostname }}\n"
dest: /var/www/html/index.html
tags: t3
- name: Ensure the default Apache port is {{ http_port }}
ansible.builtin.lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen '
insertafter: '#Listen '
line: Listen {{ http_port }}
notify: restart service httpd
handlers:
- name: restart service httpd
ansible.builtin.service:
name: httpd
state: restarted
这里使用 lineinfile 模块修改配置文件,配合 handlers 触发器,只有配置文件实际发生修改时才会触发 Apache 重启,比每次执行都重启更加优雅。
如果只需执行安装步骤,可通过 ansible-playbook apache.yml -t t1 单独运行指定标签的任务。

2.3 本地可用性检测
部署完成后,可以使用 uri 模块在本地执行健康检测,验证服务是否正常返回 200 状态码。
- hosts: localhost
gather_facts: false
become: false
tasks:
- name: Check that you can connect (GET) to a page and it returns a status 200
ansible.builtin.uri:
url: http://192.168.48.162
return_content: true
register: result
- name: Print return information from the previous task
ansible.builtin.debug:
var: result.content
这样整个部署+验证流程可以完全自动化,无需手动执行 curl 测试。
三、Ansible 一键部署 Zabbix 服务
3.1 Playbook 整体设计思路
Zabbix 部署步骤多、依赖繁杂,手动安装容易遗漏步骤。我们将整个部署流程封装为完整的 Playbook,从安装依赖、配置源、安装服务、初始化数据库到修改配置实现全自动化。
同时将数据库密码等敏感信息存入 Vault 加密的 secrets.yml 中,通过 vars_files 加载,符合安全规范。
3.2 分步任务拆解
整个剧本按执行顺序拆分为 8 个标签任务,便于分步调试:
- 安装 pymysql 依赖,为后续 MySQL 模块提供运行支持
- 添加 Zabbix 官方 yum 软件源
- 安装 Zabbix 服务端、Web 端、Agent 等全套组件
- 安装 MariaDB 数据库
- 启动数据库并设置开机自启
- 创建 zabbix 数据库,设置 utf8mb4 字符集
- 创建 zabbix 数据库用户并授权
- 导入初始 SQL 脚本、配置服务端密码、启动服务
核心任务代码参考:
- name: import zabbix initial sql
ansible.builtin.shell:
zcat /usr/share/zabbix-sql-scripts/mysql/server.sql.gz | mysql -uzabbix -pzabbix@123 zabbix
args:
creates: /var/lib/mysql/zabbix/hosts.ibd
tags: t7_import_sql
这里使用 creates 参数实现幂等判断,只要数据库文件已存在就不会重复导入,多次执行剧本也不会报错。



3.3 结合 Vault 加密敏感配置
将数据库密码放入加密的 secrets.yml 中,剧本通过 vars_files 加载,配置项使用变量引用,避免明文泄露。
- hosts: zabbix_server
become: yes
vars_files:
- secrets.yml
tasks:
...
- name: config zabbix server db password
ansible.builtin.lineinfile:
path: /etc/zabbix/zabbix_server.conf
regexp: '#DBPassword='
line: "DBPassword={{ zabbix_db_password }}"
tags: t8_config_server
执行时携带 --ask-vault-pass 输入解密密码即可;生产环境也可以使用 vault 密码文件的方式实现自动化执行。





四、核心实战:Zabbix 数据库替换为 TiDB
这部分是本次实操最折腾的环节,也是踩坑最多的部分。我们要将 Zabbix 原本的 MariaDB 数据库替换为 TiDB 分布式数据库,验证二者的兼容性。
4.1 TiDB 单机集群快速部署
TiDB 官方提供了 tiup 工具,一行命令即可快速拉起一套单机测试集群,非常便捷。
首先安装 tiup:
curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh
source ~/.bash_profile
安装成功后验证版本【图20】,随后启动 v6.5.0 版本的单机 playground 集群:
export TIUP_TIMEOUT=120
tiup playground v6.5.0 --db 1 --kv 1 --pd 1 --host 0.0.0.0
首次启动会自动下载各个组件,等待数分钟即可启动完成。启动后默认通过 4000 端口提供 MySQL 协议服务,root 用户默认无密码。

4.2 导入 Zabbix 初始 SQL 的连环踩坑
原本以为直接将 SQL 导入 TiDB 即可,结果一执行就报错,开启了长达两小时的排坑之路。
坑1:TiDB 不支持数据库触发器
第一次直接导入原始 SQL,直接抛出 1064 语法错误,定位到 create trigger 开头的语句。
ERROR 1064 (42000) at line 2124: You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use line 1 column 14 near "trigger hosts_name_upper_insert"
查阅官方文档后得知,TiDB 目前不支持数据库触发器语法,而 Zabbix 初始 SQL 末尾附带了多个触发器,用于自动更新 name_upper 字段。
最初的思路是从 SQL 文件中删除触发器部分,于是开启了文本处理的翻车之路。
坑2:sed 范围匹配失效
最开始使用 sed '/CREATE TRIGGER/,/END;/d' 进行删除,结果导入后依然报错。排查后发现两个问题:
- Zabbix 的 SQL 中触发器为小写
create trigger,sed 默认区分大小写,无法匹配 - 触发器结尾不是
END;,而是修改了分隔符的END //,范围结束标记不匹配
即使添加忽略大小写参数依然无效,受多行结构和分隔符影响,范围匹配总会遗漏内容。
坑3:awk/perl 处理生成空文件
换成 awk 状态机和 perl 多行匹配的写法,结果要么触发器没删干净,要么直接把整个文件处理成 0 字节的空文件,导入后一张表都没有,非常影响心态。
中间还尝试过按行号截断,结果匹配到了表名中包含 trigger 的行,把大半表结构都截断了,同样翻车。
最终方案:–force 强制导入跳过错误
折腾到最后突然反应过来,MySQL 客户端有个 --force 参数,遇到 SQL 错误会自动跳过并继续执行后续语句。触发器都集中在文件末尾,前面的建表语句和初始数据完全正常,直接强制导入跳过触发器报错即可。
zcat /usr/share/zabbix-sql-scripts/mysql/server.sql.gz | mysql -h127.0.0.1 -P4000 -uroot --force zabbix
执行过程中会闪过几条触发器的报错,无需理会;命令运行结束后,所有业务表和初始数据都完整导入。最终查表数量为 174 张,与原生 MariaDB 导入的数量一致,完全可用。
真的是绕了一大圈,最朴素的方法反而最管用。

4.3 Zabbix 配置修改
数据库导入完成后,修改 Zabbix 服务端配置文件,将数据库端口改为 TiDB 的 4000 端口,地址修改为对应 IP:
DBHost=127.0.0.1
DBPort=4000
DBName=zabbix
DBUser=zabbix
DBPassword=zabbix@123


随后修改 Web 端的 PHP 配置文件 /etc/zabbix/web/zabbix.conf.php,同步更新为 TiDB 的连接信息:
<?php
global $DB;
$DB['TYPE'] = 'MYSQL';
$DB['SERVER'] = '127.0.0.1';
$DB['PORT'] = '4000';
$DB['DATABASE'] = 'zabbix';
$DB['USER'] = 'zabbix';
$DB['PASSWORD'] = 'zabbix@123';
$DB['SCHEMA'] = '';
$ZBX_SERVER = '127.0.0.1';
$ZBX_SERVER_PORT = '10051';
$ZBX_SERVER_NAME = 'Zabbix Server';
$IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG;
修改完成后为配置文件设置正确权限,否则 Web 页面会抛出权限错误:
chown apache:apache /etc/zabbix/web/zabbix.conf.php
chmod 644 /etc/zabbix/web/zabbix.conf.php
4.4 重启所有服务
全部配置修改完成后,重启 Zabbix 服务端、Apache 和 PHP-FPM:
systemctl restart zabbix-server httpd php-fpm
systemctl status zabbix-server
看到服务显示 active (running) 即说明启动成功。

五、最终效果验证
5.1 服务与数据库连通性验证
首先查看 Zabbix 服务日志,确认无数据库连接报错,服务正常启动。
随后验证数据库内的数据,查询 hosts 表的记录数:
mysql -h127.0.0.1 -P4000 -uzabbix -pzabbix@123 zabbix -e "select count(*) from hosts;"
返回 431 条记录,说明初始数据完整,服务端可以正常读写 TiDB。

5.2 Web 端登录验证
浏览器访问 http://server2的IP/zabbix,使用默认账号 Admin / zabbix 登录,成功进入仪表盘。
可以看到主机、模板、监控项、触发器的数量均正常显示,Zabbix server 状态显示运行中,整个 Web 界面无任何数据库报错。

5.3 功能可用性验证
进入「配置-主机」可以看到默认的 Zabbix server 主机状态正常;「监测-最新数据」中可以看到持续更新的监控数据,说明数据采集、入库、展示全链路均无问题。
执行配置重载命令也可以正常响应:
zabbix_server -R config_cache_reload

109

被折叠的 条评论
为什么被折叠?



