Z.S.K.'s Records

cilium在kubernetes中的生产实践二(cilium部署)

在前东家的时候其实就有意将cilium强大的链路追踪能力集成到生产环境中,各种因素导致没有很大信心落地, 经过深入调研(也就把官网docs翻了四五遍)及测试, 终于有机会在生产kubernetes集群中(其中一个集群规模不算很大,2w+核心,持续增长)使用cilium做为cni,同时替换kube-proxy, 到现在已经有一段时间了,也算是有生产经验可以跟大家聊一聊这个工具,使用体验总结一句话: 轻松愉悦.
分享一下整个落地过程,同时也总结下方方面面, 工作之余尽量更新.
此篇为: cilium在kubernetes中的生产实践二(cilium部署)

总体分为以下几块内容:
cilium在kubernetes中的生产实践一(cilium介绍)
cilium在kubernetes中的生产实践二(cilium部署)
cilium在kubernetes中的生产实践三(cilium网络模型之关键配置)
cilium在kubernetes中的生产实践四(cilium网络模型之生产实践)
cilium在kubernetes中的生产实践五(cilium网络策略)
cilium在kubernetes中的生产实践六(cilium排错指南)
cilium在kubernetes中的生产实践七(cilium中的bpf hook)

内核要求

由于eBPF是个较新的特性, 因此对内核版本有要求,建议最少4.18+以上的内核版本,cilium有些功能必须要5.0+之上的内核版本,生产环境下大多数都不会满足

相关参数如下:

1
2
3
4
os: Rockey Linux release 8.7
kernel: 4.18.0-425.10.1.el8_7.x86_64
k8s: v1.22.13
docker: v20.10.18

cilium功能对内核版本要求,查看如下列表:

System Requirements — Cilium 1.12.10 documentation

基于该列表再结合已有环境的内核版本来决定是否开启相关功能

k8s安装

这里简单带一下k8s使用cilium替换kube-proxy的安装,kubeadm集群初始化配置文件参考: kubeadm-kubeproxy-free.conf

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
token: abcdef.0123456789abcdef
ttl: 24h0m0s
usages:
- signing
- authentication
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: xxx.xxx.xxx.xxx # 指定本机的ip地址用于advertise
bindPort: 6443
nodeRegistration:
kubeletExtraArgs:
node-ip: xxx.xxx.xxx.xxx # 指定本机的ip地址,用于kubelet向master注册,同advertiseAddress
criSocket: /var/run/dockershim.sock
imagePullPolicy: IfNotPresent
name: master-1
taints:
- effect: "NoSchedule"
key: "node-role.kubernetes.io/master"
---
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns: {}
etcd:
local:
dataDir: /mnt/nvme0/etcd
imageRepository: harbor.local/k8s
kind: ClusterConfiguration
kubernetesVersion: 1.22.13
controlPlaneEndpoint: xxx.xxx.xxx.xxx:6443
apiServer:
extraArgs:
authorization-mode: Node,RBAC
timeoutForControlPlane: 4m0s
certSANs:
- domain.xxx
- vip.k8s
- nodelist-xxx
networking:
dnsDomain: cluster.local
serviceSubnet: 10.96.0.0/12
podSubnet: 10.244.0.0/16
scheduler: {}
---
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
cacheTTL: 30s
enabled: true
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 10s
cacheUnauthorizedTTL: 10s
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
healthzBindAddress: 127.0.0.1
healthzPort: 10248
kind: KubeletConfiguration
cgroupDriver: systemd
failSwapOn: false
maxPods: 250
rotateCertificates: true
staticPodPath: /etc/kubernetes/manifests
systemReserved:
cpu: 1000m
memory: 2Gi
kubeReserved:
cpu: 1000m
memory: 2Gi
evictionHard:
imagefs.available: 10%
memory.available: 2Gi
nodefs.available: 5%
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs

# 说明
# 使用kubeadm-kubeproxy-free.conf做为集群的初始化配置文件, 注意修改如下参数:
# 1. InitConfiguration:
# a. localAPIEndpoint
# b. name
# 2. ClusterConfiguration(在集群中存在于Kube-system为configmap: kubeadm-config):
# a. etcd的存储路劲
# b. imageRepository的路径
# c. controlPlaneEndpoint的地址,这里使用了kube-vip提供的高可用ip
# d. certSANs,这里需要加上所有的master的节点ip及主机名,以及集群的域名

集群初始化

1
2
3
kubeadm init --upload-certs --config kubeadm-kubeproxy-free.conf --skip-phases=addon/kube-proxy --apiserver-advertise-address xxx.xxx.xxx.xxx
# --skip-phases=addon/kube-proxy 不安装kube-proxy组件
# --apiserver-advertise-address 指定需要绑定的ip,在机器有多网卡的时候用

cilium安装

