写这篇文章的目的主要是记录 在 容器/istio 下如何使用一些手段和工具来排查错误。
0 错误显现
首先先看下错误描述:
代码语言:txt复制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 排除:
代码语言:txt复制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 镜像即可,官方镜像里已经内置服务端和客户端,我们只要将这个镜像运行在集群内即可。
代码语言:txt复制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命令了。
代码语言:txt复制# 进入 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 端口。
代码语言:txt复制nc -zv <postgres-ip> 5432
结果,是 open 的。试验了这个 node 的其他pod。结果亦然。都是通的。
测试有问题的 Java 应用所在的 node 的其他 pod,发现真的是网络不通。
继续测试了集群的其他 node。结果只有 2 个 node 的上的 pod 和 远程数据库是联通的。
进一步排查,发现:集群内的主机是属于两个不同的安全组,安全组的设置是不一样的。
所以,在部署应用的时候,当应用恰好部署在正确安全组的主机上,应用就是正常。
终于真相大白。