【是否原创】是
【首发渠道】TiDB 社区
【正文】
作者:
靳献旗 汽车之家 DBA,TUG 2021 MVA
1.背景
公司 TiDB 集群前端负载均衡器使用的是 LVS,FULL NAT 模式, 这种模式最大的一个问题是:RealServer 无法获得用户 IP ,即在 TiDB 数据库中无法显示客户端真实 IP,显示的是 LVS 服务器上的一个 IP。很多时候,在排查 TiDB 问题时,需要获取客户端真实 IP,这给我们带来了一定困难。本文简单讲述下如何在 TiDB-Server 端显示客户端真实 IP 的方法和工作原理。
2.FULL NAT 概述
本文主要针对 LVS FULL NAT 场景,所以本节简单介绍下其工作原理、优缺点及解决办法。本文中使用到的 IP 均已脱敏处理。
2.1工作原理
如图所示,FULLNAT 模式是 NAT 的一种扩展,不仅仅将目的 IP 地址进行替换,同时还将源 IP 地址进行了替换,这样做的好处是将 real server 从 virtual server 的后端网络中解放出来,不再要求与 virtual server 位于同一子网内。工作过程如下:
(1) 当客户端发出一个请求的数据包到达负载均衡器后,负载均衡器会对请求数据包同时做 SNAT 和 DNAT,将请求数据包修改为:{ LVS 出口 IP 地址 192.168.2.101、LVS 端口号、LVS 的 MAC 地址 } -> { 某台 Tidb-Server 的 IP 地址、某台 Tidb-Server 的端口号、某台 Tidb-Server 的 MAC 地址 }。
(2) 这样负载均衡器就可以独立的和真实服务器进行数据包的传送。
(3) 真实服务器收到请求的数据包,返回响应的数据包:{ 某台 Tidb-Server 的 IP 地址、某台 Tidb-Server 的端口号、某台 Tidb-Server 的 MAC 地址 } -> { LVS 的入口 IP 地址、LVS 端口号、LVS 的 MAC 地址 } 。此时在真实服务器上查看 TCP 连接为:DIP -> RIP。
(4) 当返回的数据包到达负载均衡器后,负载均衡器将返回数据包再次同时做 DNAT 和 SNAT。
(5) 负载均衡器返回数据包给客户端。
FULL NAT 模式下报文变化:
发送端 | 接收端 | 备注 |
---|---|---|
192.168.1.1 (CIP) | 192.168.2.100 (VIP) | |
192.168.2.101 (DIP) | 192.168.3.1 (RIP) | SNAT+DNAT |
192.168.3.1 (RIP) | 192.168.2.101 (DIP) | |
192.168.2.100 (VIP) | 192.168.1.1 (CIP) | DNAT+SNAT |
说明:
缩写 | 说明 |
---|---|
CIP | 客户端的地址 |
VIP | LVS虚拟地址 |
RIP | 真实服务器,即TiDB Server节点的地址 |
DIP | LVS出口地址 |
SNAT | 来源地址转换 |
DNAT | 目的地址转换 |
2.2优缺点
- 优点
扩展性好,负载均衡器和真实服务器可以跨 VLAN 部署 - 缺点
真实服务器无法获取客户端真实 IP
2.3解决办法
针对上一小节中提到的 FULL NAT 模式无法获取客户端真实 IP 的问题有没有解决办法?答案是有。
TOA 名字全称是 tcp option address,是 FULL NAT 模式下能够让后端服务器获取 ClientIP 的一种实现方式,它的基本原理比较简单:
(1) 客户端用户请求数据包到达 LVS 时,LVS 在数据包的 tcp option 中插入 src ip 和 src port 信息
(2) 数据包到达后端服务器(装有 toa 模块)后,应用程序正常调用 getpeername 系统函数来获取连接的源端 IP 地址
(3) 由于在 toa 代码中 hook(修改)了 inet_getname 函数(getpeername 系统调用对应的内核处理函数),该函数会从 tcp option 中获取 LVS 填充的 src 信息
(4) 这样后端服务器应用程序就获取到了真实客户端的 ClientIP,而且对应用程序来说是透明的。
3.实战
3.1测试环境
组件名 | 版本 |
---|---|
CentOS | CentOS Linux release 7.4.1708 (Core) |
TiDB | 5.1.3 |
3.2部署步骤
(1) 升级 TiDB 到 5.0 版本
因为参数 enable-tcp4-only 是从 5.0 开始引入的,因此,如果集群版本低于 5.0 ,需要先升级集群,本文测试使用的版本是 5.1.3。
(2) 开启 enable-tcp4-only
所有 TiDB Server 节点需要开启 enable-tcp4-only,参考步骤如下
a) 编辑集群拓扑文件
tiup cluster edit-config stale_read_test
server_configs:
tidb:
enable-tcp4-only: true # 添加或者修改这一行配置为 true
b) 重新加载配置
tiup cluster reload stale_read_test -R tidb
c) 检查配置是否生效
select * from information_schema.cluster_config where TYPE=‘tidb’ and key
=‘enable-tcp4-only’;
参数说明
enable-tcp4-only 从 v5.0 版本开始引入
控制是否只监听 TCP4。
默认值:false
当使用 LVS 为 TiDB 做负载均衡时,可开启此配置项。这是因为 LVS 的 TOA 模块可以通过 TCP4 协议从 TCP 头部信息中解析出客户端的真实 IP。
(3) 安装 ip_vs_ca 模块
需要在所有 TiDB Server 服务器上安装 ip_vs_ca 模块
从 git 上(https://github.com/yubo/ip_vs_ca)下载安装包 ip_vs_ca-master.zip
yum -y install cmake
unzip ip_vs_ca-master.zip
cd ip_vs_ca-master
cmake .
cd src/
make
insmod ./ip_vs_ca.ko
cp -fr /root/ip_vs_ca-master /usr/local/src/
echo insmod /usr/local/src/ip_vs_ca*/src/ip_vs_ca.ko >> /etc/rc.local
查看计数器和版本信息
cat /proc/net/ip_vs_ca_stats
(4) 测试
客户端通过 LVS VIP 连接到 TiDB 集群后,在 TiDB 端通过 show processlist 查看客户端 IP。
3.3实际效果
未开启 enable-tcp4-only 的效果
dba@192.168.2.100:4000 : (none) > select * FROM information_schema.cluster_processlist\"G
*************************** 1. row ***************************
INSTANCE: 192.168.3.1:10080
ID: 15
USER: dba
HOST: 192.168.2.101:5394 #LVS DIP
DB: NULL
COMMAND: Query
TIME: 0
STATE: autocommit
INFO: select * FROM information_schema.cluster_processlist
DIGEST: b1e38e59fbbc3e2b35546db5c8053040db989a497ac6cd71ff8dd4394395701a
MEM: 4
DISK: 0
TxnStart: 01-19 11:25:30.423(430587964404006913)
1 row in set (0.00 sec)
开启 enable-tcp4-only 的效果
show processlist;
dba@192.168.2.100:4000 : (none) > select * FROM information_schema.cluster_processlist\"G
*************************** 1. row ***************************
INSTANCE: 192.168.3.1:10080
ID: 190927
USER: dba
HOST: 192.168.1.1:13557 #客户端真实IP
DB: NULL
COMMAND: Query
TIME: 0
STATE: autocommit
INFO: select * FROM information_schema.cluster_processlist
DIGEST: b1e38e59fbbc3e2b35546db5c8053040db989a497ac6cd71ff8dd4394395701a
MEM: 0
DISK: 0
TxnStart: 01-19 08:53:36.472(430585575236435969)
1 row in set (0.00 sec)
慢日志
# Time: 2022-01-19T09:04:18.429228693+08:00
# Txn_start_ts: 0
# User@Host: dba[dba] @ 192.168.1.1 [192.168.1.1] #客户端真实IP
# Conn_ID: 190927
# Query_time: 10.00093258
# Parse_time: 0.000098847
# Compile_time: 0.000132781
# Rewrite_time: 0.000073525
# Optimize_time: 0.00001867
# Wait_TS: 0.000006643
# Is_internal: false
# Digest: a0adeeb79b71315ac13a77f3f11162106b5ec7b48212cf17c20c754263ab9228
# Num_cop_tasks: 0
# Prepared: false
# Plan_from_cache: false
# Plan_from_binding: false
# Has_more_results: false
# KV_total: 0
# PD_total: 0.000004254
# Backoff_total: 0
# Write_sql_response_total: 0.000003211
# Succ: true
# Plan: tidb_decode_plan('gQHwVTAJM18zCTAJMQlzbGVlcCgxMCktPkNvbHVtbiMxCTEJdGltZToxMHMsIGxvb3BzOjIsIENvbmN1cnJlbmN5Ok9GRgkwIEJ5dGVzCU4vQQoxCTI1XzQJAVAQcm93czoZQxAuMDTCtRlHIAlOL0EJTi9BCg==')
# Plan_digest: a55eaee1d30304ad05500afbbfff1409ce22376e0f062c0d06ff34b8ed9c2b47
select sleep(10);
general log
[2022/01/19 11:04:42.863 +08:00] [INFO] [session.go:2796] [GENERAL_LOG] [conn=219317] [user=dba@192.168.1.1] [schemaVersion=1007] [txnStartTS=0] [forUpdateTS=0] [isReadConsistency=false] [current_db=db23] [txn_mode=PESSIMISTIC] [sql="select * from t1"]
从以上信息可以看到,无论是 show processlist,还是慢日志,还是 general log ,都可以看到客户端真实 IP 地址,为排查问题提供了很大便利。
4.总结
公司私有云提供统一的 LVS 服务,为了减少维护成本,TiDB 集群统一使用之家云 LVS 作为负载均衡器。由于 LVS FULL NAT 模式的工作原理决定了在 TiDB 端无法看到客户端真实 IP,本文主要讲解了 TiDB 在这种工作模式下如何查看客户端真实 IP 的解决方法,希望能够帮助需要的同学。
参考资料
https://docs.pingcap.com/zh/tidb/v5.1/tidb-configuration-file/
https://github.com/pingcap/tidb/pull/21552
https://www.jianshu.com/p/e770a11481e9
https://github.com/yubo/ip_vs_ca