Kubernetes 的重要性和火爆程度已无需多言,但各公司风格迥异的 IT 基础环境必然催生出各式各样的 Kubernetes 集群架构方案,这对整个生态的发展未尝不是一件好事。今天我们主要聊聊网络,集群网络系统是 Kubernetes 的核心部分,得益于 Kubernetes 灵活的接口设计,现在已有许多成熟方案和使用案例,比如 Calico,Flannel,Kube-router等。
Cilium 是一款为大规模和高度动态的容器环境而设计,基于 eBPF 的灵活、高性能网络插件,相比更流行的 Calico 和 Flannel,Cilium 出现的时间相对较晚,大规模应用案例也比前两者少。但是,由于 Cilium 基于 eBPF 和 XDP 设计,其处理数据包的速度更快,配置灵活度也更高。具体可查看 https://cilium.io/blog/2020/11/10/ebpf-future-of-networking/
在传统数据中心网络中,F5(本文特指 F5 BIG-IP LTM 设备)是非常重要的网络设备,其优秀的性能和超高的稳定性可以很好的保证服务质量(当然也很贵)。近几年,云原生发展迅速,原来主要为传统数据中心服务的 F5 公司也是紧跟时代脚步,推出了 F5 BIP-IP 云版本,还收购了软件负载均衡的大哥 Nginx,当然还有本文要介绍的 F5 Container Ingress Services(CIS)。
接下来就具体介绍下如何基于 F5 和 Cilium 构建 Kubernetes 集群网络系统(适合对 F5 有一定了解的朋友阅读)。
Cilium
无论有没有 F5, Cilium 都可以作为 CNI 插件部署到 Kubernetes 集群中。Cilium 支持 Overlay 和 Native Routing 两种网络模型:Overlay 基于 VXLAN 或者 Geneve 格式的封装,这种模式对网络基础设施的要求最低,只需要节点之间可以相互通信即可,Native Routing 支持 Cilium 节点通过 BIRD 运行 BGP 路由协议,将 Pod 的 IP 网段宣告到数据中心网络中。为了与 F5 直接通信,这里我们选择 Native Routing 模型。
网络接口
Cilium 会在主机网络空间创建三个虚拟接口:cilium_host,cilium_net 和 cilium_vxlan。cilium agent 在启动时会创建一个名为 "cilium_host -- cilium_net" 的虚拟链接对,并将 CIDR 的第一个 IP 地址分配给 cilium_host,之后该节点上的 Pod 都以 cilium_host 的地址作为网关。创建 Pod 时,在主机网络空间上创建带有 "lxc-xx" 的 veth 接口,另一端连接到 Pod 名称空间,cilium agent 安装所需的 BPF 程序以回复 ARP 请求,lxc-xx 接口的 MAC 地址用于 ARP 应答。Pod 生成的流量的下一个 L3 跳是cilium_host,而 Pod 生成的流量的下一个 L2 跳是 veth 对的主机端,cilium_vxlan 接口用于跨节点的 Pod 间的通信,cilium 使用 BPF 进行 LWT(轻型隧道)封装,将Pod跨节点通信的所有网络数据包都封装在标头中,该标头可以由 VXLAN 或 Geneve 帧组成,并通过标准 UDP 数据包标头进行传输。如果想要禁用 vxlan,在 helm 部署 cilium 时加上--set global.tunnel=disabled
参数即可。
部署命令
代码语言:javascript复制helm install cilium cilium/cilium --version 1.8.4
--namespace kube-system
--set config.ipam=kubernetes
--set native-routing-cidr=<no_masquerade_ip_segment>
--set global.ipMasqAgent.enabled=true
--set global.kubeProxyReplacement=strict
--set global.k8sServiceHost=<your_apiserver_ip>
--set global.k8sServicePort=6443
参数说明:
config.ipam=kubernetes
:使用 kubeadm 定义的 Pod 和 Service 网段native-routing-cidr=<no_masquerade_ip_segment>
:指定不做 Masquerade 的网段global.ipMasqAgent.enabled=true
:可以通过配置 ConfigMap 动态更改 Cilium 的 Masquerade 策略global.k8sServiceHost
:指定API Server 的 IP ,在禁用 kube-proxy 的情况下显式指定global.k8sServicePort
:指定API Server的端口global.kubeProxyReplacement=strict
:在缺少底层 Linux 内核支持的情况下,cilium agent 将会退出,而不会使用 kube-proxy 来代替
这里详细介绍下 Cilium Masquerade 策略。
默认情况下,Pod 发往集群外的流量(不管是 Pod 主动发起的,还是集群外部主动发起的流量)都会通过 Cilium 进行地址伪装(Masquerade),将 Pod 地址转换为节点地址,而想要让 F5 直接发送流量给 Pod,则需要设置 Cilium 转发给 F5 SNAT Pool、相关的核心交换机和 F5 健康检查地址的流量不使用地址伪装。
使用 agent-config/config 文件配置与native-routing-cidr
作用相同:
nonMasqueradeCIDRs:
# LinkLocal地址
-
#F5 SNAT Pool地址
-
#核心交换机地址
-
# F5健康检查地址
-
masqLinkLocal: false
BIRD 配置
Bird 是一个类 UNIX 系统的动态路由守护进程,它支持当代互联网中所有路由协议,如 BGP、OSPF、RIP 等。在集群所有节点以守护进程形式部署 BIRD 动态路由软件,使用 BIRD 和核心交换机建立 BGP 全互联邻居,从而将 Pod 的 IP 网段的路由宣告给核心交换机,F5通过核心交换机学到 Pod 网段的路由,从而可以将流量直接转发给 Pod,达到了流量分发的目的。
BIRD 配置:
代码语言:javascript复制log syslog all;
router id <节点IP>;
protocol device {
scan time 10; # Scan interfaces every 10 seconds
}
# Disable automatically generating direct routes to all network interfaces.
protocol direct {
disabled; # Disable by default
}
# Forbid synchronizing BIRD routing tables with the OS kernel.
protocol kernel {
ipv4 { # Connect protocol to IPv4 table by channel
import none; # Import to table, default is import all
export none; # Export to protocol. default is export none
};
}
# Static IPv4 routes.
protocol static {
ipv4;
route <pod_cidr> via "cilium_host";
}
# BGP peers
protocol bgp uplink0 {
description "BGP uplink 0";
local <节点IP> as <BGP AS号>;
neighbor <核心交换机IP> as <BGP AS号>;
ipv4 {
import filter {reject;};
export filter {accept;};
};
}
protocol bgp uplink1 {
description "BGP uplink 1";
local <节点IP> as <BGP AS号>;
neighbor <核心交换机IP> as <BGP AS号>;
ipv4 {
import filter {reject;};
export filter {accept;};
};
}
核心交换机配置:
核心交换机和每个节点建立全互联BGP邻居
代码语言:javascript复制router bgp <BGP AS号>
neighbor <节点IP> remote-as <BGP AS号>
update-source loopback0
address-family ipv4 unicast
.....
正常情况下交换机可以学到Pod网段的路由
代码语言:javascript复制# show ip route bgp
IP Route Table for VRF "default"
'*' denotes best ucast next-hop
'**' denotes best mcast next-hop
'[x/y]' denotes [preference/metric]
'%<string>' in via output denotes VRF <string>
23.3.0.0/24, ubest/mbest: 1/0
*via 11.8.38.43, [200/0], 3w4d, bgp-64512, internal, tag 64512,
23.3.1.0/24, ubest/mbest: 1/0
*via 11.8.37.57, [200/0], 3w4d, bgp-64512, internal, tag 64512,
23.3.2.0/24, ubest/mbest: 1/0
*via 11.8.36.66, [200/0], 3w4d, bgp-64512, internal, tag 64512,
23.3.3.0/24, ubest/mbest: 1/0
*via 11.8.83.11, [200/0], 3w4d, bgp-64512, internal, tag 64512,
23.3.4.0/24, ubest/mbest: 1/0
*via 11.8.84.13, [200/0], 3w4d, bgp-64512, internal, tag 64512,
23.3.5.0/24, ubest/mbest: 1/0
*via 11.8.84.12, [200/0], 3w4d, bgp-64512, internal, tag 64512,
F5 BIG-IP Container Ingress Services(CIS)
对于有外部访问的服务,需要在 Kubernetes 集群中以 Deployment 形式部署 F5 官方提供的插件 k8s-bigip-ctlr 作为 F5 在集群中的 agent。CIS 作为一个重要控制平面,监控着 API Server 并根据配置信息自动的配置 F5 BIG-IP,CIS 以 Pod 的方式运行在集群环境中,完美地融合进集群网络,让F5 Service 类似一个原生的 Kubernetes Service 监控着 Pod 资源并对外提供服务。
BIG-IP Controller Modes
--pool-member-type
选项决定了 CIS 运行的模式,有两种模式可选:NodePort 和 Cluster。
NodePort 模式是 CIS 默认运行的模式,该模式更容易设置,只需要 BIG-IP 与 集群节点能够通信即可,但是该模式有几点限制:
- Kubernetes Service 必须使用 NodePort 模式
- F5 BIG-IP 无法对隐藏在 Service 后的 Pod 做感知和健康检查
- 增加网络延迟
由此我们选择了 CIS 的 Cluster 模式,让 F5 BIG-IP 可以直接对 Pod 做健康检查和流量转发,使用Cluster 模式的前提是需要打通 BIG-IP 到集群中 Pod 的通信。
默认情况下,CIS 使用 BIG-IP Automap SNAT 为所有通过 CIS 创建的 Virtual Service 做客户端的源地址转换,通过--vs-snat-pool-name=K8S-SNAT-POOL
为 K8S 集群单独指定 SNAT POOL。
CIS 部署
部署之前在 F5 BIG-IP 上为 K8S 集群新建一个名为 K8S 的 partition,通过 PodAntiAffinity 将主备两台 F5 BIG-IP 对应的 F5 CIS 调度到不同的节点中,使用 SNAT-vlan130 地址池中的地址作为负载均衡时 F5 SNAT 的地址。
代码语言:javascript复制apiVersion: apps/v1
kind: Deployment
metadata:
name: k8s-bigip-ctlr-standby-clusterip
namespace: kube-system
spec:
# DO NOT INCREASE REPLICA COUNT
replicas: 1
selector:
matchLabels:
app: k8s-bigip-ctlr-standby
template:
metadata:
name: k8s-bigip-ctlr-standby
labels:
app: k8s-bigip-ctlr-standby
spec:
affinity:
PodAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
PodAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- k8s-bigip-ctlr-active
topologyKey: "kubernetes.io/hostname"
# Name of the Service Account bound to a Cluster Role with the required
# permissions
serviceAccountName: bigip-ctlr
containers:
- name: k8s-bigip-ctlr-standby
image: f5networks/k8s-bigip-ctlr:latest
env:
- name: BIGIP_USERNAME
value: F5 BIG-IP用户名
- name: BIGIP_PASSWORD
value: F5 BIG-IP密码
command: ["/app/bin/k8s-bigip-ctlr"]
args: [
# See the k8s-bigip-ctlr documentation for information about
# all config options
# https://clouddocs.f5.com/products/connectors/k8s-bigip-ctrl/latest
"--bigip-username=F5 BIG-IP用户名",
"--bigip-password=F5 BIG-IP密码",
"--bigip-url=F5地址",
"--bigip-partition=K8S",
"--pool-member-type=cluster",
"--insecure=true",
"--vs-snat-pool-name=SNAT-vlan130"
]
服务暴露
CIS 可以关联 K8S 原生的 Service ,通过 ConfigMap(L4)或者 Ingress(L7)对 Pod 的服务暴露提供配置。
ConfigMap(L4)
配置 Configmap 资源,关联 Service,做4层服务发布,可以指定相应的健康检查规则和负载均衡方法。
代码语言:javascript复制kind: ConfigMap
apiVersion: v1
metadata:
name: sugar-cmdb-v2-vs
labels:
f5type: virtual-server
data:
schema: "f5schemadb://bigip-virtual-server_v0.1.7.json"
data: |
{
"virtualServer": {
"backend": {
"servicePort": 80,
"serviceName": "myservice",
"healthMonitors": [{
"interval": 5,
"protocol": "http",
"send": "GET /health.html HTTP/1.1rnHost:1.1.1.1rnrn",
"recv": "server is ok",
"timeout": 16
}]
},
"frontend": {
"virtualAddress": {
"port": 80,
"bindAddr": "<F5分配的虚地址>"
},
"partition": "K8S",
"balance": "least-connections-member",
"mode": "http"
}
}
}
CIS 关联到 Service 后端的 Pod 信息,并在 F5 上创建了一个 Pool,并且使用了我们配置的 Least-Connections 负载均衡算法。
并且为 Pool member 关联了相应的健康检查规则。
最后根据我们分配的IP和端口号创建出对应的 Virtual Service。
Ingress(L7)
配置 Ingress 资源,关联2个 Service,做7层的服务发布,基于访问域名转发给不同 Service 下的 Pod。
代码语言:javascript复制apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-l7-domain-ingress
namespace: default
annotations:
virtual-server.f5.com/ip: "<F5分配的虚拟地址>"
virtual-server.f5.com/http-port: "80"
virtual-server.f5.com/partition: "Cilium"
virtual-server.f5.com/balance: "round-robin"
virtual-server.f5.com/health: |
[
{
"path": "mysite1.example.com/",
"send": "HTTP GET /",
"interval": 5,
"timeout": 10
}, {
"path": "mysite2.example.com/",
"send": "HTTP GET /",
"interval": 5,
"timeout": 10
}
]
spec:
rules:
- host: mysite1.example.com
http:
paths:
- path: /
backend:
serviceName: nginx
servicePort: 80
- host: mysite2.example.com
http:
paths:
- path: /
backend:
serviceName: tomcat
servicePort: 80
CIS 会根据 Ingress 规则在 F5 上创建一个 Virtual Service 并且关联一条 Published Policy。
F5 动态感知容器服务变化
通过部署在 K8S 集群内的 F5 CIS 动态感知容器服务的变化,解析服务的创建以及销毁事件,动态更新 F5 配置,实现服务自动发现,动态感知能力,以此来支持应用弹性伸缩能力。这部分就留给大家自己验证吧。
参考文献
- Cilium Doc: Using BIRD to run BGP
- Cilium Doc: Masquerading
- 民生银行:基于 F5 技术实现 Kubernetes 生产环境最佳实践
- F5 Doc:Using the BIG-IP Controller as an Ingress controller
- F5 Doc:Manage your BIG-IP virtual servers
- 最Cool Kubernetes网络方案Cilium入门
点击屏末 | 阅读原文 | 即刻学习
文章转载自k8s技术圈。点击这里阅读原文了解更多。
扫描二维码联系我们!
CNCF (Cloud Native Computing Foundation)成立于2015年12月,隶属于Linux Foundation,是非营利性组织。
CNCF(云原生计算基金会)致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术。我们通过将最前沿的模式民主化,让这些创新为大众所用。请长按以下二维码进行关注。