解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析

本次故障排查了三天,最后WireShark分析出结果,总结了一下原因,就是对Oracle的Rac工作方式不了解。

环境:

解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析_第1张图片
image.png

起因:

公网ip有限,不能做一对一静态NAT,只能做端口映射,而且是在一台linux服务器上,两个节点的网关为这个服务器,将SCANIP:1521端口映射为公网IP:1521

iptables -A PREROUTING -d 172.16.15.88/32 -p tcp -m tcp --dport 1521 -j DNAT --to-destination 192.168.200.15:1521 

这里的172.16.15.88地址在公网路由器有公网IP的Static NAT,所以可以处理这个ip的所有端口转发

客户端使用PL/SQL+instantclient_11_2连接公网ip:1521

故障:

测试转发VIP的1521端口并且连接两个节点的VIP没有问题,但是转发SCANIP1521端口连接SCANIP报ORA-12545错误,排查了一下local_listener,发现监听为主机名

尝试将其改为ip地址

alter system set local_listener='(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.200.13)(PORT=1521))))' scope=both sid='test1';
alter system set local_listener='(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.200.14)(PORT=1521))))' scope=both sid='test2';
alter system set remote_listener='scanip:1521';
解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析_第2张图片
image.png

再次尝试连接,报ORA-12170错误。

ORA-12170: TNS:Connect timeout occurred

TNS连接超时,在SCANIP所在服务器上尝试抓包


解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析_第3张图片
image.png

发现数据可以到达服务器,接下来看一下日志

解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析_第4张图片
image.png

服务器确实收到了请求

看下alter下的log

解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析_第5张图片
image.png

好吧,看不出什么鸟东西。
到这里我怀疑数据库安装有问题,将RAC和DB重新安装了一次,折腾了两天,然并卵,问题没解决。

由于不清楚RAC是怎么工作的,所以怀疑是否客户端在请求SCANIP后,RAC将请求分发给其他节点,问题先这么猜想,但是这里还有一个问题,分发给其他节点后,是由其他节点主动连接客户端,还是给客户端重定向一个新的服务器地址。

猜想1

客户端ip:随机端口号-->SCANIP:1521
RAC将请求分发给次节点
次节点vip:1521-->客户端ip:随机端口号

猜想2

客户端ip:随机端口号-->SCANIP:1521
SCANIP:1521(次节点vip:1521)->客户端:源端口号

由于问题无法解决,只能暂时这么猜想,进行逐一验证。

验证猜想1

在网关上将所有192.168.200.0/24的源ip:1521 SNAT --> scanip:1521

iptables -t nat -A POSTROUTING -s 192.168.200.0/24  -p tcp --sport 1521 -j  SNAT --to 192.168.200.15:1521 

再次尝试连接,依然报
ORA-12170: TNS:Connect timeout occurred

iptables -t nat -nvL --line 

发现并没有数据通过本条策略,

image.png

第一个假设不成立

验证猜想2

wireshark抓包分析
果不其然


解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析_第6张图片
image.png

在SCANIP回给客户端的数据中发现这条,里面写的看起来像是tnsnames.ora的内容,感觉像向客户端重定向一个次节点的VIP,里面的IP恰恰是次节点的VIP。
到这里问题终于有了进展,
既然是重定向到次节点的VIP,那么接下来验证一下。

果然,有到VIP的新连接产生。


解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析_第7张图片
image.png

这里因为VIP为内网ip,并无NAT映射,导致TCP三次握手失败,TCP连接不成功,最终客户端报错
ORA-12170: TNS:Connect timeout occurred
也验证了之前的猜想。

结论

客户端->scanip
scanip(次节点ip)->客户端
客户端->次节点ip
完成连接

总结

RAC的工作并不像nginx那样代理转发请求,而是向客户端重定向目标服务器来达到负载均衡的效果,由于同一个IP的同一个端口无法映射多个内部主机,也借此验证了,RAC的工作,需要确保客户端到每个次节点的VIP至少能够直接访问1521端口,并不能一个ip代理所有请求,这也恰好解释了为什么rac的每个节点的VIP都监听0.0.0.0:1521的原因。

参考文献
https://m.linuxidc.com/Linux/2015-09/122731.htm&http:/m.linuxidc.com/Linux/2015-09/122731.htm

解决方案

经过一下午的研究,想出了一个不得已的解决办法,办法有点LOW,但是经过测试,可以连接scanip,并且可以负载均衡。

