Do You Still Need Spring Cloud on Kubernetes?
Published on
·8 min read

Do You Still Need Spring Cloud on Kubernetes?

Authors
  • avatar
    Name
    Bert / DOTUNE
    Developer

"Spring Cloud vs Kubernetes" is a category error. The two don't compete; they live at different layers. Kubernetes runs and scales your workloads. Spring Cloud Alibaba governs how those services talk to each other — discovery, configuration, flow control, transactions. The useful question is narrower: of the things Spring Cloud Alibaba does, which ones does Kubernetes already do for you, and which still have to live in your code?

The answer, compressed into one line: Kubernetes is excellent at keeping the process layer healthy, and it has no idea what your business means. Everything that depends on business semantics stays in Spring Cloud. Everything that is pure infrastructure, you can stop building yourself.


Two layers, one system

Before comparing features, it helps to be explicit about the split:

  • Kubernetes owns the platform layer: deployment, scaling, networking, storage, health checks, self-healing. It decides how your service runs.
  • Spring Cloud Alibaba owns the application layer: service discovery, configuration, circuit breaking, distributed transactions. It decides how your services cooperate.

A pod that crashes gets restarted by Kubernetes. A downstream service that's slow and needs its callers to degrade gracefully is a Spring Cloud problem. Neither layer can do the other's job — and a lot of the "which one do I need" confusion comes from treating them as if they could.


Feature by feature

Here's the comparison, then the detail on each row.

CapabilityKubernetes nativeSpring Cloud Alibaba
Service discoveryService + CoreDNSNacos registry
ConfigurationConfigMap / SecretNacos Config
Load balancingkube-proxy (Service)Spring Cloud LoadBalancer / OpenFeign
Circuit breaking & flow controlprobes + HPASentinel
API gatewayIngressSpring Cloud Gateway
Distributed transactionsSeata

Service discovery

If everything runs on Kubernetes, service discovery is already solved at the platform level. A Service plus CoreDNS gives every pod a stable DNS name — order-service.default.svc.cluster.local — and the endpoints update automatically as pods come and go. There is no registry to run, no client to keep in sync.

Nacos registry earns its keep only when the platform can't answer the question for you: hybrid deployments where some services run on VMs and some in the cluster, or discovery across multiple clusters. In a single, pure-Kubernetes environment, running a Nacos registry for discovery is a redundant layer. Prefer the Kubernetes Service first; introduce Nacos discovery when you actually have something outside the cluster to discover.

Configuration

The division here is cleanest: infrastructure configuration belongs in ConfigMap/Secret; business configuration belongs in Nacos Config. A database endpoint or a message-queue address is operational and changes with the environment — put it in a ConfigMap, tied to your deployment manifests and auditable through Git. A feature flag, a timeout, a circuit-breaker threshold, a downstream URL — these are business settings that change at a different rhythm and shouldn't force a redeploy.

ConfigMap has two hard limits that push business config toward Nacos: no dynamic refresh (changing a value requires a pod restart to take effect) and no version rollback. Nacos Config gives you both — change a value in the console and it pushes to running applications:

@RefreshScope
@RestController
public class PromoController {
    @Value("${promo.threshold:10}")
    private int threshold;
    // updates when the value changes in Nacos, no restart needed
}

The operational trap to watch is duplication. If the same property exists in a Helm value, a ConfigMap, a Spring profile, and a Nacos namespace, debugging becomes a guessing game. Decide the precedence up front and write it down.

Load balancing

Kubernetes load-balances at the network layer through kube-proxy and the Service abstraction; Spring Cloud LoadBalancer (and the declarative OpenFeign clients) do it at the application layer. For the common case — spread requests across the replicas of a service — Kubernetes is enough, and it's one less moving part to configure.

OpenFeign's value isn't really load balancing in a Kubernetes world; it's the declarative client itself — an interface that compiles into a typed HTTP client, with the load-balancing and retry behavior attached. That convenience still matters on Kubernetes even though the balancing itself is redundant.

Circuit breaking and flow control

This is the row where Spring Cloud is not optional.

Kubernetes can restart a crashed pod and scale replicas based on CPU or memory. What it cannot do is make a business decision when a dependency is slow or failing. "Return cached data when the product service times out" versus "fail fast and ask the user to retry later" is a decision that only lives in your code. Sentinel exists for exactly this:

@SentinelResource(value = "getProduct", fallback = "getProductFallback")
public Product getProduct(Long id) {
    return productClient.get(id);          // remote call
}

public Product getProductFallback(Long id, Throwable t) {
    return cache.get(id);                  // degrade to cached data — a business choice
}

A Kubernetes liveness probe can't express that. It can only say "this pod is unhealthy, restart it":

livenessProbe:
  httpGet:
    path: /actuator/health
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10

The two mechanisms are complementary, not competing. Kubernetes keeps the process alive; Sentinel keeps one slow dependency from taking down every service that calls it. The moment your circuit breaker's fallback depends on the meaning of the data — cache it, ignore it, or reject the request — that logic cannot be moved to the platform.

API gateway

Kubernetes gives you Ingress for routing external traffic into the cluster; Spring Cloud Gateway is a code-based gateway that runs inside it. Ingress handles the basics — host/path routing, TLS termination. Spring Cloud Gateway earns its place when you need logic in the gateway itself: custom filters, authentication flows, response rewriting, or routing rules that depend on your domain. Many teams run Spring Cloud Gateway behind an Ingress, which is a reasonable division: Ingress does the L4/L7 plumbing, the gateway does the application-level policy.

Distributed transactions

Kubernetes has nothing here. When a single business operation spans multiple services — create an order, deduct inventory, debit the account — and needs to commit or roll back as a unit, that's an application-layer concern. Seata's @GlobalTransactional is the Spring Cloud Alibaba answer:

@GlobalTransactional
public void createOrder(OrderRequest req) {
    orderService.create(req);
    inventoryService.deduct(req.getProductId(), req.getQuantity());
    accountService.debit(req.getUserId(), req.getAmount());
}

There is no Kubernetes resource that substitutes for this. It's the clearest example of the general rule: the platform doesn't know your business invariants.


A word on service mesh

I should be upfront: I haven't run Istio in a real production system, so take this as awareness rather than firsthand experience. A service mesh moves traffic management — canary releases, fault injection, mTLS, fine-grained routing — into a sidecar proxy, with no code changes, at the cost of added latency and a per-pod sidecar. For a single-language Java team it's usually more machinery than the problem justifies; for a large multi-language fleet it earns its complexity. If you're on Kubernetes with a Java-only stack, Spring Cloud Alibaba covers most of what you'd otherwise reach for a mesh to get.


So do you still need Spring Cloud?

Yes — conditionally. The scope has narrowed, but the parts that remain are the parts Kubernetes structurally can't do.

Keep Spring Cloud Alibaba when:

  • You're a Java team and want the fastest path to working microservices.
  • You need business-level circuit breaking and flow control (Sentinel), where the fallback depends on domain logic.
  • You need distributed transactions (Seata).
  • You run a hybrid of VMs and Kubernetes, or services across multiple clusters.

Move to Kubernetes-native when:

  • Everything is on Kubernetes — drop the registry and rely on Service + CoreDNS.
  • You have a genuinely multi-language fleet that can't share a Java governance SDK.

The pragmatic default most teams land on is Spring Cloud on Kubernetes: run your Spring Cloud Alibaba services inside the cluster, let Kubernetes handle deployment and scaling, and keep Nacos/Sentinel/Seata for the application-level concerns. You don't have to pick one and abandon the other — you stop reinventing what Kubernetes already does, and you stop expecting Kubernetes to understand your business.


The Bottom Line

Draw the boundaries and the choice stops being a fight. Give the platform layer — running, scaling, restarting, service discovery, infra config — to Kubernetes. Keep the business layer — circuit-breaking fallbacks, dynamic business config, distributed transactions — in Spring Cloud Alibaba. And if traffic governance someday outgrows what code can express cleanly, that's the moment to look seriously at a service mesh, not before.

The line that keeps this all straight: don't rebuild what Kubernetes does well, and don't ask Kubernetes to understand what your business means.