Cilium 1.6:无 KVstore 操作、百分之百 kube-proxy 替换、基于套接字的负载均衡

阅读数:9860 2019 年 9 月 3 日 10:09

Cilium 1.6:无KVstore操作、百分之百kube-proxy替换、基于套接字的负载均衡

Cilium 1.6 引入了多个令人兴奋的新特性,包括无 KVStore 操作、百分百 Kube-proxy 替换、基于套接字的负载均衡、通用 CNI 链等。

Cilium 1.6:无KVstore操作、百分之百kube-proxy替换、基于套接字的负载均衡

我们很高兴地宣布 Cilium 1.6 发布。社区总共贡献了 1408 个提交,许多开发人员都是第一次提交。Cilium 1.6 引入了多个令人兴奋的新特性,比如:

  • 无 KVStore 操作:新增基于 CRD 的后端用于安全标识,现在允许在 Kubernetes 上下文中完全不使用 KVstore 来操作 Cilium。

  • 百分百 Kube-proxy 替换:许多用户都希望在不需要运行 Kube-proxy 的情况下运行 Kubernetes 集群。这个版本包括在不使用 Kube-proxy 的情况下运行 Kubernetes 集群所需的最后两个特性,Cilium 完全替换了 Kube-proxy。

  • 基于套接字的负载均衡:基于套接字的负载均衡结合了客户端和基于网络的负载均衡的优点,使用 Kubernetes 服务提供完全透明的负载均衡,在连接建立期间一次性完成从服务 IP 到端点 IP 的转换,而不是在连接的整个生命周期中转换每个网络包。

  • 改进策略伸缩性:该版本对整个策略系统进行了改进,将策略和标识定义的处理解耦,并转向一个完全增量的模型。这确保了高 Pod 调度(churn)环境,例如跨多个集群的数十万个 Pod,能够很好地处理不断变化的策略定义。

  • 通用 CNI 链:1.6 版本引入了新的 CNI 链接框架,允许在其他大多数 CNI 插件(如 Weave、Calico、Flannel、AWS VPC CNI 或 Lyft CNI 插件)上运行 Cilium。该特性支持使用高级功能,如基于 eBPF 的安全策略执行、可见性、多集群、加密和负载均衡,并且可以继续运行任何已经在使用的 CNI 插件。

  • 本地 AWS ENI 模式:一种新的 datapath 和 IPAM 模式,允许将本地 AWS ENI 路由的效率与 Cilium 策略执行、加密和多集群相结合。一种基于操作符的新设计,解决了大型 AWS ENI 用户使用节点代理时已知的许多问题。

Cilium 是什么?

Cilium 是一个开源软件,用于透明地提供和保护使用 Linux 容器管理平台(如 Kubernetes、Docker 和 Mesos)部署的应用程序服务之间的网络和 API 连接。

Cilium 基础是一种新的 Linux 内核技术 eBPF,支持在 Linux 内部动态植入强大的安全性、可视性和网络控制逻辑。eBPF 用于提供多集群路由、替代 Kube-proxy 的负载均衡功能、透明加密以及网络和服务安全。除了提供传统的网络级安全外,eBPF 的灵活性还可为应用程序协议和 DNS 请求、响应上下文中的安全提供支持。Cilium 与 Envoy 紧密集成,提供了基于 Go 的扩展框架。因为 eBPF 运行在 Linux 内核中,所以不需要更改应用程序代码或容器配置就可以应用所有 Cilium 功能。

要了解 Cilium 的更多细节,请查阅 Cilium 简介

无 KVStore 操作(CRD 存储)

这是不少开发者期待已久的功能之一,目前已经完成了在没有键值存储的情况下运行 Cilium 所需的所有工作。从 1.6 版本开始,开发人员可以在 Kubernetes 环境中运行完全基于 CRD 的 Cilium。下面是新模式的一个简单介绍。

Cilium 1.6:无KVstore操作、百分之百kube-proxy替换、基于套接字的负载均衡

  • 新缺省值:新的默认模式从标准 Kubernetes 资源获取所有必需信息,并使用自定义资源定义(CRD)存储所有状态。

  • 托管 etcd:托管 etcd 模式构建在默认模式上,使用 etcd-operator 在 Kubernetes 集群中维护 etcd 集群。这将带来更好的可伸缩性,同时继续以 Kubernetes 作为所有状态的真相来源。如果 etcd 集群失败,那么状态传播将回到基于 CRD 的状态,直到 etcd 恢复。

