批量部署生产级mysql集群关键在角色分组清、配置动态准、权限隔离严、初始化稳:inventory须硬拆mysql_masters/mysql_slaves组,my.cnf.j2按group_names动态设log-bin/read_only/skip-slave-start,server-id全局唯一,/etc/my.cnf权限644,--initialize-insecure需mysql用户及目录属主严格匹配。

直接上结论:批量部署生产级 MySQL 集群,关键不在“装得快”,而在“角色分得清、配置动得准、权限卡得死、初始化稳得住”。Ansible 本身不解决 MySQL 语义问题,它只忠实执行你定义的边界逻辑——一旦 inventory 分组错、server-id 重复、skip-slave-start 漏设,集群就卡在 Seconds_Behind_Master: NULL 或直接报 ERROR 2003 (HY000): Can't connect to MySQL server。
inventory 必须按主从角色物理分组,不能靠变量模拟
Ansible 不会自动识别哪台是主、哪台是从。它只认 group_names 列表里的字符串。如果你把所有节点都塞进 [dbservers],再靠 when: inventory_hostname == 'db1' 去分支,那扩容时改 host 名就得同步改 dozens 处 YAML,极易出错。
正确做法是硬性拆分:
-
[mysql_masters]:仅含 1 台(或 MGR 场景下多台),用于写入 + 生成 binlog -
[mysql_slaves]:可多台,只读 + 拉取 relay log -
[mysql_arbiters](可选):仅部署 MGR 仲裁服务,不存数据
这样后续所有模板、任务、handlers 都能用 {% if 'mysql_masters' in group_names %} 安全判断,无需硬编码主机名。
my.cnf.j2 模板里必须动态控制 log-bin / read_only / skip-slave-start
MySQL 主从不是靠“启动后手动 set”来区分的,而是靠配置文件加载时的行为锁定。写死参数或漏掉关键开关,会导致从库误写、主库复制线程自启、或从库无法拉取日志。
模板中这几行不能省:
- 主库必须有:
log-bin = /data/mysql/binlog/mysql-bin、binlog-format = ROW、skip-slave-start = 1 - 从库必须有:
read_only = ON、relay-log = /data/mysql/relaylog/relay-bin、skip-slave-start = 0 -
server-id必须全局唯一:主库用100,从库用201、202这类递增值,不能全写1
还要注意:/etc/my.cnf 文件权限必须是 644,否则 mysqld 启动时直接拒绝读取,日志里只报 Failed to open config file,不提示权限问题。
mysql_user 模块连不上新实例?先检查连接方式和密码空值
刚初始化的 MySQL 实例默认只允许 localhost socket 连接。mysql_user 模块若没显式指定 login_host,会尝试走 TCP 并失败——尤其在容器或非标准安装路径下。
常见错误现象:Access denied for user 'root'@'127.0.0.1' 或 Can't connect to local MySQL server through socket
解决方案:
- 强制走 TCP:
login_host: 127.0.0.1+login_port: 3306 - 确保 mysqld 没启用
skip-networking(检查my.cnf并注释该行) - 首次连接 root 时,
login_password: ""必须显式写出,不能省略或留空;Ansible 默认当 null 处理,会触发认证失败
另外,mysql_db 创建库失败,90% 是因为当前连接用户没 CREATE 权限,不是库名拼错——记得在 mysql_user 任务里配好 priv: "*.*:ALL"。
MySQL 8.0+ 初始化必须用 --initialize-insecure,且目录权限要严控
MySQL 8.0 已弃用 mysql_install_db,改用 mysqld --initialize-insecure。这个命令对用户和路径极其敏感:
- 运行用户必须是
mysql,不能是root直接跑 -
datadir(如/data/mysql)必须属主mysql:mysql,且不能有 world-writable 权限(即不能含o+w) - 若用
unarchive解压二进制包,解压后要立刻chown -R mysql:mysql /data/mysql,否则初始化失败且错误信息极不明确(只报Cannot create a new directory)
还有个隐藏坑:MySQL 8.0 默认启用 validate_password 插件。如果后续 mysql_user 任务里设了弱密码(如 test123),会被拦截。建议初始化后第一件事就是执行 mysql_query: sql: "SET GLOBAL validate_password.policy = LOW;"。
真正难的不是写完 Playbook,而是每台机器的 server-id 是否真唯一、每台的 read_only 是否真生效、每个 mysql_user 的权限是否精确落到 GRANT OPTION 级别——这些没法靠“跑一遍就完事”,得在 handlers 里加校验任务,比如用 mysql_replication: mode: get_master 抽取位点,再用 assert 核对输出字段。











