[小技巧63] MySQL 主从复制中 Slave IP 显示异常?揭秘 Docker 网络与 NAT 对复制连接的影响
在日常运维中,遇到这样一个“诡异”现象:
主库的
information_schema.processlist中显示 Binlog Dump 连接来自10.1.0.1,但从库配置的Master_Host却是192.168.100.66。
一、问题现象复现
场景 A:同主机容器化主从
- 从库容器 IP:
10.1.0.83 - 从库配置:
MASTER_HOST = '192.168.100.66', MASTER_PORT = 33306 - 主库观察结果:
SELECT * FROM information_schema.processlist WHERE state LIKE '%binlog%'\G -- HOST: 10.1.0.1:44797
场景 B:跨主机物理复制
- 从库所在主机 IP:
192.168.100.22 - 从库配置:
MASTER_HOST = '192.168.100.21', MASTER_PORT = 13307 - 主库观察结果:
-- HOST: 192.168.100.201:59763 (真实出口 IP)
为何同一套 MySQL 复制逻辑,在不同部署模式下,主库看到的 Slave IP 完全不同?
二、根本原因:Linux 网络命名空间与 Docker MASQUERADE
2.1 Docker 自定义桥接网络的工作机制
当使用 docker network create 创建自定义 bridge 网络(如 local_network_dev)时:
- Docker 会在宿主机创建一个虚拟网桥(如
br-982b127ffbf6) - 分配子网(如
10.1.0.0/24),网关为10.1.0.1 - 容器通过 veth pair 接入该网桥,获得
10.1.0.xIP
# 查看接口
ip -br a
# br-982b127ffbf6 UP 10.1.0.1/24
2.2 出站流量的 SNAT(源地址转换)
关键点在于:当容器访问非 Docker 网络内的地址(如宿主机的 192.168.100.66)时,Docker 会自动添加 iptables MASQUERADE 规则。
iptables -t nat -L POSTROUTING
# 输出包含:
# MASQUERADE all -- 10.1.0.0/24 anywhere
该规则的作用是:
将所有源自
10.1.0.0/24且目标不在该网段的流量,源 IP 替换为网桥接口 IP(即10.1.0.1)
因此,主库收到的 TCP 连接自然显示为 10.1.0.1:port。
2.3 SNAT(源地址转换)详解
SNAT(Source Network Address Translation,源网络地址转换)是 NAT(Network Address Translation,网络地址转换) 的一种形式,用于在数据包离开网络时修改其源 IP 地址。
1. SNAT 的核心作用
让内网主机能够访问外网,同时对外“隐藏”真实 IP。
典型场景:
- 家庭路由器:多台手机/电脑(内网 IP 如
192.168.1.x)共享一个公网 IP 上网。 - Docker 容器:容器使用私有 IP(如
10.1.0.83),但能访问宿主机或互联网。 - 企业 NAT 网关:内部服务器通过统一出口 IP 访问外部服务。
2. SNAT 工作原理(以 Linux + iptables 为例)
当一个数据包从内网发出时,Linux 内核在 POSTROUTING 链(路由之后、发送之前)执行 SNAT:
原始包:
源 IP: 10.1.0.83 ← 容器 IP
目标 IP: 192.168.100.66
经过 SNAT 后:
源 IP: 10.1.0.1 ← 改为网桥 IP(或公网出口 IP)
目标 IP: 192.168.100.66
返回包时,内核会自动将目标 IP 反向转换回 10.1.0.83(基于连接跟踪表 conntrack)。
🔧 在 Linux 中,常用命令:
# 查看 SNAT 规则 iptables -t nat -L POSTROUTING # 典型规则 MASQUERADE all -- 10.1.0.0/24 anywhere
MASQUERADE是 SNAT 的一种动态形式(自动使用出口网卡 IP)。
3. SNAT vs DNAT 对比
| 类型 | 全称 | 修改字段 | 典型用途 |
|---|---|---|---|
| SNAT | Source NAT | 源 IP | 内网 → 外网(如容器访问互联网) |
| DNAT | Destination NAT | 目标 IP | 外网 → 内网(如访问 宿主机:8080 转发到容器:80) |
💡 Docker 同时使用两者:
-p 8080:80→ 创建 DNAT(外部访问宿主机 8080 → 容器 80)- 容器访问外网 → 触发 SNAT/MASQUERADE
4. 当前 MySQL 场景
在当前环境中:
- 从库容器 IP:
10.1.0.83 - 它连接:
192.168.100.66:33306(宿主机上的主库) - 因为目标 IP 不属于
10.1.0.0/24网段,触发 SNAT - 源 IP 被改为 Docker 网桥 IP
10.1.0.1 - 所以主库看到的连接来自
10.1.0.1
这就是为什么 processlist.HOST = '10.1.0.1:xxxx'。
5. SNAT 的优缺点
优点
- 节省公网 IP(多个内网设备共享一个出口 IP)
- 隐藏内部网络结构,提升安全性
- 无需修改应用代码即可实现网络互通
缺点
- 主机无法直接看到真实客户端 IP(需额外日志或代理传递)
- 增加内核处理开销(需维护连接跟踪表)
- 调试网络问题时更复杂(IP 被“伪装”)
总结
SNAT 就是“出门换马甲”——内网设备用一个公共 IP 对外通信,回来时系统再把“马甲”换回真身。
三、数据流对比:同主机 vs 跨主机
3.1 同主机容器 → 宿主机服务(触发 SNAT)

