Yuri Burger

🖖🏻🖖🏼🖖🏽🖖🏾🖖🏿

All things .NET, Java, Kubernetes, Azure, ALM, DevOps and more!

Yuri Burger

6 minute read

What are we trying to accomplish? Azure runs a very decent Kubernetes service these days. I have used it as the main infrastructure in a couple of projects and am quite impressed. Impressed enough to even suggest it as the target platform for our largest projects 😊

So let’s take a typical Java project, a project consisting of one or several Spring Boot apps and deploy it to Kubernetes. And since Microsoft also runs a popular DevOps toolchain (with services like Git Repos, CI/CD pipelines, etc.), let’s try to use  that as much as possible.

The process we would like to support is as follows:

Worlds colliding.

Planets colliding (c) 2007-2019 ThornErose

Prepare our environment

Get ready for some preparations, because we need quite a few moving parts:

  • An Azure DevOps project, that will contain our build and release pipelines;
  • Azure Kubernetes Service, our target infrastructure;
  • Azure Container Registry, our Docker image(s) will be pushed there;
  • A Git repo to hold our code (can be Azure DevOps Repos, GitHub Repositories, ….);
  • A Spring Boot demo application to show it all off 😊

Azure DevOps

Microsoft offers a complete set of DevOps services like Git Repos, CI/CD pipelines, work tracking tools, etc. If you do not have an account already, you can start for free easily. We will use these services for the build and release pipelines mentioned in this article, but you can also choose to use Azure Repos for storing the source code files. More info: https://azure.microsoft.com/en-us/services/devops

Once you have an account, you will need to create a project that will contain our pipelines, default settings are fine.

Azure Kubernetes Service

For our infrastructure we need an Azure Kubernetes Service (a Kubernetes Cluster) to deploy our apps in. You can use any method you like, I will just list the Azure CLI commands. More info here: https://docs.microsoft.com/en-us/azure/aks/

  • az group create –name spring-demo-rg –location westeurope
  • az aks create –resource-group spring-demo-rg –name spring-demo-aks –node-count 1 –generate-ssh-keys
  • az aks install-cli
  • az aks get-credentials –resource-group spring-demo-rg –name spring-demo-aks

Azure Container Registry

Kubernetes needs access to a docker registry to pull the images. You can use any supported registry, but Microsoft also provides this service: the Azure Container Registry. More info here: https://docs.microsoft.com/en-us/azure/container-registry/

az acr create –resource-group spring-demo-rg –name springdemo –sku Basic

A Git Repo

You need to use a Git repository to store our source files. Azure DevOps Build Pipelines need to be able to access the files, so you need a supported Git repo. Can be Azure DevOps Repos, Bitbucket, GitHub, etc.

Spring Boot Application

For demonstration purposes we create a simple Spring Boot application. In this example I added a web dependency but anything will do. If you do not want to do this yourself, you can also grab the code from GitHub: https://github.com/yuriburger/spring-demo

You need to extract the content and add them to an online Git repository. I prefer GitHub, but any supported online repo will do (Azure Repos, BitBucket, etc.). Now it is time to add some sample code to test our application after deployment.

A simple test controller, just to provide a response and prove our application is running:

package com.example.springdemo;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(path = "/test")
public class TestController {
    @RequestMapping(method = RequestMethod.GET, produces = "application/json")
    public ResponseEntity<?> getTest()
    {
        return new ResponseEntity<>("{\\"message\\":\\"App running!\\"}", HttpStatus.OK);
    }
}

An executable Jar file

Because we want to run the application from a Docker container, we need an easy way to execute the Jar file. There are a couple of ways for doing this, you can check https://www.baeldung.com/executable-jar-with-maven for some of the options. In this case I choose the Spring Boot Maven Plugin.

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                    <configuration>
                        <classifier>spring-boot</classifier>
                        <mainClass>
                            org.baeldung.executable.ExecutableMavenJar
                        </mainClass>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

A Dockerfile

As part of the build, we need to “convert” our application into a container image. There are several useful tools (like Maven Build plugins), that can assist here. Personally I’d like to work directly with Dockerfile objects and not to generate them from code or pom.xml settings.

Spotify has a nice plugin that allows you to work with a standard Dockerfile and still integrate with the Maven build process. Check this out for more information: https://github.com/spotify/dockerfile-maven

For the demo, no plugins were used. This makes the process easy to understand, but also involves “hardcoding” filenames.

FROM openjdk:8-jre-alpine
VOLUME /tmp
COPY /target/spring-demo-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT \["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"\]

Prepping is done, now let’s get started!

Creating the build

Service Connection

Azure DevOps needs to connect to our Azure Container Registry to be able to upload the final Docker images. This works by creating a “Service Connection” and authorizing Azure DevOps. Go to “Project Settings”, “Pipelines”, “Service connections”.

The Pipeline

Creating a pipeline is pretty straightforward: from the menu on the left, choose “Pipelines” and “Build”. In my case I need the build to get the source files from GitHub:

This will open a Yaml editor and here we can update the build by specifying the required steps/tasks:

trigger:
- master

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: Docker@2
  displayName: Login to ACR
  inputs:
    command: login
    containerRegistry: acrRegistryServiceConnection1
- task: Maven@3
  inputs:
    mavenPomFile: 'pom.xml'
    mavenOptions: '-Xmx3072m'
    javaHomeOption: 'JDKVersion'
    jdkVersionOption: '1.8'
    jdkArchitectureOption: 'x64'
    publishJUnitResults: false
    testResultsFiles: '\*\*/surefire-reports/TEST-\*.xml'
    goals: 'package -Dmaven.test.skip=true'
- task: Docker@2
  displayName: Build and Push
  inputs:
    command: buildAndPush
    repository: spring-demo
    tags: latest

  • Docker task: Login to ACR. This is an Azure DevOps built in task and uses the service connection we created earlier and connect to the Azure container registry.
  • Maven task: “package”. Also a built in Task. Builds and packages the Jar file we need for our container image.
  • Docker task: Build and Push. Creates the final image and uploads this to the Azure Container Registry.

Creating the release

Service Connection

Azure DevOps needs to connect to our Azure Kubernetes Service to be able to update deployment. This works by creating a “Service Connection” and authorizing Azure DevOps. Go to “Project Settings”, “Pipelines”, “Service connections”.

Next we add a Kubernetes deployment and service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-demo-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-server
  template:
    metadata:
      labels:
        app: test-server
    spec:
      containers:
        - name: test-server
          image: loggingdemo.azurecr.io/spring-demo:latest
          imagePullPolicy: "Always"
          ports:
            - name: http-port
              containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: spring-demo-service
spec:
  ports:
    - name: http-port
      port: 80
      targetPort: http-port
      protocol: TCP
  selector:
    app: test-server

After this initial deployment in Kubernetes, we configure the release pipeline to update our components. There are a lot of ways to accomplish this, but in many cases easier is better 😊

Comparable to creating a build pipeline, we select “Pipelines” and “Release” from the menu on the left.

  • Service Connection: the connection to Kubernetes created earlier
  • Command: patch
  • Arguments: deployment spring-demo-deployment -p “{\“spec\": {\“template\": {\“metadata\": { \“labels\": {  \“redeploy\": \"$(Release.ReleaseId)\"}}}}}"

This will “patch” the deployment by adding a piece of metadata (in this case adding the release ID) and trigger Kubernetes to do a rolling update. If all goes as planned, we should see a deployment update on Kubernetes once the Azure Release Pipeline succeeds:

/Y.

Recent posts

Categories

About

Techno(logy).