# Using Azure Kubernetes Services for Java Spring Boot Microservices

## Introduction
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:

```bash
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.

, 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. 

## Prerequisites
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.
- A Microsoft Azure account with an active subscription.
- 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.

## Set up an Azure Kubernetes Service cluster
Starting from the very beginning, set up an Azure Kubernetes Service (AKS) cluster.

### Install kubectl and create an 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:
```bash
az aks install-cli
```

Log into your Azure account using the Azure CLI:
```bash
az login
```

Create an Azure Resource Group:
```bash
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.)
```bash
az aks create --resource-group myResourceGroup --name myAKSCluster --node-count 2 --enable-addons monitoring --generate-ssh-keys
```

### Configure kubectl to use your AKS cluster
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`:
```bash
az aks get-credentials --resource-group myResourceGroup --name myAKSCluster
```

### Create an Azure Container Registry (ACR)
Create an ACR to store and manage container images across all types of Azure deployments:
```bash
az acr create --resource-group  --name  --sku Basic
```
> Note: Save the app service id here. We’ll need it later when we are creating a service principal.

Log into ACR:
```bash
az acr login --name 
```
## Containerize your microservices application
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.

### Build the Docker image (not M1 Mac)
To build your image, make sure you run the following command in the same location as the `Dockerfile`. Repeat for each application.
```bash
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.

```bash
for i in movie-catalogue reviews user-management; do cd $i; ./mvnw clean package; docker build -t $i-service .; cd -; done
```
### Build the Docker image (M1 Mac)
If you are using an M1 Mac, use the following commands to use Buildx to create your images:
```bash
docker buildx install
```

Next, enable Buildx to use the Docker CLI:
```bash
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.
```bash
docker buildx build --platform linux/amd64 -t movie-catalogue-service:latest --output type=docker .
```

### Tag and push
Now, we're ready to tag and push your images. Replace `` with your actual ACR name. Repeat these two commands for each microservice. 
```bash
docker tag movie-catalogue-service .azurecr.io/movie-catalogue-service:latest
docker push .azurecr.io/movie-catalogue-service:latest
```
**Or** run this script in the terminal, like before:

```bash
ACR_NAME=".azurecr.io"

for i in movie-catalogue reviews user-management; do 
    # Tag the Docker image for Azure Container Registry
    docker tag $i-service $ACR_NAME/$i-service:latest
    # Push the Docker image to Azure Container Registry
    docker push $ACR_NAME/$i-service:latest
done
```

## Deploy your microservices to AKS
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.

### Create a Kubernetes secret for 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:

```bash
echo -n 'your-mongodb-uri' | base64
echo -n 'your-database-name' | base64
```

This is the mongodb-secret.yaml.
```yaml
apiVersion: v1
kind: Secret
metadata:
  name: mongodb-secret
type: Opaque
data:
  MONGODB_URI: 
  MONGODB_DATABASE: 

```

Run the following command to apply your secrets:
```bash
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.

### Authorize access to the ACR
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  ``, ``, ``, and `` 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.

```bash
az ad sp create-for-rbac --name  --role acrPull --scopes /subscriptions//resourceGroups//providers/Microsoft.ContainerRegistry/registries/
```

This command will output JSON that looks something like this:
```bash
{   
"appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",   
"displayName": "",   
"password": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",   
"tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 
}
```

- `appId` is your ``.
- `password` is your ``.

**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:

```bash
kubectl create secret docker-registry acr-auth \
  --namespace default \
  --docker-server=.azurecr.io \
  --docker-username= \
  --docker-password= \
  --docker-email=
```

### Create Kubernetes deployment and service YAML files
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 named `movie-catalogue-service`, using an image specified by `/movie-catalogue-service:latest`.
- `containerPort: 8080` exposes port 8080 inside the container for network communication.
- Environment variables `MONGODB_URI` and `MONGODB_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.

```yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: movie-catalogue-service-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: movie-catalogue-service
  template:
    metadata:
      labels:
        app: movie-catalogue-service
    spec:
      containers:
      - name: movie-catalogue-service
        image: /movie-catalogue-service:latest
        ports:
        - containerPort: 8080
        env:
        - name: MONGODB_URI
          valueFrom:
            secretKeyRef:
              name: mongodb-secret
              key: MONGODB_URI
        - name: MONGODB_DATABASE
          valueFrom:
            secretKeyRef:
              name: mongodb-secret
              key: MONGODB_DATABASE
      imagePullSecrets:
      - name: acr-auth
---
apiVersion: v1
kind: Service
metadata:
  name: movie-catalogue-service
spec:
  selector:
    app: movie-catalogue-service
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: LoadBalancer
---
```

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:

```bash
az aks update -n  -g  --attach-acr 
```

Replace ``, ``, and `` with your AKS cluster name, Azure resource group name, and ACR name, respectively.

### Apply the YAML file
Apply the YAML file with `kubectl`:
```bash
kubectl apply -f all-microservices.yaml
```
## Access your services
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:
```bash 
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:
```bash
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:
```bash
curl -X POST http:///reviews \
     -H "Content-Type: application/json" \
     -d '{"movieId": "573a1391f29313caabcd68d0", "userId": "59b99db5cfa9a34dcd7885b8", "rating": 4}'
```

And this review should now appear in your database. You can check with a simple:
```bash
curl -X GET http:///reviews
```

Hooray!

## Conclusion
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.

  [1]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blt7bcbee53fc14653b/661808089070f0c0a8d50771/AKS_microservices.png