news 2026/9/17 6:22:09

微服务部署到K8S容器云平台:核心对象与落地实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微服务部署到K8S容器云平台:核心对象与落地实践

简介:微服务架构将单体应用拆分为众多独立服务,随之而来的服务发现、负载均衡与集群管理问题,需要依赖Kubernetes等容器云平台加以解决。方案文档面向企业架构师、运维人员及K8S实践者,系统梳理了基于K8S容器云平台的微服务部署方案,重点介绍在DMZ与内网分别部署两套相互隔离的OpenShift环境,并细化权限管理、基于Project的多租户隔离、Router隔离、物理资源池隔离以及日志监控等关键设计。内容还涵盖OCP的认证与鉴权机制、SCC安全上下文限制、EFK日志管理平台,以及基于cAdvisor、Heapster和Hawkular的监控组件,贴合企业级真实落地场景。资源共1个docx文档,大小约417KB,轻量精炼。目前已有497人学习,适合需要规划容器云部署方案或优化现有微服务架构的读者参考。

1. 微服务部署到K8S容器云平台,先想清楚它解决什么

把几十个微服务直接扔进K8S,和用脚本在几台ECS上拉起来,最大的区别不在启动方式,而在故障恢复与流量路由。K8S容器云平台真正解决的,是让每个微服务都有一套标准的生命周期管理:镜像拉不下来会重试,Pod被杀掉会自动重建,流量只在健康实例之间分发。很多团队从Spring Cloud切到K8S后,最直观的感受是部署脚本从几百行收敛成几个yaml文件,代价是先搞懂控制器、服务访问、配置注入和流量入口四件事。这套方案不绑定某个特定微服务框架,RuoYi、Spring Cloud或go-micro都适用,落地路径是一致的。定位上看,它适合已经完成微服务改造、正在选部署形态的团队,也适合刚接管一套K8S集群、需要给几十个服务定编排规范的一线开发。

2. 基于K8S的微服务部署,先搞懂四类核心对象

先划清一个边界:Docker解决的是单台机器上镜像的打包与进程隔离,K8S解决的是跨机器的编排、调度与服务发现。微服务架构图里那些网关、业务服务、基础设施服务,落到K8S容器云平台上,对应的其实是同一套对象模型。业务实例交给Deployment管,服务之间的调用入口交给Service管,非敏感配置和敏感凭证分别交给ConfigMap和Secret管,外部流量统一走Ingress。把这四类对象想清楚,剩下的就是写yaml和调参数。

2.1 用Deployment管理无状态服务,而不是裸Pod

微服务绝大多数是无状态的,用户会话、缓存数据都外置到了Redis或数据库,因此最常用的工作负载是Deployment。它保证指定数量的Pod永远在运行,版本更新时按策略滚动替换,Pod崩溃时按restartPolicy重建。我见过从Docker Compose转过来的团队,第一个yaml直接写Pod,这是最典型的误区:

# 错误示例:直接创建Pod,节点故障后不会自愈 apiVersion: v1 kind: Pod metadata: name: user-service spec: containers: - name: user-service image: registry.example.com/ms/user-service:1.0.0

这种写法下,kubelet只能保证容器挂了以后重启,节点宕机或者Pod被驱逐,服务就彻底没了。正确做法是把Pod定义交给Deployment控制器:

apiVersion: apps/v1 kind: Deployment metadata: name: user-service namespace: ms-prod spec: replicas: 2 selector: matchLabels: app: user-service template: metadata: labels: app: user-service spec: containers: - name: user-service image: registry.example.com/ms/user-service:1.0.0 ports: - containerPort: 8080

这里的关键是spec.selector.matchLabels必须和spec.template.metadata.labels里的标签完全一致,Deployment靠这个标签选择器管理它创建的Pod。replicas: 2只是起步值。正常生产的微服务,考虑单实例QPS和节点故障容错,一般取3到5个副本;单节点K8S环境做验证时,副本数设1就够了,多副本在单节点上没有实际容错意义。

2.2 Service负责服务发现,微服务之间的调用就靠它

Pod IP在滚动更新之后一定会变,微服务A要调用服务B,不能把Pod IP写死在配置里。K8S用Service对象固定访问入口:

apiVersion: v1 kind: Service metadata: name: user-service namespace: ms-prod spec: type: ClusterIP selector: app: user-service ports: - name: http port: 8080 targetPort: 8080

