Creating Spring Boot container in a minute with Cloud Native Buildpacks for Azure Container Platform

Prerequisite

  1. pack CLI
  2. Docker desktop
  3. Spring Boot project

Getting started with Cloud Native Buildpack(CNB)

Putting CNB in one sentence, CNB takes source code and builds it into container images without writing Dockerfile. It does way more than just containerization, but let’s save it for now. CNB supports not only Java but also other languages like .NET, Go, Java, Node.js, Python, PHP, Ruby, etc. It can also containerize your static contents like Javascript with Apache HTTPD, and Nginx.

NOTE: In this article, I will mainly focus on getting started, so I will save deep technical details for the next article.

Before we go into more details, let’s just try it and see what happens. You could simply use mvn spring-boot:build-image but we won’t do it here for the sake of understanding the basics of CNB.

$ ls
HELP.md mvnw mvnw.cmd pom.xml src
$ pack build eggboy/springboot:0.0.1 --builder paketobuildpacks/builder:base
.....
===> ANALYZING
Previous image with name "eggboy/springboot:0.0.1" not found
===> DETECTING
8 of 20 buildpacks participating
paketo-buildpacks/ca-certificates 3.1.0
paketo-buildpacks/bellsoft-liberica 9.2.0
paketo-buildpacks/syft 1.10.0
paketo-buildpacks/maven 6.4.1
paketo-buildpacks/executable-jar 6.1.0
paketo-buildpacks/apache-tomcat 7.2.0
paketo-buildpacks/dist-zip 5.2.0
paketo-buildpacks/spring-boot 5.8.0
===> RESTORING
===> BUILDING
....
$ docker run -p 8080:8080 eggboy/springboot:0.0.1
Setting Active Processor Count to 6
WARNING: Unable to convert memory limit "max" from path "/sys/fs/cgroup/memory.max" as int: memory size "max" does not match pattern "^([\\d]+)([kmgtKMGT]?)$"
Calculating JVM memory based on 1514764K available memory
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx917453K -XX:MaxMetaspaceSize=85310K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1514764K, Thread Count: 250, Loaded Class Count: 12648, Headroom: 0%)

Enabling Java Native Memory Tracking
Adding 128 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -XX:+ExitOnOutOfMemoryError -XX:ActiveProcessorCount=6 -XX:MaxDirectMemorySize=10M -Xmx917453K -XX:MaxMetaspaceSize=85310K -XX:ReservedCodeCacheSize=240M -Xss1M -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -Dorg.springframework.cloud.bindings.boot.enable=true
  1. Somehow, the container image built by CNB tries to be smart to calculate the right Heap and Metaspaces sizes.
  2. Adding CA certificates to JVM truststore
  3. XX settings are auto-configured somehow.
$ docker run --env BPL_JMX_ENABLED=true --env BPL_JMX_PORT=9999 -p 9999:9999 -p 8080:8080 eggboy/springboot:0.0.1
...
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -XX:+ExitOnOutOfMemoryError -XX:ActiveProcessorCount=6 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.rmi.port=9999 -XX:MaxDirectMemorySize=10M -Xmx928413K -XX:MaxMetaspaceSize=85310K -XX:ReservedCodeCacheSize=240M -Xss1M -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -Dorg.springframework.cloud.bindings.boot.enable=true
paketo-buildpacks/ca-certificates   3.1.0
paketo-buildpacks/bellsoft-liberica 9.2.0
paketo-buildpacks/syft 1.10.0
paketo-buildpacks/maven 6.4.1
paketo-buildpacks/executable-jar 6.1.0
paketo-buildpacks/apache-tomcat 7.2.0
paketo-buildpacks/dist-zip 5.2.0
paketo-buildpacks/spring-boot 5.8.0

Building images for Azure Container Platform

We have briefly touched upon the very basics of using CNB. Now, we will take a step further to build images to run on Azure Container Platforms like Azure Kubernetes Service, Azure Container Instances, Azure App Service, and Azure Container Apps. Azure Spring Cloud is embedded with Tanzu build service which is using CNB under the hood.

