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

posted on 2018-07-26 10:59  mainred  阅读(644)  评论(0编辑  收藏  举报