惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
博客园 - 叶小钗
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
B
Blog
GbyAI
GbyAI
Google DeepMind News
Google DeepMind News
博客园 - 【当耐特】
阮一峰的网络日志
阮一峰的网络日志
The Cloudflare Blog
N
Netflix TechBlog - Medium
P
Privacy International News Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
G
Google Developers Blog
Recorded Future
Recorded Future
The Hacker News
The Hacker News
D
Darknet – Hacking Tools, Hacker News & Cyber Security
B
Blog RSS Feed
G
GRAHAM CLULEY
A
Arctic Wolf
N
News | PayPal Newsroom
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The Register - Security
The Register - Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
V
Visual Studio Blog
Webroot Blog
Webroot Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 三生石上(FineUI控件)
aimingoo的专栏
aimingoo的专栏
P
Proofpoint News Feed
H
Heimdal Security Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Microsoft Azure Blog
Microsoft Azure Blog
小众软件
小众软件
M
MIT News - Artificial intelligence
V2EX - 技术
V2EX - 技术
Jina AI
Jina AI
TaoSecurity Blog
TaoSecurity Blog
NISL@THU
NISL@THU
云风的 BLOG
云风的 BLOG
爱范儿
爱范儿
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
WordPress大学
WordPress大学
V
V2EX
Cyberwarzone
Cyberwarzone
Stack Overflow Blog
Stack Overflow Blog
Cloudbric
Cloudbric
H
Hackread – Cybersecurity News, Data Breaches, AI and More

陈少文的网站

巨变与机遇的未来十年 Kubernetes 平台管理软件压力测试方案 使用镜像部署 Hexo 静态页面 终于等到你 - GitHub 镜像仓库服务(ghcr.io) 一起来学 Go --(6)Interface 一起来学 Go --(5)Goroutine 和 Channel 什么是函数式编程 如何在 Kubernetes 集群集成 Kata 柯里化与偏函数 使用 PyGithub 自动创建 Label 软件产品是团队能力的输出 Helm 2 、Helm 3 比较 IoT 变现 Kubernetes 中的 DNS 服务 国内的 Helm 镜像源 Harbor 使用自签证书支持 Https 访问 DevOps 工具链之 Prow 如何使用 kfctl 安装 Kubeflow VS Code 无法下载 Go 插件的工具包 工程师更应具有服务精神 你不知道的 Docker 使用技巧 使用 Docker 运行 Tensorflow 论中国 什么是左移 如何清空 Git 仓库全部历史记录 一禅小和尚 有风吹过厨房 时间的玫瑰 如何在 CentOS 安装 GPU 驱动 开发 Tips(19) 使用 Velero 备份 Kubernetes 集群 Kubernetes Cheat Sheet 开发 Tips(18) 如何构建一个 Java 工程 开发 Tips(17) KubeSpray 安装 Kubernetes 报错 ip in ansible_all_ipv4_addresses 基于 Kubernetes 和 Jenkins 搭建自动化测试系统 在 Kubernetes 上动态创建 Jenkins Slave 使用 Jenkins 进行服务拨测 开发 Tips(16) Kubernetes 签发 Ingress 证书及日常故障运维 Kubernetes 中 Deployment 的基本操作 Kubernetes 中的证书 如何使用 KubeBuilder 开发一个 Operator Kubernetes 1.6.0 安装问题汇总 镜像管理工具 -- Harbor 开发 Tips(15) Docker 如何拉取镜像 开发 Tips(14) 使用 Helm 安装 harbor 开发 Tips(13) 使用 S2I 构建云原生应用 在 Kubernetes 中使用 emptyDir、hostPath、localVolume 开发 Tips(12) 开发 Tips(11) 代码质量分析工具 SonarQube 使用 Kubeadm 安装 Kubernetes 集群 一起来学 Go --(4)常用函数 Kubernetes 中的 Ceph Kubernetes 之 Volumes Kubernetes 之 Labels、Selectors 开发 Tips(10) 开源正在重构商业模式 Kubernetes 之网络 Kubernetes 之 API 使用 Helm 和 Operator 快速部署 Prometheus Kubernetes 复杂有状态应用管理框架 -- Operator Kubernetes 的包管理器 -- Helm 一起来学 Go --(3)Go Modules 如何一步一步地优化博客方案 kubectl 实用指南 Kubernetes 中的基本概念 搭建远程 Kubernetes 开发环境 大公司和小公司的 ToB 思路 开发 Tips(9) Go 入门指南 一起来学 Go --(2)数据与逻辑结构 如何预防 Web 富文本中的 XSS 攻击 django-xss-cleaner 云工作时代 一起来学 Go --(1)背景与特点 SaaS 开发团队的不同阶段 你不知道的 Git 使用技巧 输出既服务 微服务设计 继续奔跑 开发 Tips(8) 从账户安全到二次验证 Django 性能之数据库查询优化 Django 性能之分库分表 敏捷开发之研发流程 打造一致性的团队 开发 Tips(7) Pytest 进阶学习之 Mock PaaS 部署之 buildpack Go 开发配置 领域输出才是 PaaS 的核心竞争力 Pytest 入门学习 开发 Tips(6) 如何使用 Jenkins、Docker、GitLab 搭建 Django 自动化部署流程
如何给 Kubernetes 应用设置 HPA 以及相关参数
微信公众号 · 2023-06-08 · via 陈少文的网站