$ pack build eggboy/springboot:0.0.1 --builder paketobuildpacks/builder:base --buildpack paketo-buildpacks/java-azure
...
===> DETECTING
8 of 20 buildpacks participating
paketo-buildpacks/ca-certificates 3.1.0
paketo-buildpacks/microsoft-openjdk 2.2.0
paketo-buildpacks/syft 1.10.0
paketo-buildpacks/maven 6.4.1
paketo-buildpacks/executable-jar 6.1.0
paketo-buildpacks/apache-tomcat 7.2.0
paketo-buildpacks/dist-zip 5.2.0
paketo-buildpacks/spring-boot 5.8.0
...
$ docker run -p 8080:8080 eggboy/springboot:0.0.1
...
$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f32c63654e05 eggboy/springboot:0.0.1 "/cnb/process/web" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp, 0.0.0.0:9999->9999/tcp reverent_bohr
$ docker exec -it f32c63654e05 /bin/bashcnb@f32c63654e05:/workspace$ /layers/paketo-buildpacks_microsoft-openjdk/jdk/bin/java -version
openjdk version "11.0.14" 2022-01-18 LTS
OpenJDK Runtime Environment Microsoft-30257 (build 11.0.14+9-LTS)
OpenJDK 64-Bit Server VM Microsoft-30257 (build 11.0.14+9-LTS, mixed mode)
$ pack build eggboy/springboot:0.0.1 --builder paketobuildpacks/builder:base --buildpack paketo-buildpacks/java-azure --env BP_JVM_VERSION=17
...
Microsoft OpenJDK 17.0.2: Contributing to layer
Downloading from https://aka.ms/download-jdk/microsoft-jdk-17.0.2.8.1-linux-x64.tar.gz
Verifying checksum
Expanding to /layers/paketo-buildpacks_microsoft-openjdk/jdk
...

Azure Application Insights Buildpack

We have covered enough about building images. Let’s shift the gear to look at the application monitoring. For those of you who are not familiar with Application Insights, it’s an Azure native Application Performance Monitoring service that can monitor .NET, Node.js, Java, Javascript, and Python.

