在前东家的时候其实就有意将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 | os: Rockey Linux release 8.7 |
cilium功能对内核版本要求,查看如下列表:
System Requirements — Cilium 1.12.10 documentation
基于该列表再结合已有环境的内核版本来决定是否开启相关功能
k8s安装
这里简单带一下k8s使用cilium替换kube-proxy的安装,kubeadm集群初始化配置文件参考: kubeadm-kubeproxy-free.conf
1 | apiVersion: kubeadm.k8s.io/v1beta3 |
集群初始化
1 | kubeadm init --upload-certs --config kubeadm-kubeproxy-free.conf --skip-phases=addon/kube-proxy --apiserver-advertise-address xxx.xxx.xxx.xxx |
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 |
其中:
- kubeProxyReplacement: cilium在哪种方式下运行,上文已进行了解释, 这里选择kube-proxy-free模式
- k8sServiceHost: k8s集群的地址,如果是集群,则一般填写负载均衡地址(vip地址)
- k8sservicePort: k8s集群的端口,如果是集群,则一般填写负载均衡端口(vip端口)
- ipam.Operator.clusterPoolIPv4PodCIDRList: pod的CIDR
- enable-bpf-masquerade: 是否开启ip的masquerade, 下文解释
- devices: 非常重要的参数,下文解释
- 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 | cilium status --wait |
cilium-cli看到的是一些大面的信息,也可以通过cilium-agent查看,会列出一些详细信息
1 | # kubectl exec -it -n kube-system cilium-8wvvp -- cilium status |
cilium status --verbose
可以显示更多的信息,在debug时很有用.
使用cilium后,再来查看iptables-save | grep KUBE-SVC
, 干净的很了, ipvsadm -ln
也一目了然
cilium在线上集群跑了快一年了,还没有碰到过pod之间访问的奇葩网络问题, 爽.