记一次 Istio 云数据库连接失败的错误排查过程
写这篇文章的目的主要是记录 在 容器/istio 下如何使用一些手段和工具来排查错误。
0 错误显现
首先先看下错误描述:
2020-08-14 10:37:20.774 ERROR 1 --- [Create-24097622] com.alibaba.druid.pool.DruidDataSource : create connection SQLException, url: jdbc:postgresql://......, errorCode 0, state 08001
org.postgresql.util.PSQLException: The connection attempt failed.
at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:292)
at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)
at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:195)
at org.postgresql.Driver.makeConnection(Driver.java:458)
at org.postgresql.Driver.connect(Driver.java:260)
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1596)
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1662)
at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2697)
Caused by: java.net.SocketTimeoutException: connect timed out
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at org.postgresql.core.PGStream.<init>(PGStream.java:75)
at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:91)
at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:192)
... 7 common frames omitted
从错误中看到,是 postgres 连不上, 08001 表示 “SQLCLIENT UNABLE TO ESTABLISH SQLCONNECTION”,下面亦显示连接超时。但这个错误并不是每次都出现,有时候会正常工作,但绝大多数会出错。而且应用在虚拟机里运行正常,在 Istio 中会出错。
postgres 使用的是同 VPC 下的云数据库,在 TKE 环境下,网络默认是通的。
什么原因?Sidecar 有问题?Java 程序有问题?网络问题?
1、将 postgres 流量绕开 Sidecar
首先想到,如果是 envoy 阻止了外部的 postgres,可能会出现此情况。检查 egress 流量模式,已经是“Allow Any”。
干脆将连接 postgres 的流量完全绕开 envoy 试试,只需要将端口 5432 排除:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-dubbo-consumer
namespace: xyz
labels:
app: hello-dubbo-consumer
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: hello-dubbo-consumer
version: v1
template:
metadata:
annotations:
traffic.sidecar.istio.io/excludeOutboundPorts: "5432"
labels:
app: hello-dubbo-consumer
version: v1
spec:
containers:
- name: hello-dubbo-consumer
image: tencent-cloud-one-docker.pkg.coding.net/xyz-demo/images/hello-dubbo-consumer:1.0.0
command: ["java","-jar","hello-dubbo-consumer-fat.jar"]
增加注解:traffic.sidecar.istio.io/excludeOutboundPorts: "5432",这个可以让 5432 端口的流量直接绕过 envoy 发送。
测试无果,问题依然存在,初步排除是 sidecar 的问题。
2、部署 psql 客户端参与测试
为了验证连接问题,需要在集群内增加一个 psql 的客户参与验证,这样可以快速连接数据库,看是否存在问题。
很自然的,直接拿一个官方 Docker 镜像即可,官方镜像里已经内置服务端和客户端,我们只要将这个镜像运行在集群内即可。
apiVersion: apps/v1
kind: Deployment
metadata:
name: psql-client
namespace: xyz
spec:
replicas: 1
selector:
matchLabels:
app: psql-client
template:
metadata:
labels:
app: psql-client
spec:
containers:
- image: postgres
imagePullPolicy: IfNotPresent
name: psql-client
command: ["tail", "-f", "/dev/null"]
修改了 容器的启动参数为 “tail -f /dev/null” 避免直接退出,将这个容器置入了集群,现在可以进去执行psql命令了。
# 进入 psql-client 容器
kubectl exec -it <containerid> -n xyz -c psql-client sh
# 进入之后使用 psql 连接远程数据库
psql -h <postgres-ip> -U username -d postgres
“很不幸”,使用 psql 成功联入了远程数据库。有点懵。
3、网络问题
为什么同一个集群,使用 psql 客户端可以连上,Java 应用却经常连不上。有没有可能有的 node 和 数据库的网络是通的,有的却不通呢。
随机选取了一个 和 psql-client 同机的 应用 pod 进入交互,直接探查远程 5432 端口。
nc -zv <postgres-ip> 5432
结果,是 open 的。试验了这个 node 的其他pod。结果亦然。都是通的。
测试有问题的 Java 应用所在的 node 的其他 pod,发现真的是网络不通。
继续测试了集群的其他 node。结果只有 2 个 node 的上的 pod 和 远程数据库是联通的。
进一步排查,发现:集群内的主机是属于两个不同的安全组,安全组的设置是不一样的。
所以,在部署应用的时候,当应用恰好部署在正确安全组的主机上,应用就是正常。
终于真相大白。
- keras系列︱深度学习五款常用的已训练模型
- 基于VGG19的识别中国人、韩国人、日本人分类器
- 机器学习算法GBDT的面试要点总结
- 了解、接受和利用Java中的Optional (类)
- 一个强化学习 Q-learning 算法的简明教程
- 天池大赛—商场中精确定位用户所在店铺 作品分享
- 代码实战:从单体式应用到微服务的低风险演变
- 数据转换:从单体式应用到微服务的低风险演变
- JDK8 stream toMap() java.lang.IllegalStateException: Duplicate key异常解决(key重复)
- 如约而至,Java 10 正式发布!
- Intellij IDEA查看所有断点
- Spring Boot国际化支持
- 有记忆会推理的可微分神经计算机,DeepMind现在开源了代码
- Spring Boot整合Thymeleaf模板引擎
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 短视频商城源码,安卓几种弹窗方法
- Automl框架katib浅析
- NAS Network Attached Storage
- es6 随性学习之 字符串 String
- MongoDB内核:副本集选举过程分析
- MongoDB内核:主从同步之源码剖析
- OpenGL ES 帧缓冲区位块传送
- OAuth 2.0 单元测试解决方案
- 「性能提升」扩展 Spring Cache 支持多级缓存
- 如何以源码形式运行Nacos Server
- Spring Boot 2.4 配置文件将加载机制大变化
- OpenGL ES 多目标渲染(MRT)
- R包:gtable包用于处理ggplot2图像
- Sight——杀手级提升Laravel开发速度的组件现在开源了!
- 面试官带你学Android——面试中Handler 这些必备知识点你都知道吗?