Service创建后,K8S内置DNS会生成一条记录user-service.ms-prod.svc.cluster.local,同命名空间内直接用user-service这个短域名就能发起调用。port是Service对外暴露的端口,targetPort是Pod内容器实际监听的端口,两者可以不一样,比如Service走80、容器监听8080。Dubbo这类RPC框架迁移到这里时,注册中心换成K8S服务发现或者Dubbo Mesh的Service Mesh能力,Service对象本身仍然承担固定虚拟IP的职责。

2.3 ConfigMap与Secret分离配置,环境切换才不慌

把数据库地址、Redis密码直接写进镜像,改配置就得重新打镜像,密码还会留在镜像仓库里,安全隐患很大。容器云平台上的标准做法是:非敏感配置放ConfigMap,敏感内容放Secret。

apiVersion: v1 kind: ConfigMap metadata: name: user-service-config namespace: ms-prod data: SPRING_PROFILES_ACTIVE: "prod" LOG_LEVEL: "INFO" --- apiVersion: v1 kind: Secret metadata: name: user-service-secret namespace: ms-prod type: Opaque stringData: db-password: "Prod@2024"

ConfigMap和Secret在Deployment里用envFrom注入,或者以卷的形式挂载成文件。使用stringData写Secret最方便,K8S会在持久化时自动做Base64编码。但要明确一点:Base64不是加密,有权限的人用kubectl get secret -o yaml照样看得到明文,生产环境需要配合云上的KMS加密或者sealed-secrets这类工具。

2.4 Ingress在集群入口收敛流量,统一网关

几十个微服务,每个都开一个NodePort,既不安全也难维护。Ingress作为K8S的七层入口,按host和path把外部请求路由到不同的Service,对应微服务架构图里的网关层:

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ms-gateway namespace: ms-prod spec: rules: - host: api.example.com http: paths: - path: /user pathType: Prefix backend: service: name: user-service port: number: 8080 - path: /order pathType: Prefix backend: service: name: order-service port: number: 8080

pathType: Prefix是前缀匹配,/user/order分别打到两个Service。Ingress规则本身只是一个声明,集群里必须装了nginx-ingress或traefik这类Ingress Controller,规则才会被同步到代理进程里。像RuoYi微服务这种自带Knife4j接口文档的应用,可以再给/doc开一条前缀规则,把接口文档单独暴露给开发环境。

3. 在K8S容器云平台手动部署微服务的完整操作路径

前面四类对象理清了,下面按真实操作顺序走一遍。这套路径在kubeadm自建集群和阿里云ACK这类托管容器云平台上通用:Deployment和Service的yaml基本原样迁移,涉及的改动主要在Ingress Class和存储类。我一般用kubectl做日常管理,集群规模大了以后换k9s看Pod分布和日志,定位问题更快。

3.1 创建命名空间和私有镜像仓库凭证

第一步划分环境边界,用命名空间隔离prod和test,避免两套环境互相污染。然后创建拉取私有镜像的凭证。大多数团队的镜像在私有仓库,Deployment要从仓库拉镜像,没有凭证会一直停在ImagePullBackOff。

kubectl create namespace ms-prod kubectl create secret docker-registry registry-auth \ --namespace=ms-prod \ --docker-server=registry.example.com \ --docker-username=deploy \ --docker-password='密码写在这里' \ --docker-email=deploy@example.com

第一行创建命名空间,第二行创建类型为kubernetes.io/dockerconfigjson的Secret。Deployment里要在spec.template.spec.imagePullSecrets小节引用这个Secret,否则新节点上没有缓存镜像时,拉取会失败。

提示:--docker-password在shell历史里会留着明文,脚本化执行时建议从环境变量读取,避免泄露。

3.2 编写最小可用的微服务编排文件

一个完整的微服务部署清单,至少包含Deployment、Service、ConfigMap、Secret四类资源。以订单服务为例,Deployment是核心,下面这份是加了探针并引用外部配置的最小版本:

apiVersion: apps/v1 kind: Deployment metadata: name: order-service namespace: ms-prod spec: replicas: 3 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: imagePullSecrets: - name: registry-auth containers: - name: order-service image: registry.example.com/ms/order-service:2.1.3 imagePullPolicy: IfNotPresent ports: - containerPort: 8080 envFrom: - configMapRef: name: order-service-config - secretRef: name: order-service-secret readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 60 periodSeconds: 20 resources: requests: cpu: 250m memory: 512Mi limits: cpu: "1" memory: 1Gi

readinessProbe决定Pod何时进入Ready状态,初始化还没完成时,Pod会从Service的Endpoints里摘掉,流量不会打过来;livenessProbe决定进程是否假死,连续多次失败后kubelet会重启容器。RuoYi微服务这类Spring Boot应用,健康端点默认是actuator暴露的/actuator/health,要在pom里引入spring-boot-starter-actuatorresources段里的requests是调度依据,limits是运行约束,只写limits不写requests,调度器会把实例堆到同一台机器,峰值期互相争抢CPU。