1. 业务背景

当企业达到一定规模时,完全依赖于公有云基础设施,IT 成本会很高。

采购物理机器的成本可以摊薄到未来 3~5 年,之后机器并不会报废,而是会继续超期服役。私有云需要配比一定运维人员、购买专线带宽、机房费用等,IT 服务达到一定规模才能有效降低成本。

因此,中大型企业才会采用混合云的方案,将一部分应用部署在公有云,一部分应用部署在私有云。这也促成了托管云服务的发展,云厂商提供纳管私有机器的服务,以便能够快速对接自家的公有云服务。

如上图,私有云的机器用来满足基本的业务资源需求,公有云的机器用来补充业务高峰期的资源需求。而 Kubernetes 作为基础设施,当然也需要适配混合云的场景。

如上图,是一个混合云下的 Kubernetes 集群,私有机房(Master + 部分 Worker)+ 公有云(部分 Worker)。采用 Cluster Autoscaler 组件,对接公有云的弹性伸缩组服务,按需扩容或缩容 Worker 节点。

私有云的机器成本基本是固定的,而公有云的机器成本是按需计费的。在保障业务 SLA 的前提下,尽可能的减少公有云机器的使用,就是我最近在做的事情。

2. Request 为何如此重要

2.1 有利于调度均衡

Request 是资源的请求量。如上图,当调度器在给 Pod 选择一个合适的 Node 时,Node 上 Pod 的 Request 总和越少,打分越高,越容易被选中。

如果给每个 Pod 都设置了很低的 Request,你会发现调度非常不均,有些节点负载很高,但调度器还是会选择这些节点。

因为默认的调度器是静态调度,只看 Request 和 Node 上的 Request 总量,而不考虑实际使量。

Request 保障当前应用分配有足够资源,是用来保护自己的;Limit 是用来限制当前应用,是用来保护其他应用的。

你可以不设置 Limit,但一定要设置 Request。

2.2 HPA 依赖于 Request 值扩缩容

HPA 计算资源使用率的公式是:

currentUtilization = int32((metricsTotal * 100) / requestsTotal)

当使用率超过阈值时,HPA 会增加 Pod 副本数量。而这里的 Total 意味着,HPA 计算的是全部 Pod 的资源消耗。

这里有两个问题需要思考

  • 一个 Pod 可能有多个容器,一个容器使用率 90%,一个容器使用率 10%

在 Kubernetes v1.27 中有一个 Beta 特性 ContainerResource,可以指定容器作为计算对象,而忽略 Sidecar 等其他容器。

  • 多个 Pod 的资源消耗不一样,一个 Pod 使用率 90%,一个 Pod 使用率 10%

从研发侧,在开发应用时,就应该考虑多副本的均衡性,避免出现单个副本任务过重的情况。如果是长链接导致的不均衡,应该有再平衡机制。同时,还应该支持优雅重启,避免某个 Pod 负载过高被 Kill 之后,影响服务的 SLO。

从运维侧,可以适当降低 Limit 值,通过 Kill Pod 的方式,让请求分散到其他 Pod 上。

3. 如何设置 Request 和 Limit

3.1 设置 Request

前面说到 Request 是用来保护当前应用的,应该能够满足应用的基本使用。但 Request 太高,又会导致资源浪费。

  • CPU

