Service、扩容与滚动更新——从单点到高可用的实战之路 阅读提示本文假设你已经掌握 kubectl 基础命令。建议按顺序阅读本系列文章。前言那是一个让我永生难忘的凌晨2点。公司搞促销活动流量突然暴涨。我正在家睡觉突然电话被打爆了——服务崩了。手忙脚乱地爬起来开电脑登录 K8s 集群一看只有一个 Pod 在运行CPU 使用率飙到 100%大量请求超时Pod 不断重启那时候我才意识到上了 K8s 不代表就高可用了如果你只有一个 Pod没有负载均衡没有自动扩容那和传统单点部署没什么区别。K8s 只是提供了能力怎么配置还得靠自己。那晚上折腾了3个小时我学会了用Service做负载均衡用scale手动扩容用Rolling Update零停机发布今天这篇文章把我那晚的血泪教训和实战经验分享出来帮你提前避开这些坑。一、ServicePod 的代言人1.1 为什么需要 Service在 K8s 中Pod 是临时的、动态的Pod 的 IP 地址是不固定的重启后会变Pod 可能被调度到不同的节点Pod 可能因为故障被重建那其他应用怎么访问这个 Pod 呢总不能每次都改 IP 吧这就是Service存在的意义——为 Pod 提供一个稳定的访问入口。1.2 Service 的工作原理用户/其他应用 ↓ Service固定IP/域名 ↓ Label Selector标签选择 ↓ Pod 1, Pod 2, Pod 3... ↓ 负载均衡分发请求Service 的核心能力服务发现为 Pod 提供固定的访问地址ClusterIP 或域名负载均衡自动将请求分发到后端的多个 Pod解耦调用方不需要知道 Pod 的具体位置1.3 Service 的三种类型K8s 提供了三种 Service 类型适应不同的访问场景类型访问方式适用场景生产环境推荐度ClusterIP集群内部 IP集群内部服务间通信⭐⭐⭐⭐⭐NodePortNodeIP:NodePort开发测试、简单暴露⭐⭐LoadBalancer云厂商负载均衡器 IP生产环境对外服务⭐⭐⭐⭐ClusterIP默认apiVersion:v1kind:Servicemetadata:name:nginx-servicespec:type:ClusterIP# 默认就是 ClusterIP可以省略selector:app:nginxports:-port:80targetPort:80特点只在集群内部可访问分配一个内部虚拟 IPClusterIP适用于微服务间的内部通信访问方式# 集群内通过 Service 名访问curlhttp://nginx-service:80# 或者通过 ClusterIPcurlhttp://10.96.10.90:80NodePortapiVersion:v1kind:Servicemetadata:name:nginx-servicespec:type:NodePortselector:app:nginxports:-port:80targetPort:80nodePort:32600# 范围30000-32767特点在每个节点上开放一个端口NodePort通过NodeIP:NodePort访问会自动创建 ClusterIP访问方式# 任意节点的 IP NodePortcurlhttp://192.168.0.112:32600curlhttp://192.168.0.113:32600⚠️注意NodePort 的端口范围是 30000-32767如果不指定K8s 会随机分配。LoadBalancerapiVersion:v1kind:Servicemetadata:name:nginx-servicespec:type:LoadBalancerselector:app:nginxports:-port:80targetPort:80特点在云环境AWS、阿里云、腾讯云等中会自动创建云负载均衡器分配一个外部可访问的 IPEXTERNAL-IP会自动创建 NodePort 和 ClusterIP访问方式# 查看分配的公网 IPkubectl get svc nginx-service# 输出示例# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)# nginx-service LoadBalancer 10.96.10.90 47.102.34.56 80:32600/TCP# 通过公网 IP 访问curlhttp://47.102.34.56类型关系LoadBalancer ⊃ NodePort ⊃ ClusterIP。选择高级类型时低级类型的能力也具备。1.4 三种类型的对比选择决策流程图需要外部访问 ├─ 否 → ClusterIP内部服务间通信 └─ 是 → 在哪个环境 ├─ 开发/测试 → NodePort简单快速 └─ 生产环境 → LoadBalancer配合 Ingress 使用生产环境最佳实践内部服务使用 ClusterIP DNS外部服务使用 LoadBalancer IngressNodePort 慎用端口冲突、安全性差仅用于临时测试二、实战为 Nginx 添加 Service2.1 创建 Service YAML创建一个文件nginx-service.yamlapiVersion:v1kind:Servicemetadata:name:nginx-servicelabels:app:nginxspec:type:NodePort# 使用 NodePort 类型方便测试访问selector:app:nginx# 选择带有 appnginx 标签的 Podports:-name:nginx-portprotocol:TCPport:80# Service 的端口集群内部访问nodePort:32600# NodePort外部访问targetPort:80# 转发到 Pod 的 80 端口字段解释字段说明selector关键字段决定哪些 Pod 属于这个 ServiceportService 自身的端口ClusterIP 使用targetPortPod 容器的端口流量最终转发到这里nodePort节点上开放的端口NodePort 类型特有坑点警示selector必须和 Deployment 中 Pod 的labels匹配如果不匹配Service 找不到 Pod请求就会失败。2.2 部署 Service# 应用配置kubectl apply-fnginx-service.yaml# 预期输出# service/nginx-service created2.3 验证 Service# 查看 Service 详情kubectl get svc nginx-service-owide预期输出NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR nginx-service NodePort 10.96.10.90 none 80:32600/TCP 77s appnginx字段说明CLUSTER-IPService 的内部虚拟 IPPORT(S)80 是 Service 端口32600 是 NodePortSELECTOR标签选择器appnginx2.4 测试访问# 通过 NodePort 访问任意节点的 IPcurlhttp://192.168.0.112:32600# 或者本地测试curlhttp://localhost:32600如果看到 Nginx 的欢迎页面说明 Service 工作正常三、Scaling扩容应对流量增长还记得文章开头的那个凌晨吗如果当时我配置了多个 Pod服务就不会崩了。3.1 为什么要扩容单个 Pod 的问题性能瓶颈无法处理大量并发请求单点故障Pod 挂了服务就不可用没有冗余无法应对突发流量扩容的价值提高吞吐量实现高可用负载均衡分散压力3.2 手动扩容方式一修改 YAML编辑nginx-deployment.yaml修改replicasspec:replicas:4# 从 1 改为 4然后应用kubectl apply-fnginx-deployment.yaml方式二命令行快速扩容推荐# 将 nginx-deployment 扩容到 4 个副本kubectl scale deployment nginx-deployment--replicas4优势不需要修改 YAML 文件快速响应。3.3 验证扩容结果# 查看 Deploymentkubectl get deployment nginx-deployment预期输出NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 4/4 4 4 47m4/4表示期望 4 个实际可用 4 个。# 查看 Podkubectl get pods-lappnginx预期输出NAME READY STATUS RESTARTS AGE nginx-deployment-746fbb99df-tdmb7 1/1 Running 0 48m nginx-deployment-746fbb99df-v6l9c 1/1 Running 0 2m nginx-deployment-746fbb99df-v8cm5 1/1 Running 0 2m nginx-deployment-746fbb99df-w4qqx 1/1 Running 0 2m现在有4 个 Pod在运行Service 会自动将请求负载均衡到这 4 个 Pod3.4 验证负载均衡# 多次访问观察请求被分发到不同 Podforiin{1..10};docurl-shttp://localhost:32600|grepWelcome;done虽然输出内容相同但请求实际上被分发到了不同的 Pod。3.5 自动扩容HPA手动扩容还是不够灵活生产环境通常使用HPAHorizontal Pod Autoscaler自动扩容。# 创建 HPA当 CPU 超过 50% 时自动扩容最多 10 个 Podkubectl autoscale deployment nginx-deployment\--cpu-percent50\--min2\--max10# 查看 HPAkubectl get hpaHPA 工作原理监控 Pod 的 CPU/内存使用率超过阈值时自动增加 Pod 数量低于阈值时自动减少 Pod 数量保持在min和max之间前置条件HPA 需要 metrics-server 支持。如果没安装参考前文部署章节。四、滚动更新零停机发布4.1 为什么需要滚动更新传统的发布方式停止旧版本服务部署新版本启动新版本问题发布过程中服务不可用停机时间。滚动更新的价值零停机整个更新过程服务持续可用风险可控逐步替换有问题可以及时回滚用户体验好用户无感知4.2 滚动更新的原理初始状态 Pod A (v1) Pod B (v1) Pod C (v1) Pod D (v1) ← 4个旧版本 更新过程 1. 创建新 Pod E (v2)替换 Pod A (v1) Pod E (v2) Pod B (v1) Pod C (v1) Pod D (v1) 2. 创建新 Pod F (v2)替换 Pod B (v1) Pod E (v2) Pod F (v2) Pod C (v1) Pod D (v1) 3. 继续替换... 最终状态 Pod E (v2) Pod F (v2) Pod G (v2) Pod H (v2) ← 全部新版本核心机制始终保证有一定数量的 Pod 可用新 Pod Ready 后才删除旧 PodService 自动将流量切换到新 Pod4.3 执行滚动更新修改镜像版本编辑nginx-deployment.yaml修改镜像标签spec:template:spec:containers:-name:nginximage:nginx:1.8# 从 1.7.9 升级到 1.8ports:-containerPort:80应用更新kubectl apply-fnginx-deployment.yaml4.4 观察更新过程# 实时查看 Pod 变化watchkubectl get pods-lappnginx你会看到这样的变化# 初始状态 - 全是旧版本 nginx-deployment-746fbb99df-tdmb7 1/1 Running 0 52m nginx-deployment-746fbb99df-v6l9c 1/1 Running 0 6m nginx-deployment-746fbb99df-v8cm5 1/1 Running 0 6m nginx-deployment-746fbb99df-w4qqx 1/1 Running 0 6m # 更新中 - 新旧版本共存 nginx-deployment-746fbb99df-tdmb7 1/1 Terminating 0 52m # 旧版本被终止 nginx-deployment-7cd7fc4dbf-ckdtk 1/1 Running 0 5s # 新版本启动 nginx-deployment-746fbb99df-v6l9c 1/1 Running 0 6m ... # 最终状态 - 全是新版本 nginx-deployment-7cd7fc4dbf-ckdtk 1/1 Running 0 2m nginx-deployment-7cd7fc4dbf-m4ppb 1/1 Running 0 2m nginx-deployment-7cd7fc4dbf-pq27j 1/1 Running 0 2m nginx-deployment-7cd7fc4dbf-xjndw 1/1 Running 0 2m注意Pod 名称中间的哈希值变了746fbb99df→7cd7fc4dbf说明 ReplicaSet 已更新。4.5 查看更新状态# 查看滚动更新状态kubectl rollout status deployment nginx-deployment# 预期输出# Waiting for deployment nginx-deployment rollout to finish: 2 of 4 updated replicas are available...# deployment nginx-deployment successfully rolled out4.6 查看更新历史# 查看 Deployment 的修订历史kubectl rollouthistorydeployment nginx-deployment# 预期输出# deployment.apps/nginx-deployment# REVISION CHANGE-CAUSE# 1 none# 2 none4.7 回滚到上一个版本如果更新后发现问题可以快速回滚# 回滚到上一个版本kubectl rollout undo deployment nginx-deployment# 回滚到指定版本kubectl rollout undo deployment nginx-deployment --to-revision1# 验证回滚kubectl get pods-lappnginx回滚原理K8s 会保留旧的 ReplicaSet回滚时只需切换回旧 ReplicaSet 即可速度很快。4.8 自定义滚动更新策略可以精细控制滚动更新的行为apiVersion:apps/v1kind:Deploymentmetadata:name:nginx-deploymentspec:replicas:4strategy:type:RollingUpdaterollingUpdate:maxSurge:1# 更新时最多可以超出的 Pod 数绝对值或百分比maxUnavailable:1# 更新时最多不可用的 Pod 数selector:matchLabels:app:nginxtemplate:metadata:labels:app:nginxspec:containers:-name:nginximage:nginx:1.8参数说明参数说明示例maxSurge更新时可超出的 Pod 数量1表示最多比期望多 1 个 PodmaxUnavailable更新时允许不可用的 Pod 数量1表示允许 1 个 Pod 不可用不同场景的配置建议场景maxSurgemaxUnavailable说明快速更新100%0先创建全部新 Pod再删除旧 Pod需要双倍资源保守更新025%先删除旧 Pod再创建新 Pod资源占用少但有不可用时间平衡方案25%25%默认值平衡速度和可用性五、完整实战从单点到高可用5.1 一键部署完整示例创建一个完整的 YAML 文件nginx-ha.yaml---# Deployment部署应用apiVersion:apps/v1kind:Deploymentmetadata:name:nginx-deploymentlabels:app:nginxspec:replicas:3# 3 个副本实现高可用strategy:type:RollingUpdaterollingUpdate:maxSurge:1maxUnavailable:1selector:matchLabels:app:nginxtemplate:metadata:labels:app:nginxspec:containers:-name:nginximage:nginx:1.8ports:-containerPort:80resources:requests:cpu:100mmemory:128Milimits:cpu:200mmemory:256Mi---# Service暴露服务apiVersion:v1kind:Servicemetadata:name:nginx-servicelabels:app:nginxspec:type:NodePortselector:app:nginxports:-port:80targetPort:80nodePort:32600---# HPA自动扩容apiVersion:autoscaling/v2kind:HorizontalPodAutoscalermetadata:name:nginx-hpaspec:scaleTargetRef:apiVersion:apps/v1kind:Deploymentname:nginx-deploymentminReplicas:3maxReplicas:10metrics:-type:Resourceresource:name:cputarget:type:UtilizationaverageUtilization:50部署kubectl apply-fnginx-ha.yaml5.2 验证高可用配置# 1. 查看 Deploymentkubectl get deployment nginx-deployment# 2. 查看 Pod应该有 3 个kubectl get pods-lappnginx# 3. 查看 Servicekubectl get svc nginx-service# 4. 查看 HPAkubectl get hpa nginx-hpa# 5. 测试访问curlhttp://localhost:326005.3 故障演练验证高可用# 1. 手动删除一个 Pod模拟故障kubectl delete pod-lappnginx --grace-period0--force# 2. 立即查看 Pod 状态kubectl get pods-lappnginx-w# 你会发现# - 被删除的 Pod 变成 Terminating# - Deployment 立即创建新 Pod 补充# - 服务一直可用因为还有 2 个 Pod 在运行这就是高可用的力量六、我踩过的坑坑 1Service 的 selector 写错现象Service 创建成功但访问时连接超时。原因selector和 Pod 的labels不匹配。解决# 检查 Service 的 selectorkubectl get svc nginx-service-oyaml|grepselector-A2# 检查 Pod 的 labelskubectl get pods-lappnginx --show-labels# 确保两者匹配坑 2NodePort 端口冲突现象Service 创建失败提示端口已占用。原因指定的nodePort已被其他 Service 使用。解决# 查看所有 NodePortkubectl get svc --all-namespaces|grepNodePort# 不指定 nodePort让 K8s 自动分配# 或者换一个未被使用的端口坑 3滚动更新时服务中断现象更新过程中部分请求失败。原因maxUnavailable设置过大导致可用 Pod 太少。解决strategy:rollingUpdate:maxUnavailable:0# 不允许不可用 PodmaxSurge:1# 最多超出 1 个坑 4HPA 不生效现象CPU 飙高但 Pod 没有自动扩容。原因metrics-server 未安装没有设置 resources.requests解决# 检查 metrics-serverkubectl get apiservices|grepmetrics# 确保 Pod 设置了 resourcesresources: requests: cpu: 100m# 必须设置HPA 依赖这个值计算使用率七、生产环境配置建议7.1 Service 选择建议场景推荐类型配置内部服务间通信ClusterIP配合 CoreDNS开发测试环境NodePort限制端口范围生产环境对外LoadBalancer Ingress使用云厂商 LB7.2 副本数建议环境建议副本数说明开发环境1-2节省资源测试环境2验证高可用生产环境≥3跨节点分布避免单点7.3 滚动更新策略建议strategy:type:RollingUpdaterollingUpdate:maxSurge:25%# 最多超出 25%maxUnavailable:25%# 最多不可用 25%7.4 HPA 配置建议spec:minReplicas:3# 最少 3 个保证高可用maxReplicas:20# 最多 20 个防止无限制扩容metrics:-type:Resourceresource:name:cputarget:averageUtilization:60# CPU 60% 时扩容八、命令速查表命令说明kubectl get svc查看所有 Servicekubectl get svc -o wide查看 Service 详情kubectl describe svc name查看 Service 详细信息kubectl scale deployment name --replicas4手动扩容kubectl autoscale deployment name --cpu-percent50 --min2 --max10创建 HPAkubectl get hpa查看 HPA 状态kubectl rollout status deployment name查看滚动更新状态kubectl rollout history deployment name查看更新历史kubectl rollout undo deployment name回滚到上一个版本kubectl rollout undo deployment name --to-revision2回滚到指定版本总结这篇文章我们学习了 K8s 高可用的三大支柱1. Service稳定的访问入口ClusterIP内部通信NodePort外部访问开发测试LoadBalancer生产环境配合云厂商2. Scaling应对流量增长手动扩容kubectl scale快速响应自动扩容HPA根据 CPU/内存自动扩缩容3. Rolling Update零停机发布原理逐步替换保证可用性配置maxSurge和maxUnavailable回滚一键回滚到上一个版本从单点到高可用的转变特性单点部署高可用部署Pod 数量1≥3故障影响服务中断自动恢复发布方式停机更新零停机滚动更新应对流量手动扩容自动扩容建议收藏关注持续更新 K8s 系列教程