记一次 Istio 云数据库连接失败的错误排查过程

2020-08-14 15:14:31 浏览数 (1)

写这篇文章的目的主要是记录 在 容器/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 和 远程数据库是联通的。

进一步排查,发现:集群内的主机是属于两个不同的安全组,安全组的设置是不一样的。

所以,在部署应用的时候,当应用恰好部署在正确安全组的主机上,应用就是正常。

终于真相大白。

0 人点赞