the/experts. Blog

Cover image for Cloud native Buildpacks?
kevin-bos
kevin-bos

Posted on

Cloud native Buildpacks?

In the world of cloud computing, two different methods of deploying applications are becoming increasingly popular: Docker and Cloud Native Buildpacks. Both are great options, but each has its unique advantages and disadvantages.

I think every Java developer remembers the famous words: 'Make JAR, not WAR!', said in a talk by Josh Long back in 2015, in which he explained how you can create a Spring Boot app and how you want to package and distribute your artifact.

As we witness the seemingly unstoppable rise of cloud native production environments, this quote becomes a bit more irrelevant each day. In this new world, an image is the new artifact that you would deliver to your clients!

Instead, we should start to say 'Make container, not JAR!'. Sorry, Josh...

Let's dive into how we can create production artifacts!

Docker

Docker is an open-source platform that provides a way to package applications and services into containers. Containers are isolated and portable, which makes them an ideal choice for deploying applications in a variety of environments without having to worry about system dependencies. Docker is easy to set up and use, and it has a wide range of features that make it an attractive option for developers and system administrators.

The pain of using Dockerfiles for your artifacts lies in the need to maintain them all through your projects. Upgrading them when you're starting with a newer framework, keeping up to date with Java SDK versions, and needing to implement all the best practices yourself!

So how can we avoid this extra work? Enter the world of Buildpacks.

Build packs

From https://buildpacks.io/:

The Cloud Native Buildpacks project was initiated by Pivotal and Heroku in January 2018 and joined the Cloud Native Sandbox in October 2018. The project aims to unify the buildpack ecosystems with a platform-to-buildpack contract that is well-defined and that incorporates learnings from maintaining production-grade buildpacks for years at both Pivotal and Heroku.

Cloud Native Buildpacks are a more modern way of deploying applications. Buildpacks are a series of scripts and practices that allow applications to be deployed quickly and easily to the cloud. Unlike Docker, Buildpacks don’t require a custom DockerFile and can be used to deploy applications to any cloud platform.
 
Cloud Native Buildpacks are designed to be extensible and are often used in conjunction with other cloud tools like Kubernetes and Helm.

Let's take a look at how buildpacks can help you to create a containerized spring boot application in seconds. To do this, we're going to use Paketo Buildpacks.

Using Paketo

Buildpacks provides a higher-level abstraction for building apps

Image descriptionLogo sources: CNCF logo , Buildpacks logo , Paketo.io logo

Paketo.io is an implementation of the Cloud Native Buildpack interface specification for a wide variety of languages. No matter if you want to use .Net Core , Go , Node.js , Java , Ruby or PHP – you don’t need to write a Dockerfile anymore!

Paketo build process
The advantages:

  1. Automatically detect and creates secure container images without requiring developers to write and maintain Dockerfiles
  2. Permit build functionality to be modularized in a re-usable, context-independent and transparent way
  3. Optimize the build performance by minimizing the number of rebuilt image layers
  4. Provide a simple method for patching OS-level vulnerabilities
  5. Provide metadata about the contents of container images, via an extensive Bill-of-Materials

ARM disclaimer
At the time of writing, there is no official support for ARM-based images. This limits the ability of building projects on Apple Silicon machines Arm support.
But there is a workaround!
We have to thank DaShaun!

Arm compatible buildpacks
We can make use of his implementation of an ARM-compatible Buildpack!
dashaun/java-native-builder-multiarch:7.41.0 at this moment.
You can read more about this on his blog.

DaShaun creating a custom Buildpack for ARM-based machines shows the power of the open-source community behind Buildpacks.io! Everyone can create their own Buildpack tailored to their specific needs, or can just make use of the predefined ones.

So we have now some background on what Buildpacks are and why they are great, but the proof is in the pudding! Let's create a sample project to play around with Buildpacks.

Create a new Spring project

Every new Spring adventure starts of course at start.spring.io!

Select your preferred project settings, as Buildpacks is already build in from Version 2.3.0! I'm going with the latest and greatest Spring Boot 3.0.

To test our implementation, I'm adding Spring web as a dependency.

Let's generate the project!

Image description

Add a simple Spring MVC Rest Controller

import org.springframework.web.bind.annotation.GetMapping  
import org.springframework.web.bind.annotation.RestController  


@RestController  
class BlogController {  