3.3 用kubectl apply完成部署和滚动更新

文件放在conf/ms-prod/目录后,部署操作收敛为两条命令:

kubectl apply -f conf/ms-prod/ kubectl rollout status deployment/order-service -n ms-prod --timeout=300s

kubectl apply是声明式操作,K8S对比当前状态和期望状态的差异,只做增量更新;rollout status阻塞等待滚动完成,超时300秒后自动失败退出,CI里能直接捕获。发新版本时,流水线里我喜欢用kubectl set image只替换镜像标签,不重写整个文件:

kubectl set image deployment/order-service \ order-service=registry.example.com/ms/order-service:2.1.4 \ -n ms-prod

第二个参数order-service是容器名,Deployment里有多个容器时必须写清楚替换哪一个。滚动更新默认策略是RollingUpdate,新Pod Ready后旧Pod才摘流量,这个过程能做到不停服。注意如果复用一个旧tag,镜像不会重新拉取,版本号最好每次递增。

3.4 服务访问不通时的三个排查命令

部署完发现服务间调用不通,先别怀疑代码。我一般按下面顺序查:

kubectl get endpoints -n ms-prod user-service kubectl get pod -n ms-prod -l app=user-service -o wide kubectl describe svc user-service -n ms-prod

第一条看Service后面有没有挂上Pod IP,Endpoints为空就是标签选择器没匹配上;第二条确认Pod是Running且READY是1/1,过滤条件-l app=user-service要和Service选择器一致;第三条看Service的端口映射和后端列表。这三个命令能覆盖九成的服务发现问题,剩下的再看网络策略和节点安全组。

4. K8S微服务部署的资源配额、探针与HPA参数这样设

yaml能跑起来只是第一步,参数设得不对,流量一上来就出问题。这一章把三个最常踩坑的参数讲透。

4.1 给每个微服务设requests和limits,避免资源争抢

容器云平台是共享的,多个微服务跑在同一个节点池里。不设资源限制,一个服务的内存异常增长能拖垮整台节点。我给不同类型微服务定的参考值:

微服务类型requests.cpulimits.cpurequests.memorylimits.memory
网关类(gateway)500m2512Mi1Gi
业务类(user/order)250m1512Mi1Gi
定时任务类100m500m256Mi512Mi
异步消费者250m1512Mi1Gi

CPU是可压缩资源,250m的requests允许突发到1核,节点资源紧张时会被限制回250m;内存不可压缩,达到limits上限容器直接被OOM Kill。所以内存limits要给JVM堆外留出余量,堆内、元空间、线程栈加上堆外缓存,总量通常比上限低20%到30%。

Java里一个典型的坑是JVM不认识容器配额。JDK 8u131以前的版本默认按宿主机物理内存计算堆上限,宿主机32G时最大堆接近8G,容器limits.memory只有512Mi,Pod必然被杀。8u191之后的版本默认开启容器支持,配合-XX:MaxRAMPercentage=50.0,JVM会按容器内存配额计算堆大小,这个参数我一般直接写进镜像启动命令。

4.2 存活探针与就绪探针怎么区分、怎么调参

探针参数的调整原则是:给应用留足启动时间。initialDelaySeconds设置太短,服务还没初始化完成就探测,失败后K8S重启Pod,然后就陷入无限重启循环。这个值一般取应用从启动到能应答健康检查的真实耗时的1.5到2倍。

periodSeconds决定探测频率。livenessProbe建议20秒一次,太密集的探测在高并发下会消耗线程;readinessProbe可以调到10秒,让新Pod尽快接流量。failureThreshold默认3次,服务启动时要做缓存预热的话,调到5次更稳妥。

readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 5

timeoutSeconds容易被忽略。如果健康检查端点里加了数据库连接检查,数据库慢查询会让探针超时,Pod被摘流量甚至重启。我建议健康检查只做进程自身存活检查,外部依赖的健康度单独走另一套链路。

4.3 HPA自动扩缩容,应对秒杀这类突发流量

秒杀和活动场景的流量曲线陡峭,人工扩副本永远来不及。HPA根据CPU、内存或自定义指标动态调整副本数,运行中的K8S集群一行命令就能开启:

kubectl autoscale deployment seckill-service \ --cpu-percent=70 \ --min=3 \ --max=20 \ -n ms-prod

