Building Apps Using Cloud Native Buildpacks
Nov 14, 2021
The buzz around buildpacks is growing within the container community – for several good reasons. For one, they are a proven concept. They were first introduced by Heroku in 2011, and they were quickly adopted by Cloud Foundry and other PaaS platforms. The new iteration of Cloud Native Buildpacks (CNB) is built upon lessons learned from using buildpacks at scale to standardize and expand the buildpack ecosystem over the last decade.
The CNB project falls under the umbrella of the Cloud Native Computing Foundation (CNCF), which is best known for supporting Kubernetes. This means that using containers at scale is at the core of the project. Organizations like GitLab are already adopting CNB as an option in their build pipelines.
What Will Cloud Native Buildpacks Do for Me?
Cloud Native Buildpacks produce OCI-compliant containers without the need to create a Dockerfile. This is because they have an internal detect and build pipeline that packages the application on top of a base image provided by CNB.
This is convenient because it allows developers to focus on code rather than having to learn the intricacies of building a supported container image. (CNB can leverage an existing Dockerfile, but it is not required.) These containers can run on any of the major container runtime options that use Docker and Kubernetes, including containerd, podman, and CRI-O, meaning the image will run where you need it to – from hyperscale public clouds to small footprint deployments like k3s.
There are already dozens of buildpacks available across almost any language you need, and there are even buildpacks that automatically detect the language in which the application is written. As long as the application source code containers have defined their dependencies using the most popular tool for that language, CNB can then execute the build and create a container image. Out of the box, the pack CLI suggests six different buildpacks, most of which are based on Ubuntu Bionic 18.04:
As with any new technology, there are always limitations. The single biggest limitation of buildpacks is related to how you structure your application source code and what you use for dependency management. Since it is a prescriptive system, you have to follow the formula. This won’t work for all situations, but it will likely work for most. In most organizations, you’ll need to make decisions about updating your internal best practices and style guides to reflect the ways in which buildpacks require you to work.
The second limitation concerns customization. When working with Dockerfiles, developers and operators can essentially build a custom world to hold any application. This is often a popular option for digital transformation projects where applications are being migrated to containers with the goal of refactoring them to be more cloud native in the future. Buildpacks do allow for a degree of customization, but it’s probably better for greenfield development and smaller applications like microservices. I do not recommend trying to move large and complex applications to buildpacks until you are very comfortable with them.
A Quick Example of Going from Code to Container with CNB
The simplest example from the buildpacks.io website consists of just three commands to go from cloning to building to running the app (assuming that you have pack, Git, and Docker installed):
# clone the repo $ git clone https://github.com/buildpacks/samples # build the app $ pack build sample-app --path samples/apps/java-maven --builder cnbs/sample-builder:bionic # run the app $ docker run --rm -p 8080:8080 sample-app
Note: Windows and Mac users (especially those on Silicon M1 devices) may need to investigate alternative builders or build strategies to get this example to work.
When the app is structured to work with CNB, the pack CLI quickly goes through its process, which is essentially the following three steps:
- The buildpack detects that the sample app is written in Java and uses Maven. Then, it sets up the appropriate build environment.
- Since the buildpack knows that this sample uses Maven, it knows how to resolve dependencies that the developer defined in the application’s pom.xml.
- Finally, it runs Maven to handle the build and package the application.
All of the dependencies and packages are added as layers on the CNB’s predefined base image, and then the final output is ready to run a container image. One nice thing about the layered approach is that each iterative build will only update the layers that were changed, which makes for faster builds and updates across both registries and each node when restarting the instances.
Cloud Native Buildpacks are a fantastic solution for helping developers start using containers without having to learn a whole new way of thinking – as long as they’re willing to follow the best practices. There’s no need to build complex pipelines or Dockerfiles for each project; instead, you just need to build an application with properly defined dependencies, and CNB will take care of the rest. It will produce a container image that operations can then deploy on essentially any container orchestration platform on any cloud or in any datacenter. It almost seems too good to be true.
About the Author
Vince Power is an Enterprise Architect with a focus on digital transformation built with cloud enabled technologies. He has extensive experience working with Agile development organizations delivering their applications and services using DevOps principles including security controls, identity management, and test automation. You can find him at @vincepower on Twitter.