.net core 源码_k8s1.13版本源码分析 kubeproxy (三)

a843263ca86aeb829eeadcfe49537e7b.png

源码分析系列文章汇总在github,开源电子书地址如下:

  • github:
    https://github.com/farmer-hutao/k8s-source-code-analysis

  • gitbook:
    http://hutao.tech/k8s-source-code-analysis/

3ac0fa3b1879f05d8c314fbbb49817a3.png

Ipvs-mode proxier

  • 概述

  • Ipvs-mode proxer 对象创建与初始化

  • Proxier 服务与端点更新机制

  • SyncProxyRules 同步 Proxy 规则

    • 更新 service 与 endpoint变化信息

    • 创建 kube 顶层链和连接信息

    • Dummy 接口和 ipset 默认集创建

    • 每个服务生成 ipvs 规则

    • SyncIPSetEntries 同步 ipset 记录

    • 创建 iptables 规则数据

    • 刷新 iptables 规则

tip: 公众号推文中部分外链无法使用,超链接失效的可以从原文查阅:
http://hutao.tech/k8s-source-code-analysis/core/kube-proxy/ipvs.html

1. 概述

关于ipvs-mode proxier基础知识可参看官方文档(英文版、中文版),其官方文档主要介绍以下几方面内容:

  1. ipvs技术简介和对比iptables-mode所带来的好处;

  2. ipvs-mode proxier按用户配置不同所生成的用户层iptables规则示例(masquerade-all/cluster-cidr/Load Balancer/NodePort/externalIPs);

  3. 如何kube-proxy运行ipvs模式、运行必要条件、运行debug和排错操作。

本文分析将聚焦在代码层的实现解析 (如运行时必要条件检测的代码实现是怎么样的?ipvs为实现service层是如何实现的?iptables规则代码是怎样的?proxier完整的实现逻辑与方式是怎样?等等) 。

ipvs proxy模式主要依赖几个底层技术如 ipvs/ipset /iptables/netlink(用户空间与内核空间异步通信机制),有必要预先对其基础用途或技术细节进行扩展知识的熟悉,将有助于对整个ipvs-mode proxier的实现更深层次的理解。

Ipvs-mode proxier使用ipvs NAT模式实现,ipvs集群操作(如虚拟服务器、RealServer)是通过netlink内核通迅创建标准的协议格式通迅消息体进行交互实现。 Ipvs-mode proxier也同样使用了iptables固定模板规则结合ipset集来进行动态管理变化更新。

Ipvs-mode proxier整个代码机制逻辑与iptables-mode一致(参看iptable-mode代码逻辑示意图)。同样是通过同步apiserver事件及更新信息,生成相应的路由规则。但ipvs-mode服务规则不同于iptables-mode,不仅使用了ipset扩展的方式简化iptables规则条目和优化性能,而且还使用ipvs技术实现更丰富的集群负载策略管理。其规则生成操作须对ipset集、iptables规则、ipvs集群进行同步更新操作,关键逻辑代码在syncProxyRules()内。

2. Ipvs-mode proxer 对象创建与初始化

ProxyServer实例化时初始化了proxier模式,如果代理模式指定为Ipvs,则创建proxier对象,且指定其service与endpoints的事件处理器。

cmd/kube-proxy/app/server_others.go:59

