TransWikia.com

Deploying a single-page web application with a java backend on kubernetes and haproxy

DevOps Asked by pulse00 on August 22, 2021

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?

One Answer

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.

Correct answer by taleodor on August 22, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP