Part II — Spring Boot App Development With Skaffold, Kustomize, and Jib
This is the second part of a series of articles where I describe my development workflow and CI/CD practices. If you are interested in knowing the motivations, take a look at the introductory article.
In this article, I will describe how I develop a Spring Boot app (or microservice) using Skaffold and Google Jib. This article assumes familiarity with Spring, Maven, Docker, and Kubernetes: I’m not going to explain them here since the focus is the development workflow, not the technology itself.
The app that we are going to use in these examples is quite a simple: it exposes a GET HTTP endpoint that returns a “Hello World” salute.
Since we want to show the deployment process and not provide a complex application, this will suffice our goal.
Why using Skaffold, Kustomize and Jib
We can leverage the same tools for both local development and CI/CD workflows. Let’s take a look at these tools.
Skaffold is a tool, developed by Google, that
[…] handles the workflow for building, pushing and deploying your application, allowing you to focus on what matters most: writing code.
In practice, you have your favourite IDE, you write your code and deploy it to cluster.
You usually run Skaffold in background while coding, either in dev or debug mode, so that build and deployment happens transparently to you.
Skaffold uses a descriptor file called skaffold.yaml where you describe your service, build actions and related metadata. It supports several build backends (e.g., local Docker for Desktop or remote Cloud Build) and also different backends for generating the Kubernetes YAML descriptors that are then applied to the target cluster (like Helm or Kustomize).
I’m used to Kustomize, so by skaffold.yaml is configured accordingly. And, by the way, it also included in Kubectl 1.14.x onwards :)
In Kubernetes world there are several tools that helps DevOps to deploy software applications consistently, you may have heard of Helm . I find the use of placeholders in my YAML descriptors to be … disturbing. With Kustomize, instead, you provide a base skeleton and then patches for them: in this way we can provide a base deployment and then only describe the differences for different environments (e.g., the amount of replicas).
Google provides extensions for some popular IDEs like Visual Studio Code and IntelliJ Idea, so you can benefit a bit of ease to use. Anyway, who’s scared of using the command line these days? :)
In order to build Docker images, we could either write our own Dockerfile or use Jib, a tool (developed by Google) for containerizing Java applications, which is good for any Spring Boot app.
I use Jib because it helps build images in a way that is:
* Fast — Deploy your changes fast. Jib separates your application into multiple layers, splitting dependencies from classes. Now you don’t have to wait for Docker to rebuild your entire Java application — just deploy the layers that changed.
* Reproducible — Rebuilding your container image with the same contents always generates the same image. Never trigger an unnecessary update again.
* Daemonless — Reduce your CLI dependencies. Build your Docker image from within Maven or Gradle and push to any registry of your choice. No more writing Dockerfiles and calling docker build/push.
Additionally, Jibs enforces some good practices, like distroless containers, that reduce the attack surface .
For the purpose of using Jib and Skaffold together, we just have to add the Jib Maven plugin to our pom.xml and we are done and update the skaffold.yaml file.
Note that Spring devs are actively supporting Cloud Native Buildpacks from Spring Boot 2.3.x, so you may want to use their support instead of using Jib but since Skaffold provides out-of-the-box support for Jib I will stick with it for … historical reasons (well, yes, I’m too lazy to change things unless I gain some real advantages :))
The Git repository structure
The sample application code is available on GitHub and it has a familiar layout, being generating with Spring Initialzr . Then there are
- skaffold.yaml — the Skaffold deployment descriptor
- kustomization/ — contains the YAML descriptors organized by profile
In skaffold.yaml we configure the usage of Jib for building the image and two profiles:
- local — which will be automatically activate if you use Docker for Desktop
- aws — which must we are going to use in our CI/CD pipeline:
Note that there is no Docker registry prefix since we are going to use environment variables to set them at build time when building on AWS with CodeBuild.
All Kubernetes descriptors are provided in kustomization/base/ directory, where they are used by default. I like to keep YAML descriptors separated so I have
- app.deployment.yaml — the deployment resource for this application (which image, how many replicas, environment variables, …)
- app.service.yaml — the load balanced service resource that will act as entry point for our sample app;
- app.cm.yaml — a simple configuration map that we pull configuration options from and then use in app.deployment.yaml
Being an example, the only changes we execute in aws profile are:
- to update the number of replicas to 2;
- to change the Kubernetes namespace to “hello-world-application” in order to match the one we are going to use in our dev cluster.
Note that these are just a couple of arbitrary changes to make this example less boring: take a look at Kustomize documentation in order to understand more.
Creating your own microservices
If you want to create your own microservices, I’ve written a Maven Archetype that you can use for your own convenience: nothing fancy but at least your can start without copying and pasting code around ;)
Well, there is nothing more to be explained here: we have our simple service to deploy to our cluster. Please familiarize with it and try to change it and see how Skaffold reacts: take a look at the README.md if you need some more practical insight about the code or need more references!
In next part we are going to take a look at how to create our infrastructure for supporting our development workflow in AWS using AWS CDK.