尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【Kubernetes从入门到精通】第14篇:ReplicaSet——Deployment背后的“影子武士“

【Kubernetes从入门到精通】第14篇:ReplicaSet——Deployment背后的“影子武士“ 上一篇【第13篇】Deployment——无状态应用的“自动档“下一篇【第15篇】Service——K8s的服务发现和负载均衡摘要上一篇文章咱们把Deployment夸成了一朵花——版本管理、滚动更新、一键回滚听着就跟应用管家似的。但你有没有想过一个问题这些花活背后谁在埋头苦干保证Pod的数量答案是ReplicaSet。Deployment发号施令ReplicaSet负责执行——“我需要3个v1.25的Pod”、“现在开始逐步替换成v1.27的Pod”……这些命令最终都是ReplicaSet在落地。ReplicaSet的身世比Deployment早得多——它的前身叫ReplicationControllerRC是K8s社区最早的控制器之一。本文从RC到RS的进化讲起拆解RS的三大看家本事然后聊聊一个让人纠结的问题什么时候跳过Deployment直接撸RS一、从RC到RS——一段进化史K8s最早的副本控制器叫ReplicationControllerRC诞生于K8s 1.0时代。后来社区发现RC的Label选择器太死板——只能用等值匹配appnginx、envprod不支持集合操作。于是ReplicaSetRS在K8s 1.2诞生了基本取代了RC。【RC → RS 进化史】 ┌─────────────────────────────────────────┐ │ ReplicationController (RC) —— 老前辈 │ │ ───────────────────────────────── │ │ • 选择器只支持等值匹配 │ │ selector: │ │ app: nginx ✅ 支持 │ │ env: production ✅ 支持 │ │ │ │ • 不支持集合表达式 │ │ matchExpressions: ❌ 不支持 │ │ - {key: env, operator: In, │ │ values: [prod,staging]} │ └────────────────┬────────────────────────┘ │ K8s 1.2 大升级 ▼ ┌─────────────────────────────────────────┐ │ ReplicaSet (RS) —— 新一代接班人 │ │ ───────────────────────────────── │ │ • 等值匹配 ✅ 完全兼容RC │ │ matchLabels: │ │ app: nginx │ │ │ │ • 集合表达式 ✅ 新增能力 │ │ matchExpressions: │ │ - {key: env, operator: In, │ │ values: [prod,staging]} │ │ - {key: version, operator: Exists} │ └─────────────────────────────────────────┘要点虽然ReplicaSet已经基本取代了RC但RC还没被废弃——你在kubectl里还是能看到rc这个缩写。不过除非你在维护老古董集群否则永远选RS。记不住区别也没关系记住一句话RS能做RC的所有事还支持集合选择器没有理由再用RC。二、ReplicaSet的三大职责——“说几就是几一个不能少”ReplicaSet所有代码的核心逻辑就围绕三件事【ReplicaSet 的三大职责】 ┌──────────────────────────────────────────────────────┐ │ ReplicaSet │ │ │ │ 职责1确保Pod数量 │ │ ┌───────────────────────────────────────────────┐ │ │ │ 我要3个Pod现在只有2个立刻补1个 │ │ │ │ 我要3个Pod跑了5个立刻杀2个 │ │ │ │ │ │ │ │ 期望3个 ─────► ReplicaSet ─────► 实际3个 │ │ │ │ 不够补多了杀 │ │ │ └───────────────────────────────────────────────┘ │ │ │ │ 职责2故障替换 │ │ ┌───────────────────────────────────────────────┐ │ │ │ Pod-1 挂了Node崩了/OOM/被误删 │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ ReplicaSet 立刻检测到少了一个 │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ 在健康的Node上创建一个新Pod替代它 │ │ │ └───────────────────────────────────────────────┘ │ │ │ │ 职责3弹性伸缩 │ │ ┌───────────────────────────────────────────────┐ │ │ │ kubectl scale --replicas5 │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ 现在要5个了给我多起2个Pod │ │ │ │ kubectl scale --replicas1 │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ 现在只要1个了杀4个Pod │ │ │ └───────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────┘2.1 职责一确保Pod数量——调谐循环在行动ReplicaSet的核心是个调谐循环Reconciliation Loop——它每隔一段时间就检查现在跑着的Pod数量跟我期望的一样吗不一样就调到一样。# 来做个实验手动删掉一个RS管理的Podkubectl get pods-lappmyapp# NAME READY STATUS# myapp-rs-abc12 1/1 Running# myapp-rs-def34 1/1 Running# myapp-rs-ghi56 1/1 Running# 手动删一个Podkubectl delete pod myapp-rs-abc12# 立刻再查——新的Pod已经起来了kubectl get pods-lappmyapp# NAME READY STATUS AGE# myapp-rs-def34 1/1 Running 5m# myapp-rs-ghi56 1/1 Running 5m# myapp-rs-xyz78 1/1 Running 3s ← 自动补上了要点ReplicaSet不是靠Pod挂了发通知来补Pod的——它是主动轮询。每隔一会儿就数一遍自己管着的Pod少了就建多了就杀。这种不断检查不断纠正的模式叫控制循环Control Loop是K8s所有控制器的核心工作机制。2.2 职责二故障替换——“无缝抢修队”当Pod所在的Node宕机了kubelet停止向API Server上报心跳K8s会把该Node标记为NotReady。ReplicaSet发现Node上的Pod状态异常后会在其他健康Node上重新创建Pod【故障替换过程】 Node-1 (健康) Node-2 (宕机) Node-3 (健康) ┌───────────┐ ┌───────────┐ ┌───────────┐ │ Pod-A │ │ Pod-B │ │ Pod-C │ │ Running │ │ 挂了 │ │ Running │ └───────────┘ └───────────┘ └───────────┘ │ │ Node-2心跳超时 ▼ RS检测到Pod-B状态异常实际可用Pod 2期望 3 │ ▼ ┌───────────┐ │ ┌───────────┐ │ Pod-A │ │ │ Pod-C │ │ Running │ │ Running │ │ │ │ Pod-D │ │ │ │ Running │ └───────────┘ └───────────┘ RS在健康的Node-3上创建Pod-D替代挂掉的Pod-B2.3 职责三弹性伸缩——“说几就是几”# HPA自动伸缩器最终也是通过调RS的replicas来实现的# 大促来了HPA说RS给老子扩到20个kubectl scale replicaset myapp-rs--replicas20# 大促结束HPA说RS留3个就行kubectl scale replicaset myapp-rs--replicas3三、ReplicaSet YAML——完整拆解apiVersion:apps/v1kind:ReplicaSetmetadata:name:myapp-rslabels:app:myappversion:v1.25spec:replicas:3# ★ 期望副本数selector:# ★ 选择器——怎么找到自己的PodmatchLabels:# 等值匹配AND逻辑app:myappversion:v1.25matchExpressions:# 集合匹配更灵活-{key:environment,operator:In,values:[production,staging]}-{key:tier,operator:NotIn,values:[backend]}-{key:monitoring,operator:Exists}# 只要有monitoring标签就行template:# ★ Pod模板metadata:labels:app:myappversion:v1.25environment:productionmonitoring:enabledspec:containers:-name:appimage:myapp:v1.25ports:-containerPort:80803.1 selector.matchLabels vs matchExpressions——什么时候用哪个这是RS以及所有用Label Selector的控制器里最让人纠结的地方。一张表讲清楚场景用 matchLabels用 matchExpressions“选所有 appnginx 的Pod”✅app: nginx也能但没必要“选 envprod 或 envstaging 的Pod”❌ 做不到AND逻辑✅{key: env, operator: In, values: [prod, staging]}“排除 tierbackend 的Pod”❌ 做不到✅{key: tier, operator: NotIn, values: [backend]}“只要有 monitoring 标签的Pod都算”❌ 做不到✅{key: monitoring, operator: Exists}“选 appnginx 且 versionv1.25”✅ 写两行也能但两行expression是AND关系# matchLabels 和 matchExpressions 的关系是 AND# 下面这个配置的意思是# 必须满足 matchLabels 里的所有条件appmyapp AND versionv1.25# AND# 必须满足 matchExpressions 里的所有条件environment在[prod,staging] AND tier不在[backend]selector:matchLabels:app:myappversion:v1.25matchExpressions:-{key:environment,operator:In,values:[production,staging]}-{key:tier,operator:NotIn,values:[backend]}要点如果你只是想精准选Pod“appnginx,envprod”matchLabels就够用了。matchExpressions的真正价值在于模糊匹配——“所有带monitoring标签的Pod”、“env是prod或staging都算”、“只要不是tierbackend的都要”。3.2 selector的不可变性——一个坑# ReplicaSet创建后selector不允许修改# 如果你必须改选择器的逻辑只能# 1. 删除旧的RS# 2. 创建新的RS用新的selector# 这也是为什么Deployment每次更新都创建新RS的原因之一四、被遗忘的Pod——孤儿Pod是怎么来的ReplicaSet通过Label Selector找到自己管的Pod而不是通过某种父子关系。这就引出一个有趣的现象——如果你的Pod的Label恰好匹配了某RS的Selector这个Pod就会被RS收养。【孤儿Pod被收养】 场景你手动创建了一个Pod标签是 appmyapp, versionv1.25 ┌───────────────────┐ │ ReplicaSet │ selector: {app: myapp, version: v1.25} │ myapp-rs │ 期望副本3 │ │ 实际在跑的Pod当前有2个 └────────┬──────────┘ │ │ RS检查匹配我的selector的Pod有几个 │ 发现2个自己创建的 1个手动创建的 3个 │ 够了不用补了 │ ▼ ┌────────┐ ┌────────┐ ┌──────────────┐ │ Pod-1 │ │ Pod-2 │ │ Pod-手动创建 │ ← 被RS收养了 │ (RS建) │ │ (RS建) │ │ (你自己建的) │ └────────┘ └────────┘ └──────────────┘ 如果你删了这个手动创建的Pod RS发现只剩2个了补1个——又恢复了3个# 实验看RS能不能收养手动创建的Pod# 先创建一个RSreplicas2kubectl apply-fmyapp-rs.yaml# 手动创建一个标签匹配的Podkubectl run orphan-pod--imagemyapp:v1.25--labelsappmyapp,versionv1.25# RS发现已经有3个匹配的Pod了不会再多建kubectl get pods-lappmyapp# NAME READY STATUS# myapp-rs-abc12 1/1 Running ← RS创建的# myapp-rs-def34 1/1 Running ← RS创建的# orphan-pod 1/1 Running ← 手动创建的但被RS收养# 你删掉手动创建的PodRS立刻补一个kubectl delete pod orphan-pod kubectl get pods-lappmyapp# myapp-rs-abc12 1/1 Running# myapp-rs-def34 1/1 Running# myapp-rs-ghi56 1/1 Running ← 自动补的要点RS不认Pod的出生证明只看Label。这意味着你需要严格管理Pod的标签——如果有多个资源Service、RS、NetworkPolicy用同一个Label选择Pod改标签可能触发连锁反应。Pod标签管理是K8s运维的基本功。五、什么时候跳过Deployment直接操作ReplicaSetDeployment是好用但并不是所有场景都需要它。直接操作RS的情况总结如下【决策树用Deployment还是直接用RS】 你的应用需要滚动更新/回滚吗 │ ┌──┴──┐ │ 是 │──► 用 Deployment ✅ └─────┘ │ ┌──┴──┐ │ 否 │ ──► 你用的是什么部署工具 └──┬──┘ │ ┌────┴────────────────────┐ │ │ Helm/ArgoCD/ │ Kustomize/CD工具 │ 手动 kubectl 管理 │ │ ▼ ▼ 工具自己可能直接 Deployment 有回滚能力 创建RS不经过 更安全 ✅ Deployment✅ 但更常见工具还是 用Deployment 自己 的版本管理 ✅以下是真正直接操作RS的典型场景场景为什么直接操作RS注意事项学习/调试理解RS本质了解Deployment底层仅限实验环境部署工具内部实现Helm/ArgoCD等工具可能自己管理RS工具负责版本管理静态副本集永远不改镜像、不需要回滚的一次性任务极少见自定义Operator自己写控制器RS是你代码里管理Pod的原语高级用法# 手动创建一个独立的RS不通过Deploymentkubectl apply-f-EOF apiVersion: apps/v1 kind: ReplicaSet metadata: name: standalone-rs spec: replicas: 3 selector: matchLabels: app: standalone template: metadata: labels: app: standalone spec: containers: - name: nginx image: nginx:1.25 EOF# 查看RS包括被Deployment管理的和独立的kubectl get rs# NAME DESIRED CURRENT READY AGE# nginx-deployment-7d8f9c6a5b 3 3 3 1h ← Deployment的RS# standalone-rs 3 3 3 10s ← 独立RS# 扩容独立RSkubectl scale rs standalone-rs--replicas5# 改镜像——但RS本身不支持滚动更新所有Pod会被同时杀掉重建kubectlsetimage rs/standalone-rsnginxnginx:1.27# ⚠️ RS直接改镜像会导致所有Pod同时重建——没有滚动更新的保护要点直接操作RS的最大坑在于——RS没有滚动更新和回滚能力。你直接改RS的Pod模板所有匹配的Pod会立刻被同步杀掉重建没有maxSurge/maxUnavailable的保护。除非你知道自己在干什么否则永远通过Deployment操作。六、ReplicaSet vs ReplicationController——对比总结维度ReplicationController (RC)ReplicaSet (RS)API版本v1apps/v1选择器类型仅等值匹配等值匹配 集合表达式matchExpressions❌ 不支持✅ 支持 In/NotIn/Exists/DoesNotExist是否被推荐❌ 不推荐遗留✅ 推荐Deployment使用❌ 不用RC✅ Deployment内部用RSkubectl缩写rcrs七、RS的常用操作命令# 查看所有RSkubectl get rs# 查看RS详情kubectl describe rs myapp-rs-7d8f9c6a5b# 查看RS管理的Podkubectl get pods-lappmyapp,versionv1.25# 扩容RSkubectl scale rs myapp-rs-7d8f9c6a5b--replicas5# 查看RS的YAML看它记录的Pod模板kubectl get rs myapp-rs-7d8f9c6a5b-oyaml|grep-A20spec:# 删除RS会级联删除它管理的Podkubectl delete rs myapp-rs-7d8f9c6a5b# 删除RS但不删除Pod让Pod变成孤儿kubectl delete rs myapp-rs-7d8f9c6a5b--cascadeorphan# 查看RS的事件日志kubectl describe rs myapp-rs-7d8f9c6a5b|grep-A10Events本篇小结ReplicaSet是沉默的大多数——你不直接跟它打交道的概率远超你直接操作它的概率但它时时刻刻在守护你的PodRC→RS进化RS支持集合选择器In/NotIn/Exists/DoesNotExist是RC的完全替代品三大职责确保Pod数量、故障自动替换、弹性伸缩——核心就是一个调谐循环两个选择器matchLabels做精准匹配matchExpressions做模糊匹配两者是AND关系别手贱直接操作RSRS没有滚动更新和回滚能力直接改模板所有Pod会同时重建下一篇咱们终于要聊Service了——Pod IP是临时的每次重建都会变。那外部怎么稳定地访问Podkube-proxy怎么把流量引到正确的Pod上这些就是Service的核心问题。上一篇【第13篇】Deployment——无状态应用的“自动档“下一篇【第15篇】Service——K8s的服务发现和负载均衡
返回列表