3.2 跨主机直接连接(无 SNAT)

四、关键验证命令汇总
| 目标 | 命令 | 说明 |
|---|---|---|
| 查看容器 IP | docker inspect <name> | grep IPAddress | 确认容器在 10.1.0.0/24 |
| 查看端口映射 | ss -tulnp | grep <port> | 确认主库通过 docker-proxy 暴露 |
| 查看 NAT 规则 | iptables -t nat -L POSTROUTING | 确认存在 MASQUERADE 10.1.0.0/24 |
| 查看复制状态 | SHOW SLAVE STATUS\G | 确认 IO/SQL Running: Yes |
| 查看主库连接 | SELECT * FROM processlist WHERE ... | 观察 HOST 字段 |
五、影响与最佳实践
5.1 是否影响复制功能?
否。只要:
- 复制账号授权正确
- 网络连通
- binlog 格式兼容
复制即可正常工作。
5.2 复制账号授权建议
| 部署模式 | 推荐授权方式 |
|---|---|
| 同主机容器 | GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.1.0.1'; |
| 跨主机 | GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.100.%'; |
| 混合环境 | 同时授权多个 host,或使用 'repl'@'%'(需评估安全风险) |
若未授权
10.1.0.1,将出现Access denied for user 'repl'@'10.1.0.1'错误。
5.3 架构优化建议(可选)
若希望主库看到真实容器 IP,可采用:
- 方案 1:主从均加入同一 Docker 网络,从库直连主库容器 IP(如
10.1.0.86) - 方案 2:使用
host网络模式运行从库容器(--network host) - 不推荐:修改 iptables 禁用 MASQUERADE(破坏 Docker 网络模型)
六、面试题
Q1:为什么 MySQL 主库看到的 Slave IP 是 10.1.0.1,而不是容器的真实 IP?
答:因为从库容器通过宿主机 IP(如
192.168.100.66)访问主库,触发 Docker 的 MASQUERADE 规则,将源 IP 伪装为 Docker 网桥 IP(10.1.0.1)。这是 Linux 内核对跨网络命名空间流量的标准处理方式。
Q2:如何让主库看到真实的容器 IP?
答:让主从容器处于同一 Docker 网络,并配置从库直连主库的容器 IP(如
10.1.0.86),避免经过宿主机端口映射和 SNAT。
Q3:10.1.0.1 是什么?它能被外部访问吗?
答:
10.1.0.1是 Docker 自定义 bridge 网络的网关 IP,对应宿主机上的虚拟网桥接口(如br-982b...)。它仅用于容器出站流量的 SNAT 源地址,通常不能被外部主动连接。
Q4:跨主机复制为何能显示真实 IP?
答:因为流量直接通过物理网络传输,未经过 Docker 的 MASQUERADE 规则,源 IP 保持为从库所在主机的真实 IP(或企业 NAT 后的公网 IP)。
更多推荐
所有评论(0)