DCOS 场景 访问 l4lb vip 偶现超时
环境信息
centos7,linux 3.10,lvs 使用 nat 模式。client 容器化部署,使用veth连接到linux网桥
业务背景
redisclient 使用长连接访问 vip,由lvs转发到后端redis,偶现出现redisclient请求报文报文超时,连接断开
分析进展:
- 问题出现时,veth,bridge上抓包有报文,lvs转发后接口没抓到,filter表input链里添加规则,也能匹配到包。
- 出现问题时,客户端因为没收到回复重传了多次都没转发出去,最后因为服务器没收到报文发送tcp keepalive,客户端能收到,但是keepalive 回复报文也没转出去。
- 跟同事交流说可能是gro的问题,我们设备开启了gro,但是抓到的报文都是不超过200的小包(奇怪为啥gro没生效),所以gro导致报文过大影响lvs转发也说不过去。
环境信息
centos7,linux 3.10,lvs 使用 nat 模式。client 容器化部署,使用veth连接到linux网桥
业务背景
redisclient 使用长连接访问 vip,由lvs转发到后端redis,偶现出现redisclient请求报文报文超时,连接断开
分析进展:
- 问题出现时,veth,bridge上抓包有报文,lvs转发后接口没抓到,filter表input链里添加规则,也能匹配到包。
- 出现问题时,客户端因为没收到回复重传了多次都没转发出去,最后因为服务器没收到报文发送tcp keepalive,客户端能收到,但是keepalive 回复报文也没转出去。
- 跟同事交流说可能是gro的问题,我们设备开启了gro,但是抓到的报文都是不超过200的小包(奇怪为啥gro没生效),所以gro导致报文过大影响lvs转发也说不过去。
原因是由于删除和创建service的判断逻辑有问题。
分析如下:
之前分析过lvs和overlay的配置没有发现问题。后面发现系统日志中频繁出现如下日志,即lvs删除service,而且是指删除。
Jul 12 21:14:57 test-agent-3 kernel: IPVS: __ip_vs_del_service: enter
Jul 12 21:14:57 test-agent-3 kernel: IPVS: __ip_vs_del_service: enter
Jul 12 21:14:57 test-agent-3 kernel: IPVS: __ip_vs_del_service: enter
Jul 12 21:14:57 test-agent-3 kernel: IPVS: __ip_vs_del_service: enter
这些删除lvs service是由dcos-navstar发起的,调高日志级别发现,navstar删除service后,又重新批量添加回来services,如下代码所示,非常奇怪。
apply_diff({ServicesToAdd, ServicesToRemove, ServicesToModify}, Namespace, State) ->
lists:foreach(fun({VIP, _BEs}) -> remove_service(VIP, Namespace, State) end, ServicesToRemove),
lists:foreach(fun({VIP, BEs}) -> add_service(VIP, BEs, Namespace, State) end, ServicesToAdd),
再往前追溯修改的原因,发现是由于 vip 变动导致的,即有使用内部负载均衡的服务被删除或者创建,或者配额有改变。
push_vips(VIPs0) ->
VIPs1 = ordsets:from_list(VIPs0),
gen_statem:cast(?SERVER, {vips, VIPs1}).
之后引发状态机的变化,apply_diff 根据下面Diff变量得到删除或者添加service,Diff 根据最新获取的service列表及之前的缓存做对比得到的。
LastConfigured 是之前的 service 缓存, VIPs1是最新得到 service 列表,两个列表都做过排序。
maintain(VIPs0, State = #state{ns = Namespaces, routes = Routes, last_configured_vips = LastConfigured}) ->
VIPs1 = process_reachability(VIPs0, State),
Diff = generate_diff(LastConfigured, VIPs1),
NewRoutes = ordsets:from_list([VIP || {{_Proto, VIP, _Port}, _Backends} <- VIPs1]),
lager:debug("Last Configured: ~p, VIPs: ~p, NewRoutes ~p, Diff ~p", [LastConfigured, VIPs1, NewRoutes, Diff]),
lists:foreach(fun(Namespace) ->
do_update_routes(NewRoutes, Routes, Namespace, State),
apply_diff(Diff, Namespace, State)
end, Namespaces),
State#state{last_configured_vips = VIPs1, last_received_vips = VIPs0, routes = NewRoutes}.
假设有这样的service列表,原有的service为[1,2,4,5], 最新的service列表为[1,2,3,4,5],有如下执行的结果
修复方案: 修改上面两个函数判断条件如下,即可。
generate_diff(Lhs = [VIPLhs|_RestLhs], [VIPRhs|RestRhs], VIPsToAdd0, VIPsToRemove, Mutations) when VIPLhs > VIPRhs
->
generate_diff(Lhs, RestRhs, [VIPRhs|VIPsToAdd0], VIPsToRemove, Mutations);
%% To delete VIP
generate_diff([VIPLhs|RestLhs], Rhs = [VIPRhs|_RestRhs], VIPsToAdd, VIPsToRemove0, Mutations) when VIPLhs < VIPRhs
->
generate_diff(RestLhs, Rhs, VIPsToAdd, [VIPLhs|VIPsToRemove0], Mutations);
修改 PR 链接 :
https://github.com/dcos/navstar/pull/100
https://github.com/dcos/dcos-net/pull/59
jira问题跟踪链接 :
https://jira.mesosphere.com/browse/DCOS_OSS-3602