Kubernetes(以下简称 K8s)已成为大中型企业调度和管理服务的事实标准。引入 K8s 的初衷之一是为了节省成本,通过减少云平台计算实例的数量,实现用更少的实例承载相同的业务负载,从而降低云计算成本。然而,随着业务的发展,为了保障业务的稳定性,我们通常会为一个服务设置多个副本,以避免在故障转移或扩容时,容器无法及时启动导致服务中断。这意味着,我们需要为同时运行的多个副本支付额外的费用。此外,由于 K8s 服务资源设置不合理,再加上多副本的影响,进一步加剧了费用的增加与资源的浪费。
在实际操作中,通常会在一个或多个 K8s 集群中部署大量的服务。这些服务从归属上来看,可能属于不同的业务线、产品或团队;从服务类型上看,可能涵盖 CICD、大数据、大模型、监控、Web 服务等多种形式。K8s 本身并未提供基于上述维度的成本拆分能力。同时,K8s 集群是一个高度动态的运行环境,节点数量、Pod 所在的节点、可用区(Zone)或区域(Region)都可能随着时间的推移而发生变化。这为 K8s 的成本分析带来了巨大的挑战。为了实现有效的成本管理和优化,我们需要更细致且动态的成本追踪和分析能力,以应对不断变化的集群环境和复杂的多租户架构。
在这种背景下,Kubecost 进入了我们的视野。Kubecost 是一款商业化工具,专为 K8s 集群的成本监控和优化而设计。该工具最早于 2019 年推出,旨在解决组织在使用 K8s 时面临的成本管理和资源优化问题。以下是它的主要功能介绍:
实时成本监控: 可以根据实际资源使用情况实时监控成本。
成本分配: 支持按 Namespace、Controller、服务以及标签等多种维度分配成本。
成本优化: 提供优化建议,例如调整资源请求和限制、节点池管理等。
多集群支持: 支持同时监控多个 K8s 集群。
云平台支持: 可以与 AWS、GCP、Alibaba 等多云环境集成,以提供更精准的价格。
报告和分析: 提供详细的成本报告和历史数据分析。
Kubecost的功能及使用方式
1.1 Overview Dashboard
Overview 页面提供了 K8s 集群整体的费用概览及资源利用效率。具体来说,该仪表板展示了集群的总花费、过去 7 天的费用趋势以及集群资源的利用率等关键指标。通过这些指标,用户可以对集群的费用状况有一个初步的了解,从而更好地进行成本管理和资源优化。
图 1 Overview Dashboard 页面
1.2 Allocations
Kubecost Allocations 允许用户快速查看所有原生 K8s 概念(例如命名空间、K8s 标签和服务)的费用分配情况。该功能还支持将成本分配到团队、产品/项目、部门或环境等组织层面,并将这些分配保存为报告,方便后续查看和分析。用户可以根据时间、K8s 概念以及自定义过滤条件对数据进行筛选、聚合和统计。默认情况下,费用按命名空间进行聚合,点击具体名称后可以进一步下钻到 Controller 层级,以查看各类资源的详细花费情况。
图 2 Allocations Dashboard 页面
1.3 Assets
Kubecost Assets 显示了 K8s 集群中各类资产(如节点、磁盘和其他资源)的成本细分情况。通过此功能,用户可以识别随时间变化的支出因素,并对数据进行审计。该视图还支持选择性地显示集群外的资产,例如按服务、标签等维度进行分类。用户可以点击不同的资源,查看其详细规格和成本信息。
图 3 Assets Dashboard 页面
1.4 Efficiency
Efficiency 是 Kubecost 2.3 中引入的 K8s 效率仪表板,为用户提供了强大的效率分析工具,帮助用户识别其 K8s 环境中的集群、节点和工作负载的浪费支出。通过这些效率报告,用户可以全面了解所有 K8s 集群中的资源浪费情况。Kubecost 定义了三种空闲类型,用于评估基础设施的效率:
按类型的空闲:提供集群的浪费支出的高层次视图,按工作负载空闲和基础设施空闲进行细分。通过此视图,用户可以看到集群的总空闲、基础设施空闲、工作负载空闲以及集群效率。
按工作负载的资源空闲:针对所有 K8s 工作负载,提供工作负载空闲和工作负载效率的资源特定细分视图(如 CPU、RAM 等)。工作负载空闲被定义为已请求但未使用的资源成本。
按集群的资源空闲:针对所有集群和节点,提供基础设施空闲的资源特定细分视图(如 CPU、RAM 等)。
Efficiency 面板 和接下来要介绍的资源优化建议是我们降低成本的利器。通过此面板,我们可以直观地看到所采取的降本措施的效果,并为后续的降本工作提供指导。通过不断重复“数据洞察 -> 采取行动”这一循环,我们可以持续并稳定地确保资源利用率保持在合理水平。
图 4 Efficiency Dashboard 页面
1.5 Network Monitoring
Network Monitoring 是一个用于监控网络成本的页面,帮助用户可视化与网络相关的支出。它支持按 Pod、命名空间或集群的连接分析网络成本,这对于大型团队或需要深入了解网络成本的复杂环境中的团队非常有用。
在网络监控页面上,网络由可拖动的图标组成,这些图标代表网络成本的来源及其与 Pod、命名空间或集群的连接。绿色边框的图标表示网络成本,蓝色边框的图标则表示推动支出的 K8s 对象。连接图标的箭头颜色区分内部支出和外部支出。
图 5 Network Monitoring 页面
1.6 Saving
Saving 页面提供了多项功能,帮助用户更有效地使用资源并评估浪费的开支。在页面中央,用户可以看到每月可预估的节省金额。该金额是通过页面右上角的下拉菜单,基于启用的所有节省功能,指定集群配置文件计算得出的。
图 6 Saving 页面
Saving 页面包含多个面板,提供了不同的建议,帮助用户减少 K8s 的支出。以下是 Kubecost 支持的节省建议类型:
调整集群节点的大小:Kubecost 根据历史资源使用情况,提出集群节点数量和规格的优化建议。
调整容器请求的大小:Kubecost 会根据容器过去一段时间的资源使用情况,提供资源请求的建议。通过调整过度预留的资源,用户可以显著降低成本。
解决被遗弃的工作负载:Kubecost 能检测在可配置的持续时间内未发送或接收有意义流量的工作负载。用户可根据收集的数据,自主处理这些没有负载的任务。
管理未声明的存储卷:Kubecost 能检测未被任何 Pod 使用的存储卷。用户可以考虑删除这些卷,或将它们移动到更便宜的存储层。
管理本地磁盘:Kubecost 能检测到使用率较低的本地磁盘,并提供调整大小的建议及预计的成本节省。
Kubecost前端:运行 Nginx 并处理到 Kubecost 后端和 Prometheus/Grafana 的路由;
Kubecost后端(成本模型):提供对 Prometheus 的读写成本分配计算和指标;
Prometheus 服务器:用于存储成本和健康指标的时间序列数据;
cAdvisor(Container Advisor) 收集正在运行的容器的资源使用情况和性能特征。它是一个运行守护进程,用于收集、汇总、处理和导出有关正在运行的容器的信息。
图 7 Kubecost 的架构
3.1 整体资源费用整体的数据收集计算的流程大致如下:
3.1.1 数据收集
资源(CPU、RAM、CPU 等)用量数据通过 Prometheus 收集。Prometheus 会定期抓取各个节点和容器的资源使用情况,并将这些数据存储为时间序列。
3.1.2 数据处理
资源利用数据被处理和聚合。具体步骤如下:
查询数据:通过 Prometheus 查询资源使用数据。常见的查询包括:源请求量(Request)、资源使用量(Usage)等。
聚合数据:将查询到的时间序列数据进行聚合,计算出每个时间段内的平均使用量和请求量。
处理缺失数据:在某些情况下,可能会有缺失的数据点。需要对这些缺失的数据进行处理,确保计算的准确性。
3.1.3 计算各个资源的花费数据
获取定价信息:从配置文件或云提供商 API 获取资源的定价信息。定价信息可能包括按小时计费的资源价格。
计算使用成本:根据聚合后的资源使用数据和定价信息,计算每个时间段内的资源使用成本。具体公式如下:
计算总成本:将所有时间段的资源使用成本累加,得到总的资源成本。
3.2 下面就资源定价及使用成本的计算进行进一步的解释
3.2.1 资源定价:
一些公有云平台会提供不同资源的单位时间的定价,Kubecost 会直接使用该数据用于成本的计算。但是,部分公有云提供商只提供实例规格的整体定价,而未单独提供 CPU、GPU 和 RAM 的单位时间定价。这时,Kubecost 会根据预设值,将实例的规格将总成本分摊到每个 CPU、GPU 和 RAM 单位上,这些预设值通常根据云提供商的典型定价来设置,以下为计算逻辑的描述:
计算示例总基础成本:
假设一个实例包含 v个 CPU、m GB 内存和g个 GPU,总实例成本为 $X/小时。基础总成本使用默认值计算为:
假设实例有 2 个 CPU、4 GB 内存和 1 个 GPU,使用上述默认值,基础总成本为:
基础总成本=(0.05×2)+(0.01×4)+(1.00×1)=0.10+0.04+1.00=1.14/小时
归一化资源价格:
接下来,Kubecost 会将基础总成本归一化,使其与实际实例的总成本 $X 匹配。归一化的比例保持资源价格之间的相对比例不变。
归一化后的价格为:
计算示例:
假设实例的总成本为 $2.00/小时,基础总成本为 $1.14/小时,归一化因子为:
归一化后的价格为:
经过 Kubecost 的归一化后, 2 个 CPU、4 GiB 内存和 1 个 GPU,每个小时费用为 2$ 的实例,其每个 CPU/小时价格为 $0.0877,每GB RAM/小时价格为 $0.0175,每个 GPU/小时价格为 $1.754。经过这样的转换后,每个资源的定价就独立了,方便了后续每个资源的费用的单独计算。
3.2.2 资源费用的整体计算逻辑:
通过执行预设的 Prometheus 查询语句,可以获取某一特定资源在指定时间窗口内的单位时间平均利用率。然后,将每个时间单位的利用率与相应的成本相乘,并对所有结果进行汇总,即可得出该资源在该时间窗口内的总成本。
其中 是通过 Prometheus 查询得到的特定资源类型单位时间内的平均利用率。
3.2.3 CPU 成本计算:
通过查询 Prometheus 的 container_cpu_usage_seconds_total 和 kube_pod_container_resource_requests 指标来分别获取 CPU 的实际利用率和 CPU Request 的数据。
对应的查询语句:
# CPU Useage
`avg(rate(container_cpu_usage_seconds_total{container!="", container_name!="POD", container!="POD", %s}[%s])) by (container_name, container, pod_name, pod, namespace, instance, %s)`
# CPU Request
`avg(avg_over_time(kube_pod_container_resource_requests{resource="cpu", unit="core", container!="", container!="POD", node!="", %s}[%s])) by (container, pod, namespace, node, %s)`
3.2.4 GPU 成本计算:
通过查询 Prometheus 的 DCGM_FI_DEV_GPU_UTIL 和 kube_pod_container_resource_requests 指标来分别获取 GPU 的实际利用率和 GPU Request 的数据。
对应的查询语句:
# GPU Useage
`avg(avg_over_time(DCGM_FI_DEV_GPU_UTIL{container!=""}[%s])) by (container, pod, namespace, %s)`
# GPU Request
`avg(avg_over_time(kube_pod_container_resource_requests{resource="nvidia_com_gpu", container!="",container!="POD", node!="", %s}[%s])) by (container, pod, namespace, node, %s)`
3.2.5 内存成本计算:
通过查询 Prometheus 的 container_memory_working_set_bytes 和 kube_pod_container_resource_requests 指标来分别获取 RAM 的实际利用和 RAM Request 的数据。
对应的查询语句:
# RAM Useage
`avg(avg_over_time(container_memory_working_set_bytes{container!="", container_name!="POD", container!="POD", %s}[%s])) by (container_name, container, pod_name, pod, namespace, instance, %s)`
# RAM Request
`avg(avg_over_time(kube_pod_container_resource_requests{resource="memory", unit="byte", container!="", container!="POD", node!="", %s}[%s])) by (container, pod, namespace, node, %s)`
3.2.6 网络成本计算:
Kubecost 网络费用分为三类:
区域内网络流量费用(Zone Network Egress Cost)
区域间网络流量费用(Region Network Egress Cost)
互联网网络流量费用(Internet Network Egress Cost)
通过查询 Prometheus 的 kubecost_network_zone_egress_cost、kubecost_network_region_egress_cost 和 kubecost_network_internet_egress_cost 指标来分别获取。
对应的查询语句:
`avg(avg_over_time(kubecost_network_zone_egress_cost{%s}[%s])) by (%s)`
`avg(avg_over_time(kubecost_network_region_egress_cost{%s}[%s])) by (%s)`
`avg(avg_over_time(kubecost_network_internet_egress_cost{%s}[%s])) by (%s)`
关于 Kubecost 的展望
Kubecost 使用平均数作为利用率的计算基础,尽管在学术上对平均数这个指标有很多批评,认为其不能准确反映数据的分布,进而影响统计的准确性。笔者认为这并不那么重要,有很多因素会影响成本的计算,难以精确计算,特别是对于 K8s 这种复杂且动态变化的系统而言。比精确成本更重要的是, Kubecost 提供了一个基准的计算模型,使得我们可以在同一基准下获取、优化和回顾 K8s 的成本。基于此,我们需要做的就是让资源的利用水平维持在一个合理的区间。随着业务的发展,服务器资源的需求是自然上升的,我们需要确保的不产生额外的浪费。
根据我们的业务实际情况,我们的资源利用目标为:CPU:35%-50%,内存:50%-60%, 存储:20%-35%。利用率目标很大程度上取决于资源使用的可预测性和分布高利用率对核心产品、业务指标的影响等。虽然资源利用率过低是浪费的,但过高的利用率会导致延迟增加、可靠性问题和其他负面行为。因此,选择一个合适的利用水平是非常有必要的。
感谢领创集团运维团队对 Kubecost 项目的大力支持,特别感谢郭雨明和郭迎波同学在服务部署和调试过程中的帮助!
关于领创集团