Cilium在kubernetes安装有2种运行模式,一种是完全替换kube-proxy, 如果底层 Linux 内核版本低,可以替换kube-proxy的部分功能,与原来的kube-proxy 共存。因此这里要注意kubeProxyReplacement参数,从官网上查看有如下几个选项:

  • kubeProxyReplacement=strict: 该选项期望使用无kube-proxy的Kubernetes设置,其中Cilium有望完全替代所有kube-proxy功能。 Cilium代理启动并运行后,将负责处理类型为ClusterIP,NodePort,ExternalIP和LoadBalancer的Kubernetes服务。如果不满足基本内核版本要求(请参阅不带kube-proxy注释的Kubernetes),则Cilium代理将在启动时退出并显示一条错误消息。
  • kubeProxyReplacement=probe: 此选项适用于混合设置,即kube-proxy在Kubernetes集群中运行,其中Cilium部分替换并优化了kube-proxy功能。一旦Cilium agent启动并运行,它就会在基础内核中探查所需BPF内核功能的可用性,如果不存在,则依靠kube-proxy补充其余的Kubernetes服务处理,从而禁用BPF中的部分功能。在这种情况下,Cilium代理将向其日志中发送一条信息消息。例如,如果内核不支持 Host-Reachable Services,则节点的主机名空间的ClusterIP转换是通过kube-proxy的iptables规则完成的。
  • kubeProxyReplacement=partial: 与探针类似,此选项用于混合设置,即kube-proxy在Kubernetes集群中运行,其中Cilium部分替换并优化了kube-proxy功能。与探查基础内核中可用的BPF功能并在缺少内核支持时自动禁用负责BPF kube-proxy替换的组件的探针相反,该部分选项要求用户手动指定应替换BPF kube-proxy的组件。与严格模式类似,如果不满足基本内核要求,则Cilium代理将在启动时通过错误消息进行援助。对于细粒度的配置,可以将global.hostServices.enabled,global.nodePort.enabled和global.externalIPs.enabled设置为true。默认情况下,所有三个选项均设置为false。

由于k8s集群是全新安装,所以这里直接使用kubeProxyReplacement=strict, 使用helm安装, cilium 版本 v1.12.0

1
helm install cilium cilium/cilium --version 1.12.0 --namespace kube-system --set kubeProxyReplacement=strict --set k8sServiceHost=xxx.xxx.xxx.xxx --set k8sservicePort=6443 --set ipam.Operator.clusterPoolIPv4PodCIDRList=10.244.0.0/16 --set enable-bpf-masquerade=true --set ipam.Operator.ClusterPoolIPv4MaskSize=24 --set devices=ens192 --set hubble.Relay.Enabled=true --set hubble.Ui.Enabled=true --set prometheus.Enabled=true --set operator.Prometheus.Enabled=true --set hubble.Enabled=true --set hubble.Metrics.Enabled="{dns, drop, tcp, flow, port-distribution, icmp, http}" --set image.UseDigest=false --set operator.image.useDigest=false --set operator.Image.Repository=harbor.io/k8s/cilium/operator --set image.Repository=harbor.io/k8s/cilium/cilium --set hubble.Rela y.Image.UseDigest=false --set hubble.Relay.Image.Repository=harbor.io/k8s/cilium/hubble-relay --set hubble.ui.Backend.Image.Repository=harbor.io/k8s/cilium/hubble-ui-backend --set hubble.Ui.Backend.Image.Tag=v0.9.0 --set hubble.ui.Frontend.Image.Repository=harbor.io/k8s/cilium/hubble-ui --set hubble.ui.Frontend.Image.Tag=v0.9.0

其中:

  1. kubeProxyReplacement: cilium在哪种方式下运行,上文已进行了解释, 这里选择kube-proxy-free模式
  2. k8sServiceHost: k8s集群的地址,如果是集群,则一般填写负载均衡地址(vip地址)
  3. k8sservicePort: k8s集群的端口,如果是集群,则一般填写负载均衡端口(vip端口)
  4. ipam.Operator.clusterPoolIPv4PodCIDRList: pod的CIDR
  5. enable-bpf-masquerade: 是否开启ip的masquerade, 下文解释
  6. devices: 非常重要的参数,下文解释
  7. hubble.xx: 跟hubble相关的参数,能开启的尽量开启hubble吧

后续如果需要对配置项进行更新,则可使用以下命令复用之前的配置(--reuse-values),只更新给的配置项即可, 比如将将host routing 由 legacy 改成 bpf

1
helm upgrade cilium --version 1.12.0 --namespace kube-system --reuse-values --set --bpf.Masquerade=true

因为cilium号称可以完全取代kube-proxy, 但也需要支持kube-proxy已存在的场景,所以cilium会自动根据情况进行设置,还是比较人性化的

默认情况下cilium是使用kubernetes CRD-based来进行状态管理的,当然也支持使用外部的key-value数据库来存储状态, 一般情况下都会使用默认方式

这里要特别注意的参数做如下解释:

enable-bpf-masquerade