这种新的托管 etcd 模式比 1.5 版本以前的 etcd-operator 模式更可靠。由于保持了 Kubernetes 的真相来源,etcd 的失败不再是一个严重的问题。

  • 外部 KVStore:对于大规模环境,这仍然是最可靠的模式。它需要维护一个外部 KVStore,可以使用 Kubernetes etcd 或专用 etcd、consul 集群。

要了解更多信息,请查阅文档中的快速安装部分。

移除 Kube-proxy

Cilium 1.6:无KVstore操作、百分之百kube-proxy替换、基于套接字的负载均衡

1.6 版带来了移除 kube-proxy 所需的最后一个缺失部分——支持 NodePort 类型的服务,以及使 Kubernetes 服务可用于主机和在主机网络中运行的 Pod 进程。新特性允许在没有 kube-proxy 的情况下运行一个功能完整的 Kubernetes 集群。这意味着不再需要长长的 iptables 规则列表来启用 Kubernetes 服务抽象。

为什么这很重要?

下图显示了 Kubernetes 服务到运行在远程主机上的 nginx Pod 的每个 HTTP GET 请求的延迟(越低越好),这是通过 ab 顺序发送 10 万个请求测出来的。我们可以看到,eBPF 服务实现并不依赖于集群中部署的服务数量,而使用 iptables 的 kube-proxy 实现则依赖于此。

Cilium 1.6:无KVstore操作、百分之百kube-proxy替换、基于套接字的负载均衡

基于 eBPF 的服务实现不仅提高了 Kubernetes 服务流量的延迟和吞吐量,还减少了处理服务定义更改所需的时间和开销。对于 iptables,则必须在 datapath 中替换整个规则表,而 eBPF 支持在 datapath 中应用单个服务更改。

另一个关键的区别是预定义行为。基于 iptables 的服务实现需要针对每个服务和服务后端使用多个 iptables 规则。这实际上创建了一个很长的规则列表,这些规则必须逐一遍历。要匹配的服务在列表上越靠下,代价会越高,一些服务的负载均衡代价是只需要遍历几条规则,其他数据包在找到匹配项之前必须遍历数千条规则,行为是非确定性的。使用 eBPF,因为使用哈希表实现,所以查找成本接近 O(1),并且行为是确定的,正如上面的基准测试所示。

消除每个节点的 iptables 规则混乱

任何在较大的规模下运营过 Kubernetes(超过几十个服务)的人都会注意到,生成了大量的 iptables 规则。下面是一个 NodePort 类型的 Kubernetes 服务的例子,它有两个端点作为后端。

kube-proxy(iptables)规则

复制代码
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/nginx:" -m tcp --dport 30905 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/nginx:" -m tcp --dport 30905 -j KUBE-SVC-253L2MOZ6TC5FE7P
-A KUBE-SEP-PCCJCD7AQBIZDZ2N -s 10.217.1.154/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-PCCJCD7AQBIZDZ2N -p tcp -m tcp -j DNAT --to-destination 10.217.1.154:80
-A KUBE-SEP-UFVSO22B5A7KHVMO -s 10.217.1.159/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-UFVSO22B5A7KHVMO -p tcp -m tcp -j DNAT --to-destination 10.217.1.159:80
-A KUBE-SERVICES ! -s 10.217.0.0/16 -d 10.107.41.178/32 -p tcp -m comment --comment "default/nginx: cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.107.41.178/32 -p tcp -m comment --comment "default/nginx: cluster IP" -m tcp --dport 80 -j KUBE-SVC-253L2MOZ6TC5FE7P
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j
KUBE-NODEPORTS
-A KUBE-SVC-253L2MOZ6TC5FE7P -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-PCCJCD7AQBIZDZ2N
-A KUBE-SVC-253L2MOZ6TC5FE7P -j KUBE-SEP-UFVSO22B5A7KHVMO

基于 eBPF 的 k8s 服务规则

复制代码
# This snippet is intentionally left blank

本地 eBPF NodePort 和 SNAT 实现

在后台,我们将一个 NodePort eBPF 程序附加到主机上具有默认路由的网络设备上。这使得 NodePort 服务可以通过设备的 IP 地址访问。此外,得益于新的主机级服务特性,可以通过 loopback 或 cilium_host IP 地址在集群中从主机或 Pod 访问 NodePort 服务。

当服务端点在请求的目标主机之外的其他主机上运行时,eBPF 程序会在将请求转发到运行端点的主机之前执行 SNAT 转换。

