前阵子接手一套生产环境的K8s集群,版本被供应商锁定在1.27.5,而且客户要求控制面必须高可用,不能出现apiserver单点。我本来想直接kubeadm init一把梭,但想想生产环境还是要能追溯、能复现,最后定下来用kubeasz来部署。kubeasz圈子里用得不少,好处是部署过程全部走ansible playbook,组件是纯二进制方式安装,出问题可以直接翻role源码定位,比kubeadm黑盒初始化要踏实很多。这篇就把整个部署过程、高可用设计、关键配置以及后来做故障演练踩到的坑一起记录下来,给打算用kubeasz搭高可用集群的朋友一份能直接抄作业的参考。
先说结论:kubeasz部署高可用K8s 1.27.5这条路是走得通的,组件版本和生产环境兼容性都没问题,而且因为从二进制入手,后续改参数、换插件、升级版本都很透明。文章内容会覆盖从环境规划、kubeasz初始化、hosts编排、全局变量修改,到分步执行playbook、验证VIP和故障切换,最后附上常见问题排查实录,属于那种“跟着操作基本上能跑通”的实战记录。
1. 为什么选kubeasz来搭高可用K8s集群
1.1 高可用到底在解决什么问题
一个K8s集群里最容易出现单点故障的位置就是控制面,尤其是kube-apiserver。apiserver是整个集群的入口,kubectl、kubelet、controller-manager、scheduler都要连它,一旦它挂了,整个集群的控制面也就瘫痪了,业务Pod虽然还在跑,但你已经无法调度、无法伸缩、无法扩容,跟“不可用”没什么区别。
高可用集群的本质就是用多个Master节点把控制面组件冗余起来。apiserver本身是无状态的,可以多副本部署,前面挂一个负载均衡器,把请求分散到多个apiserver实例上。而controller-manager和scheduler是有状态的组件,它们通过选主机制保证同一时刻只有一个实例在工作,另一个实例是备用状态,主节点挂了以后,备用节点自动抢占领导者角色。etcd则是通过Raft共识算法组成一个奇数节点集群,少数派节点挂了不影响整个集群的读写,这也是为什么etcd节点数必须是3、5、7这样的奇数。
有了这层冗余设计,就算某个Master节点整机宕机,集群的控制面和数据面依旧能对外正常提供服务。当然这只是高可用的“一半”,网络层、存储层的冗余也得跟上,但控制面高可用是第一步,也是最基础的一步。
1.2 kubeasz的工作原理
kubeasz本质上是把K8s各组件的部署过程固化成了ansible playbook,借助ansible在目标节点上准备环境、分发二进制、生成证书、配置systemd服务,最终把整个集群拉起。它和kubeadm最大的区别在于:kubeadm帮你把很多细节藏起来了,生成的组件配置由kubeadm统一管理,你要改底层参数比较麻烦;而kubeasz是把kube-apiserver、etcd、kubelet这些组件的启动参数、配置文件全部以明文形式暴露给运维,调优、排错、二次定制都很方便。
我之前在生产环境踩过kubeadm的坑,比如证书轮换、apiserver的extraArgs合并策略不透明,出了问题想查原因经常要扒一堆生成的配置。而kubeasz的playbook里每个role做了什么、配置怎么写的一清二楚,比如etcd的证书在哪生成、apiserver的启动参数有哪些、kubelet的cgroup驱动怎么设置,都可以直接打开文件看。这种“透明感”对生产运维来说太重要了。
而且kubeasz支持离线部署。下载好release包和二进制文件后,内网节点只要连通性没问题就能把集群拉起来,这在没有外网的生产环境里是刚需。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与前置条件
2.1 节点规划和硬件建议
我这次是搭建一个3主2从的高可用集群,规格如下:
| 角色 | 节点数 | 配置建议 | 说明 |
|---|---|---|---|
| Master节点 | 3 | 4核8G,系统盘100G | 承载apiserver、controller-manager、scheduler、etcd |
| Worker节点 | 2 | 8核16G,系统盘200G | 承载业务Pod,按业务量弹性伸缩 |
| 部署机 | 1 | 2核4G即可 | 安装ansible,运行kubeasz控制所有部署动作 |
| VIP虚拟IP | 1 | 不占节点 | 比如192.168.100.100,绑定在Master节点上 |
etcd节点数必须保持奇数。这里我把etcd直接复用Master节点的资源,3个Master也就是3个etcd节点,组成一个3节点etcd集群,这是最常见也最省机器的组合。如果你的机器足够多,也可以把etcd单独拆出来部署在独立的机器上,在hosts文件里单独划分一组etcd节点就行,但那样网络延迟和管理成本都会上升,除非机器很充裕,否则其实没必要。
操作系统方面,我建议用Ubuntu 22.04 LTS或Rocky Linux 9这类内核版本较新的发行版。K8s 1.27版本对内核要求已经提高,CentOS 7的内核是3.10,跑1.27的kubelet会遇到各种兼容性问题,别在这上面浪费时间,直接上22.04或9.x。所有节点的hostname不能重复,这是K8s集群的硬要求,规划好以后再动手。
2.2 系统初始化和SSH免密
部署机需要能免密SSH登录到所有Master和Worker节点。这是ansible工作的前提,用普通的密码登录方式也能跑,但遇到分批执行playbook时会很痛苦,每次都要输入密码。我建议直接用ssh-keygen生成密钥对,然后用ssh-copy-id把公钥分发到所有目标节点。
执行完免密登录之后,顺便在每个节点上做几件基础的事情,避免后边部署时卡住:把hostname写进/etc/hosts、关闭swap、开启必要的内核模块和sysctl参数。kubeasz的prepare阶段会自动完成一部分初始化工作,但hosts和swap处理最好自己先确认一遍,尤其是swap,容器和kubelet对swap极度敏感,系统残留swap分区会导致kubelet启动后报错,prepare虽然也会处理,但提前处理掉更省心。
内核参数里最常用的是把net.bridge.bridge-nf-call-iptables设为1,否则iptables规则对K8s的Service通信不生效。还有net.ipv4.ip_forward必须为1,Pod之间的转发依赖它。这些参数kubeasz会自动写入,但你手工确认一遍没坏处。
2.3 部署机安装Ansible和kubeasz
kubeasz对部署机的要求不高,一台普通的Linux虚拟机就够了,需要先安装ansible和git。ansible版本建议2.11以上,太老版本的ansible在解析某些playbook语法时会出问题。装完后从kubeasz官方仓库拉取release版本,解压到指定的工作目录。
展开以后,kubeasz的目录结构大致是这样的:
bash复制/etc/ansible/
├── ansible.cfg # ansible主配置
├── hosts # 集群拓扑编排文件,核心中的核心
├── playbooks/
│ ├── 01.prepare.yml
│ ├── 02.etcd.yml
│ ├── 03.container.yml
│ ├── 04.kube-master.yml
│ ├── 05.kube-node.yml
│ ├── 06.network.yml
│ ├── 90.setup.yml
│ └── ...
├── roles/ # ansible role,每个组件的部署逻辑都在这里
└── group_vars/
├── all.yml # 全局变量配置
├── kube-master.yml
└── kube-node.yml
首次使用kubeasz时它会生成默认配置和默认的hosts文件,如果你发现节点没有全部在默认hosts里,需要自己按实际规划修改。配置文件的注释都写得很详细,但正因为信息量大,新手反而容易看晕,我下面直接把需要改的关键点梳理出来。
3. 高可用相关的核心配置
3.1 hosts文件里的集群拓扑
hosts文件是kubeasz的inventory文件,它决定了集群的拓扑结构。高可用模式下最重要的就是[kube-master]组里至少要有3个节点,etcd组里也对应写上同样的3个节点。系统会按照节点分组来编排部署流程。
下面是我这次用的hosts核心片段:
ini复制[deploy]
192.168.100.1
[kube-master]
192.168.100.11
192.168.100.12
192.168.100.13
[kube-node]
192.168.100.21
192.168.100.22
[etcd]
192.168.100.11
192.168.100.12
192.168.100.13
[kube-master:vars]
NODE_NAMES="master01 master02 master03"
[kube-node:vars]
NODE_NAMES="worker01 worker02"
[etcd:vars]
ETCD_NAMES="etcd01 etcd02 etcd03"
这几个节点的顺序和你后面在all.yml里配置的数组一定要对上,因为kubeasz会按顺序把etcd节点地址写入etcd集群配置。如果顺序对不上,很可能导致节点加入etcd集群时报错。NODE_NAMES和ETCD_NAMES的值要和各个节点的hostname保持一致,这块我踩过坑,后面在常见问题里细说。
3.2 all.yml里的关键参数
all.yml是整个集群配置的核心,几乎所有的全局变量都在这里。打开文件后,我习惯先看几个最关键的参数:
yaml复制# 版本号
K8S_VER: "1.27.5"
ETCD_VER: "3.5.7"
# 容器运行时,1.27必须用containerd
CONTAINER_RUNTIME: "containerd"
# 高可用VIP和负载均衡端口
K8S_VIP: "192.168.100.100"
K8S_VIP_PORT: "16443"
EX_APISERVER_URL: "https://192.168.100.100:16443"
EX_APISERVER_PORT: "16443"
第一个要确认的是K8S_VER版本,确保你下载的kubeasz版本确实支持1.27.5,查看kubeasz的release说明就能看到它的支持范围。如果版本过老,直接拉最新release。第二个是CONTAINER_RUNTIME,1.27的K8s已经不再内置dockershim,无法直接使用Docker作为容器运行时,所以必须使用containerd或者CRI-O,这里我直接用containerd,兼容性和性能都更稳。
最核心的是高可用相关的VIP配置。K8S_VIP是虚拟IP,我规划成192.168.100.100,这个IP不绑定在任何物理网卡上,由keepalived动态绑定到Master节点上。K8S_VIP_PORT是负载均衡的监听端口,我用了16443,避免和apiserver默认的6443端口混淆。所有控制面组件和kubelet连接apiserver的时候,都通过EX_APISERVER_URL这个地址,也就是VIP加16443端口。这样即使某个Master节点挂了,组件也能通过VIP自动切换到另一个健康的Master上,不会断连。
3.3 证书、命名空间和网络插件
kubeasz会在部署过程中自动生成集群证书,无需手工签发,但有个地方一定要留心:生成的apiserver证书默认会包含各个Master节点的IP和hostname,但不会自动包含你的VIP。如果你设置的VIP没有提前告诉kubeasz,后面通过VIP访问apiserver时就会报证书校验错误。
解决方式是在all.yml里把VIP加到证书的SAN列表里。不同版本的kubeasz变量名有些不同,有的版本是在all.yml里有类似EX_APISERVER_URL的变量,有的版本还需要设置证书生成时的IP清单。我在3.5.x版本上实测,只要EX_APISERVER_URL配置正确,kubeasz在生成apiserver证书时会把这里面的域名和IP自动加入SAN。如果你用的是其他版本,建议打开roles/kube-master里面的证书生成模板确认一下,防止踩坑。
网络插件方面,kubeasz支持flannel、calico、cilium等主流插件。我这次选了calico,因为1.27版本下calico兼容性最好,BGP模式和IPIP模式切换方便,网络策略功能也完整。cilium虽然eBPF性能更好,但部署后对内核版本要求更高,出问题的概率相对更大。如果你追求性能极致且团队对cilium有经验,可以选cilium;否则calico是稳妥之选。
4. 分步执行部署
4.1 prepare阶段:初始化节点
一切配置就绪后,我习惯用分步执行的方式跑playbook,这样每一步都能单独确认结果,出错了也容易定位。第一次部署可以先用90.setup.yml一键执行,但如果中途出错,重跑整个流程会浪费时间,所以我还是按下面顺序分步执行:
bash复制cd /etc/ansible
ansible-playbook playbooks/01.prepare.yml
prepare阶段主要做这几件事:配置主机名和hosts解析、关闭swap、加载内核模块、设置sysctl参数、安装基础依赖包,以及把K8s相关的二进制文件分发到目标节点。这一步跑完,各节点基本就具备安装组件的前提条件了。
执行前建议先看一眼ansible.cfg里默认的remote_user是否正确,我这次使用root用户直接登录所有节点,所以不用额外配置。如果用了普通用户再sudo,需要确认sudo免密已经配好,不然ansible会在执行sudo命令时卡住,日志显示permission denied,那场面很尴尬,排查也要花很长时间。
prepare跑完后,在任意Master节点上执行ip addr可以看到还没有VIP地址,这是正常的,VIP要等keepalived部署完成后才会出现。
4.2 etcd集群初始化
接下来初始化etcd,命令如下:
bash复制ansible-playbook playbooks/02.etcd.yml
etcd在K8s集群里的地位相当于整个状态的“数据库”,所有K8s对象、配置、Pod状态都存储在etcd中。它挂了集群就完了。这里需要注意etcd节点之间必须能通过内网互通,而且延迟要低,不建议跨机房组一个etcd集群。
kubeasz会为每个etcd节点生成独立的证书,并且自动初始化集群成员。执行完以后,在任意一个Master节点上检查etcd集群状态:
bash复制ETCDCTL_API=3 etcdctl --cacert=/etc/etcd/ssl/ca.pem \
--cert=/etc/etcd/ssl/etcd.pem \
--key=/etc/etcd/ssl/etcd-key.pem \
--endpoints=https://192.168.100.11:2379,https://192.168.100.12:2379,https://192.168.100.13:2379 \
endpoint health
正常情况下三个endpoint都会返回healthy。如果某一个节点不健康,不要急着继续往下部署,先把etcd集群恢复成全员健康再走。这一步如果带病继续,后边K8s组件会各种报错,排查起来非常痛苦。
4.3 控制平面和负载均衡组件
这一步是整个高可用集群的核心,也是kubeasz里最复杂的环节:
bash复制ansible-playbook playbooks/04.kube-master.yml
这个playbook会在所有Master节点上部署kube-apiserver、kube-controller-manager、kube-scheduler,以及高可用的负载均衡组件haproxy和keepalived。
kube-apiserver是集群入口,多个apiserver实例之间没有主从关系,任何一个Master上的apiserver都能提供服务。haproxy的作用就是把请求转发到各个apiserver上,keepalived则负责维护VIP。当某个Master节点宕机时,keepalived会把VIP漂移到另一个健康节点上,同时haproxy会探活apiserver端口,如果某个apiserver不可用,会自动把它从后端列表摘除。
这里有个细节值得注意:controller-manager和scheduler是通过选主机制保证高可用的,两个组件都设置了--leader-elect=true,这意味着同一时刻只有一个实例在工作,但它们的端口和健康检查都是独立的。选主时它们连接的是etcd里的某个lease资源,如果etcd出问题,选主也会受影响。
部署完成后,在所有Master节点上检查VIP是否绑定在某一台机器上:
bash复制ip addr | grep 192.168.100.100
正常情况下VIP会绑定在其中一台Master的网卡上。如果跑哪一步卡住,可以先看playbook的输出,一般都会明确指出是哪一台节点执行失败。
4.4 worker节点与容器运行时
bash复制ansible-playbook playbooks/05.kube-node.yml
worker节点上需要安装和配置containerd、kubelet和kube-proxy。kubeasz会把kubelet和kube-proxy的二进制分发到所有node节点,然后注册systemd服务并启动。
kubelet连接apiserver的地址就是前面配置的EX_APISERVER_URL,也就是VIP加16443端口,这样即使某台Master节点挂了,kubelet也不会因为apiserver地址变化而断连。kubelet注册到集群的时候是使用节点自己的IP和证书,证书由kubeasz统一签发,所以这里没有额外的证书交换动作。
我在这里遇到过一个坑:containerd的pause镜像如果没有提前准备好,kubelet启动Pod时会一直卡在拉取pause镜像的环节。内网环境一定要提前把pause镜像推到本地镜像仓库,并配置好containerd的registry镜像源,否则Pod创建不出来,kubectl get nodes是Ready了,但调度上去的Pod全是ContainerCreating,排查起来很费劲。
这一步跑完,看看节点是否都已经注册:
bash复制kubectl get nodes
正常情况下所有Master和Worker都应该是Ready状态,如果节点还在NotReady,不要急,等一会,kubelet和kube-proxy是后台服务,有点启动时间,等半分钟再刷一下。如果超过两分钟还是NotReady,再用journalctl -u kubelet -f看日志。
4.5 网络插件和集群自检
节点Ready后,接着部署网络插件:
bash复制ansible-playbook playbooks/06.network.yml
这一步会安装calico,并配置Pod网络和Service网络的CIDR。网络插件生效后,Pod之间、Pod到Service的通信才真正通畅。执行完这个playbook,用以下命令做一次集群自检:
bash复制kubectl get pods -A
这时候你会看到kube-system里的calico-kube-controllers、calico-node这些Pod从ContainerCreating变成Running,花一两分钟等Pod交替是正常的,别一看没Running就去折腾,容易误判。等所有Pod稳定Running后,再用下面的命令做一次端到端检查:
bash复制kubectl run test-pod --image=busybox --rm -it --restart=Never -- sh
进去后随便ping一下某个Pod的IP,能通说明网络没问题。如果网络插件一直起不来,优先看calico-node的日志,以及各节点防火墙有没有放行BGP协议端口,这是calico最常挂的地方。
5. 高可用验证与故障演练
5.1 验证VIP和负载均衡是否生效
集群部署完不代表高可用就真的有效,一定要做验证。第一步验证负载均衡和VIP。通过VIP去访问apiserver,看是否能正常拿到集群信息:
bash复制curl -k https://192.168.100.100:16443/version
这应该返回一组JSON,里面包含gitVersion之类的字段,说明VIP加haproxy的链路是通的。然后再看haproxy的转发效果,可以把某个Master节点上的haproxy停掉再试,但这一步我放到后面的故障演练里一起做。
还可以用kubectl客户端地址配置直接用VIP去控制集群:
bash复制kubectl --server=https://192.168.100.100:16443 get nodes
如果这一步返回正常,说明通过VIP的整个请求链路没问题,负载均衡是真的在工作。建议把这个命令记下来,以后集群出问题的时候,用VIP访问apiserver能通,就说明问题在业务侧,不通则问题在控制面,排查范围一下子就缩小了。
5.2 模拟Master节点故障
这个环节是整个高可用验证的“重头戏”。我挑了一个持有VIP的Master节点,比如192.168.100.11,在上面执行reboot,模拟整机宕机场景。然后观察集群行为和VIP漂移情况。
重启之前,先截获一下当前的VIP所在位置:
bash复制ip addr show dev eth0 | grep 192.168.100.100
记录它当前在哪台机器上。然后去那台机器执行reboot。节点重启后,等一分钟左右,再在剩下的Master节点上检查VIP是否漂移过来:
bash复制ip addr show dev eth0 | grep 192.168.100.100
正常情况下,VIP会迁移到另外一台Master上。再用VIP访问一次apiserver,确认集群控制面没有中断。此时再执行:
bash复制kubectl get nodes
你会发现刚刚重启的Master节点还在NotReady状态,等它重新上线后会自己恢复成Ready,etcd节点也会自动重新加入集群。整个过程不会影响其他Master和Worker上的Pod运行,这就是控制面高可用生效的直接证据。
这个演练在真实生产环境里很有价值,别小看它。很多集群“看起来高可用”,真宕机了才发现VIP根本没漂移,或者haproxy后端没摘除,一开生产故障就抓瞎。提前做几次演练,把流程固化下来,是对系统最基本的尊重。
5.3 监控告警和日常巡检
集群跑起来之后,监控体系也得跟上,不然哪天节点资源耗尽了你都不知道。kubeasz在playbooks目录下有专门的addon,可以一键把prometheus、grafana、node-exporter这些监控组件装上去:
bash复制ansible-playbook playbooks/07.addon.yml -e addon=prometheus
安装完成后,node-exporter会自动部署到每个节点上采集节点指标,prometheus负责存储和告警,grafana提供可视化面板。部署完后在grafana里导入k8s的官方dashboard,就可以看到节点CPU、内存、磁盘、网络等核心指标了。
磁盘告警是配置监控时最容易忽略但又最重要的告警之一。磁盘满以后,etcd会拒绝写入,容器镜像无法拉取,业务直接不可用。可以在prometheus里面加一条磁盘告警规则,比如当节点根分区使用率超过85%持续5分钟时触发告警。规则大概长这样:
yaml复制groups:
- name: node-alerts
rules:
- alert: NodeDiskUsageHigh
expr: (1 - (node_filesystem_avail_bytes{fstype=~"ext4|xfs"} / node_filesystem_size_bytes{fstype=~"ext4|xfs"})) > 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "Node {{ $labels.instance }} disk usage is high"
当然这不是kubeasz的默认配置,需要你在prometheus的rules里手动补上。我当时就在这个上面吃了亏,某台Worker节点日志分区被写满后,整个节点上的容器全部卡死,日志服务和监控也没提前告警,还好及时清理了,不然后果很严重。
5.4 临时提权与只读用户配置
在日常运维中,给不同角色分配不同的K8s权限是一件很常见的事情。比如运维侧希望看到一个只读账号,能查看所有namespace、Pod、Service,但不能做修改和删除。K8s内置了view这个ClusterRole,配合RBAC就能快速实现。
如果只是临时创建一个只读用户,我一般直接用ServiceAccount的方式,几行命令搞定:
bash复制kubectl create sa readonly
kubectl create clusterrolebinding readonly-binding --clusterrole=view --serviceaccount=default:readonly
创建一个token或kubeconfig交给需要的人,他就可以用只读权限去查看集群资源了。需要注意普通用户拿到的ServiceAccount token在1.24版本之后不会自动创建对应的Secret,需要手动创建一个绑定到该ServiceAccount的Secret,然后从中获取token,这个细节网上很多人没提,实际配置的时候会卡一下。
6. 常见问题与排错实录
6.1 证书相关:SAN缺失导致访问失败
高可用集群最经典的证书问题就是apiserver证书里没有包含VIP地址。你通过VIP访问apiserver时会报证书校验错误,但直接用各Master节点的IP访问却正常,这就是SAN没加VIP导致的。
解决办法是回到all.yml里确认EX_APISERVER_URL配置正确,然后重新执行kubeasz的证书生成和分发playbook。可以找到roles/kube-master中关于证书生成的role,单独跑一遍,把所有节点上的apiserver证书和kubeconfig都更新掉。证书更新后需要重启各Master节点的kube-apiserver、kube-controller-manager、kube-scheduler服务,让新证书生效。如果不想整组重装,这就是最快的方式。
6.2 keepalived脑裂与VIP漂移异常
keepalived运行两个Master节点时,最容易出现的问题是脑裂,也就是两个节点同时占据VIP,导致网络通信异常。多数情况是VRRP组配置不一致,或者防火墙拦掉了VRRP协议的组播包。在云环境或者一些开启了安全组的环境中,组播包经常被拦截,需要配置单播方式。
kubeasz的keepalived role默认使用组播方式,如果你的环境里出现VIP漂移不稳定、两个节点同时有VIP的情况,需要手动修改keepalived配置,把组播改成单播。配置方式是添加unicast_peer,把对端Master节点的IP写上。
检查keepalived状态时,优先级也很重要。kubeasz默认把每个Master节点的keepalived优先级配置为一样的,这种情况下VIP会随机优先绑定在一个节点上,我觉得在生产环境中这样反而不好,可以手动调整一下优先级,让某个确定节点成为优先持有VIP的节点,这样故障时行为更可预期。
6.3 etcd集群异常:成员不一致问题
etcd集群异常时,最常见的就是成员节点状态不一致。某个节点宕机时间长了,etcd日志里会出现大量的leader选举超时和follow消息异常。这时可以通过etcdctl member list查看集群成员,先判断是哪一台节点有问题,再决定是重启还是重新加入。
如果只是单台节点短暂不可用,重启它是没问题的。如果这台节点已经长时间无法恢复,或者磁盘损坏,最好把它从etcd集群中移除,然后重新以空数据加入。千万不要直接在数据不一致的情况下强行启动etcd,这会导致集群出现数据分裂,恢复起来会麻烦得多。
好在kubeasz把etcd的系统服务管理都接入了systemd,重启服务只需要systemctl restart etcd,如果确认需要重加节点,用etcdctl member remove把旧节点摘掉,再重新初始化etcd服务即可。
6.4 CPU Throttling等容器运行问题
集群跑起来之后,业务方反馈接口偶发延迟抖动,排查了很久,发现是CPU Throttling导致的。这个现象在K8s中很常见,尤其在给Pod设置resources.limits.cpu时,如果只把limits设成了100m这种比较小的数值,业务一旦出现CPU密集的瞬时操作,cgroup的CPU配额就会被限制,导致容器被throttle,接口延迟飙升。
排查方式是用kubectl top pod观察Pod的CPU使用率和限制值的比值,如果使用率经常顶到limits,那大概率就是CPU Throttling在作怪。更进一步可以看容器内的container_cpu_cfs_throttled_periods_total这个指标,它是cgroup层面的CPU被限制的统计次数,数值持续增长就坐实了问题。
解决思路有两个方向,一个是在不改变limits的情况下把request调低,让调度器把Pod分散到更多节点上,降低单节点上的CPU争抢;另一个是适当调高limits,或者干脆不给CPU limits,只给requests。生产环境到底怎么设置,要结合业务实际情况来判断,没有一刀切的标准,但至少你要知道这类延迟抖动和CPU Throttling强相关,而不是一开始就去查应用代码。
另外,如果集群里跑的是批量任务,GPU、内存、磁盘这类资源问题也会频繁出现,建议多关注node-exporter的指标,把资源水位告警先配起来。
这套集群从部署到现在跑了有一段时间,整体稳定性出乎我的意料,中间除了我主动做的故障演练,没有出现过真正的意外宕机。kubeasz在1.27.5这个版本上表现很稳,整个部署链路走完一遍之后,我对集群的每个组件都有了更深的理解。如果你也想在生产环境里用kubeasz搭高可用集群,我个人的建议是:不要一键部署完就扔一边,至少把playbook里的每个role都翻一遍,理解它做了什么,这样后面无论是调优还是排查问题都会省很多力气。最后再说一个小技巧,把常用的部署命令和VIP验证命令整理成一个checklist文档放在部署机上,每次变更节点或者升级组件后,按着清单过一遍,能避免很多低级失误。
