批量部署 mysql 8.0 从节点,核心是 inventory 必须用 mysql_slaves 组、my.cnf.j2 模板依 group_names 动态设 read_only=on 和 skip-slave-start=0、server-id 全局唯一且动态生成——任一出错将导致从库拒绝启动或同步卡在 seconds_behind_master: null。

直接说结论:批量部署 MySQL 8.0 从节点,核心是 inventory 分组必须为 mysql_slaves、my.cnf.j2 模板里靠 group_names 动态写 read_only = ON 和 skip-slave-start = 0、server-id 必须全局唯一且不能硬编码——这三点错一个,从库要么拒绝启动,要么能连但同步卡在 Seconds_Behind_Master: NULL。
inventory 必须用 mysql_slaves 组名,不能靠变量或主机名判断角色
Ansible 不会自动识别“这是从库”,它只看你 inventory 里写了什么组。如果你把所有节点都塞进 [dbservers],再靠 when: inventory_hostname == 'slave1' 或 slave: true 这类自定义变量分支,扩容加一台从库时就得改十几处 YAML,极易漏掉某处 when 判断或模板条件。
正确做法是物理分组:
-
[mysql_slaves]下只放真实从节点,比如slave1、slave2 - Playbook 中直接写
hosts: mysql_slaves,不写hosts: dbservers - 后续所有逻辑(模板渲染、任务跳过、handler 触发)都基于
{% if 'mysql_slaves' in group_names %},而不是主机名或任意变量
my.cnf.j2 模板里必须动态启用 read_only 和禁用 skip-slave-start
MySQL 8.0 的主从行为由配置文件加载时锁定,不是靠启动后执行 SET GLOBAL read_only = ON 临时生效。写死或漏设,会导致从库被误写入、复制线程无法拉日志、甚至主从数据不一致。
模板关键段落必须长这样:
{% if 'mysql_slaves' in group_names %}
read_only = ON
relay-log = /data/mysql/relaylog/relay-bin
skip-slave-start = 0
{% endif %}
注意两点:
-
skip-slave-start = 0是为了让从库启动 mysqld 后自动拉取 relay log;主库必须设为1,否则可能意外启复制线程 -
/etc/my.cnf文件权限必须是644,否则 mysqld 静默拒绝读取,日志只报Failed to open config file,不提示权限问题
server-id 必须全局唯一,推荐用组内索引生成
所有从节点的 server-id 不能相同,也不能和主库重复,否则复制直接失败,错误日志里会出现 Got fatal error 1236 from master 或 Client requested master to start replication from impossible position。
不要在 vars/main.yml 里硬写 server_id: 201,而应动态生成:
- 用
groups['mysql_slaves'].index(inventory_hostname) | int + 201,保证每台从库获得递增且唯一的值(如 201、202、203) - 主库用固定值如
100,避免和从库冲突 - 确保主库和所有从库的
server-id在整个集群中无重复,哪怕跨不同 Ansible 执行批次
mysqld --initialize-insecure 前必须清空 datadir 并校验属主
MySQL 8.0+ 已弃用 mysql_install_db,改用 mysqld --initialize-insecure 初始化数据目录。这个命令对路径和用户极其敏感,出错时只报 Directory not empty 或静默失败,不提示具体原因。
执行前务必确认:
-
/data/mysql(或你定义的datadir)目录为空,不能残留旧数据文件 - 该目录属主必须是
mysql:mysql,且不能有 group 或 other 写权限(即chmod 755 /data/mysql不行,得是750或更严) - 命令必须以
mysql用户身份运行,不能用root直接执行,否则初始化后的ibdata1等文件属主错乱,mysqld 启动失败
最稳妥的做法是在 Playbook 中加一步校验任务:
- name: ensure datadir is clean and owned by mysql
file:
path: /data/mysql
state: directory
owner: mysql
group: mysql
mode: '0750'
recurse: yes
然后才执行初始化命令。这点容易被跳过,但一旦出错,重装比修复快得多。