可以通过将 enable-node-port 设置为 true 来启用该特性。有关更多配置选项,请参阅 NodePort 入门指南

基于套接字的负载均衡

Cilium 1.6:无KVstore操作、百分之百kube-proxy替换、基于套接字的负载均衡

通常,负载均衡以如下几种方式中的一种实现:

  • 应用程序执行客户端负载均衡并选择目标端点。这样做的好处是,负载均衡的成本是在建立连接时一次性预先支付的,在连接的生命周期内不存在额外开销,这种方法的缺点是对应用程序不透明。

  • 通过将请求转换为特定的服务 IP,网络通过中间的方框执行负载均衡。与客户端负载均衡相比,这种方法的优点是透明,不涉及应用程序本身。然而,缺点是每个网络包都需要在请求和响应方向上更改其 IP 地址。

在 Cilium 1.6 中,我们引入了基于套接字的负载均衡,它结合了两种方法的优点:

  • 透明:负载均衡对应用程序保持 100% 透明。服务是使用标准的 Kubernetes 服务定义定义的。

  • 高效:通过在 connect(2) 系统调用中转换地址实现套接字层负载均衡,负载均衡的成本在建立连接时预先支付,在连接之后就不需要额外的转换。性能与应用程序直接访问后端的性能相同。

进一步探讨

这种基于套接字的负载均衡实现利用了 Linux 内核中最新的 eBPF cgroup 钩子(hook),Cilium 将其连接到 connect(2)、sendmsg(2) 和 recvmsg(2) 系统调用。eBPF 程序然后用服务后端信息重写 sockaddr_in{,6}数据。对于 UDP 服务,我们扩展了内核以使 recvmsg(2)(更多细节)可以进行反向 sock 地址转换。这些钩子的优点是:操作对应用程序透明;以独立于设备的方式操作;后端选择只需要在 connect(2) 时间执行。后者意味着包头在连接的整个生命周期内不需要 NAT。因此,对于内核来说,它看起来就像应用程序直接连接到后端。主机可达的服务同时适应于 TCP 和 UDP,在 UDP 的情况下,已连接和未连接 UDP 都是支持的。

可以通过将守护进程配置里的 enable-host-reachable-services 设置为“true”来启用该特性,然后它会同时暴露 TCP 和 UDP 服务。协议公开可以通过守护进程标志 host-reachab -services-protos 来控制,该标志的默认值为“tcp、udp”,可以将其设置为其中之一,以便可以兼容更早的内核。

通用 CNI 链

CNI(容器网络接口)是 Kubernetes API,面向的是为 Kubernetes Pod 提供网络的插件。虽然 Cilium 也直接实现了这个 API,但是在某些情况下,我们希望使用另一个 CNI 插件进行联网,并使用 Cilium 提供附加功能,比如安全策略实施、多集群、加密或负载均衡。

Cilium 1.6:无KVstore操作、百分之百kube-proxy替换、基于套接字的负载均衡

对于所有这些的用户,我们将介绍通用 CNI 链。它是一个新的可插拔架构,构建在 CNI 的标准链特性之上。所有基于 veth 的 CNI 插件都可以与一个新的通用 veth 链接器集成,后者应该为任何使用 veth 设备连接 pod 的 CNI 插件提供开箱即用的支持。其他 CNI 插件可以通过编写一些 Go 代码来定义 Cilium 和 CNI 插件之间的接口来实现集成。

策略伸缩性

虽然 Cilium1.5 版本关注的是在具有大量节点的集群中运行 Cilium 的可伸缩性,但 1.6 版本改进了这些环境中节点本地操作的可伸缩性,特别是策略计算。

对于节点上运行的每个 Pod,每当集群管理员修改全局集群状态或策略时,Cilium 的策略计算就会发生,例如当一个新的 Pod 启动,或与本地策略相关的状态更改,诸如当 Cilium 得知策略批准了一个对应 FQDN 的新 IP 地址。当这些事件发生时,Cilium 确定每个本地端点选择了哪些规则,并计算出允许哪些对应的对象(例如,集群中的其他 Pod、FQDN、CIDR 等)。作为这个计算的一部分,Cilium 现在会缓存与适用的策略规则中的每个标签选择器相匹配的标签标识。以下列策略规则为例:

复制代码
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
description: "L3 ingress from env=qa"
metadata:
name: "l3-ingress-qa"
spec:
endpointSelector:
matchLabels:
app: foo
ingress:
- fromEndpoints:
- matchLabels:
env: qa

