1

My team (full stack) team develops single-page web applications using react as a client and spring-boot java apps as back-end.

We deploy the applications on kubernetes. However, we have troubles making rolling updates of the kubernetes pods seamless. Here's our packaging / deployment process:

  1. The CI pipeline builds a docker images packaging the java application as an executable jar file and the compile javascript/css assets that form the react client.
  2. That image has 2 entry points: one for starting the jar file (java back-end) and one which starts an nginx that serves the client assets (js/css).
  3. A haproxy cluster routes requests based on URL paths to either nginx or the java back-end.

This approach works fine so far.

However, during a rolling update, there will be a number of pods with an "old" java back-end. This leads to the situation that clients with a newly deployed version of the react client make requests to old java back-ends, because haproxy simply routes api traffic to any available java backend.

As i understand haproxy seems to have support for sticky-backends based on some rules, but this defeats the purpose of having a cluster of backends for load-balancing.

How do others tackle this problem?

2

Modern way to achieve seamless roll-out in this scenario is not on the load balancing (i.e. haproxy) side, but instead via proper deployment planning, backward compatibility and/or feature flags.

The issue with load-balancer tinkering is that in the end of day even if you get sticky sessions right it's not going to solve your problem. The reason for that - imagine a user that opened your app in a browser with old react. Underneath, you deploy new java back-end. If the user doesn't reload her browser - she's still going to use old react app with new back-end no matter what (just because javascript is already loaded to the browser).

Instead, look at following example how you can achieve it using API versioning:

  • Let's assume that data model on one of your api end-points is going to change
  • Let's say your old api was /v1 (or even un-versioned)

Here is how you can solve it:

  1. You create /v2 version of your api, register /v2 version in your react app
  2. Your latest java back-end needs to still have both /v1 and /v2
  3. You first deploy all java back-ends to your back-end deployment with /v2 support (all your UI is still /v1 - but this is fine, because latest back-end supports it)
  4. Once all back-ends are deployed, you can start roll-out of your UI deployment with latest image (again, experience is seamless since all back-ends support both /v1 and /v2)

As you can see, in the above approach, version conflict is avoided and experience is seamless.

P.s. You might want to consider splitting your docker image in 2 - it's a little odd to have a multi-purpose image with 2 entrypoints. There may be little justification for this design.

| improve this answer | |
  • I agree with @toledor. The usual use case when it comes to microservices is that you keep your deployable as minimal in size as possible with many instances as needed. This is something that K8S handles very well and is suited for the purpose. Best practice is to have 1 deployment for each service. That way you can control your entrypoint better too. – Karthik Venkatesan Jul 17 at 3:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.