故障现象
Istio 网格开启 allow any 访问模式,在一个注入了 sidecar 的 pod 内,mysql 客户端访问 mysql-ip-1:3306 成功,访问 mysql-ip-2:10000 没有响应:
# mysql -h55.135.153.1 -utest -pxxxx -P3306
Welcome to the MariaDB monitor. Commands end with ; or \g.
# mysql -h55.108.108.2 -utest -pxxxx -P10000
(no response)
故障分析
查看日志,把 access log 设置为 debug、trace 均没有发现有用信息。
分析发现,网格内有一个 http server,也使用了和 mysql-ip-2 相同的端口 10000:
apiVersion: v1
kind: Service
metadata:
name: irrelevant-svc
......
spec:
ports:
- name: http
nodePort: 31025
port: 10000 # 端口相同
protocol: TCP
targetPort: 8080
我们尝试把该服务端口改成 10001,访问 mysql-ip-2:10000 成功,推测和端口冲突相关:
# mysql -h55.108.108.2 -utest -pxxxx -P10000
Welcome to the MariaDB monitor. Commands end with ; or \g.
我们再尝试对 mysql-ip-1 复现故障:在网格内创建了一个包括 3306 端口的 http 服务,mysql 请求无响应,问题复现。
另外我们还尝试过,如果把冲突端口的协议定义为 tcp(通过 port name),该问题不存在:
apiVersion: v1
kind: Service
metadata:
name: irrelevant-svc
......
spec:
ports:
- name: tcp # 如果是 tcp 则不会出问题
nodePort: 31025
port: 10000
protocol: TCP
targetPort: 8080
故障原因
Server Speaks First
Mysql 协议是一种 Server Speaks First 协议,也就是说 client 和 server 完成三次握手后,是 server 会先发起会话, 简要过程:
S: 服务端首先会发一个握手包到客户端
C: 客户端向服务端发送认证信息 ( 用户名,密码等 )
S: 服务端收到认证包后,会检查用户名与密码是否合法,并发送包告知客户端认证信息。
除了 Mysql,常见的 Server Speaks First 协议还包括 SMTP,DNS,MongoDB 等。下面是一个 SMTP 交互流程:
S: 220 smtp.example.com ESMTP Postfi
C: HELO relay.example.com
S: 250 smtp.example.com, I am glad to meet you
C: MAIL FROM:<bob@example.com>
S: 250 Ok
C: RCPT TO:<alice@example.com>
S: 250 Ok
C: RCPT TO:<theboss@example.com>
S: 250 Ok
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: From: "Bob Example" <bob@example.com>
C: To: Alice Example <alice@example.com>
C: Cc: theboss@example.com
C: Date: Tue, 15 Jan 2008 16:02:43 -0500
C: Subject: Test message
C:
C: Hello Alice.
C: This is a test message with 5 header fields and 4 lines in the message body.
C: Your friend,
C: Bob
C: .
S: 250 Ok: queued as 12345
C: QUIT
S: 221 Bye
{The server closes the connection}
istio 不是完全透明
当前 istio 的某些特性,不能做到透明兼容 Server Speaks First 协议,这些特性包括:
- 协议嗅探
- PERMISSIVE mTLS
- Authorization Policy
这些特性都希望 client 能先发起会话,以协议嗅探为例,envoy 是通过分析 client 发出的初始若干字节来推测协议类型。
对于 Server Speaks First 协议,比如 mysql,三次握手后,这时候 mysql client 在等待 mysql server 发起初次会话,而 client 端的 envoy 尝试做协议嗅探,也在等 mysql client 发出数据,这类似一个死锁,最终超时。
解决方案
以下是一些可行的方案:
- 为 Server Speaks First 协议服务创建一个 ServiceEntry,并指定协议为 TCP。
- 避免 Server Speaks First 协议服务端口和网格内服务端口重叠,这样请求可以直接走 passthrough。
- 把 Server Speaks First 服务 ip 放到 excludeIPRanges,这样请求不经过 envoy 处理,适用于 DB 服务不需要网格治理的情况。