ipvs 原理解析(一)初始化和 ipvsadm 创建

文章目录

  • 前言
  • 初始化
  • 创建

前言

在 kubernetes 工作过程中,service 的实现有很多种选择;例如一些 cni 支持的 lb 功能可以实现 service 的能力,如 cilium 的 ebpf,kubeOVN 的 ovn-lb 等,默认情况下,kubernetes 也提供 iptables 或 ipvs 来实现 service 的能力。

虽然 ipvs 和 iptables 都是基于 netfilter 实现的,但随着 service 的数量越大,基于 ipvs + ipset 实现的 service 能力在性能上是优于 iptables 的,所以在日常实现和使用中,通常都会选择 ipvs 来做 service 的能力实现。

网上基于 ipvs 的介绍有很多,此处不进行详细介绍,只记录在看代码过程中的分析和心得。kernel(4.19)

初始化

初始化和大多数模块一样
net/netfilter/ipvs/ip_vs_core.c

module_init(ip_vs_init);

ip_vs_init
| -- ip_vs_control_init        初始化长度 256 的 svc_table 和 ip_vs_svc_fwm_table 两个 hash 散列表,用来存 svc 和 fwm_svc
  | --  register_netdevice_notifier 会重放当前系统已注册设备所有过去的 NETDEV_REGISTER 和NETDEV_UP,ipvs 这里主要监测接口 DOWN 的动作,释放链表里 svc 的 realserver 数据
| -- ip_vs_protocol_init      根据 linux 参数注册协议包括 TCP/UDP/SCTP/AH/ESP 注册对应协议的处理方法
| -- ip_vs_conn_init       初始化长度为 4096 的 conn_tab ipvs 链接 hash 散列表 
| -- register_pernet_subsys/register_parnet_device  注册网络 namespace 子空间,在每个 namespace 都进行初始化
  | -- ipvs_core_ops
    | -- __ip_vs_init
| -- ip_vs_register_nl_ioctl   注册两种用户态和内核态通信,通过 sockopt 和 netlink 

hash 散列表 介绍 https://blog.csdn.net/u012294613/article/details/127994882

__ip_vs_init
 | -- ip_vs_control_net_init     初始化 realserver 的 hash 散列表,hash桶大小为 16,创建 统计信息 ip_vs_stats/ip_vs_stats_percpu
 | -- ip_vs_protocol_net_init    初始化注册 协议 (TCP/UDP/SCTP/AH/ESP),注册函数 如 TCP 的 ip_vs_protocol_tcp
 | -- ip_vs_app_net_init     历史遗留问题,一般指 非 linux netfilter 的 nat
 | -- ip_vs_conn_net_init    各个命名空间的 ip_vs_conn,ip_vs_conn_sync 的初始化,可已在 /proc//net/ 下查看
 | -- nf_register_net_hooks   注册 hook 函数到 netfilter 框架,ip_vs_ops 包含,ip_vs_reply4(LOCAL_IN),ip_vs_remote_request4(LOCAL_IN),ip_vs_local_reply4(LOCAL_OUT),ip_vs_local_request4(LOCAL_OUT),ip_vs_forward_icmp(FORWARD),ip_vs_reply4(FORWARD) 等

通过 register_ip_vs_scheduler 注册 各种调度器

调度器包括,ip_vs 调度算法如 rr, wrr, ftp, sh 等等;

如 ip_vs_rr

ip_vs_rr_init
  | -- register_ip_vs_scheduler  将调度器注册到全局链表 ip_vs_schedulers 中

创建

初始化时有 sockopts ,用户使用 ipvsadm 时通过该 sockopts 下发配置

sockopts 的操作
static struct nf_sockopt_ops ip_vs_sockopts = {
	.pf		= PF_INET,
	.set_optmin	= IP_VS_BASE_CTL,
	.set_optmax	= IP_VS_SO_SET_MAX+1,
	.set		= do_ip_vs_set_ctl,
	.get_optmin	= IP_VS_BASE_CTL,
	.get_optmax	= IP_VS_SO_GET_MAX+1,
	.get		= do_ip_vs_get_ctl,
	.owner		= THIS_MODULE,
};

ipvsadm -A -t 192.168.100.2:9090 -s rr
ipvsadm -a -t 192.168.100.2:9090 -r 192.168.100.2:80 -m

do_ip_vs_set_ctl    解析用户输入内容信息和 cmd,解析后格式检查
| -- __ip_vs_service_find    查找输入的 service
  | -- ip_vs_add_service    -A 创建 service 
    | -- ip_vs_use_count_inc    模块引用计数增加
    | -- ip_vs_scheduler_get     通过 配置 获取调度器 sched,如 rr
    | -- ip_vs_pe_getbyname    如果配置了持久化引擎,需要检查是否支持,如支持 sip
    | kzalloc 内存并写入信息,svc->destinations 循环双链表结构(real server  的指针),svc->scheduler 绑定调度器;
    | 如果 svc 的端口是 ftp, ipvs 的 ftpsvc_counter 加 1,如果 port 是 0,ipvs nullsvc_counter 加 1,如果有 pe,conn_out_counter 加 1
    | -- INIT_LIST_HEAD(&svc->destinations); 初始化存 rs 的链表
    | -- if sched {ip_vs_bind_scheduler}  如果有调度器选择,进行绑定
      | -- ip_vs_bind_scheduler 将sched赋值给svc->scheduler,并执行 调度器的 init_service(svc)
    | -- ip_vs_start_estimator 设置 状态更新时间
    | -- ip_vs_svc_hash 计算 svc hash,存到 svc_table 中,设置 svc 的 flags IP_VS_SVC_F_HASHED,svc->refcnt 加 1
      | -- ip_vs_svc_hashkey 计算 key 有 proto,svc 地址,端口
 

  | -- ip_vs_add_dest 验证权重
    | -- ip_vs_lookup_dest 通过 endpoint 的 ip + port 验证是否已经存在于 svc->destinations 
    | -- ip_vs_trash_get_dest 验证是否已经存在于 ipvs->dest_trash(dest_trash 是指已经从 service table 移除,但仍然被一些链接引用;一些临时 down 掉的后端)
      | -- __ip_vs_update_dest 
    | -- ip_vs_new_dest  新建 destination
      | -- __ip_vs_update_dest   添加 svc 的引用计数,添加到 destination 中

你可能感兴趣的:(linux,ipvs,linux,ipvs)