该规则适用于任何以 app=foo 为标签运行的 Pod。在这条规则中,我们可以看到,我们应该允许任何对应于 env = qa 的远程目标接入。给定一个集群,如果有多个标签为 env = qa 的端点(例如,{app=foo, env=qa}、{app=bar, env=qa}、{app=baz, env=qa}),那么一个唯一的数字标识符(称为安全标识)会分配给每个标签组,例如:

复制代码
51718 --> {app=foo, env=qa}
56949 --> {app=bar, env=qa}
61625 --> {app=baz, env=qa}

一旦分配的这些安全标识分发到本地节点,Cilium 会将每个本地使用的选择器与新标识相匹配,并缓存这个信息,这样以后就不需要再从头开始执行这个匹配操作。下面是这种缓存实际的样子:

复制代码
$ cilium policy selectors -o json
[
{
"identities": [
51718,
56949,
61625
],
"selector": "\u0026LabelSelector{MatchLabels:map[string]string{any.env: qa,k8s.io.kubernetes.pod.namespace: default,},MatchExpressions:[],}"
"users": 2
}
]

缓存这些信息现在有一个前期成本,然后,选择器的一个简单 map 查找就可以看到对应的目的地。这允许对大量规则的大量标识进行有效的策略计算。作为新的选择器缓存功能的一部分,当新的 Pod 在集群中的任何地方启动时,Cilium 现在可以更新 eBPF 策略状态,而不执行完整的策略重算。我们自豪地说,当运行数千个 Pod 的大型集群中配置了数千条规则时,Cilium 可以有效地计算 Pod 的策略。基准测试结果就是证明:

复制代码
PASS: resolve_test.go:225: PolicyTestSuite.BenchmarkRegenerateL3IngressPolicyRules 1000 2150217 ns/op
PASS: resolve_test.go:235: PolicyTestSuite.BenchmarkRegenerateL3EgressPolicyRules 1000 2209893 ns/op

这些基准测试针对大量标识测试了不同类型的规则。上述分析的输出表明,即使 1000 条规则的入口或出口和 3000 个标识,当选择器缓存前期已经执行完成时,三层策略计算大概需要 2 毫秒。

随着这些策略计算的变化,我们也改进了生成端点 datapath 的其他方面。与大量标识搅动的情况下——即集群中每秒钟创建几十个以上的 Pod——它通常花费不到 50 毫秒就可以更新每个端点的 datapath,实现白名单策略,以及如何应用于以集群中的新端点为目标的流量。

本地 AWS ENI Datapath

Cilium 1.6:无KVstore操作、百分之百kube-proxy替换、基于套接字的负载均衡

AWS ENI 分配器是特定于运行在 AWS 云中的 Cilium 部署的,它通过与 AWS EC2 API 通信,基于 AWS 弹性网络接口(ENI)的 IP 进行 IP 分配。

该架构确保只有一个操作符与 EC2 服务 API 通信,以避免大型集群中的速率限制问题。预分配水印允许维护多个 IP 地址,以便在集群中调度新的 Pod 时,不用与 EC2 API 交互就可以在任何时候在节点上使用它们。

新 IPAM 模式,加上特定的 AWS ENI datapath ,使 AWS ENI IP 以兼容多集群、策略执行和负载均衡子系统的直接路由模式连接 Pod。

用于透明加密的大规模数据平面

Cilium 提供了透明的加密支持,它利用 eBPF 使用 Linux 内核加密子系统编排加密。Cilium 1.6 版本扩展了这种支持,提供了一种新的子网模式,允许用户指定要应用透明加密的 IP 子网。Cilium 还将管理 FIB 表,自动调整 MTU,并暴露流级加密状态,以确保连接。

节点到节点加密

即使不涉及 Pod 通信,加密支持也扩展到节点到节点通信。这允许轻松加密所有网络流量,无论源端点是 Kubernetes Pod,还是目标端点是 Kubernetes Pod。

支持 Kubernetes 服务类型 External IP

继续讨论可伸缩性,我们添加了 External IP 服务类型支持。借助这种类型的服务,你可以将请求负载均衡到集群外的 IP。通过提供与服务对象匹配的 Endpoint 资源,该功能已经可用。通过添加 External IP 支持,可以节省 Endpoint 资源,从而减少在所有节点上传播所需的 Kubernetes 对象的数量。

复制代码
apiVersion: v1
kind: Service
metadata:
name: external-service
namespace: kube-system
spec:
ports:
- name: service-port
protocol: TCP
port: 2379
externalIPs:
- 10.0.0.1
- 10.0.0.2
- 10.0.0.3