func newProxyServer(...) (*ProxyServer, error) {
//...
else if proxyMode == proxyModeIPVS { //当proxy模式指定为IPVS模式(命令参数或配置文件)
klog.V(0).Info("Using ipvs Proxier.")
proxierIPVS, err := ipvs.NewProxier( //创建ipvs-mode proxier对象
//...
)
// porxyServer的proxier对象与事件处理器的指定
proxier = proxierIPVS
serviceEventHandler = proxierIPVS
endpointsEventHandler = proxierIPVS
//...
}

ipvs-mode proxier对象实例化NewProxier(),对ipvs环境进行初始化。

相关内核参数调整说明:

  • net/ipv4/conf/all/route_localnet 是否允许外部访问localhost;

  • net/bridge/bridge-nf-call-iptables 为二层的网桥在转发包时也会被iptables的FORWARD规则所过滤,这样就会出现L3层的iptables rules去过滤L2的帧的问题;

  • net/ipv4/vs/conntrack 开启NFCT(Netfilter connection tracking连接与状态跟踪);

  • net/ipv4/vs/conn_reuse_mode 网络连接复用模式的选择;

  • net/ipv4/vs/expire_nodest_conn 值为0,当LVS转发数据包,发现目的RS无效(删除)时,会丢弃该数据包,但不删除相应连接。值为1时,则马上释放相应连接;

  • net/ipv4/vs/expire_quiescent_template 值为0,当RS的weight值=0(如,健康检测失败,应用程序将RS weight置0)时,会话保持的新建连接 还会继续调度到该RS上;值为1,则马上将会话保持的连接模板置为无效,重新调度新的RS。如果有会话保持的业务,建议该值配置为1;

  • net/ipv4/ip_forward 是否打开ipv4的IP转发模式;

  • net/ipv4/conf/all/arp_ignore 定义对目标地址为本地IP的ARP询问不同的应答模式(0~8),模式1表示:只回答目标IP地址是来访网络接口本地地址的ARP查询请求;

  • net/ipv4/conf/all/arp_announce 对网络接口上,本地IP地址的发出的,ARP回应,作出相应级别的限制;值为2表示:对查询目标使用最适当的本地地址;

pkg/proxy/ipvs/proxier.go:280

func NewProxier(...) (*Proxier, error) {
// sysctl配置项 "net/ipv4/conf/all/route_localnet" 值为1
if val, _ := sysctl.GetSysctl(sysctlRouteLocalnet); val != 1 {
if err := sysctl.SetSysctl(sysctlRouteLocalnet, 1); err != nil {
return nil, fmt.Errorf("can't set sysctl %s: %v", sysctlRouteLocalnet, err)
}
}
//...
// sysctl配置项 "net/bridge/bridge-nf-call-iptables" 值为1
sysctl.GetSysctl(sysctlBridgeCallIPTables)
// sysctl配置项 "net/ipv4/vs/conntrack" 值为1
sysctl.SetSysctl(sysctlVSConnTrack, 1)
// sysctl配置项 "net/ipv4/vs/conn_reuse_mode" 值为0
sysctl.SetSysctl(sysctlConnReuse, 0)
// sysctl配置项 "net/ipv4/vs/expire_nodest_conn" 值为1
sysctl.SetSysctl(sysctlExpireNoDestConn, 1)
// sysctl配置项 "net/ipv4/vs/expire_quiescent_template" 值为1
sysctl.SetSysctl(sysctlExpireQuiescentTemplate, 1)
// sysctl配置项 "net/ipv4/ip_forward" 值为1
sysctl.SetSysctl(sysctlForward, 1)
// sysctl配置项 "net/ipv4/conf/all/arp_ignore" 值为1
sysctl.SetSysctl(sysctlArpIgnore, 1)
// sysctl配置项 "net/ipv4/conf/all/arp_announce" 值为2
sysctl.SetSysctl(sysctlArpAnnounce, 2)
//...
// 生成masquerade标志用于SNAT规则
masqueradeValue := 1 << uint(masqueradeBit)
masqueradeMark := fmt.Sprintf("%#08x/%#08x", masqueradeValue, masqueradeValue)

// node ip检测
if nodeIP == nil {
klog.Warningf("invalid nodeIP, initializing kube-proxy with 127.0.0.1 as nodeIP")
nodeIP = net.ParseIP("127.0.0.1")
}

isIPv6 := utilnet.IsIPv6(nodeIP)
klog.V(2).Infof("nodeIP: %v, isIPv6: %v", nodeIP, isIPv6)

// 检测是否有为proxier配置clusterCIDR参数
// clusterCIDR指定集群中pod使用的网段,以此来区分内部与外部流量
if len(clusterCIDR) == 0 {
klog.Warningf("clusterCIDR not specified, unable to distinguish between internal and external traffic")
} else if utilnet.IsIPv6CIDR(clusterCIDR) != isIPv6 {
return nil, fmt.Errorf("clusterCIDR %s has incorrect IP version: expect isIPv6=%t", clusterCIDR, isIPv6)
}

// 检测是否指定了proxy调度器scheduler算法,如果未指定,则为默认"RR"平均负载算法
if len(scheduler) == 0 {
klog.Warningf("IPVS scheduler not specified, use %s by default", DefaultScheduler)
scheduler = DefaultScheduler
}

// healthcheck服务器对象创建
healthChecker := healthcheck.NewServer(hostname, recorder, nil, nil)

// 创建Proxier对象
proxier := &Proxier{
//更新SVC、EP信息存放map和changeTracker
portsMap: make(map[utilproxy.LocalPort]utilproxy.Closeable),
serviceMap: make(proxy.ServiceMap),
serviceChanges: proxy.NewServiceChangeTracker(newServiceInfo, &isIPv6, recorder),
endpointsMap: make(proxy.EndpointsMap),
endpointsChanges: proxy.NewEndpointChangeTracker(hostname, nil, &isIPv6, recorder),
//同步周期
syncPeriod: syncPeriod,
minSyncPeriod: minSyncPeriod,

excludeCIDRs: excludeCIDRs,
iptables: ipt, //iptables执行处理器
masqueradeAll: masqueradeAll, //伪装所有访问Service的ClusterIP流量
masqueradeMark: masqueradeMark, //伪装标志号
exec: exec, // osExec命令执行器
clusterCIDR: clusterCIDR,
hostname: hostname,
nodeIP: nodeIP,
portMapper: &listenPortOpener{},
recorder: recorder,
healthChecker: healthChecker,
healthzServer: healthzServer,
ipvs: ipvs, //ipvs接口
ipvsScheduler: scheduler, //集群调度算法(默认RR)
ipGetter: &realIPGetter{nl: NewNetLinkHandle()}, //node ip获取器
//iptables规则数据存放buffer
iptablesData: bytes.NewBuffer(nil),
filterChainsData: bytes.NewBuffer(nil),
natChains: bytes.NewBuffer(nil),
natRules: bytes.NewBuffer(nil),
filterChains: bytes.NewBuffer(nil),
filterRules: bytes.NewBuffer(nil),

netlinkHandle: NewNetLinkHandle(), //netlink执行处理器
ipset: ipset, //ipset执行处理器
nodePortAddresses: nodePortAddresses,
networkInterfacer: utilproxy.RealNetwork{},
gracefuldeleteManager: NewGracefulTerminationManager(ipvs), // RS清理管理器
}
// 遍历ipsetInfo定义,初始化kubernetes ipset默认集。(后面在ipset默认集创建时有详细介绍)
proxier.ipsetList = make(map[string]*IPSet)
for _, is := range ipsetInfo {
proxier.ipsetList[is.name] = NewIPSet(ipset, is.name, is.setType, isIPv6, is.comment)
}
burstSyncs := 2
klog.V(3).Infof("minSyncPeriod: %v, syncPeriod: %v, burstSyncs: %d", minSyncPeriod, syncPeriod, burstSyncs)
proxier.syncRunner = async.NewBoundedFrequencyRunner("sync-runner", proxier.syncProxyRules, minSyncPeriod, syncPeriod, burstSyncs) //同步runner
proxier.gracefuldeleteManager.Run() //后台线程定时(/分钟)清理RS(realServer记录)
return proxier, nil
}

3. Proxier 服务与端点更新机制

ipvs模式和iptables模式的service和endpoints更新变化信息同步机制是一致的(更详细说明可参考iptables-mode proxier文章),但为了本文的完整性和相对独立性,这里我们也简单的过一下部分代码。

在构建ipvs-mode proxier对象时指定同步运行器async.NewBoundedFrequencyRunner,同步proxy的规则处理则是syncProxyRules()。同样ipvs-proxier类对象有两个属性对象:serviceChanges(ServiceChangeTracker)和endpointsChanges(EndpointChangeTracker)是就是用来跟踪并记录service和endpoints的变化信息更新至相应的两个属性Items map(serviceChange和endpointsChange)。

pkg/proxy/ipvs/proxier.go:429

proxier.syncRunner = async.NewBoundedFrequencyRunner("sync-runner", proxier.syncProxyRules, minSyncPeriod, syncPeriod, burstSyncs)

在框架层第二层proxy server的运行时最后的调用就是"s.Proxier.SyncLoop()"

pkg/proxy/ipvs/proxier.go:631

func (proxier *Proxier) SyncLoop() {
// Update healthz timestamp at beginning in case Sync() never succeeds.
// ...
proxier.syncRunner.Loop(wait.NeverStop) //执行NewBoundedFrequencyRunner对象Loop
}
Copy

pkg/util/async/bounded_frequency_runner.go:169

func (bfr *BoundedFrequencyRunner) Loop(stop chan struct{}) {
bfr.timer.Reset(bfr.maxInterval)for {select {case stop:
bfr.stop()returncase bfr.timer.C(): //定时器方式执行
bfr.tryRun()case bfr.run: //按需方式执行(发送运行指令信号)
bfr.tryRun()}}}

BoundedFrequencyRunner.tryRun() 按指定频率执行回调函数func "bfr.fn()"

pkg/util/async/bounded_frequency_runner.go:211

func (bfr *BoundedFrequencyRunner) tryRun() {
bfr.mu.Lock()
defer bfr.mu.Unlock()

//限制条件允许运行func
if bfr.limiter.TryAccept() {
bfr.fn() // 重点执行部分,调用func,上下文来看此处就是
// 对syncProxyRules()的调用
bfr.lastRun = bfr.timer.Now() // 记录运行时间
bfr.timer.Stop()
bfr.timer.Reset(bfr.maxInterval) // 重设下次运行时间
klog.V(3).Infof("%s: ran, next possible in %v, periodic in %v", bfr.name, bfr.minInterval, bfr.maxInterval)
return
}

//限制条件不允许运行,计算下次运行时间
elapsed := bfr.timer.Since(bfr.lastRun) // elapsed:上次运行时间到现在已过多久
nextPossible := bfr.minInterval - elapsed // nextPossible:下次运行至少差多久(最小周期)
nextScheduled := bfr.maxInterval - elapsed // nextScheduled:下次运行最迟差多久(最大周期)
klog.V(4).Infof("%s: %v since last run, possible in %v, scheduled in %v", bfr.name, elapsed, nextPossible, nextScheduled)

if nextPossible < nextScheduled {
bfr.timer.Stop()
bfr.timer.Reset(nextPossible)
klog.V(3).Infof("%s: throttled, scheduling run in %v", bfr.name, nextPossible)
}
}

4. SyncProxyRules 同步 Proxy 规则

syncProxyRules()为proxier的核心逻辑,类似于iptables proxier实现了对apiserver同步的service、endpoints信息的同步与监听,同时在其生成初始和变化时同步ipvs规则(iptables、ipvs虚拟主机、ipset集规则),最终实现kubernetes的"service"机制。

syncProxyRules()代码部分过长,下面将分开对重点部分一一进行分析。

ipvs-mode proxier的同步ipvs规则主要完成以下几个主要步骤操作:

  • 同步与新更service和endpoints;

  • 初始化链和ipset集;

  • 每个服务构建ipvs规则(iptables/ipvs/ipset),服务类型不同生成的规则也相应不同;

  • 清理过旧规则及信息 。

4.1. 更新 service 与 endpoint变化信息

ipvs-mode proxier的service和endpoint变化更新的机制与iptables-mode的完全一致,详细可以参考iptables-mode的"syncProxyRule 同步配置与规则"内的相关内容,这里就不再详细赘述。

Proxier类对象有两个属性:serviceChanges和endpointsChanges是就是用来跟踪Service和Endpoint的更新信息,以及两个Tracker及方法:ServiceChangeTracker服务信息变更Tracker,EndpointChangeTracker 端点信息变更Tracker,实时监听apiserver的变更事件。

UpdateServiceMap() svc 服务的更新实现,将serviceChanges的服务项与proxier serviceMap进行更新(合并、删除废弃项)返回,UpdateEndpointsMap() 端点更新的实现,将endpointsChanges的端点项与proxier endpointMap进行更新(合并、删除废弃项)并返回已更新信息。

pkg/proxy/ipvs/proxier.go:730

serviceUpdateResult := proxy.UpdateServiceMap(proxier.serviceMap, proxier.serviceChanges)
endpointUpdateResult := proxy.UpdateEndpointsMap(proxier.endpointsMap, proxier.endpointsChanges)

4.2. 创建 kube 顶层链和连接信息

pkg/proxy/ipvs/proxier.go:748

    proxier.natChains.Reset()     //nat链
proxier.natRules.Reset() //nat规则
proxier.filterChains.Reset() //filter链
proxier.filterRules.Reset() //filter规则
//写表头
writeLine(proxier.filterChains, "*filter")
writeLine(proxier.natChains, "*nat")

proxier.createAndLinkeKubeChain() //创建kubernetes的表连接链数据

pkg/proxy/ipvs/proxier.go:1418

func (proxier *Proxier) createAndLinkeKubeChain() {

//通过iptables-save获取现有的filter和NAT表存在的链数据
existingFilterChains := proxier.getExistingChains(proxier.filterChainsData, utiliptables.TableFilter)
existingNATChains := proxier.getExistingChains(proxier.iptablesData, utiliptables.TableNAT)

// 顶层链数据的构建
// NAT表链: KUBE-SERVICES / KUBE-POSTROUTING / KUBE-FIREWALL
// KUBE-NODE-PORT / KUBE-LOAD-BALANCER / KUBE-MARK-MASQ
// Filter表链: KUBE-FORWARD
for _, ch := range iptablesChains {
//不存在则创建链,创建顶层链
if _, err := proxier.iptables.EnsureChain(ch.table, ch.chain); err != nil {
klog.Errorf("Failed to ensure that %s chain %s exists: %v", ch.table, ch.chain, err)
return
}
//nat表写链
if ch.table == utiliptables.TableNAT {
if chain, ok := existingNATChains[ch.chain]; ok {
writeBytesLine(proxier.natChains, chain) //现存在的链
} else {
// "KUBE-POSTROUTING"
writeLine(proxier.natChains, utiliptables.MakeChainLine(kubePostroutingChain))
}
} else { // filter表写链
if chain, ok := existingFilterChains[KubeForwardChain]; ok {
writeBytesLine(proxier.filterChains, chain) //现存在的链
} else {
// "KUBE-FORWARD"
writeLine(proxier.filterChains, utiliptables.MakeChainLine(KubeForwardChain))
}
}
}
// 默认链下创建kubernete服务专用跳转规则
// iptables -I OUTPUT -t nat --comment "kubernetes service portals" -j KUBE-SERVICES
// iptables -I PREROUTING -t nat --comment "kubernetes service portals" -j KUBE-SERVICES
// iptables -I POSTROUTING -t nat --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
// iptables -I FORWARD -t filter --comment "kubernetes forwarding rules" -j KUBE-FORWARD
for _, jc := range iptablesJumpChain {
args := []string{"-m", "comment", "--comment", jc.comment, "-j", string(jc.to)}
if _, err := proxier.iptables.EnsureRule(utiliptables.Prepend, jc.table, jc.from, args...); err != nil {
klog.Errorf("Failed to ensure that %s chain %s jumps to %s: %v", jc.table, jc.from, jc.to, err)
}
}
// 写kubernetes专用的POSTROUTING nat规则
// -A KUBE-POSTROUTING -m comment --comment "..." -m mark --mark $masqueradeMark -j MASQUERADE
writeLine(proxier.natRules, []string{
"-A", string(kubePostroutingChain),
"-m", "comment", "--comment", `"kubernetes service traffic requiring SNAT"`,
"-m", "mark", "--mark", proxier.masqueradeMark,
"-j", "MASQUERADE",
}...)

// 写kubernetes专用的masquerade伪装地址标记规则
// -A KUBE-MARK-MASQ -j MARK --set-xmark $masqueradeMark
writeLine(proxier.natRules, []string{
"-A", string(KubeMarkMasqChain),
"-j", "MARK", "--set-xmark", proxier.masqueradeMark,
}...)
}

4.3. Dummy 接口和 ipset 默认集创建

pkg/proxy/ipvs/proxier.go:760

 //为服务地址的绑定,确保已创建虚拟接口kube-ipvs0
_, err := proxier.netlinkHandle.EnsureDummyDevice(DefaultDummyDevice)
if err != nil {
klog.Errorf("Failed to create dummy interface: %s, error: %v", DefaultDummyDevice, err)
return
}

// 确保kubernetes专用的ipset集已创建
for _, set := range proxier.ipsetList {
if err := ensureIPSet(set); err != nil {
return
}
set.resetEntries()
}

proxier.ipsetList的定义信息,在proxier对象创建时初始化了ipsetList列表

pkg/proxy/ipvs/proxier.go:113

var ipsetInfo = []struct {
name string //ipset set名称
setType utilipset.Type //set类型{HashIPPortIP|HashIPPort|HashIPPortNet|BitmapPort}
comment string //comment描述信息
}{
{kubeLoopBackIPSet, utilipset.HashIPPortIP, kubeLoopBackIPSetComment},
//...
}
Copy
ipset集名称类型描述
KUBE-LOOP-BACKhash:ip,port,ipKubernetes endpoints dst ip:port, source ip for solving hairpin purpose
KUBE-CLUSTER-IPhash:ip,portKubernetes service cluster ip + port for masquerade purpose
KUBE-EXTERNAL-IPhash:ip,portKubernetes service external ip + port for masquerade and filter purpose
KUBE-LOAD-BALANCERhash:ip,portKubernetes service lb portal
KUBE-LOAD-BALANCER-FWhash:ip,portKubernetes service load balancer ip + port for load balancer with sourceRange
KUBE-LOAD-BALANCER-LOCALhash:ip,portKubernetes service load balancer ip + port with externalTrafficPolicy=local
KUBE-LOAD-BALANCER-SOURCE-IPhash:ip,port,ipKubernetes service load balancer ip + port + source IP for packet filter purpose
KUBE-LOAD-BALANCER-SOURCE-CIDRhash:ip,port,netKubernetes service load balancer ip + port + source cidr for packet filter purpose
KUBE-NODE-PORT-TCPBitmapPortKubernetes nodeport TCP port for masquerade purpose
KUBE-NODE-PORT-LOCAL-TCPBitmapPortBitmapPort,Kubernetes nodeport TCP port with externalTrafficPolicy=local
KUBE-NODE-PORT-UDPBitmapPortKubernetes nodeport UDP port for masquerade purpose
KUBE-NODE-PORT-LOCAL-UDPBitmapPortKubernetes nodeport UDP port with externalTrafficPolicy=local
KUBE-NODE-PORT-SCTPBitmapPortKubernetes nodeport SCTP port for masquerade purpose
KUBE-NODE-PORT-LOCAL-SCTPBitmapPortKubernetes nodeport SCTP port with externalTrafficPolicy=local

4.4. 每个服务生成 ipvs 规则

篇幅限制,本节参考github!

4.5. SyncIPSetEntries 同步 ipset 记录

pkg/proxy/ipvs/proxier.go:1176

for _, set := range proxier.ipsetList {
set.syncIPSetEntries()
}
Copy

pkg/proxy/ipvs/ipset.go:125

func (set *IPSet) syncIPSetEntries() {
appliedEntries, err := set.handle.ListEntries(set.Name)
if err != nil {
klog.Errorf("Failed to list ip set entries, error: %v", err)
return
}

// currentIPSetEntries代表从apiServer上一直监听着的endpoints列表
currentIPSetEntries := sets.NewString()
for _, appliedEntry := range appliedEntries {
currentIPSetEntries.Insert(appliedEntry)
}

// 求差集
// s1 = {a1, a2, a3}
// s2 = {a1, a2, a4, a5}
// s1.Difference(s2) = {a3}
// s2.Difference(s1) = {a4,a5}
if !set.activeEntries.Equal(currentIPSetEntries) {
// 清理过旧记录(取currentIPSetEntries在activeEntries中没有的entries)
for _, entry := range currentIPSetEntries.Difference(set.activeEntries).List() {
if err := set.handle.DelEntry(entry, set.Name); err != nil {
if !utilipset.IsNotFoundError(err) {
klog.Errorf("Failed to delete ip set entry: %s from ip set: %s, error: %v", entry, set.Name, err)
}
} else {
klog.V(3).Infof("Successfully delete legacy ip set entry: %s from ip set: %s", entry, set.Name)
}
}
// 新增记录(取activeEntries在currentIPSetEntries中没有的entries)
for _, entry := range set.activeEntries.Difference(currentIPSetEntries).List() {
if err := set.handle.AddEntry(entry, &set.IPSet, true); err != nil {
klog.Errorf("Failed to add entry: %v to ip set: %s, error: %v", entry, set.Name, err)
} else {
klog.V(3).Infof("Successfully add entry: %v to ip set: %s", entry, set.Name)
}
}
}
}
Copy

4.6. 创建 iptables 规则数据

篇幅限制,本节参考github!

4.7. 刷新 iptables 规则

pkg/proxy/ipvs/proxier.go:1186

    // 合并iptables规则
proxier.iptablesData.Reset()
proxier.iptablesData.Write(proxier.natChains.Bytes())
proxier.iptablesData.Write(proxier.natRules.Bytes())
proxier.iptablesData.Write(proxier.filterChains.Bytes())
proxier.iptablesData.Write(proxier.filterRules.Bytes())

klog.V(5).Infof("Restoring iptables rules: %s", proxier.iptablesData.Bytes())
// 基于iptables格式化规则数据,使用iptables-restore刷新iptables规则
err = proxier.iptables.RestoreAll(proxier.iptablesData.Bytes(), utiliptables.NoFlushTables, utiliptables.RestoreCounters)
if err != nil {
klog.Errorf("Failed to execute iptables-restore: %v\nRules:\n%s", err, proxier.iptablesData.Bytes())
// Revert new local ports.
utilproxy.RevertPorts(replacementPortsMap, proxier.portsMap)
return
}
Copy

ipvs-mode Proxier整个逻辑实现已分析完,其关键逻辑即syncProxyRules(){…}内代码,其中还有一些细节技术未展开叙述,如几个关键的依赖底层技术ipset的实现runner、ipvs路由(VS/RS)操作基于netlink机制通迅机制的实现等,因篇幅过长,后续再看具体情况补充。

~本文 END~

a9a63fab96945fcd86cb706d10dfcb5e.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值