如上图,Request 应该满足大部分时间段下 CPU 使用。

clamp_min(max(quantile_over_time(0.6, irate(container_cpu_usage_seconds_total{cluster="$cluster", namespace="$namespace", deployment="$deployment"}[2m])[1w:2m])), 0.5)

quantile_over_time 函数统计 60 百分位的使用需求,clamp_min 函数设置最小值为 0.5。

  • Memory

clamp_min(max(quantile_over_time(0.8, max(sum (container_memory_working_set_bytes{cluster="$cluster",image!="",name=~"^k8s_.*", namespace=~"$namespace", deployment=~"$deployment"}) by (pod)))[1w:2m]), 500 * 1024 * 1024)

内存是不可压缩资源,需要设置一个较高的 Request,以保障应用的正常运行。因此,设置的百分位比 CPU 要高。

3.2 设置 Limit

设置 Limit 是为了保护其他应用,避免当前应用的资源消耗过高时,影响其他应用的正常运行。

  • CPU

如下图,应用经常会碰到,CPU 使用率很低,但是 CPU 限流很严重,需要不断地提高 CPU Limit,而过高的 Limit 又会导致节点不稳定。同时,有些计费系统,是以 Limit 为基础进行计费的,过高的 Limit 会增加业务成本。

这种情况是因为,Prometheus 的采样频率是 15s,监控粒度太粗,采集不到实时的 CPU 使用情况。而如果采集 1s、100ms 监控数据时,很有可能是这样的。

使用率已经超过 400%。

在这种情况下,首先得升级内核版本至 5.14 及以上。5.14 内核新增的 CPU Burst 策略,通过累计算法,可以应对这种瞬时的 CPU 需求。

clamp_min(max(quantile_over_time(0.99, irate(container_cpu_usage_seconds_total{cluster="$cluster", namespace="$namespace", deployment="$deployment"}[2m])[1w:2m])) + quantile_over_time(0.99, (sum(irate(container_cpu_cfs_throttled_seconds_total{cluster="$cluster", name=~"^k8s_.*", namespace=~"$namespace", deployment=~"$deployment"}[10s])))[1w]), 0.52)

Limit = 99 百分位 CPU 使用核数 + 99 百分位 CPU 限流核数,同时不小于 0.52 核。

如果不升级内核,99 百分位 CPU 限流核数会很高,需要适当调整。

  • Memory

内存超了会被内核 OOM,你会发现内存的监控值始终不会超过 Limit。因此 Limit 应该超过 Request,但又不会触发 OOM 为宜。

quantile_over_time(0.995, container_memory_working_set_bytes{cluster="$cluster", namespace="$namespace", deployment="$deployment"}[1w:5m])

可以以 99.5 百分位的内存作为 Limit 起始值,逐步增加,直到不再触发 OOM。

4. 调试前的准备工作

了解了业务背景、相关的要点,在正式配置之前还需要进行一些预防措施,避免事故。

4.1 业务 SLO 告警

紧盯业务的关键 SLI 是能够大胆调试的关键,如果不能保障 SLA 一切的优化都是徒劳。

这里选取的是成功率、堆积量 A\B 作为核心指标。

成功率 > 99%

这一成功率要求,也影响了很多参数百分位的设置。

堆积量 A < 1000

堆积量 B < 1000

建议以最能体现使用方体验的指标作为 SLI,而不是以研发、运维的视角来定义 SLI。

4.2 集群应用 Pod 相关告警

由于调试 HPA 会涉及 Pod 的创建,为了避免扩容失败,需要对 Pod 的相关指标进行监控。

  • Pod 未就绪
sum by (cluster, app, pod)(kube_pod_status_ready{condition='false',exported_namespace='default'})
  • Pod 等待调度
sum by (cluster, app, pod)(kube_pod_status_phase{phase='Pending',exported_namespace='default'})
  • Pod OOM
sum by (namespace,pod) ((kube_pod_container_status_restarts_total{exported_namespace="default"} - kube_pod_container_status_restarts_total{exported_namespace="default"} offset 10m >= 1) and ignoring (reason) min_over_time(kube_pod_container_status_last_terminated_reason{exported_namespace="default",reason='OOMKilled'}[10m]) == 1)
  • Pod 限流
