Kubernetes (GKE) + Spring Boot + Flask = Awesomeness
Deploying Spring Boot and Flask application on Kubernetes with auto scaling enabled
Ever thought of leveraging that cool auto-scaling feature of Kubernetes for your app, and also practice deploying a Spring Boot application on Kubernetes, and throw in a flask app in the mix too just for fun?
Because that’s what we did. Mostly to test deploying spring boot app on Kubernetes, but also to just see the deployment scale up as the load increases and scale back down when the traffic reduces, which, again, is really cool.
To begin with, you’d need a GKE cluster, here’s the quickstart guide: https://cloud.google.com/kubernetes-engine/docs/quickstart
For our case we created two nodepools
- api-pool
- 1 node, set to autoscale to 3 nodes max
- e2-small - flask-pool
- 1 node, no autoscaling
- custom e2 instance with 6vcpu and 16GB memory
Why this specific configuration? We’ll get to that in a bit ;)
Now we get to the core of our app, the backend.
lyrics-scraper-google
So I tried looking up some free APIs that could provide me lyrics for a song, but couldn’t really find anything. I had a script that used selenium to scrap lyrics using Genius API. But it seems like they made some changes to their page structure and that no longer works now. I tried making some changes but nah, nothing worked.
And there I was, almost defeated even before I had started. Listening to some songs that you listened to when you feel defeated. When I had to look lyrics of a song I couldn’t understand.
And guess what, I didn’t use Genius, I used Google. I just typed in the song’s name, followed by “lyrics”, as the query and there it was.
So I modified the scraper and now we are getting lyrics directly from google results. Check it out here.
You need to provide the track name as the track query parameter and the artist’s name as the artist query parameter. The listener is set on the path /lyrics-scraper
Of course this also puts a limitation that if google doesn’t return the lyrics as a search result, the scraper would fail.
Create an image
Next we are gonna deploy this flask app on GKE. Remember that flask-pool nodepool we created earlier? Yep that’s where this app is going. But first…
We need to create an image, and we need to take care of dependencies too since we are using selenium with chromedriver.
Here’s the Dockerfile I made, after doing some “google research”.
And this is the requirements.txt file
I’m not gonna put all the source code here, mostly because I don’t want this post to be of the length of a scientific paper.
Now we can just build this image and push it to GCR using the following command
gcloud builds — project <your gcp project name> submit — tag gcr.io/<your gcp project name>/<your image name>:<your image tag>
Deploy the application on GKE
Once we have the image, we just need to create a yaml file to deploy our pods on the node, here’s what I made.
Apply this yaml file to create the app deployment and once we have that, expose it as a service using the Expose option shown in the below screenshot.
You would need to provide the service name. Keep a track of that, we are gonna need that for our next step.
Expose the application to the internet using ingress
Deploy an ingress resource on the cluster to forward incoming traffic to the backend flask app. Here’s the one I created, you would need to provide the service name mentioned in the previous step.
If it all goes right, you should be able to see the public endpoint under the Ingress section of GKE.
We now have an endpoint that returns lyrics of a song. Test it providing some artists and track parameters.
lyrics-api
We are now going to create a Spring Boot application that uses the flask app created above to get the lyrics and of a song and then store it in a database.
Check it out here.
The basic logic here is to scrap for the lyrics if its not present in the database and then store it in the database, so when the next time the request for the same lyrics come in, we just fetch it from the DB, which is way quicker than scraping it.
Database
Create a DB Server on GCP to store lyrics on. You can follow this guide.
Once done, you would also need to add a network under the Server’s Connections setting in order to enable access.
Get the DB Server IP which we are going to use to create a connection string.
We would be receiving the incoming requests on the path /lyrics-api.
We are creating an Entity, SongLyrics, which would create a table by the same in the database, and store all these scraped song lyrics.
App to DB Connectivity
Add the following to the app’s application.properties file. In our case we have named our DB as LyricsAPI
App Dependencies
Check out the gradle file for our application to find the dependencies we have set up.
Creating the image
Use Google’s jib tool to create an image out of your spring boot app following this guide.
Make sure you properly authenticate the tool before exporting the image.
Now we can create the image using the following command:
gradle jib — image=gcr.io/<your project name>/lyrics-api:v3
Deployment on GKE
Now that we have our image, let’s deploy it on GKE. This is the deployment yaml file I created, and this is the corresponding ingress resource to expose the app to the internet.
Make sure to create a service before deploying the ingress resource like we did for the flask app.
One thing to note here is that I had to add listeners on the root path for both the ingress resources. GKE by default seems to add a health probe on the root path of the backend. This isn’t really an issue, if anything, we can set a listener on the root path which can be used with a readiness probe.
Now you should have a public endpoint to access your spring boot app to get the lyrics of your favorite songs (just like they say it in the ads).
We have successfully deployed our application on GKE (if nothing goes south that is).
But that’s not where the story ends. Enter…
Auto-scaling
Remember when I said we’ll get into the nodepool configuration later? Yeah, this is later.
- flask-pool: we have set this nodepool to have only one node and this single node has enough resources to support 3 pods of our flask-app.
Then go to the Workloads section and set the flask app deployment to auto scale up to 3 pods max.
This is an implementation of Horizontal Pod Scaling where we are only using single node to host all the pod replicas. - api-pool: for this nodepool we are doing something different. We are not scaling up the app on the same node, instead we are scaling up the nodepool such that each node holds only one replica of the pod.
Here, we have the nodepool set to auto scale up to 3 nodes max. Now, go the Workloads section again, and the spring boot app deployment to auto scale up to 3 pods max.
If you notice, you’d see that in our spring boot deployment yaml file we are setting up pod anti affinity rule, this ensures that if a node already hosts a pod of our app, no other pod of the same app is scheduled on it.
This means that when our deployment scales up and creates a new pod, the pod remains unscheduled, consequently, the nodepool is scaled up as well so as to create a new node to host the pod on.
This is an implementation of Cluster Auto-scaling.
And now, we are all set. Go the GCP console, send high volume of requests to your app endpoints (you could send get requests in a loop or use a tool like Apache ab), and watch your apps scale up. Trust me its very satisfying.
Our application has a lot a room for improvement. To begin with, we could definitely use a better lyrics scraper. Also, we should probably separate the interaction with the flask app in our spring boot, and put it in a new method.
Feel free to drop any questions in the comments section. I’ll check them out and update the story if I missed something.