$ ls
HELP.md mvnw mvnw.cmd pom.xml src
$ mkdir appinsights
$ cd appinsights
$ echo "ApplicationInsights" > type
$ cd ..
$ ls
HELP.md appinsights mvnw mvnw.cmd pom.xml src
$ pack build eggboy/springboot:0.0.1 --builder paketobuildpacks/builder:base --buildpack paketo-buildpacks/java-azure --volume "$(pwd)/appinsights:/platform/bindings/application-insights"...9 of 20 buildpacks participating
paketo-buildpacks/ca-certificates 3.1.0
paketo-buildpacks/microsoft-openjdk 2.2.0
paketo-buildpacks/syft 1.10.0
paketo-buildpacks/maven 6.4.1
paketo-buildpacks/executable-jar 6.1.0
paketo-buildpacks/apache-tomcat 7.2.0
paketo-buildpacks/dist-zip 5.2.0
paketo-buildpacks/spring-boot 5.8.0
paketo-buildpacks/azure-application-insights 5.3.2
...Paketo Azure Application Insights Buildpack 5.3.2
https://github.com/paketo-buildpacks/azure-application-insights
Azure Application Insights Java Agent 3.2.8: Contributing to layer
Downloading from https://github.com/microsoft/ApplicationInsights-Java/releases/download/3.2.8/applicationinsights-agent-3.2.8.jar
Verifying checksum
Copying to /layers/paketo-buildpacks_azure-application-insights/azure-application-insights-java
Writing env.launch/JAVA_TOOL_OPTIONS.append
Writing env.launch/JAVA_TOOL_OPTIONS.deli
...$ docker run -p 8080:8080 eggboy/springboot:0.0.1...
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_microsoft-openjdk/java-security-properties/java-security.properties -XX:+ExitOnOutOfMemoryError -javaagent:/layers/paketo-buildpacks_azure-application-insights/azure-application-insights-java/applicationinsights-agent-3.2.8.jar -XX:ActiveProcessorCount=6 -XX:MaxDirectMemorySize=10M -Xmx941095K -XX:MaxMetaspaceSize=97352K -XX:ReservedCodeCacheSize=240M -Xss1M -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -Dorg.springframework.cloud.bindings.boot.enable=true
2022-03-19 02:04:02.558Z ERROR c.m.applicationinsights.agent -
*************************
ApplicationInsights Java Agent 3.2.8 failed to start (PID 1)
*************************
Description:
No connection string or instrumentation key provided
Action:
Please provide connection string or instrumentation key.
...
$ docker run -p 8080:8080 --env APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=[REDACTED];IngestionEndpoint=https://southeastasia-0.in.applicationinsights.azure.com/" --env APPLICATIONINSIGHTS_ROLE_NAME=springboot eggboy/springboot:0.0.1
Setting Active Processor Count to 6
WARNING: Unable to convert memory limit "max" from path "/sys/fs/cgroup/memory.max" as int: memory size "max" does not match pattern "^([\\d]+)([kmgtKMGT]?)$"
Calculating JVM memory based on 1524092K available memory
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx914739K -XX:MaxMetaspaceSize=97352K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1524092K, Thread Count: 250, Loaded Class Count: 14774, Headroom: 0%)
Enabling Java Native Memory Tracking
Adding 128 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_microsoft-openjdk/java-security-properties/java-security.properties -XX:+ExitOnOutOfMemoryError -javaagent:/layers/paketo-buildpacks_azure-application-insights/azure-application-insights-java/applicationinsights-agent-3.2.8.jar -XX:ActiveProcessorCount=6 -XX:MaxDirectMemorySize=10M -Xmx914739K -XX:MaxMetaspaceSize=97352K -XX:ReservedCodeCacheSize=240M -Xss1M -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -Dorg.springframework.cloud.bindings.boot.enable=true
2022-03-19 02:40:33.081Z INFO c.m.applicationinsights.agent - ApplicationInsights Java Agent 3.2.8 started successfully (PID 1)
2022-03-19 02:40:33.086Z INFO c.m.applicationinsights.agent - Java version: 11.0.14, vendor: Microsoft, home: /layers/paketo-buildpacks_microsoft-openjdk/jdk
Performance metrics
End to End transaction details
Live metrics of requests, CPU, etc

Using CNB in Github Action

Github action provides buildpacks/github-actions/setup-pack that can set up pack CLI in the pipeline. Here is the sample snippet for reference.

    - name: Install pack CLIs including pack and yq
uses: buildpacks/github-actions/setup-pack@v4.1.0
with:
pack-version: 0.24.0
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_PASS }}
- name: Set the version
run: |
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Build app with pack CLI
run: |
pack build index.docker.io/eggboy/springboot:${{ env.VERSION }} \
--builder paketobuildpacks/builder:base \
--cache-image index.docker.io/eggboy/springboot:${{ env.VERSION }} \
--volume "$(pwd)/appinsights:/platform/bindings/application-insights" \
--publish

az acr pack build

The Azure CLI command az acr pack build uses the pack CLI tool, from Buildpacks, to build an app and push its image into an Azure container registry. This is still in the preview that has a few limitations, especially around specifying the buildpacks. But, it could be handy for some simple use cases where you just need a quick way to build images from source code without Docker installed on the laptop.

$ az acr pack build --registry acrjay --image acrjay.azurecr.io/containerapp-build:0.0.1 --builder paketobuildpacks/builder:base .

Conclusion

I hope ‘buildpack experiences’ resonate with you by now after quick getting started hands-on experiences. Cloud Native Buildpack page has a nice summary of comparisons between different tools in the market.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Jay Lee

Jay Lee

Cloud Native Enthusiast. Java, Golang, Spring, Kubernetes.