sum (rate (container_cpu_cfs_throttled_seconds_total{namespace="default",name=~"^k8s_.*"}[5m])) by (pod)

4.2 应用分级

线上的应用面对潮汐流量,在资源使用上总会有波动。一旦这种波动超过了节点的承载能力,就会导致节点驱逐应用,影响业务的稳定性。

而 Kubernetes 驱逐的策略是根据 QoS 类型来决定的,QoS 类型分为三种:

  • BestEffort:没有设置 Request 或 Limit,当节点资源不足时,优先驱逐
  • Burstable:Request 和 Limit 不相等,当节点资源不足时,可以驱逐
  • Guaranteed:Request 和 Limit 相等时,当节点资源不足时,尽量不驱逐

只用区分两种类型就可以,重点业务应用 Request、Limit 相等,非重点业务应用 Request、Limit 不相等。

4.3 开启 Pod 调度的亲和性

对于副本较多,普通应用可以开启软亲和性,尽量避免在同一个机器上调度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
affinity:
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        podAffinityTerm:
          labelSelector:
            matchExpressions:
              - key: app
                operator: In
                values:
                  - myapp
          topologyKey: kubernetes.io/hostname

对于副本较少,重点应用可以开启硬亲和性,强制分散到不同机器上。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
            - key: app
              operator: In
              values:
                - myapp
        topologyKey: kubernetes.io/hostname

对于副本较多的应用,最好使用软亲和性,避免 Cluster Autoscaler 不断扩容增加额外的成本。

5. 开始配置 HPA

5.1 新建 HPA 对象

如果有一个 Deployment 部署的应用 example,只需要创建一个 HPA 对象,指定 Deployment 的名称即可。

应用以下 yaml 对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: example
  namespace: default
spec:
  maxReplicas: 5
  minReplicas: 2
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: example
  targetCPUUtilizationPercentage: 60

或者执行命令

1
kubectl autoscale deployment example --cpu-percent=60 --min=2 --max=5

创建 HPA 对象,它的目标是 example Deployment,最大副本数 5,最小副本数 2,目标 CPU 使用率 60%。当 Pod 平均 CPU 使用率超过 60% 时,HPA 会增加副本数量。

5.2 HPA 参数

由于我主要使用的是 HPA v1 cpu 指标,这里只介绍 v1 的几个主要参数。

  • 副本数量下限

生产环境最少需要 2 个 Pod,避免单点故障。重点应用,最少需要 3 个 Pod。

  • 副本数量上限

Pod 的数量会随着负载的上升,不断增加,按需使用,因此上限应该尽量大一些。如果平时副本 2-3 个,就给上限为 5。如果平时副本数量为 10-20 个,就给上限为 30。

上限最好设置得比平时多一些,同时设置为 5 的倍数为宜,方便识别扩容数量达到 HPA 上限之后,继续增加。

  • CPU 使用率

CPU 使用率设置得越低,扩容时就越灵敏;设置得越高,资源的利用率就越低。通常可以根据应用的负载情况,设置为 50%-70%。

设置的策略是,先设置为 50%,等稳定之后,再逐步增加 5%。

6. 总结

本篇主要是记录了在给 60 个应用设置 HPA 的之后,遇到的问题和一些思考。主要内容如下:

  • Request 在集群调度、HPA 中发挥了很重要的作用
  • 通过 PromeQL 设置应用的 Request 和 Limit
  • 调试 HPA 之前应该配置一些告警,保障服务的 SLA
  • 配置 HPA 及相关参数

给不同应用设置 Request、Limit、HPA 副本上限、HAP 副本下限、HPA CPU 使用率,是一件繁琐的事情,建议先绘制一个 Grafana 计算面板,可以实时计算调试。类似下图:

最终效果如下:

资源的请求量会随着使用量波动,而请求量又直接影响到公有云的机器使用数量。如下图是线上 Usage/ Request 的情况,也是我不断优化 HPA 相关参数的指引指标。

从云厂控制台的账单来看,HPA 相较于没有弹性,成本降低了 50%;在之前的实践中,CronHPA 相较于没有弹性,成本降低了 30%。

另外可以考虑的是,将长期占用的弹性公有云机器转移到私有云,或者采用公有云包年的结算方式,因为云厂的按需付费弹性主机价格比较高。

7. 参考