Home

Raft 的成员变更与 Etcd 实现

本文配合 Etcd v3.4 的实现来分析 Raft 协议中有关成员变更的内容。

集群的成员变化即是集群配置的变化。Raft 允许在一个集群不重启的前提下,自动化地对一个集群的配置进行变更。

单成员变更

安全性

对一个集群配置的变更而言,首先要考虑的就是安全性,即不破坏集群的大多数(majorities)。若在集群上每次只增加或删除一个 server,无论原始集群的个数是奇数还是偶数,一个旧集群的大多数和一个新集群的大多数必然会产生一个重叠,如下图所示。这个重叠就避免了一个集群被分离为两个大多数集群,因为它同时拥有向两端大多数的投票权,若新配置在集群中没有被复制到大多数,它的一票还是会决定集群继续使用旧配置;若新配置在集群中被复制到了大多数,它的一票就会将集群的配置切换为新配置。这种切换可以是直接切换,因为是安全的。

overlap

Read more

MetalLB 工作原理解析

本文代码基于 MetalLB v0.13.9 展开。

MetalLB 是一个基于标准路由协议的,用于裸机(bare-metal)k8s 集群的负载均衡器。这里裸机是指,直接部署的 k8s 集群并不能使用 LoadBalancer 类型的 Service,因为它没有提供一种负载均衡器的实现,只有在一些云服务 IaaS 平台(例如 AWS、GCP 等)上才能使用。

MetalLB 从两个方面实现了这么一个负载均衡器:地址分配(Address Allocation)和外部广播(External Announcement)。

地址分配

类似于各种云厂商的实现,对每个向负载均衡器的请求分配 IP 地址。MetalLB 则负责在裸机集群中分配 IP 地址,这个 IP 地址是从预先配置的地址池(AddressPool)中获取的;同样当 Service 被删除后,MetalLB 也负责回收该地址。

核心方法

reconcileService

此方法是 service-controller 的调协方法,位于 MetalLB 的 controller 组件中,负责监听所有类型的 Service,然后对它们的 IP 地址进行管理(分配或回收)。

// internal/k8s/controllers/service_controller.go

func (r *ServiceReconciler) reconcileService(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	// ...
	var service *v1.Service

	// 根据 Endpoint 提供的 NamespacedName 对象寻找对应的 Service 对象
	service, err := r.serviceFor(ctx, req.NamespacedName)
	if err != nil {					\
		return ctrl.Result{}, err		 \
	}						  -->-- r.Get(ctx, name, &res)

        // 若 MetalLB 的配置文件中指定了 LoadBalancerClass,则比对它和 Service 的是否一致
        // 只有一致或无指定配置时才可通过,默认情况下,配置文件不指定该字段
	if filterByLoadBalancerClass(service, r.LoadBalancerClass) {
		return ctrl.Result{}, nil
	}

	// 根据 Service 获取其所代理的 Endpoints 或 EndpointSlice
	epSlices, err := epsOrSlicesForServices(ctx, r, req.NamespacedName, r.Endpoints)
	if err != nil {
		return ctrl.Result{}, err
	}
	// 此时根据 Service 是否为空,可以判断出此次调谐是对 Service 的删除还是更新

	// 对 Service 进行处理,包括 IP 地址的分配和回收
	res := r.Handler(r.Logger, req.NamespacedName.String(), service, epSlices)
	switch res {
	case SyncStateError:
		return ctrl.Result{}, retryError
	case SyncStateReprocessAll:
		// 重新进行全量的调谐
		r.forceReload()
		return ctrl.Result{}, nil
	case SyncStateErrorNoRetry:
		return ctrl.Result{}, nil
	}
	return ctrl.Result{}, nil
}

Read more

Envoy 中的 Internal Listener

Envoy 支持用户态的 socket,而且在 Enovy 中,用于接受用户态连接的 listener 被称为 internal listener。internal listener 一般用于接受来自 Envoy 内部的连接,例如从 upstream cluster 接受连接请求并建立 TCP 流。使用 internal listener 时,必须将它的 name 作为一个 upstream cluster 的 endpoint 地址。

envoy-il-base

Read more

The nftables in Linux Kernel

本文代码基于 Linux Kernel v4.10 展开。

nftables(Netfilter Tables,下文简称 nft)是 linux 内核于 v3.13 引入的,意在取代传统的 xtables 工具(比如 iptables、arptables、ebtables 和 ipset 等)。nft 与它们相比,在便捷性、功能和性能上有着巨大的提升。

数据结构

开始分析 nft 之前,先自顶向下熟悉一下 nft 涉及的几种基本数据结构。

nft_table

nft 中规则集 ruleset 表示所有规则的集合,table 作为 ruleset 的顶层容器,能存储 chains、sets、stateful objects 等对象,其结构如下:

// include/net/netfilter/nf_tables.h

struct nft_table {
	struct list_head	list;          // 内部遍历使用的参数,下同
	struct list_head	chains;        // chains in table
	struct list_head	sets;          // sets in table
	struct list_head	objects;       // objects in table
	u64			hgenerator;    //
	u32			use;           // 计数,有多少 chain 引用了该 table
	u16			flags:14,      // nft_table_flags 的二进制位掩码
                                   \
                                    \
                                      --->--- enum nft_table_flags {
                                                  NFT_TABLE_F_DORMANT = 0x1, // table 不可用
                                              };

				genmask:2;     //
	char		        name[NFT_TABLE_MAXNAMELEN];  // table name
};

Read more

kube-proxy 实现原理与源码解析

本文代码基于 Kubernetes v1.26 展开。

kube-proxy,以下简称 kp,是负责实现 Service VIP 机制(ExternalName类型除外)的组件。

代理模式

kp 的代理模式可由配置文件来指定:kp 的配置通过 ConfigMap 实现,ConfigMap 的配置参数合法性并不会被 kp 全部验证,比如宿主机是否禁止使用了 iptables 命令。

kubectl describe -n kube-system configmaps kube-proxy

iptables

该模式下,kp 监听 k8s 控制面增加和移除 Service、EndpointSlice 对象的事件。

  • kp 只能看到通过 readiness 探针测试的后端 pod,从而避免将流量发送到失败的 pod 上

对于一个大型集群来说,kp 对于 iptables 规则的更新成为了 Service 性能的瓶颈,对此 kp 存在两个性能优化参数:

iptables:
  minSyncPeriod: 1s  # kp 与内核同步 iptables 的时机,默认在 svc 等资源更新后 1s 再同步
  syncPeriod: 30s    # kp 与内核同步 iptables 的周期,默认 30s 同步一次,无论是否有资源更新

Read more

Multus CNI 工作原理解析

本文代码基于 Multus CNI v3.7 展开。

Multus CNI 专门负责为 Pod 增加新的网络接口,以接入不同类型的网络,比如 macvlan 等等。而且它的定位是个 Meta CNI,即可代理调用其他 CNI 集群网络插件,因此可以与 calico、flannel 等 CNI 共存。

multus-cni-arch

Read more