Kubernetes性能、扩展性优化及网络模型解析
1. 区域管理与特殊服务介绍
在进行集群规划时,区域管理至关重要。云平台按区域和可用区组织,部分服务和机器配置仅在特定区域可用,云配额也在区域层面管理。区域内数据传输的性能和成本远低于跨区域传输,通常区域内传输是免费的。因此,规划集群时需谨慎考虑地理分布策略。若要跨多个区域运行集群,在冗余性、可用性、性能和成本方面可能需做出艰难决策。
有两个特殊的服务值得关注:
-
Hyper.sh
:这是一个支持容器的托管服务。用户只需启动容器,该服务会负责硬件分配,容器能在数秒内启动,无需像启动新虚拟机那样等待数分钟。Hypernetes 是基于 Hyper.sh 的 Kubernetes,它完全消除了扩展节点的需求,因为对用户而言只有容器(或 Pod)。
-
AWS Fargate
:AWS 最近推出的 Fargate 同样抽象了底层实例,允许用户在云端调度容器。结合 EKS,它可能成为部署 Kubernetes 最流行的方式。
2. Kubernetes的极限挑战
Kubernetes 团队不断挑战其性能极限。在实际应用中,存在拥有 3000 个节点的 Kubernetes 集群。例如,CERN 的 OpenStack 团队实现了每秒 200 万次请求;Mirantis 在其扩展实验室中,在 500 台物理服务器上部署了 5000 个 Kubernetes 节点(在虚拟机中);OpenAI 将其机器学习 Kubernetes 集群扩展到 2500 个节点,并获得了宝贵经验,如关注日志代理的查询负载,将事件存储在单独的 etcd 集群中。
3. Kubernetes性能与扩展性的提升
Kubernetes 团队在 1.6 版本中着重提升了性能和扩展性。从 1.2 版本支持最多 1000 个节点,到 1.3 版本翻倍至 2000 个节点,再到 1.6 版本惊人地提升到每个集群 5000 个节点。以下是实现这些显著改进的关键技术:
-
API 服务器的读取缓存
:Kubernetes 将系统状态存储在 etcd 中,etcd 可靠但速度并非极快(etcd3 为支持更大规模的 Kubernetes 集群进行了重大改进)。Kubernetes 各组件基于状态快照运行,不依赖实时更新,因此可以用一定的延迟换取吞吐量。API 服务器引入了内存读取缓存,由 etcd 监视器更新,这显著减轻了 etcd 的负载,提高了 API 服务器的整体吞吐量。
-
Pod 生命周期事件生成器(PLEG)
:集群节点数量的增加对水平扩展性很关键,但 Pod 密度同样重要。Pod 密度指 Kubelet 在单个节点上能有效管理的 Pod 数量。在 Kubernetes 1.1 中,官方测试的每个节点支持 30 个 Pod,实际运行 40 个 Pod 会导致 Kubelet 开销过大。到 1.2 版本,每个节点支持的 Pod 数量跃升至 100 个。此前 Kubelet 会为每个 Pod 单独启动协程不断轮询容器运行时,这给容器运行时带来很大压力,特别是在性能高峰时会出现可靠性问题。PLEG 通过列出所有 Pod 和容器的状态并与之前状态比较,确定哪些 Pod 需要同步,从而使 Kubelet 和容器运行时的 CPU 使用率降低了四倍,还缩短了轮询周期,提高了响应速度。
-
使用协议缓冲区序列化 API 对象
:API 服务器原本使用 JSON 作为序列化格式,在大规模 Kubernetes 集群中,频繁的 JSON 解析和组合成本很高。在 1.3 版本中,Kubernetes 团队引入了高效的协议缓冲区序列化格式,虽然 JSON 格式仍然保留,但 Kubernetes 组件之间的内部通信都使用协议缓冲区序列化格式。
-
从 etcd2 切换到 etcd3
:在 1.6 版本中,Kubernetes 从 etcd2 切换到 etcd3,这是一个重大转变。由于 etcd2 的局限性,特别是与监视实现相关的问题,Kubernetes 无法扩展到 5000 个节点。etcd3 的改进包括:使用 gRPC 而非 REST(etcd2 有 REST API,etcd3 有 gRPC API 并通过 gRPC 网关提供 REST API),gRPC 基于的 http/2 协议可使用单个 TCP 连接处理多个请求和响应流;使用租约而非 TTL(etcd2 按每个键的生存时间 TTL 过期键,etcd3 使用带 TTL 的租约,多个键可共享同一键,显著减少了心跳流量);etcd3 的监视实现利用 gRPC 双向流,通过单个 TCP 连接发送多个事件,将内存占用至少降低了一个数量级;Kubernetes 开始使用 protobuf 存储所有状态,消除了大量 JSON 序列化开销。
-
其他优化
:Kubernetes 团队还进行了许多其他优化,如优化调度器(使调度吞吐量提高 5 - 10 倍);将所有控制器切换到使用共享通知器的新推荐设计,减少了控制器管理器的资源消耗;优化 API 服务器中的单个操作(转换、深度复制、补丁);减少 API 服务器中的内存分配(显著影响 API 调用的延迟)。
4. Kubernetes性能与扩展性的衡量
为了提升性能和扩展性,需要明确改进目标并采用合理的衡量方法,同时确保不违反基本属性和保证。
-
Kubernetes服务水平目标(SLOs)
:Kubernetes 规定 API 调用的响应时间为 1 秒(即 1000 毫秒),实际上大多数时候响应速度比这个目标快一个数量级。
-
衡量 API 响应性
:API 有多个不同的端点,没有简单的 API 响应性数值,每个调用都需单独测量。由于系统的复杂性、分布式特性以及网络问题,测量结果可能有很大波动。一种可靠的方法是将 API 测量按端点分开,长时间进行大量测试并查看百分位数。同时,使用足够的硬件来管理大量对象也很重要,Kubernetes 团队在测试中为主节点使用了具有 120GB 内存的 32 核虚拟机。
-
衡量端到端 Pod 启动时间
:大型动态集群的一个重要性能指标是端到端 Pod 启动时间。Kubernetes 不断创建、销毁和移动 Pod,可以说调度 Pod 是其主要功能。在不同版本的 Kubernetes 中,Pod 启动时间不断优化,如在 1000 节点的集群中,Kubernetes 1.2 的 99% 端到端 Pod 启动时间小于 3 秒,Kubernetes 1.3 在 2000 节点集群中的表现略好于 1000 节点集群,而 1.6 版本在更大集群中表现更优。
5. 大规模测试 Kubernetes
拥有数千个节点的集群成本高昂,即使是有谷歌等行业巨头支持的 Kubernetes 项目,也需要找到经济合理的测试方法。Kubernetes 团队每个版本至少在真实集群上进行一次全面测试,以收集实际性能和扩展性数据。同时,为了以更轻量级和低成本的方式试验潜在改进并检测回归问题,引入了 Kubemark 工具。
Kubemark 工具介绍 :Kubemark 是一个运行模拟节点(称为空心节点)的 Kubernetes 集群,用于对大规模(空心)集群进行轻量级基准测试。一些真实节点上的 Kubernetes 组件,如 kubelet 被空心 kubelet 取代,空心 kubelet 模拟了真实 kubelet 的许多功能,但实际上并不启动容器或挂载卷。另一个重要的空心组件是空心代理,它模拟了 Kubeproxy 组件。
设置 Kubemark 集群步骤
:
1. 创建一个常规的 Kubernetes 集群,用于运行 N 个空心节点。
2. 创建一个专用虚拟机,启动 Kubemark 集群的所有主组件。
3. 在基础 Kubernetes 集群上调度 N 个空心节点 Pod,这些空心节点配置为与运行在专用虚拟机上的 Kubemark API 服务器通信。
4. 通过在基础集群上调度附加组件 Pod 并配置它们与 Kubemark API 服务器通信来创建附加组件 Pod。
Kubemark 集群与真实集群的比较 :Kubemark 集群的性能与真实集群相当相似。对于 Pod 启动的端到端延迟,差异可以忽略不计;对于 API 响应性,差异较大,但通常小于两倍。而且趋势完全相同,即真实集群中的改进或退化在 Kubemark 中会以类似的百分比变化体现。
6. Kubernetes网络模型
Kubernetes 作为一个编排平台,管理运行在不同机器(物理或虚拟)上的容器/Pod,需要明确的网络模型。
-
网络模型基础
:Kubernetes 网络模型基于扁平地址空间,集群中的所有 Pod 可以直接相互访问,每个 Pod 都有自己的 IP 地址,无需配置 NAT。同一 Pod 内的容器共享该 Pod 的 IP 地址,可通过 localhost 进行通信。这种模型虽然有一定的倾向性,但一旦设置好,能大大简化开发人员和管理员的工作,便于将传统网络应用迁移到 Kubernetes 上。
-
Pod 内通信
:运行中的 Pod 总是调度到一个(物理或虚拟)节点上,因此 Pod 内的所有容器在同一节点上运行,可以通过本地文件系统、任何 IPC 机制或使用 localhost 和知名端口进行通信。不同 Pod 之间不存在端口冲突的风险,因为每个 Pod 都有自己的 IP 地址。但如果要将端口暴露给主机,需要注意 Pod 与节点的亲和性,可以使用 DaemonSet 和 Pod 反亲和性等机制来处理。
-
Pod 间通信
:Kubernetes 中的 Pod 被分配一个网络可见的 IP 地址(不是节点私有地址),Pod 可以直接通信,无需网络地址转换、隧道、代理或其他混淆层。可以使用知名端口号进行无配置的通信方案,Pod 的内部 IP 地址与其他 Pod 看到的外部 IP 地址相同(在集群网络内,不暴露到外部世界),这意味着标准的命名和发现机制(如 DNS)可以直接使用。
以下是 Kubernetes 性能提升关键技术的对比表格:
| 优化技术 | 优化前情况 | 优化后效果 |
| ---- | ---- | ---- |
| API 服务器读取缓存 | 由 etcd 监视器更新所有快照,etcd 负载大 | 引入内存读取缓存,减轻 etcd 负载,提高 API 服务器吞吐量 |
| Pod 生命周期事件生成器(PLEG) | Kubelet 为每个 Pod 单独轮询容器运行时,压力大 | CPU 使用率降低四倍,缩短轮询周期,提高响应速度 |
| 协议缓冲区序列化 | 使用 JSON 序列化,解析和组合成本高 | 组件内部通信使用协议缓冲区,降低成本 |
| etcd3 替换 etcd2 | etcd2 存在局限性,限制集群扩展 | 支持更大规模集群,减少内存占用和序列化开销 |
mermaid 流程图展示设置 Kubemark 集群的流程:
graph LR
A[创建常规 Kubernetes 集群] --> B[创建专用虚拟机启动主组件]
B --> C[调度空心节点 Pod 到基础集群]
C --> D[创建附加组件 Pod 并配置通信]
通过以上对 Kubernetes 性能、扩展性优化及网络模型的分析,我们可以更好地理解和运用 Kubernetes 来构建高效、稳定的集群。
Kubernetes性能、扩展性优化及网络模型解析
7. 标准接口与网络解决方案
Kubernetes 支持多种标准接口,这些接口为其网络功能的实现提供了基础,同时也有多种网络解决方案来满足不同的需求。
-
EXEC 接口
:EXEC 接口允许在容器内部执行命令。通过该接口,可以在运行的容器中执行特定的操作,例如查看容器内部的文件系统、运行诊断命令等。这对于调试和管理容器非常有用。
-
Kubenet 接口
:Kubenet 是 Kubernetes 自带的一种简单网络插件。它为每个节点创建一个网桥,并为每个 Pod 分配一个 IP 地址。Kubenet 适用于简单的测试环境或对网络功能要求不高的场景。
-
CNI(容器网络接口)
:CNI 是 Kubernetes 中广泛使用的网络接口标准。它允许不同的网络插件实现统一的接口,从而为 Kubernetes 提供灵活的网络解决方案。常见的 CNI 插件有 Calico、Flannel 等。Calico 提供了强大的网络策略功能,可以对 Pod 之间的通信进行细粒度的控制;Flannel 则以简单易用著称,能够快速搭建起 Kubernetes 集群的网络。
以下是不同网络接口的特点对比表格:
| 网络接口 | 特点 | 适用场景 |
| ---- | ---- | ---- |
| EXEC 接口 | 允许在容器内执行命令 | 调试和管理容器 |
| Kubenet 接口 | 简单,自带网桥,为 Pod 分配 IP | 简单测试环境 |
| CNI 接口 | 灵活,支持多种插件 | 复杂生产环境 |
8. 网络策略与负载均衡
- 网络策略 :网络策略是 Kubernetes 中用于控制 Pod 之间通信的规则。通过定义网络策略,可以限制 Pod 可以访问的 IP 地址、端口和协议,从而增强集群的安全性。例如,可以创建一个网络策略,只允许特定的 Pod 访问某个服务的端口。
-
负载均衡
:在 Kubernetes 中,负载均衡用于将流量均匀地分配到多个 Pod 上,以提高服务的可用性和性能。Kubernetes 支持多种负载均衡方式,包括 NodePort、LoadBalancer 和 Ingress。
- NodePort:NodePort 是一种简单的负载均衡方式,它在每个节点上开放一个端口,将外部流量转发到对应的 Pod 上。
- LoadBalancer:LoadBalancer 是基于云提供商的负载均衡器,它可以自动创建一个外部负载均衡器,并将流量转发到 Kubernetes 集群中的 Pod 上。
- Ingress:Ingress 是一种更高级的负载均衡方式,它可以根据域名和路径将流量转发到不同的服务上。Ingress 需要配合 Ingress Controller 使用,常见的 Ingress Controller 有 Nginx Ingress Controller 和 Traefik。
以下是不同负载均衡方式的对比表格:
| 负载均衡方式 | 特点 | 适用场景 |
| ---- | ---- | ---- |
| NodePort | 简单,在节点上开放端口 | 测试环境或内部服务 |
| LoadBalancer | 基于云提供商,自动创建负载均衡器 | 生产环境,需要外部访问 |
| Ingress | 高级,根据域名和路径转发流量 | 多服务共享一个 IP 地址 |
9. 编写自定义 CNI 插件
如果现有的 CNI 插件无法满足特定的需求,可以编写自定义 CNI 插件。编写自定义 CNI 插件的一般步骤如下:
1.
了解 CNI 规范
:首先需要深入了解 CNI 规范,包括插件的输入输出格式、环境变量等。
2.
选择编程语言
:可以选择合适的编程语言来实现插件,常见的有 Go、Python 等。
3.
实现插件逻辑
:根据需求实现插件的逻辑,例如创建网络接口、配置 IP 地址等。
4.
测试和部署
:对插件进行测试,确保其功能正常,然后将其部署到 Kubernetes 集群中。
以下是一个简单的自定义 CNI 插件的实现思路流程图:
graph LR
A[了解 CNI 规范] --> B[选择编程语言]
B --> C[实现插件逻辑]
C --> D[测试插件]
D --> E[部署到集群]
10. 总结
通过对 Kubernetes 性能、扩展性优化及网络模型的全面分析,我们了解到 Kubernetes 在不断发展和优化的过程中,通过一系列的技术手段提升了其性能和扩展性。在性能优化方面,包括 API 服务器的读取缓存、Pod 生命周期事件生成器、协议缓冲区序列化、etcd3 的使用以及其他各种优化措施,使得 Kubernetes 能够支持更大规模的集群。在网络模型方面,Kubernetes 基于扁平地址空间的设计,简化了 Pod 之间的通信,同时支持多种标准接口和网络解决方案,以及灵活的网络策略和负载均衡方式。此外,还可以通过编写自定义 CNI 插件来满足特定的需求。
在实际应用中,我们可以根据具体的场景和需求,选择合适的优化技术和网络方案,以构建高效、稳定的 Kubernetes 集群。同时,对于性能和扩展性的衡量也非常重要,通过合理的测试和监控手段,可以及时发现问题并进行优化。总之,深入理解 Kubernetes 的性能、扩展性和网络模型,将有助于我们更好地利用这一强大的容器编排平台。
超级会员免费看
808

被折叠的 条评论
为什么被折叠?



