Using Azure Kubernetes Services for Java Spring Boot Microservices
Tim Kelly9 min read • Published Apr 15, 2024 • Updated Apr 15, 2024
FULL APPLICATION
Rate this tutorial
In the early days of software development, application development consisted of monolithic codebases. With challenges in scaling, singular points of failure, and inefficiencies in updating, a solution was proposed. A modular approach. A symphony of applications managing their respective domains in harmony. This is achieved using microservices.
Microservices are an architectural approach that promotes the division of applications into smaller, loosely coupled services. This allows application code to be delivered in manageable pieces, independent of each other. These services operate independently, addressing a lot of the concerns of monolithic applications mentioned above.
While each application has its own needs, microservices have proven themselves as a viable solution time and time again, as you can see in the success of the likes of Netflix.
In this tutorial, we are going to deploy a simple Java Spring Boot microservice application, hosted on the Azure Kubernetes Service (AKS). AKS simplifies deploying a managed Kubernetes cluster in Azure by offloading the operational overhead to Azure. We'll explore containerizing our application and setting up communication between our APIs, a MongoDB database, and the external world. You can access the full code here:
1 git clone https://github.com/mongodb-developer/simple-movie-microservice.git
Though we won't dive into the most advanced microservice best practices and design patterns, this application gives a simplistic approach that will allow you to write reviews for the movies in the MongoDB sample data, by first communicating with the review API and that service, verifying that the user and the movie both exist. The architecture will look like this.
Simple, right? Exactly! And AKS makes it even easier to communicate with other APIs managed on the service. In a microservices architecture, services need to discover each other dynamically. Kubernetes itself acts as a service registry, where each service in the cluster has a unique name. So when we communicate with our other services in ReviewController.java, we simply send a request to
http://user-management-service/users/
. In this demo application, communication is done with RESTful HTTP/S requests, using RestTemplate.Before you begin, you'll need a few prerequisites to follow along with this tutorial, including:
- A MongoDB Atlas account, if you don't have one already, with a cluster ready with the MongoDB sample data.
- Azure CLI, or you can install Azure PowerShell, but this tutorial uses Azure CLI. Sign in and configure your command line tool following the steps in the documentation for Azure CLI and Azure PowerShell.
- Docker for creating container images of our microservices.
- Java 17.
- Maven 3.9.6.
Starting from the very beginning, set up an Azure Kubernetes Service (AKS) cluster.
Install
kubectl
, the Kubernetes command-line tool, via the Azure CLI with the following command (you might need to sudo this command), or you can download the binaries from:1 az aks install-cli
Log into your Azure account using the Azure CLI:
1 az login
Create an Azure Resource Group:
1 az group create --name myResourceGroup --location northeurope
Create an AKS cluster: Replace
myAKSCluster
with your desired cluster name. (This can take a couple of minutes.)1 az aks create --resource-group myResourceGroup --name myAKSCluster --node-count 2 --enable-addons monitoring --generate-ssh-keys
After successfully creating your AKS cluster, you can proceed to configure
kubectl
to use your new AKS cluster. Retrieve the credentials for your AKS cluster and configure kubectl
:1 az aks get-credentials --resource-group myResourceGroup --name myAKSCluster
Create an ACR to store and manage container images across all types of Azure deployments:
1 az acr create --resource-group <your-resource-group> --name <your-container-registry> --sku Basic
Note: Save the app service id here. We’ll need it later when we are creating a service principal.
Log into ACR:
1 az acr login --name <your-container-registry>
Each of your applications (User Management, Movie Catalogue, Reviews) has a
Dockerfile
. Create a .jar by running the command mvn package
for each application, in the location of the pom.xml file. Depending on your platform, the following steps are slightly different.For those wielding an M1 Mac, a bit of tweaking is in order due to our image's architecture. As it stands, Azure Container Apps can only jive with linux/amd64 container images. However, the M1 Mac creates images as
arm
by default. To navigate this hiccup, we'll be leveraging Buildx, a handy Docker plugin. Buildx allows us to build and push images tailored for a variety of platforms and architectures, ensuring our images align with Azure's requirements.To build your image, make sure you run the following command in the same location as the
Dockerfile
. Repeat for each application.1 docker build -t movie-catalogue-service .
Or you can run the following command from the simple-movie-microservice folder to loop through all three repositories.
1 for i in movie-catalogue reviews user-management; do cd $i; ./mvnw clean package; docker build -t $i-service .; cd -; done
If you are using an M1 Mac, use the following commands to use Buildx to create your images:
1 docker buildx install
Next, enable Buildx to use the Docker CLI:
1 docker buildx create --use
Open a terminal and navigate to the root directory of the microservice where the
Dockerfile
is located. Run the following command to build the Docker image, replacing movie-catalogue-service
with the appropriate name for each service.1 docker buildx build --platform linux/amd64 -t movie-catalogue-service:latest --output type=docker .
Now, we're ready to tag and push your images. Replace
<your-acr-name>
with your actual ACR name. Repeat these two commands for each microservice.1 docker tag movie-catalogue-service <your-acr-name>.azurecr.io/movie-catalogue-service:latest 2 docker push <your-acr-name>.azurecr.io/movie-catalogue-service:latest
Or run this script in the terminal, like before:
1 ACR_NAME="<your-acr-name>.azurecr.io" 2 3 for i in movie-catalogue reviews user-management; do 4 # Tag the Docker image for Azure Container Registry 5 docker tag $i-service $ACR_NAME/$i-service:latest 6 # Push the Docker image to Azure Container Registry 7 docker push $ACR_NAME/$i-service:latest 8 done
Now that we have our images ready, we need to create Kubernetes deployment and service YAML files for each microservice. We are going to create one mono-file to create the Kubernetes objects for our deployment and services. We also need one to store our MongoDB details. It is good practice to use secrets for sensitive data like the MongoDB URI.
First, you'll need to create a secret to securely pass the MongoDB connection string to your microservices. In Kubernetes, the data within a secret object is stored as base64-encoded strings. This encoding is used because it allows you to store binary data in a format that can be safely represented and transmitted as plain text. It's not a form of encryption or meant to secure the data, but it ensures compatibility with systems that may not handle raw binary data well.
Create a Kubernetes secret that contains the MongoDB URI and database name. You will encode these values in Base64 format, but Kubernetes will handle them as plain text when injecting them into your pods. You can encode them with the bash command, and copy them into the YAML file, next to the appropriate data keys:
1 echo -n 'your-mongodb-uri' | base64 2 echo -n 'your-database-name' | base64
1 apiVersion: v1 2 kind: Secret 3 metadata: 4 name: mongodb-secret 5 type: Opaque 6 data: 7 MONGODB_URI: <Base64 encoded MongoDB URI> 8 MONGODB_DATABASE: <Base64 encoded MongoDB database name>
Run the following command to apply your secrets:
1 kubectl apply -f mongodb-secret.yaml
So, while base64 encoding doesn't secure the data, it formats it in a way that's safe to store in the Kubernetes API and easy to consume from your applications running in pods.
If your ACR is private, you'll need to ensure that your Kubernetes cluster has the necessary credentials to access it. You can achieve this by creating a Kubernetes secret with your registry credentials and then using that secret in your deployments.
The next step is to create a service principal or use an existing one that has access to your ACR. This service principal needs the
AcrPull
role assigned to be able to pull images from the ACR. Replace <your-service-principal-name>
, <your-subscription-id>
, <your-resource-group-name>
, and <your-acr-name>
with your own values. : This can be any unique identifier you want to give this service principal. : You can get the id for the subscription you’re using with az account show --query id --output tsv
. : Use the same resource group you have your AKS set up in. : This is the Azure Container Registry you have your images stored in.1 az ad sp create-for-rbac --name <your-service-principal-name> --role acrPull --scopes /subscriptions/<your-subscription-id>/resourceGroups/<your-resource-group-name>/providers/Microsoft.ContainerRegistry/registries/<your-acr-name>
This command will output JSON that looks something like this:
1 { 2 "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", 3 "displayName": "<your-service-principal-name>", 4 "password": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", 5 "tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 6 }
appId
is your<your-service-principal-app-id>
.password
is your<your-service-principal-password>
.
Note: It's important to note that the
password
is only displayed once at the creation time. Make sure to copy and secure it.Create a Kubernetes secret with the service principal's credentials. You can do this with the following command:
1 kubectl create secret docker-registry acr-auth \ 2 --namespace default \ 3 --docker-server=<your-acr-name>.azurecr.io \ 4 --docker-username=<your-service-principal-app-id> \ 5 --docker-password=<your-service-principal-password> \ 6 --docker-email=<your-email>
There are a couple of points to note in the YAML file for this tutorial, but these points are not exhaustive of everything happening in this file. If you want to learn more about configuring your YAML for Kubernetes, check out the documentation for configuring Kubernetes objects.
- We will have our APIs exposed externally. This means you will be able to access the endpoints from the addresses we'll receive when we have everything running. Setting the
type: LoadBalancer
triggers the cloud provider's load balancer to be provisioned automatically. The external load balancer will be configured to route traffic to the Kubernetes service, which in turn routes traffic to the appropriate pods based on the service's selector. - The
containers:
section defines a single container namedmovie-catalogue-service
, using an image specified by<your-container-registry>/movie-catalogue-service:latest
. containerPort: 8080
exposes port 8080 inside the container for network communication.- Environment variables
MONGODB_URI
andMONGODB_DATABASE
are set using values from secrets (mongodb-secret
), enhancing security by not hardcoding sensitive information. imagePullSecrets: - name: acr-auth
allows Kubernetes to authenticate to a private container registry to pull the specified image, using the secret we just created.
1 --- 2 apiVersion: apps/v1 3 kind: Deployment 4 metadata: 5 name: movie-catalogue-service-deployment 6 spec: 7 replicas: 1 8 selector: 9 matchLabels: 10 app: movie-catalogue-service 11 template: 12 metadata: 13 labels: 14 app: movie-catalogue-service 15 spec: 16 containers: 17 - name: movie-catalogue-service 18 image: <your-container-registry>/movie-catalogue-service:latest 19 ports: 20 - containerPort: 8080 21 env: 22 - name: MONGODB_URI 23 valueFrom: 24 secretKeyRef: 25 name: mongodb-secret 26 key: MONGODB_URI 27 - name: MONGODB_DATABASE 28 valueFrom: 29 secretKeyRef: 30 name: mongodb-secret 31 key: MONGODB_DATABASE 32 imagePullSecrets: 33 - name: acr-auth 34 --- 35 apiVersion: v1 36 kind: Service 37 metadata: 38 name: movie-catalogue-service 39 spec: 40 selector: 41 app: movie-catalogue-service 42 ports: 43 - protocol: TCP 44 port: 80 45 targetPort: 8080 46 type: LoadBalancer 47 ---
Remember, before applying your Kubernetes YAML files, make sure your Kubernetes cluster has access to your ACR. You can configure this by granting AKS the ACRPull role on your ACR:
1 az aks update -n <your-AKS-Cluster> -g <your-resource-group> --attach-acr <your-ACR>
Replace
<your-AKS-Cluster>
, <your-resource-group>
, and <your-ACR>
with your AKS cluster name, Azure resource group name, and ACR name, respectively.Apply the YAML file with
kubectl
:1 kubectl apply -f all-microservices.yaml
Once deployed, it may take a few minutes for the LoadBalancer to be provisioned and for the external IP addresses to be assigned. You can check the status of your services with:
1 kubectl get services
Look for the external IP addresses for your services and use them to access your microservices.
After deploying, ensure your services are running:
1 kubectl get pods
Access your services based on the type of Kubernetes service you've defined (e.g., LoadBalancer in our case) and perform your tests.
You can test if the endpoint is running with the CURL command:
1 curl -X POST http://<ReviewAPI-External-IP>/reviews \ 2 -H "Content-Type: application/json" \ 3 -d '{"movieId": "573a1391f29313caabcd68d0", "userId": "59b99db5cfa9a34dcd7885b8", "rating": 4}'
And this review should now appear in your database. You can check with a simple:
1 curl -X GET http://<ReviewAPI-External-IP>/reviews
Hooray!
As we wrap up this tutorial, it's clear that embracing microservices architecture, especially when paired with the power of Kubernetes and Azure Kubernetes Service (AKS), can significantly enhance the scalability, maintainability, and deployment flexibility of applications. Through the practical deployment of a simple microservice application using Java Spring Boot on AKS, we've demonstrated the steps and considerations involved in bringing a microservice architecture to life in the cloud.
Key takeaways:
- Modular approach: The transition from monolithic to microservices architecture facilitates a modular approach to application development, enabling independent development, deployment, and scaling of services.
- Simplified Kubernetes deployment: AKS abstracts away much of the complexity involved in managing a Kubernetes cluster, offering a streamlined path to deploying microservices at scale.
- Inter-service communication: Utilizing Kubernetes' internal DNS for service discovery simplifies the communication between services within a cluster, making microservice interactions more efficient and reliable.
- Security and configuration best practices: The tutorial underscored the importance of using Kubernetes secrets for sensitive configurations and the Azure Container Registry for securely managing and deploying container images.
- Exposing services externally: By setting services to
type: LoadBalancer
, we've seen how to expose microservices externally, allowing for easy access and integration with other applications and services.
The simplicity and robustness of Kubernetes, combined with the scalability of AKS and the modularity of microservices, equip developers with the tools necessary to build complex applications that are both resilient and adaptable. If you found this tutorial useful, find out more about what you can do with MongoDB and Azure on our Developer Center.
Are you ready to start building with Atlas on Azure? Get started for free today with MongoDB Atlas on Azure Marketplace.
Top Comments in Forums
There are no comments on this article yet.