暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

kube-proxy分析之iptables篇

工程师站台 2021-10-29
601


kube-proxy作用


完成service到后端endpoint的网络映射


实现方式


1. 用户态转发

2. iptables转发

3. ipvs转发


实现架构





1. 通过informerFactory监听service和endpoint的信息


3. 通过不同的Proxier来更新调用实际底层的handler实现 (// userspace.NewProxier // ipvs.NewProxier // iptables.NewProxier)

4. 何时调用handler?

   1. 有update event时,会主动触发

   2. 周期性的会有sync, iptables模式默认是1小时,ipvs模式默认是30s

5. handler长啥样?

   handler的具体实现在proxier.syncProxyRules // ipvs/iptables的预处理都是一样的

6. 注册给informer的eventHandler最终的处理方法是通过asyncRunner(bounder_frequency_runnner)来完成异步调用的, 在proxyServer.Run()在最后会调用SyncLoop进入死循环,SyncLoop会处理周期性的同步和主动触发的同步。


// pkg/proxy/iptables/proxier.go
// We pass syncPeriod to ipt.Monitor, which will call us only if it needs to.
// We need to pass *some* maxInterval to NewBoundedFrequencyRunner anyway though.
// time.Hour is arbitrary.
proxier.syncRunner = async.NewBoundedFrequencyRunner("sync-runner", proxier.syncProxyRules, minSyncPeriod, time.Hour, burstSyncs)


// proxier.syncProxyRules // 当有svc、ep变化是会被触发, ipvs/iptables的预处理都是一样的
// pkg/proxy/ipvs/proxier.go
proxier.syncRunner = async.NewBoundedFrequencyRunner("sync-runner", proxier.syncProxyRules, minSyncPeriod, syncPeriod, burstSyncs)


// pkg/proxy/apis/config/v1alpha1/defaults.go
if obj.IPTables.SyncPeriod.Duration == 0 {
obj.IPTables.SyncPeriod = metav1.Duration{Duration: 30 * time.Second}
}
if obj.IPVS.SyncPeriod.Duration == 0 {
obj.IPVS.SyncPeriod = metav1.Duration{Duration: 30 * time.Second}
}


server 启动


cmd/kube-proxy/app/server.go
func (s *ProxyServer) Run() error {


serviceConfig := config.NewServiceConfig(informerFactory.Core().V1().Services(), s.ConfigSyncPeriod)


if s.UseEndpointSlices {
endpointSliceConfig := config.NewEndpointSliceConfig(informerFactory.Discovery().V1beta1().EndpointSlices(), s.ConfigSyncPeriod)
endpointSliceConfig.RegisterEventHandler(s.Proxier)
go endpointSliceConfig.Run(wait.NeverStop)
} else {
endpointsConfig := config.NewEndpointsConfig(informerFactory.Core().V1().Endpoints(), s.ConfigSyncPeriod)
endpointsConfig.RegisterEventHandler(s.Proxier)
go endpointsConfig.Run(wait.NeverStop)
}



iptables syncProxyRules流程


// pkg/proxy/iptables/proxier.go
var iptablesJumpChains = []iptablesJumpChain{
{utiliptables.TableFilter, kubeExternalServicesChain, utiliptables.ChainInput, "kubernetes externally-visible service portals", []string{"-m", "conntrack", "--ctstate", "NEW"}},
{utiliptables.TableFilter, kubeServicesChain, utiliptables.ChainForward, "kubernetes service portals", []string{"-m", "conntrack", "--ctstate", "NEW"}},
{utiliptables.TableFilter, kubeServicesChain, utiliptables.ChainOutput, "kubernetes service portals", []string{"-m", "conntrack", "--ctstate", "NEW"}},
{utiliptables.TableFilter, kubeServicesChain, utiliptables.ChainInput, "kubernetes service portals", []string{"-m", "conntrack", "--ctstate", "NEW"}},
{utiliptables.TableFilter, kubeForwardChain, utiliptables.ChainForward, "kubernetes forwarding rules", nil},
{utiliptables.TableNAT, kubeServicesChain, utiliptables.ChainOutput, "kubernetes service portals", nil},
{utiliptables.TableNAT, kubeServicesChain, utiliptables.ChainPrerouting, "kubernetes service portals", nil},
{utiliptables.TableNAT, kubePostroutingChain, utiliptables.ChainPostrouting, "kubernetes postrouting rules", nil},
}

1. 创建kube相关的chain

2. 先保存当前Filter和NAT表的chain和rule

3. 添加一MARK的rule(这个mark会有3种情况下用到: 

   1. endpoints pod本身发出的情况,最后请求返回到这个pod的时候,需要伪装SNAT,否则RSP在POD内部就转掉了,回程路径会不正确。

   2. nodePort的访问,如果选择的endppoint不是当前节点的,需要通过CNI网络传输到其他节点,这个时候也必须要用SNAT伪装下,否则回程就会有问题

   3. 通过cni从其他节点过来的流量,原因和上两个相同)

4. 为每个svc建一条KUBE-SVC-XXX的chain, 如果有ep则添加dnat rule,如果没有ep则添加一条reject rule直接丢包。并且添加一条如果源不是pods网络的jump到KUBE-MARK-MASQ chain上进行mark(针对的是上一条的第3点)。

5. 给nodePort占用端口

6. 在KUBE-NODEPORTS chain中添加MARK及DNAT的rule,

7. 给每一个endpoint创建一条chain

8. 对应的SVC chain中添加到对应SEP-xxx的rule, 因为iptables的rule是按顺序命中的,因此前n-1条配置以1/(n-1)的概率命中,最后一条100%命中

9. 配置SEP的rule, 特殊处理源来自ep自身的流量(进行MASQ的mark), 再做DNAT处理

10. 删除不在存在的chain(SVC或者SEP的删除触发的)

11. 在KUBE-SERVICES chain最后添加到KUBE-NODEPORTS的rule

12. forwarding chain的一些静态规则配置


因此根据规则生成的逻辑,iptables的条目会随着SVC和ENDPOINTS的数量呈线性增长。



文章转载自工程师站台,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论