Masquerading — Cilium 1.13.3 documentation

是否开启bpf的封装, 一张图应该很容易看明白

当在容器内部即访问外部资源时,如何让回来的流量可以再沿着出去的路径,kube-proxy的场景下是基于iptables实现,

而bpf比iptables高效的多, 它需要Linux内核4.19+的版本上才支持

当然,它需要devices参数的配合,即能访问外部资源所在的网卡必须在devices列表里

cilium还能容器哪些CIDR不被封装

devices

如果k8s节点存在多张网卡,如果网卡层面上做了网络隔离的话,那么在指定devices就需要注意了

举个生产遇到的例子:

节点上有eth0, eth1, 在部署k8s集群时,如果没有指定网口名,则会选择默认的网口名或者是kubeadm挑出的第一个网卡名,但有时候不一定能选中我们期望的,因此建议最好是使用–apiserver-advertise-address来显式地指定需要绑定的IP地址(networ interface),但是只能指定一个,比如指定eth0, 这样在集群中启动的容器会有一个eth0 ip, bind node eth0, 它想要访问集群外的资源,最终都会通过node eth0这个网口出入(可能做了masquerade也可能不做)

但有些集群外的资源只能通过eth1这个网段才能访问,这个时候容器里的网络就是不通的, 怎么办?

如果放在以前,那么可能会通过折腾multi-CNI的方式将eth0/eth1这两块网卡都映射进容器,即在容器里会同时看到两个ip地址,跟宿主机一样,这样的话,网络上就能通了,

但事实上这种实现方式还是有些复杂,成本较高,

放在cilium可就方便多了,直接devices列表里指定多个网卡即可实现上述能力,

devices参数的作用是: 当请求从容器到达节点时,允许从node的哪些network interface出去

指定多个network interface后, 容器里还是只有一个eth0,但是cilium会自行判断应该从哪个interface出入,是不是简单、清爽了很多. 在网络复杂的情况下,devices参数还是非常有用的

查看状态

可以单独使用cilium-cli工具查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cilium status --wait
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Hubble: disabled
\__/¯¯\__/ ClusterMesh: disabled
\__/

DaemonSet cilium Desired: 2, Ready: 2/2, Available: 2/2
Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2
Containers: cilium-operator Running: 2
cilium Running: 2
Image versions cilium quay.io/cilium/cilium:v1.9.5: 2
cilium-operator quay.io/cilium/operator-generic:v1.9.5: 2

cilium-cli看到的是一些大面的信息,也可以通过cilium-agent查看,会列出一些详细信息

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
# kubectl exec -it -n kube-system cilium-8wvvp -- cilium status
Defaulted container "cilium-agent" out of: cilium-agent, mount-cgroup (init), wait-for-node-init (init), clean-cilium-state (init)
KVStor: Ok Disabled
Kubernetes: Ok 1.24 (v1.24.0) [linux/amd64]
Kubernetes APIs: ["cilium/v2::CiliumClusterwideNetworkPolicy", "cilium/v2::CiliumEgressNATPolicy", "cilium/v2::CiliumLocalRedirectPolicy", "cilium/v2::CiliumNetworkPolicy", "cilium/v2::CiliumNode", "cilium/v2alpha1::CiliumEndpointSlice", "core/v1::Namespace", "core/v1::Node", "core/v1::Pods", "core/v1::Service", "discovery/v1::EndpointSlice", "networking.k8s.io/v1::NetworkPolicy"]
KubeProxyReplacement: Strict [eth0 172.16.127.45 (Direct Routing)]
Host firewall: Disabled
Cilium: Ok 1.11.4 (v1.11.4-9d25463)
NodeMonitor: Disabled
Cilium health daemon: Ok
IPAM: IPv4: 4/254 allocated from 10.244.0.0/24,
Allocated addresses:
10.244.0.105 (kube-system/coredns-6d4b75cb6d-wjk9p)
10.244.0.146 (kube-system/coredns-6d4b75cb6d-956sh)
10.244.0.225 (router)
10.244.0.239 (health)
BandwidthManager: EDT with BPF [eth0]
Host Routing: BPF
Masquerading: BPF [eth0] 10.244.0.0/16 [IPv4: Enabled, IPv6: Disabled]
Clock Source for BPF: ktime
Controller Status: 30/30 healthy
Name Last success Last error Count Message
bpf-map-sync-cilium_ipcache 1s ago 34m13s ago 0 no error


# ...省略

cilium status --verbose可以显示更多的信息,在debug时很有用.

使用cilium后,再来查看iptables-save | grep KUBE-SVC, 干净的很了, ipvsadm -ln也一目了然

cilium在线上集群跑了快一年了,还没有碰到过pod之间访问的奇葩网络问题, 爽.

参考文章:

转载请注明原作者: 周淑科(https://izsk.me)

 wechat
Scan Me To Read on Phone
I know you won't do this,but what if you did?