阿里云上的启用IPVS的K8S集群,无法从Pod经外部访问自己的排障流水账。
问题描述:
- 阿里云上的托管版K8S集群(下面简称ACK),启用了IPVS
- 集群中有两个应用Foo和Bar,Bar使用Ingress暴露外网地址,bar.xxx.com
- Foo应用无法访问 bar.xxx.com ,得到的错误是 Connection refused
初步排障
在集群外部测试
curl http://bar.xx.com 能够返回结果
ping bar.xxx.com,能够ping通:
|
|
注意:
- 解析得到的IP是ACK创建时自动创建的SLB实例的公网IP。
在集群内部测试
在K8S集群中启动一个临时Pod,nicolaka/netshoot
curl http://bar.xxx.com
得到错误:curl: (7) Failed to connect to bar.xxx.com port 80: Connection refused
ping bar.xxx.com,能够ping通,得到结果
|
|
注意:
- 得到的IP同样是SLB实例的公网IP
- 解析得到名字是Ingress Controller在集群内部的SVC的DNS Name。
用tcpdump抓包:
tcpdump -nn host bar.xxx.com,得到 port 80 unreachable的结果
|
|
和阿里同学沟通
建了工单描述了情况,得到的反馈如下:
Ingress Controller Service的externalTrafficPolicy这个为Local(ACK初始化的默认值)的时候跨节点访问SVC SLB地址就是不行,这个和Nginx Ingress Controller没有关系。这个行为在ipvs和kube-proxy实现的service集群上行为是一致的,如果之前是好的,现在不行了,只有一种可能,就是之前访问Ingress入口Url的Pod和两个Nginx Ingress Controller Pod在一个节点上。建议把externalTrafficPolicy改成Cluster。
解决办法
把externalTrafficPolicy改成Cluster之后的确解决了这个问题。
不过K8S文档里说到如果这样设置,那么Pod就得不到客户端的源IP了,要得到客户端源IP只能设置为Local,但是Local又有无法访问的问题。
阿里的同学说到过:
如果之前是好的,现在不行了,只有一种可能,就是之前访问Ingress入口Url的Pod和两个Nginx Ingress Controller Pod在一个节点上
就是说如果发起请求的Pod和Ingress Controller的Pod在同一个节点上的话,访问是没有问题的。我实验了一下果然如此。
于是我把Ingress Controller从Deployment改成DaemonSet,让每个节点上都跑一个Ingress Controller Pod,于是问题解决。
其他资料
关于这个问题又找了一些资料,不过看不太明白:
- 从service的externalTrafficPolicy到podAntiAffinity
- 访问 externalTrafficPolicy 为 Local 的 Service 对应 LB 有时超时
另外注意到,用Rancher部署的K8S集群的Ingress Controller都是DaemonSet的。
评论