    @GetMapping("hello")  
    fun hello(): String =  
         "Hello, is it me you're looking for?"  
}
Enter fullscreen mode Exit fullscreen mode

Using GitHub actions to build our image

As previously stated Buildpacks are great for creating artifacts so when developing locally I would still use a Dockerfile to speed up development. That being said, let's create a GitHub workflow to create our production artifact!

Your repo must have a folder named .github/workflows. This folder should contain all the workflow files you can write. Workflow files are written as yaml files.

on: [push]  

jobs:  
  autodeploy:  
    runs-on: ubuntu-latest   

    steps:  
      - uses: actions/checkout@v2  

      - name: Install pack CLI via the official buildpack Action https://github.com/buildpacks/github-actions#setup-pack-cli-action  
        uses: buildpacks/github-actions/setup-pack@v4.8.1  

      - name: Build app with pack CLI & publish to GitHub Container Registry  
        run: |  
          pack build ghcr.io/kevinbos-cc/buildpacks-demo:latest \  
              --builder paketobuildpacks/builder:tiny \  
              --path . \  
              --env "BP_JVM_VERSION=17"  
Enter fullscreen mode Exit fullscreen mode

This script is fairly easy but contains quite some important parts, let's break it down!

In order to be able to use Paketo CLI in our workflow, we leverage the buildpacks/github-actions/setup-pack action. Now, we can run the pack build CLI command to build our Spring Boot app into a Docker container.

We're using the tiny builder provider by Paketo as it's the smallest in size and perfect for our sample project. You can read more about the different builder here.

I've added the BP_JVM_VERSION=17 parameter because Spring boot 3.0 requires a minimum Java version of 17. If omitted, Paketo defaults to Java 11 and will throw errors.

When we run the build command, it will create a new artifact under the name ghcr.io/kevinbos-cc/buildpacks-demo:latest.

That was easy! So far we're not doing anything useful with the image yet, let's push it to a container registry so our 'Clients' can use the artifact.

Push image to the GitHub container registry

As with every container registry, we need to log into GitHub Container Registry before we will be able to push new images.

We can do this easily in the workflow by adding:

- name: Login to GitHub Container Registry  
  uses: docker/login-action@v1  
  with:  
    registry: ghcr.io  
    username: ${{ github.actor }}  
    password: ${{ secrets.GITHUB_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

After the login, we only need to slightly enhance our Pack CLI command, since Paketo has a nice --publish parameter available for us. Adding it will publish our Spring Boot app container to the GitHub Container Registry without the need for us to use the Tag & publish commands of docker:

- name: Build app with pack CLI & publish to GitHub Container Registry  
  run: |  
    pack -v build ghcr.io/kevinbos-cc/buildpacks-demo:latest \  
        --builder paketobuildpacks/builder:tiny \
        --path . \  
        --env "BP_JVM_VERSION=17" \  
        --env "BP_OCI_SOURCE=https://github.com/kevinbos-cc/buildpacks-demo" \
        --cache-image ghcr.io/kevinbos-cc/buildpacks-demo-paketo-cache-image:latest \  
        --publish
Enter fullscreen mode Exit fullscreen mode

You may also have noticed the second addition: the BP_OCI_SOURCE parameter creates a link between the GitHub Container Registry package and the GitHub repository. This way, the container image will be directly visible on the repository.

In addition to the command we used locally, I added the parameter --cache-image to enable caching of our build dependencies.

Cache Images are a way to preserve build optimizing layers across different host machines. These images can improve performance when using pack in ephemeral environments such as CI/CD pipelines.

In order to use the --cache-image parameter, we also have to use the --publish flag (since the cache image needs to get published to your container registry!).

Once the Pack command was executed successfully and the image was published, we will be able to find it in the package view of our repository. And indeed, the new image is listed as expected:

Github image coupled

Final thoughts

We’ve now got a production artifact ready for our clients! Ready to be used where ever they want. We made use of all the power of Buildpacks without breaking a sweat!

No need for creating a Dockerfile or to set some kind of configuration! We just use the latest version of the builder, and we're off!

You can find the source code for this blog on my Github account.

What's next?

So now we have a production artifact in our repository! But it's a shame that we're not doing anything with this image at the moment.

Stay tuned for my next blog to find out how we can use the created artifact to deploy to any cloud environment!

Discussion (0)