With the growth in the use of containers, the need to bundle your application into a container has never been stronger. Many Red Hat customers will be familiar with Source-to-Image (S2I) as an easy way to build container images from application source code. While S2I is a convenient way to build images on Red Hat OpenShift, Red Hat works to support our customers when using a variety of approaches, and we’ve recently seen an increasing interest in building applications using Cloud Native Computing Foundation (CNCF) Buildpacks.
This is the first in a 5-part series of articles on building your applications with CNCF Buildpacks and UBI. The series will include:
- Building applications with Paketo Buildpacks and Red Hat UBI container images (this post)
- Running applications with Paketo Buildpacks and Red Hat UBI container images in OpenShift
- The journey to enable UBI with the Paketo Buildpacks
- Building applications with UBI and Paketo Buildpacks in CI/CD
- Building a container image for a Quarkus project using buildpacks
CNCF Buildpacks
There are a number of CNCF Buildpacks implementations and one of the leading ones is Paketo Buildpacks, which have a long pedigree, having been based on buildpacks used in Cloud Foundry. Traditionally the Paketo ecosystem was centered around Debian (Ubuntu) based images along with external tarballs to install runtimes. Use of the Linux distribution runtime packages was difficult because CNCF Buildpacks deliberately disallowed dependencies that required root to modify the build environment. The TLDR; is that buildpacks pulled in third party tarballs (unsupported) instead of being able to leverage runtimes from Linux distributions like Red Hat supported rpms.
Universal Base Images (UBI)
Red Hat Universal Base Images (UBI) are OCI-compliant container base operating system images with complementary runtime languages and packages that are freely redistributable. They make great images to build your applications on, bringing with them the advantages of the Red Hat Enterprise Linux (RHEL) ecosystem. You can read more about UBI in Introducing the Red Hat Universal Base Image.
Enabling UBI in buildpacks
A few years ago, due to the increasing interest in building applications using CNCF Buildpacks expressed by our customers, Red Hatters started to work with the CNCF community to extend the buildpack specification to enable the creation of UBI builder images that made use of the supported Red Hat Runtimes.
The key addition was support for image extensions. With that support, Red Hatters have worked with Paketo to create extensions to support Node.js and Java as well as the ubi-base-stack and builder-ubi-base that you need in order to use them with UBI container images. These are still working their way through the hardening process as the image extension feature is still experimental in the specification, however, now is a great time to try them out and give us feedback so that we can improve them based on real-world experience.
What’s the same or different when using UBI?
The builder-ubi-base and the bundled buildpacks support the same options as when building with other stacks. There are a few differences in the end result which we feel are well aligned with the Red Hat approach to supporting runtimes. These include:
- The use of UBI images (of course).
- The application will always be built with the most recent runtime, and staged onto the most recent version of the container for the major version of the runtime selected. For example, if your application requests Node.js 20, the latest version of the ubi8/nodejs-20 container will be used. This fits the model where you get vulnerability fixes through the latest container and, therefore, want to be running on the latest version of the container for a major version of a runtime.
Building applications with UBI and the Paketo Buildpacks
In order to be able to build and run an application using buildpacks from the command line, we need to install the tools for the build process and for running containers. This can be done with Docker or Podman. We will show you how to do it with Podman.
For the build process as well as running the container, you need Podman version 5.1.0 or later. We installed it on Fedora 40 following these instructions. Make sure you start the socket and set DOCKER_HOST
as directed in the instructions. Once installed, you should check that the version is 5.1.0 or later with:
podman --version
If the version is not yet 5.1.0 or later you can try updating with:
dnf update podman
If that still does not get you 5.1.0 you can install the test version of 5.1.0 with the following command:
sudo dnf upgrade --enablerepo=updates-testing --refresh --advisory=FEDORA-2024-c612accb0f
In addition, for the build process, we also need the Pack CLI. On Fedora you can install it with:
dnf install pack
In our case it was version 0.32.0. You can install newer versions by following the instructions from the official website. At the time this article was written, we also tried out the latest version, 0.34.1, and it worked fine as well.
After installing the tools, we are ready to build our applications. We’ll walk you through the process of building and running some of the existing Paketo application samples for a Node.js application as well as a Java application.
A Node.js application
Start by cloning the Paketo samples repository:
git clone https://github.com/paketo-buildpacks/samples
Navigate to the root directory of the sample Node.js app:
cd samples/nodejs/npm
As mentioned earlier, the extension that the builder is using during the build process is currently in an experimental state. Therefore, we need to enable the experimental features of pack CLI:
pack config experimental true
Build the app with pack CLI by using the ubi-base builder:
pack --docker-host=inherit build my-node-app --builder paketocommunity/builder-ubi-base
If the build has finished successfully, the image should be visible and named my-node-app
.
Run the image:
podman run -d -p 8080:8080 my-node-app
The container should be up and running on port 8080, so we can make an HTTP request with curl
or any other HTTP client you prefer.
curl http://localhost:8080
A Java application
Start by cloning the Paketo samples repository:
git clone https://github.com/paketo-buildpacks/samples
Navigate to the root directory of the sample Java app:
cd samples/java/maven
As mentioned earlier, the extension that the builder is using during the build process is currently in an experimental state. Therefore, we need to enable the experimental features of pack CLI:
pack config experimental true
Build the app with pack CLI by using the ubi-base builder:
pack --docker-host=inherit build my-java-app --builder paketocommunity/builder-ubi-base
If the build has finished successfully, the image should be visible and named my-java-app
.
Run the image:
podman run -d -p 8080:8080 my-java-app
The container should be up and running on port 8080, so we can make an HTTP request with curl
or any other HTTP client you prefer on /actuator/health
:
curl http://localhost:8080/actuator/health
Summing up
From the last section, you can see that the ubi-base-builder
builds your Node.js or Java application just like with any other Paketo builder image. The same configuration options are available and the application should run the same, except that it will be using the UBI container and the Red Hat version of either the Node.js or Java runtime.
We hope you found this article interesting and that we have motivated you to try out the new Paketo UBI Builder Image. In the follow-up posts we will provide more detail on running applications with Paketo Buildpacks in OpenShift, the journey to enable UBI within the Paketo ecosystem, using buildpacks in CI/CD, and using CNCF Buildpacks with Quarkus.
Last updated: July 1, 2024