某天收到一个奇怪的问题,这个故障感觉还挺好玩,所以分享出来。
背景
某个生产业务部署在k8s中,业务的nginx和服务a(2个副本 下面称为pod-a-1,pod-a-2 ),全部运行在同一个ns下面,同时为pod-a创建一个headless服务,以下称为svc-headless-a。
nginx关键配置如下:
原始配置 upstream testapp {server svc-headless-a:8080;ip_hash; }server {listen 30001;location / {proxy_set_header Host $http_host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_pass http://testapp;}}
故障现象
业务突然反馈,服务时通时不通。经过几天后,两个服务都不通了。因为没有排查到具体问题,所以业务侧只能临时将upstream 直接指向后端pod-a-1和pod-a-2的ip,业务恢复。
排查过程
进入nginx pod内,
(1)使用curl 命令,访问pod-a-1和pod-a-2确认2副本,可以正常访问,服务正常
(2)使用curl 命令,svc-headless-a服务,域名解析正常,可以正常访问,服务正常
(3)nginx报错后端server不存在(排查时没有日志,根据业务侧反馈是有类似的报错日志)
那么到这一步就可以确认 service到pod这一个链路是没有问题的,问题就出在nginx上面。
(4)当我看到svc-headless-a时,发现这是一个无头服务。这时候我就有点疑问了,会不会和无头服务有关?但是经过第3步的验证,确认无头服务没有问题,那么问题可能就出现在nginx和healess服务之间。
所以经过各种查找,最后确认问题的原因:
原因分析
基础知识
(1)Nginx 在启动时或重载配置(reload)时,会对 proxy_pass 或 upstream 块中定义的域名进行一次 DNS 解析,并将解析到的 IP 地址缓存下来。之后的所有请求,Nginx 都会直接使用这个缓存的 IP 地址。
(2) k8s headless服务,因为它是无头服务,所有没有clusterip,无头服务的域名解析为后端podip
(3) k8s pod重启后podip会变化
(4) ip_hash 根据源地址将请求路由到同一个后端,即一个用户请求由同一个pod处理
分析
(1)当后端pod由于某种原因重启后,podip会变化,例如,svc-headless-a域名解析ip为A和B,pod重启后,svc-headless-a域名解析为B和C,而nginx缓存的地址池仍为A和B,导致了业务同事反馈的时通时不通的现象。
(2)如果后端pod都重启过,svc-headless-a域名解析为C和D,而nginx缓存的地址池仍为A和B,这样会导致svc-headless-a域名解析后的ip和nginx缓存的地址池完全不一致,nginx内部会出现连通性报错。
(3)而我们进入到nginx pod内部测试域名解析没有问题,是因为每次测试时,都会由k8s的dns进行解析,所以连通性测试没有问题。
排查结果
因此定位问题的根因和nginx缓存机制与headless服务特性导致。
解决方案
(1)使用headless服务,ng增加动态解析配置,周期性刷新缓存池
(2)将服务类型改为clusterip,建议这种方式
最后的最后,有描述不对的地方还请大家指正,但是我不改!!!
