概述
查看 Cilium 是什么情况下,会给物理网卡绑定 eBPF 程序,下面以 v1.14.4 的代码进行源码的解析。
代码分析
只要不开启以下的特性,Cilium 就不会将 eBPF 程序挂载到物理网卡上。
- EnableNodePort
- EnableHostFirewall
- EnableBandwidthManager
- EnableWireguard
- EnableHighScaleIPcache
- EnableL2Announcements
1
2
3
4
5
6
7
|
// pkg/option/config.go
// AreDevicesRequired returns true if the agent needs to attach to the native
// devices to implement some features.
func (c *DaemonConfig) AreDevicesRequired() bool {
return c.EnableNodePort || c.EnableHostFirewall || c.EnableBandwidthManager ||
c.EnableWireguard || c.EnableHighScaleIPcache || c.EnableL2Announcements
}
|
Loader.reloadHostDatapath 这个方法会负责根据不同情况,对物理网卡进行 eBPF 程序的附着,下面的代码省略了关于 cilium_host 和 cilium_net 的部分。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
// pkg/datapath/loader/loader.go
// reloadHostDatapath (re)attaches BPF programs to:
// - native devices: ingress and (optionally) egress if certain features require it
func (l *Loader) reloadHostDatapath(ctx context.Context, ep datapath.Endpoint, objPath string) error {
...
// Replace programs on physical devices.
for _, device := range option.Config.GetDevices() {
if _, err := netlink.LinkByName(device); err != nil {
log.WithError(err).WithField("device", device).Warn("Link does not exist")
continue
}
netdevObjPath := path.Join(ep.StateDir(), hostEndpointNetdevPrefix+device+".o")
if err := patchHostNetdevDatapath(ep, objPath, netdevObjPath, device,
node.GetMasqIPv4AddrsWithDevices(), node.GetMasqIPv6AddrsWithDevices()); err != nil {
return err
}
progs := []progDefinition{
{progName: symbolFromHostNetdevEp, direction: dirIngress},
}
// 这里就是上面提到的,如果不开启某些特性,宿主机网卡是不会加载eBPF程序的
if option.Config.AreDevicesRequired() &&
// Attaching bpf_host to cilium_wg0 is required for encrypting KPR
// traffic. Only ingress prog (aka "from-netdev") is needed to handle
// the rev-NAT xlations.
device != wgTypes.IfaceName {
progs = append(progs, progDefinition{symbolToHostNetdevEp, dirEgress})
} else {
// Remove any previously attached device from egress path if BPF
// NodePort and host firewall are disabled.
err := RemoveTCFilters(device, netlink.HANDLE_MIN_EGRESS)
if err != nil {
log.WithField("device", device).Error(err)
}
}
// replaceDatapath本质上是调用tc在宿主机上进行eBPF的程序的加载和卸载
finalize, err := replaceDatapath(ctx, device, netdevObjPath, progs, "")
if err != nil {
scopedLog := ep.Logger(Subsystem).WithFields(logrus.Fields{
logfields.Path: objPath,
logfields.Veth: device,
})
if ctx.Err() == nil {
scopedLog.WithError(err).Warningf("JoinEP: Failed to load program for physical device %s", device)
}
return err
}
defer finalize()
}
return nil
}
|
下面再看看,Cilium 是如何给网卡编译和加载对应的 eBPF 程序的,重点是如何 compile 以及 attach 这两个过程。
梳理流程
从上面的分析中,可以将 Cilium 中是否在宿主机的网卡上加载 eBPF 程序的逻辑整理如下。
测试和验证
测试集群开启了 kube-proxy-replacement,并且设置 enable-node-port
和 enable-host-firewall
为 false。
1
2
3
|
kube-proxy-replacement: partial
enable-node-port: "false"
enable-host-firewall: "false"
|
cilium-agent 启动,创建一个 Pod,运行一段时间之后检查 bond0 和 bond0.212 上是否有 ingress/egress。
创建 CiliumNetworkPolicy,再次检查 bond0 和 bond0.212 上是否有 ingress/egress。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l4-rule"
spec:
endpointSelector:
matchLabels:
app: network-multitool
ingress:
- fromEndpoints:
- matchLabels:
app: network-multitool
toPorts:
- ports:
- port: "80"
protocol: TCP
|
参考资料
- 链式CNI插件与portmap端口映射
- 支持hostPort
- Cilium 1.6: 无KVstore操作、百分之百kube-proxy替换、基于套接字的负载均衡
- Tutorial: How to Use Cilium Hubble for Observability in CNI Chaining Mode (Part 1)
- Kubernetes Security — Explore Cilium host firewall and host policies
- cilium中的datapath简解
警告
本文最后更新于 2023年11月12日,文中内容可能已过时,请谨慎参考。