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.goproxier.syncRunner = async.NewBoundedFrequencyRunner("sync-runner", proxier.syncProxyRules, minSyncPeriod, syncPeriod, burstSyncs)// pkg/proxy/apis/config/v1alpha1/defaults.goif 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.gofunc (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.govar 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的数量呈线性增长。