HPA的计算公式是期望副本数 = 当前副本数 × (当前指标值 / 目标指标值)--cpu-percent=70表示期望所有Pod的CPU使用率达到70%,超过就扩容,回落后按冷却时间逐步缩容,默认缩容冷却5分钟。注意这里对比的基线是Pod的requests.cpu,不是节点物理CPU。requests设得太大会让扩容不敏感,设得太小单个Pod容易被流量打满。

CPU指标在Java应用上经常滞后,流量上来后的一两分钟,GC和类加载才把CPU顶起来。如果业务对突发流量敏感,建议走Prometheus的QPS自定义指标,配置custom.metrics.k8s.io后HPA按请求数扩缩容,反应速度比纯CPU快一个量级。单节点K8S集群里HPA的意义不大,min和max都设1到3即可,没有跨节点扩展的实际效果。

5. 微服务在K8S上部署完,用可观测性收尾验证

5.1 用kubectl events和日志确认部署状态

压测之前先看三样东西:事件、日志、容器内健康状态。

kubectl get events -n ms-prod --sort-by=.lastTimestamp | tail -20 kubectl logs deployment/seckill-service -n ms-prod --tail=200 kubectl exec -it deployment/user-service -n ms-prod -- sh

第一条把最近20条集群事件倒序打印,FailedSchedulingBackOff会直接显示调度失败与重启原因,比逐个看Pod状态直观;第二条看启动日志里有没有异常堆栈,定位启动崩溃最直接;第三条进入容器执行curl localhost:8080/actuator/health,确认容器内部健康检查真实可用。有些精简镜像里没装curl,改用wget -qO-python3 -c

5.2 接入Prometheus与链路追踪,拿数据说话

微服务排障比单体难在链路长。A调用B、B调用C,任何一环变慢,用户侧就是一个超时。部署完成后的下一步,是把指标、日志、链路三件套补齐。

可观测性维度常用选型接入方式
指标Prometheus + GrafanaServiceMonitor自动发现Pod
日志Loki或EFKDaemonSet采集,按namespace聚合
链路SkyWalking或JaegerJava agent或OpenTelemetry SDK

指标采集推荐Prometheus Operator。它在集群里创建ServiceMonitor,按标签自动发现目标Pod:

apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: ms-monitor namespace: ms-prod spec: selector: matchLabels: app: user-service endpoints: - port: http path: /actuator/prometheus interval: 15s

Spring Boot微服务引入micrometer-registry-prometheus后,actuator的/actuator/prometheus端点会自动暴露JVM、线程池和HTTP调用指标。链路追踪方面,Java应用用SkyWalking agent接入成本最低,Nest.js这类Node微服务走OpenTelemetry SDK自动埋点,上报到Jaeger后按traceId串起整条调用链。

压测人员拿着JMeter脚本打Ingress网关时,我会同时盯两个地方:Grafana上每个微服务的P99延迟,以及HPA的扩容事件。扩容滞后时间如果超过30秒,优先检查HPA指标来源是CPU还是QPS,以及requests值是否偏大。每个服务的探针参数和HPA阈值,建议每压测一轮就回看一次指标再调整,Xmx比例、探针延迟、副本基数这些值,往往会随业务迭代继续变化。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 6:21:45

MATLAB实现ORL人脸识别:PCA特征脸从原理到实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 6:19:24

基于Vue3与Spring Boot的同城宠物上门服务系统设计与实现

1. 项目背景与核心需求解析去年暑假帮邻居代管宠物时,发现临时出差人群存在强烈的宠物照护需求。市面上虽有宠物店寄养服务,但存在应激反应、交叉感染等问题。基于Web的同城上门服务系统正是为解决这类痛点而生,其核心价值在于:解…

作者头像 李华
网站建设 2026/9/17 6:19:06

便携式双脉冲测试平台:IGBT与SiC MOSFET动态特性分析利器

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 6:18:20

深入解析Node.js CommonJS模块系统与最佳实践

1. CommonJS 模块系统深度解析CommonJS 规范是 Node.js 生态中最重要的基础设计之一,它定义了模块如何编写、导出和导入的完整机制。与前端开发中常见的 ES Modules 不同,CommonJS 采用同步加载方式,这使得它在服务器端场景下表现尤为出色。1…

作者头像 李华
网站建设 2026/9/17 6:18:08

从PPT到知识图谱:大模型+Neo4j可追溯问答系统

简介:这份PPT围绕企业级知识图谱与大模型融合实践展开,面向人工智能算法、知识工程、数据治理及企业架构从业者,也可供研究者与学生梳理技术脉络。内容从知识图谱与大模型的定义、发展历程与核心特征切入,比较两者在结构化语义推理…

作者头像 李华
网站建设 2026/9/17 6:16:29

PLC中文界面与中文编程的本质区别及工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华