简介:这份文档面向正在推进微服务架构落地的架构师、运维工程师与技术决策者,围绕K8S容器云平台给出可参考的部署方案,帮助解决服务依赖、服务发现、负载均衡、集群管理与有状态数据管理等微服务化过程中的典型难题。资源包内仅含1个docx文档,约417KB,篇幅紧凑但内容完整,便于快速通读与内部传阅。文档从容器云部署框架切入,讲解DMZ与内网两套Openshift环境彼此隔离的部署思路,并依次展开权限管理、多租户管理、日志与监控等关键环节:包括基于OAuth的认证与细粒度鉴权、以project为核心的租户隔离、网络与Router隔离、物理资源池独占,以及EFK日志采集与Heapster、Hawkular、Cassandra监控链路。目前已有497人学习,适合需要梳理企业级容器云部署框架与落地要点的读者参考借鉴。
1. 从一份 docx 到一套能跑的 K8S 微服务部署方案:先搞清楚要解决什么
很多团队第一次把 Spring Cloud 微服务往 K8S 容器云平台上搬,都会经历一个相似的阶段:本地用 docker-compose 跑得好好的,一上集群就开始出各种玄学问题——服务发现失灵、配置读不到、滚动更新卡住、Pod 反复重启。最后翻车的根因往往不是代码,而是那份躺在文档里的「部署方案」根本没把 K8S 的调度模型和微服务的治理需求对齐。
这份基于 K8S 容器云平台的微服务部署方案,要解决的核心问题就一句话:让一组有依赖关系的微服务,在容器云平台上做到可发现、可配置、可扩缩、可回滚。它适合正在做微服务拆分、准备上容器云平台的后端团队,也适合已经上了 K8S 但部署流程还靠手敲 kubectl 的运维同学。下面按「先立住原理、再动手复现、最后讲坑」的顺序拆开讲,每一步都给到能直接抄的配置和命令。
2. 微服务上 K8S 的部署模型:为什么不能照搬 docker-compose
2.1 从「一台机器跑多个容器」到「一个集群调度多个 Pod」
docker-compose 的思维模型是「一台宿主机 + 固定端口 + 本地网络」,服务之间靠容器名或 localhost 直连。K8S 的模型完全不同:Pod 是最小调度单元,IP 会随重建变化,节点由调度器决定,服务之间必须通过 Service 这种稳定抽象来通信。把 compose 文件直接翻译成 Deployment,最常见的后果就是服务 A 的配置里写死了服务 B 的容器名,Pod 一重建就解析失败。
正确的映射关系是:一个微服务对应一个 Deployment(无状态)或 StatefulSet(有状态),一组 Pod 对应一个 Service 提供稳定虚拟 IP 和 DNS 名,对外暴露用 Ingress,配置和密钥用 ConfigMap 与 Secret 注入。理解这层映射,是后面所有 YAML 能写对的前提。
2.2 微服务架构图在 K8S 里对应哪些资源对象
一张典型的微服务架构图,画的是网关、业务服务、注册中心、配置中心、数据库、消息队列这几类角色。落到 K8S 资源上,它们各自有对应写法:
| 架构图角色 | K8S 资源 | 关键点 |
|---|---|---|
| API 网关 | Deployment + Service + Ingress | 对外唯一入口,Ingress 做七层路由 |
| 业务微服务 | Deployment + Service | 副本数按 QPS 调,配 HPA |
| 注册中心 | StatefulSet + Headless Service | 需要稳定网络标识,集群内互访 |
| 配置中心 | Deployment + ConfigMap | 配置外置,避免打进镜像 |
| 数据库/中间件 | StatefulSet + PVC | 有状态,必须挂持久卷 |
这张表不是让你照抄,而是提醒:架构图上的每个框,在 K8S 里都要找到对应的资源对象,缺一个环节,部署方案就是残缺的。
2.3 部署方案文档该写哪些内容才算完整
一份能落地的部署方案,至少要覆盖五块:镜像构建与推送规范、命名空间与资源配额、各服务的 Deployment/Service 清单、配置与密钥管理方式、发布与回滚策略。很多 docx 方案只写了「用 kubectl apply 部署」,没写镜像 tag 规则和回滚触发条件,结果线上出问题时没人敢动。方案的价值不在于写得多全,而在于每个环节都有明确的执行命令和判断标准。
3. 动手搭一套最小可用的部署流程
3.1 命名空间与资源配额先划好边界
上集群第一件事不是写 Deployment,而是划命名空间和配额。没有配额限制,某个服务内存泄漏会把整个节点的资源吃光,连累其他服务。下面是一个可直接用的命名空间加配额清单:
# namespace-quota.yaml apiVersion: v1 kind: Namespace metadata: name: micro-svc # 微服务专用命名空间,与默认空间隔离 --- apiVersion: v1 kind: ResourceQuota metadata: name: micro-quota namespace: micro-svc spec: hard: requests.cpu: "8" # 全命名空间 CPU 请求上限 requests.memory: 16Gi # 全命名空间内存请求上限 limits.cpu: "16" limits.memory: 32Gi pods: "50" # 防止副本数失控逻辑说明:Namespace 做逻辑隔离,ResourceQuota 做总量兜底。requests 决定调度器往哪个节点放 Pod,limits 决定容器能用多少上限。参数上,requests 建议按服务稳态占用设,limits 设成 requests 的 1.5 到 2 倍,留出突发余量。执行kubectl apply -f namespace-quota.yaml后,用kubectl describe quota -n micro-svc确认配额生效。
3.2 一个微服务的 Deployment 与 Service 标准写法
以订单服务为例,Deployment 负责副本管理,Service 负责稳定访问。下面这份清单把健康检查、资源限制、环境变量注入都写全了:
# order-service.yaml apiVersion: apps/v1 kind: Deployment metadata: name: order-service namespace: micro-svc spec: replicas: 3 # 初始副本数,后续交给 HPA selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: containers: - name: order-service image: registry.example.com/order-service:1.0.0 # 镜像 tag 必须带版本 ports: - containerPort: 8080 envFrom: - configMapRef: name: order-config # 配置从 ConfigMap 注入 resources: requests: cpu: 500m memory: 512Mi limits: cpu: "1" memory: 1Gi readinessProbe: # 就绪探针,决定是否接流量 httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 20 periodSeconds: 10 livenessProbe: # 存活探针,决定是否重启 httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 40 periodSeconds: 15 --- apiVersion: v1 kind: Service metadata: name: order-service namespace: micro-svc spec: selector: app: order-service ports: - port: 8080 targetPort: 8080 type: ClusterIP # 集群内访问,对外走 Ingress逻辑说明:readinessProbe 没通过时 Pod 不会进 Endpoints,流量不会打进来,这是滚动更新不中断的关键;livenessProbe 失败会触发重启,用来处理死锁类故障。参数上,initialDelaySeconds 要大于应用冷启动时间,否则会陷入「没起来就被杀」的循环。Service 的 selector 必须和 Deployment 的 labels 完全一致,差一个字符就选不到 Pod。
3.3 配置外置:ConfigMap 与 Secret 的正确用法
微服务的配置不能打进镜像,否则改一个数据库地址就要重新构建。ConfigMap 存普通配置,Secret 存密码和密钥:
# 从配置文件创建 ConfigMap kubectl create configmap order-config \ --from-file=application.yml=./config/order-application.yml \ -n micro-svc # 创建 Secret,注意用 stringData 避免手动 base64 kubectl create secret generic db-secret \ --from-literal=username=order_user \ --from-literal=password='S3cureP@ss' \ -n micro-svc逻辑说明:ConfigMap 以文件形式挂载或环境变量注入,Secret 同理但内容会被加密存储。参数上,--from-file适合整份配置文件,--from-literal适合零散键值。注意 Secret 默认只是 base64 编码不是加密,生产环境要开启 etcd 加密或对接外部密钥管理。改完配置后,用kubectl rollout restart deployment/order-service -n micro-svc触发滚动重启让新配置生效。
3.4 用 Ingress 暴露网关,统一南北向流量
集群内服务用 ClusterIP,对外统一走 Ingress,避免每个服务都开 NodePort:
# gateway-ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: gateway-ingress namespace: micro-svc annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: api.example.com http: paths: - path: /order pathType: Prefix backend: service: name: order-service port: number: 8080逻辑说明:Ingress 把外部域名和路径映射到集群内 Service,网关服务通常在这里做统一入口。参数上,pathType 用 Prefix 做前缀匹配,rewrite-target 处理路径重写。部署后用kubectl get ingress -n micro-svc看 ADDRESS 是否分配成功,再用 curl 带 Host 头验证路由。
4. 服务发现、扩缩容与滚动更新怎么配才不出事
4.1 服务发现:别再用 IP 直连,交给 Service DNS
微服务之间调用,配置里写的应该是http://order-service.micro-svc.svc.cluster.local:8080这种 DNS 名,而不是 Pod IP。K8S 的 CoreDNS 会为每个 Service 生成稳定 DNS 记录,Pod 重建后记录自动更新。如果用的是 Spring Cloud,Eureka 或 Nacos 这类注册中心可以保留,但底层通信仍建议走 Service,避免注册中心本身成为单点。常见做法是:注册中心负责服务列表,K8S Service 负责网络可达,两者职责分开。
4.2 HPA 自动扩缩容:指标选错等于白配
HPA 按 CPU 或内存使用率扩缩副本,配置不当会出现「频繁抖动」或「扩了不缩」:
# order-hpa.yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: order-service-hpa namespace: micro-svc spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: order-service minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 # CPU 平均使用率超 70% 扩容 behavior: scaleDown: stabilizationWindowSeconds: 300 # 缩容前观察 5 分钟,防抖动逻辑说明:HPA 依赖 metrics-server 采集指标,没装的话 HPA 会一直显示 unknown。参数上,averageUtilization 设太低会频繁扩容,设太高扩容不及时,70% 是常见起点。stabilizationWindowSeconds 给缩容加冷却期,避免流量刚降就缩、马上又扩。用kubectl top pods -n micro-svc确认指标能采到,再kubectl get hpa -n micro-svc看当前副本变化。
4.3 滚动更新与回滚:maxSurge 和 maxUnavailable 怎么定
Deployment 默认滚动更新,关键是两个参数:
spec: strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 # 最多超出期望副本数 1 个 maxUnavailable: 0 # 更新期间不允许不可用副本逻辑说明:maxUnavailable 设 0 表示先起新 Pod 再杀旧 Pod,保证更新期间服务容量不下降,代价是需要额外资源。maxSurge 设 1 控制资源峰值。如果集群资源紧张,可以把 maxUnavailable 设 1、maxSurge 设 0,用可用性换资源。回滚用kubectl rollout undo deployment/order-service -n micro-svc,配合kubectl rollout history查看历史版本。血泪经验是:镜像 tag 千万别用 latest,否则回滚时根本不知道回滚到哪个版本。
5. 部署微服务最容易踩的五个坑
5.1 坑一:Pod 一直 CrashLoopBackOff,日志却看不到报错
现象:Pod 反复重启,kubectl logs只看到启动一半就断了。原因:多半是 livenessProbe 的 initialDelaySeconds 小于应用冷启动时间,应用还没起来就被判定死亡杀掉。解决:把 initialDelaySeconds 调到大于实测冷启动时间,或者改用 startupProbe 专门处理慢启动,让 liveness 在 startup 通过后才开始计时。
5.2 坑二:服务间调用偶发超时,重启后又好了
现象:A 调 B 偶尔超时,重启 B 的 Pod 后恢复。原因:Service 的 Endpoints 里残留了已经不健康的 Pod,流量打到了正在终止的实例上。解决:确保 readinessProbe 配置正确,并在容器里加 preStop 钩子做优雅停机,配合 terminationGracePeriodSeconds 给足退出时间,让 Pod 先从 Endpoints 摘除再停止进程。
5.3 坑三:配置改了但服务读到的还是旧值
现象:更新了 ConfigMap,Pod 里读到的配置没变。原因:以环境变量方式注入的 ConfigMap 不会热更新,只有以 volume 挂载的才会同步。解决:要么用 volume 挂载配置文件并让应用支持热加载,要么改完配置后执行 rollout restart 触发重建。别指望改 ConfigMap 后什么都不做就生效。
5.4 坑四:HPA 显示 unknown,扩缩容完全不工作
现象:kubectl get hpa的 TARGETS 列显示 unknown。原因:集群没装 metrics-server,或者 Pod 没配 resources.requests,HPA 算不出使用率百分比。解决:先确认 metrics-server 正常运行,再检查 Deployment 里每个容器都写了 requests.cpu,缺一个 HPA 就失效。
5.5 坑五:滚动更新卡住,新 Pod 一直不 Ready
现象:kubectl rollout status长时间不结束,新 Pod 卡在 0/1。原因:readinessProbe 的路径写错,或者应用依赖的下游服务没就绪导致健康检查失败。解决:先kubectl describe pod看探针失败详情,再进容器手动 curl 健康检查路径确认返回码。依赖下游的场景,健康检查要区分「自身存活」和「依赖可用」,别把下游故障算成自身不健康。
6. 把部署方案沉淀成可复用的发布流水线
前面讲的都是单次部署,真正让方案有价值的是把它变成可重复执行的流水线。我一般会把所有 YAML 按服务分目录放进 Git 仓库,用 Kustomize 管理多环境差异,base 目录放公共清单,overlays 目录放 dev、staging、prod 的差异化配置。这样同一份 Deployment 模板,改几个副本数和镜像 tag 就能推到不同环境,避免手工改 YAML 改出事故。
验证部署是否真的成功,不能只看 Pod 是 Running。我习惯按这个顺序检查:先kubectl get pods -n micro-svc确认全部 Ready,再kubectl get endpoints -n micro-svc确认每个 Service 都有后端地址,然后从网关发一条真实请求走通全链路,最后看监控里错误率和延迟有没有异常。只有这四步都过了,才算部署完成。
一个具体技巧是给每个 Deployment 加上版本标签和变更记录注解:
metadata: annotations: kubernetes.io/change-cause: "release 1.0.0 修复订单超时"这样kubectl rollout history能看到每次发布的说明,回滚时不用靠猜。发布流水线里把镜像构建、推送、apply、rollout status 串成一条命令,任何一步失败就自动停止,别让半成品状态留在集群里。
我自己踩过最深的坑,是早期图省事用 latest 标签加手工 apply,结果一次线上故障要回滚时,发现根本找不到上一个稳定版本的镜像。从那以后我定了个死规矩:镜像 tag 必须带 Git commit 短哈希,所有变更走流水线,手工 kubectl 只用于排查不用于发布。这套习惯坚持下来,部署这件事从「每次提心吊胆」变成了「点一下等结果」。希望帮到你。
本文还有配套的精品资源,点击获取