Helm Charts

随着支持的 datapath 和控制平面的数量越来越多,维护预生成的 YAML 资源部署变得困难。作为其中的一部分,我们已经集成了 Helm ,现在提供几个可用于模板和部署的 Helm 图表。所有的指南都已转换为使用 Helm

示例:面向 GKE 的 Cilium 配置

复制代码
helm template cilium \
--namespace cilium \
--set nodeinit.enabled=true \
--set nodeinit.reconfigureKubelet=true \
--set nodeinit.removeCbrBridge=true \
--set global.cni.binPath=/home/kubernetes/bin \
> cilium.yaml
kubectl create namespace cilium
kubectl create -f cilium.yaml

1.6 版本重要特性

  • 策略
    • 新的选择器缓存可以大大加快基于标签的选择器与标识对象的关联
    • 如果所需的策略 map 大小超过默认的 16K,就可以配置
    • 在默认情况下,从注解创建的标签现在会被标识忽略
    • 为了减少开销,Allow all 现在是通过 datapath 表中的一条记录执行的
    • 新垃圾收集器可以移除 CiliumNetworkPolcies 状态部分中的过时节点
  • 无 Kvstore 操作
  • 所有需要的状态存储都是基于 CRD 的
  • 能够在没有配置键值存储的情况下启动 Cilium
  • Kubernetes
  • 简化 init 容器,移除 Cilium 状态
  • 不同的用户代理用于区分 Cilium 与 apiserver 的通信
  • 使用 NetworkUnavailable=false 修补 Patch Kubernetes Node 状态
  • 在启动时跳过 CRD 创建的新选项
  • 默认情况下,容器运行时集成现在是禁用的。只有当用户需要容器运行时标签和 Pod 标签来标识服务时,才需要启用它。
  • Kubernetes 1.15 支持
  • 去掉了访问集群 DNS 的操作符依赖
  • 支持 ExternalIP 服务
  • 通用 CNI 链
  • 新的 CNI 链插件架构
  • 内置面向 aws-cni-vpc、Calico、Portmap 和 Weave-Net 的链接支持
  • 通用 veth 链接插件,可以运行在任何基于 veth 的 CNI 插件之上
  • 资源消耗
    • 在有大量 Pod 的环境中,通过大量代码优化来减少内存分配和内存占用。
    • 新的最小状态响应,以减少资源消耗
    • 禁用 CiliumNetworkPolicy 状态的新选项,以减少资源大小
  • 基于套接字的负载均衡
    • 套接字级的高效 Kubernetes 服务负载均衡
    • 用于主机进程的 Kubernetes 服务负载均衡
  • Datapath
    • 新的基于 TPROXY 的 L7 重定向 datapath,不再需要重写任何包头来透明地重定向到代理。
    • 使用静态 IP 169.254.42.1 进行服务重定向环回 SNAT,而不是分配 IP
    • 新的 datapath 事件聚合现在默认为 maximum
    • 新的 datapath 模式(—enable-end -route),到本地端点的包通过每个端点的路由进行路由,而不是通过单独的 veth 对路由
    • 新的本地 AWS ENI datapath 模式
  • IPAM
    • 新的基于 CRD 的 IPAM 机制
    • AWS ENI 分配方法
  • NodePort 支持
    • NodePort 服务的原生 eBPF 实现(–enable-node-port)
  • 透明加密
    • 除 Pod 到 Pod 加密、Pod 到节点加密之外的节点到节点加密
    • 计算加密流量和非加密流量的新指标
  • CLI
    • 新的 cilium identity list --endpoints 命令
    • 新的 cilium policy selectors,可以列出策略选择器缓存中的内容
  • 文档
  • 所有指南已经重写,转为使用 Helm charts
  • 关于如何搭配使用 Cilium 和 Kata 容器的新指南
  • 关于在 microk8s 上简单部署的新指南
  • 新增疑难解答文档
  • Istio
  • 支持 1.2.4

入门

如果你是 Cilium 的新用户,请查阅入门指南

升级说明

和往常一样,按照升级指南升级你的Cilium 部署就可以了。欢迎随时在 Slack 上联系我们。

发布

查看英文原文: Cilium 1.6: KVstore-free operation, 100% kube-proxy replacement, Socket-based load-balancing, Generic CNI Chaining, Native AWS ENI support, …

收藏

评论

微博

用户头像
发表评论

注册/登录 InfoQ 发表评论