tech
Kubernetes 不是微服务全家桶
从「K8s 要不要先拆微服务」说起,顺着内置资源、YAML 如何生效,讲到服务发现、中间件落点和混合部署。
上 K8s 和上微服务经常被说成一件事。其实是三层:K8s 管进程怎么调度和自愈;微服务是系统怎么拆;注册中心、配置中心、消息队列是另一套中间件。混在一起,就会问出「源码里是不是已经有 Nacos」这种问题。下面按这个顺序拆开。
先说清楚:用 K8s 不必先拆服务
K8s 编排的是容器,不要求你把系统切成几十个服务。单体、CronJob、批处理、CI runner 都能跑。一个 WAR 塞进镜像,它还是单体,只是换了宿主机抽象。
两者常被绑在一起,是因为拆多了之后,部署、发现、扩缩会变麻烦,编排正好擅长这些。因果关系是「服务多了用编排」,不是「要用 K8s 必须先拆」。一台机器、一个进程、发布很少时,systemd 或 Compose 更短——K8s 的税是 etcd、网络插件、证书和升级。
运行时和拆法正交。那如果真把工作负载放进集群,平台到底内置了什么?
内置资源是有限的一份名单
官方类型在 staging/src/k8s.io/api/:core/v1 的 Pod、Service、ConfigMap;apps/v1 的 Deployment、StatefulSet;再加上 Job、Ingress、HPA、PVC 等。kube-apiserver 启动时把它们挂进 REST 存储。
Istio 的 VirtualService、Prometheus 的 ServiceMonitor、自己写的类型,源码里没有。集群靠 CRD 或聚合 API 事后加。kubectl get 能看到的,不等于仓库里写死的。
所以 K8s 给的是编排原语,不是微服务组件目录。接下来的问题是:apply 一份 YAML,集群里是不是就有一个专门程序在伺候它?
YAML 只是对象,不是一人一个处理器
YAML 是序列化。对象进集群经过几层,而且不是每层都参与:
YAML
→ kube-apiserver(校验、准入、写入 etcd)
→ 有的再交给 controller 调谐
→ 真正干活的还可能是 kubelet / kube-proxy / DNS
| 对象 | REST 存储 | 专用 Controller | 真正执行者 |
|---|---|---|---|
| Deployment | 有 | 有 | controller → ReplicaSet → Pod |
| Service | 有 | 无(有 EndpointSlice controller) | kube-proxy + CoreDNS |
| ConfigMap | 有 | 无 | kubelet 挂载或注入环境变量 |
| Pod | 有 | 无 | 调度器选节点,kubelet 起容器 |
API 层只保证「合法,写进 etcd」。Deployment 才有循环去凑副本;ConfigMap 没有「配置中心控制器」,kubelet 用到才读。Ingress 对象在源码里,流量要另装 Ingress Controller;CoreDNS 是插件,不是 apiserver 的一部分。对象有了,实现可以在集群外。
表里的 Service 和 ConfigMap,正好是大家拿来对标注册中心和配置中心的两样。
集群内互调,一般不必再叠 Nacos
K8s 自带的服务发现是 Service + EndpointSlice + DNS:
user-svc.default.svc.cluster.local
│
▼
Service ClusterIP ──kube-proxy──► 后端 Pod(EndpointSlice)
业务写 http://user-svc 即可,扩缩容时 EndpointSlice 跟着变。这是 DNS + VIP,不是客户端拉注册表。
配置用 ConfigMap / Secret:能存 KV 和文件,热更新弱。挂 volume 能看到文件变,进程要自己 reload;环境变量改了通常要重建 Pod。没有 SDK 监听、灰度、回滚。
只在集群里跑、HTTP/gRPC 互调、配置不频繁热更,这两样够用。还要 Spring Cloud 全家桶、配置灰度、虚机和容器混部、按元数据路由,可以继续用 Nacos,和 K8s 叠加,不是二选一。
发现和配置只是缺口里最显眼的两行。网关、队列、追踪同样不在二进制里。
微服务栈里,K8s 只覆盖运行时
| 需求 | K8s 原生 | 通常另装 |
|---|---|---|
| 部署 / 扩缩 | Deployment、HPA | — |
| 服务发现 | Service + DNS | 可选 Nacos(混部、Spring 生态) |
| 配置 | ConfigMap、Secret | 可选 Apollo / Nacos |
| L4 负载 | kube-proxy / Service | — |
| L7 网关 | Ingress / Gateway API(只有对象) | nginx、envoy、istio 等实现 |
| 熔断、mTLS、金丝雀 | 无 | 服务网格或应用 SDK |
| 消息队列、链路追踪、数据库 | 无 | Kafka、Jaeger、RDS… |
编排平台不会变成配置中心、网关和消息队列。不在源码里,不等于不用跑——进程还是要有落脚点。
中间件总得跑在某处,不一定是 Pod
是不是 Pod,取决于你把它放哪:
- 集群内:Nacos、Kafka、Redis 用 Deployment 或 StatefulSet。有状态用 StatefulSet + PVC。
- 集群外:已有虚机、云托管。业务 Pod 用 IP 或域名连出去。集群里可以建无 selector 的 Service 指到外部地址,不是必须。
用了 Nacos 不等于必须再起一个 Nacos Pod。选择「交给这个集群管」时,它才是 Pod。业务服务同理:K8s 只管进了这个集群的工作负载,没进的继续在原来的机器上。混合不是折中,是默认形态。真正要设计的是两边怎么看见对方。
混部能通,前提是别假装还在一张网上
集群内 → 外面:Pod 出网一般没问题,外面给稳定域名或 SLB。
外面 → 集群内:ClusterIP 只在集群里有效。要用 NodePort、LoadBalancer、Ingress,或专线打进集群网络。这是最容易踩的坑。
服务发现:全在 K8s 时 svc.cluster.local 够用;一半在外面时,这套 DNS 外面解析不了。要么共用注册中心,要么走统一网关或内部域名。前面说「混部才需要 Nacos」,原因就在这里。
无状态、弹性大、发布勤的适合先上;已经稳定的数据库和重量级中间件,外置往往更省事。
运行时、拆法、中间件分开选:不必为了用 K8s 先拆微服务,也不必为了微服务把 Nacos 和 Kafka 都塞进同一个集群。各管一段,边界清楚就行。