解决办法如下

  • 修改数据库的local_listener改为vip对应的主机名
  • 修改数每个据库实例的监听端口为非目前scanip的监听端口不同端口
  • 网关上做端口转发将不同实例的ip映射到公网ip的不同端口
  • 修改客户端hosts表,客户端连接

修改数据库local_listener

alter system set local_listener='(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=ora01-vip)(PORT=1511))))' sid='test1';
alter system set local_listener='(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=ora02-vip)(PORT=1512))))' sid='test2';

这么做的原因是,因为local_listener如果配置ip地址,rac返回给客户端的连接方式就是ip,因为是内网ip,所以显然是不行,此处改为主机名,客户端在收到之后,需要对主机名进行dns解析。

那么解决问题的关键点就是在这。

修改数据库的监听端口

节点1

+ASM1:/home/grid@ora01-16:39/>cat /u01/app/oracle/product/11.2/dbhome_1/network/admin/listener.ora 
# listener.ora Network Configuration File: /u01/app/oracle/product/11.2/dbhome_1/network/admin/listener.ora
# Generated by Oracle configuration tools.

ENABLE_GLOBAL_DYNAMIC_ENDPOINT_LISTEN_1 = ON

LISTENER = 
        (DESCRIPTION_LIST = 
                (DESCRIPTION = 
                (ADDRESS = (PROTOCOL = TCP)(HOST = ora01-vip)(PORT = 1511))
                (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC0))
                )
        )
+ASM1:/home/grid@ora01-16:39/>

节点2

+ASM2:/home/grid@ora02-16:05/>cat /u01/app/oracle/product/11.2/dbhome_1/network/admin/listener.ora 
# listener.ora Network Configuration File: /u01/app/oracle/product/11.2/dbhome_1/network/admin/listener.ora
# Generated by Oracle configuration tools.

ENABLE_GLOBAL_DYNAMIC_ENDPOINT_LISTEN_1 = ON

LISTENER =
        (DESCRIPTION_LIST =
                (DESCRIPTION =
                (ADDRESS = (PROTOCOL = TCP)(HOST = ora02-vip)(PORT = 1512))
                (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC0))
                )
        )
+ASM2:/home/grid@ora02-16:40/>

为什么要修改数据库的监听端口,这里解释一下,

因为RAC会负载均衡,客户端可能会收到不同的次节点VIP,如果端口号为scanip的监听1521端口,那么在网关上将无法再做端口转发,所以这里两个次节点的端口分别修改为1511/1512。

现在用客户端进行连接,wireshark抓个包看看

解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析_第8张图片
image.png

红线部分为要达到的效果,
既然返回了TNSNAME,那么就可以在DNS解析上做点手脚

修改客户端hosts表

image.png

这里的1.1.1.1为服务器的公网ip

到了这里,就差在网关上做端口映射了

端口映射


iptables -t nat -A PREROUTING -p tcp --dport 1511 -d 172.16.15.88/32 -j DNAT --to 192.168.200.13:1511
iptables -t nat -A PREROUTING -p tcp --dport 1512 -d 172.16.15.88/32 -j DNAT --to 192.168.200.14:1512 

映射过后的效果如下

  • 客户端->scanip:1521
  • scanip此时认为ora01节点负载较小,向客户端重定向目标服务器ora01-vip:1511
  • 客户端dns解析ora01-vip -> 1.1.1.1
  • 客户端->1.1.1.1:1511
  • 网关端口转发 1.1.1.1:1511 -> 192.168.200.13:1511
  • 192.168.200.13:1511(ora01-vip) -> 1.1.1.1:1511(PREROUTING) -> 客户端
  • 客户端 <-> ora01-vip

到这里来回都没问题了,测试一下

解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析_第9张图片
image.png

测试一下负载均衡

import cx_Oracle

USER = 'system'
PASSWD = 'system'
DB='x.x.x.x/1521/test'

def conn():
    try:
        conn = cx_Oracle.connect(USER , PASSWD ,DB)
        cursor = conn.cursor()
        sql = 'SELECT INSTANCE_NAME FROM V$INSTANCE '
        cursor.execute(sql)
        data = cursor.fetchall()
        cursor.close()
        conn.commit()
        conn.close()
        return data[0][0]
    except Exception as e:
        return e


if __name__ == '__main__':
    for i in range(1, 11):
        print("第%s次,连接到实例:%s" % (i, conn()))

解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析_第10张图片
image.png

OK,Perfect

你可能感兴趣的:(解决Oracle Rac Scanip NAT连接报ORA-12545/ORA-12170及原因分析)