必须物理拆分inventory为mysql_masters/mysql_slaves组、动态生成my.cnf、用mysql用户执行mysqld --initialize-insecure;否则即使playbook语法正确,也会出现error 2003或seconds_behind_master: null。

直接批量部署 MySQL 8.0 节点,必须物理拆分 inventory 组、动态生成 my.cnf、用 mysql 用户执行 mysqld --initialize-insecure——否则哪怕 Playbook 语法全对,也会卡在 ERROR 2003 (HY000): Can't connect to MySQL server 或 Seconds_Behind_Master: NULL。
inventory 必须硬拆成 mysql_masters / mysql_slaves 组
Ansible 不会识别“哪台该当主库”,只认 group_names 列表里的字符串。把所有节点塞进 [dbservers],再靠 when: inventory_hostname == 'db1' 分支,扩容时改主机名就得同步修几十处任务和模板,极易漏。
正确做法是物理分组:
-
[mysql_masters]:只放 1 台(MGR 场景可多台),负责写入 + 生成 binlog -
[mysql_slaves]:可多台,只读 + 拉 relay log -
[mysql_arbiters](可选):仅跑 MGR 仲裁,不存数据
后续所有配置、权限、复制链路都基于 group_names 判断,不依赖主机名。
my.cnf.j2 模板里 log-bin / read_only / skip-slave-start 必须动态控制
MySQL 主从角色由配置加载时行为锁定,不是靠启动后 SET 临时切换。写死或漏配会导致从库被误写、主库复制线程自启、从库拉不到日志。
模板关键段落必须这样写:
{% if 'mysql_masters' in group_names %}
log-bin = /data/mysql/binlog/mysql-bin
binlog-format = ROW
skip-slave-start = 1
{% else %}
read_only = ON
relay-log = /data/mysql/relaylog/relay-bin
skip-slave-start = 0
{% endif %}
注意三点:
-
server-id必须全局唯一:主库用100,从库用201、202这类递增值,不能全写1 -
/etc/my.cnf文件权限必须是644,否则mysqld启动时报Failed to open config file,但日志不提示权限问题 -
skip-slave-start = 1在主库设为1防止误启复制;从库必须设为0,否则START SLAVE不生效
mysqld --initialize-insecure 必须用 mysql 用户执行且 datadir 属主严格匹配
MySQL 8.0 废弃了 mysql_install_db,改用 mysqld --initialize-insecure。这个命令对用户和路径权限极其敏感:
- 必须用
mysql用户执行,不能用root直接跑,否则报Failed to find valid data directory -
datadir(如/data/mysql)需提前执行chown -R mysql:mysql /data/mysql - 命令必须显式指定
--user=mysql和--datadir,否则即使目录属主正确也失败 - 首次运行后不会生成临时密码,适合自动化场景,但前提是初始化成功后必须立即设置 root 密码
mysql_user 模块必须指定 socket 路径并预先切换 root 认证方式
MySQL 8.0 默认使用 caching_sha2_password 插件,而 Ansible 的 mysql_user 模块默认走 TCP 连接,容易因插件不兼容报错。
解决方法是:
- 初始化完成后,先用
mysql命令行通过socket连接本地 root(此时仍可用空密码或临时密码) - 执行
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'xxx'; - 后续所有
mysql_user操作都加参数login_unix_socket: /tmp/mysql.sock(或你实际的 socket 路径) - 避免在未切换认证方式前就调用
mysql_user,否则直接失败且无明确错误指向
最易被忽略的是:socket 路径必须与 my.cnf 中定义的 socket 一致,且 mysql 用户对该 socket 文件有读写权限——这点在非标准路径(如 /data/mysql/mysql.sock)下尤其容易出错。











