Build lightweight and secure container images using RHEL UBI | Red Hat Developer (2024)

Deploying applications in lightweight container images has practical benefits because container images pack all of the dependencies required for your application to function properly. However, you could lose the benefits of containerization if the container images are too large, and thus take several minutes to boot up the application. In this article, I guide you through how to use Red Hat Universal Base Images (UBI) as the foundation to build lightweight and secure container images for your applications.

Building containerized applications with UBI

Red Hat Universal Base Images provides a lightweight and secure foundation for building cloud-based applications and web applications in containers. With UBI images, reliability, security, performance, and image-lifecycle features are baked in. You can build a containerized application on a UBI image, push it to your choice of registry server, share it easily, and even deploy it on non-Red Hat platforms.

Every UBI image is based on Red Hat Enterprise Linux (RHEL), the most popular enterprise-grade Linux distribution for the past two decades. Building your container image on a foundation of RHEL software ensures the image is reliable, secure, and freely distributable. You don’t need to be a Red Hat customer to use or redistribute UBI images: Just use them and let Red Hat manage the base images for you at no cost.

Ready to get started with Universal Base Images? Let's go!

Where to get UBI images

You might be wondering where to get UBI images. Well, they're easily available on both the Red Hat Container Catalog and Docker Hub's official Red Hat repository. I personally prefer using the Red Hat Container Catalog console because it shows information such as the image size, version, health index, package list, Dockerfile, and multiple options available for fetching the image. The animation in Figure 1 demonstrates these features.

Choose the UBI image for your build

Red Hat Universal Base Images are offered in several flavors:

  • Micro: A stripped-down image that uses the underlying host's package manager to install packages, typically using Buildah or multi-stage builds with Podman.
  • Minimal: A stripped-down image that uses microdnf as a package manager.
  • Standard: Designed and engineered to be the base layer for all of your containerized applications, middleware, and utilities.
  • Init: Designed to run a system as PID 1 (the Linux init process) for running multi-services inside a container.

Table 1 presents a simple matrix to help you choose the right type of image for your build.

Table 1. Choose the right UBI image for your build (scroll or drag to view all categories).
UBI typeSize compressedSize uncompressedHealth indexOCI compliant?MaintainerTotal packagesBase OSPull command
Micro12.9MB35.0MBAYesRed Hat18RHEL 8.4docker pull registry.access.redhat.com/ubi8/ubi-micro
Minimal37.5MB97.7MBAYesRed Hat101RHEL 8.4docker pull registry.access.redhat.com/ubi8/ubi-minimal
Standard79.5MB215.0MBAYesRed Hat190RHEL 8.4docker pull registry.access.redhat.com/ubi8/ubi
Init84.5MB232.1MBAYesRed Hat192RHEL 8.4docker pull registry.access.redhat.com/ubi8/ubi-init

Container images for language runtimes

In the next sections, we'll package UBI images for two different language runtimes—one for Golang and one for Python. I've already created a sample application for each runtime, so we will simply pack the application into a UBI image. You can get the code for the sample applications, including the Dockerfile, from my GitHub repository.

Build and run a Golang application with UBI

We'll start with a Dockerfile:

FROM golang AS builderWORKDIR /go/src/github.com/alexellis/href-counter/RUN go get -d -v golang.org/x/net/html COPY ./app.go ./go.mod ./RUN CGO_ENABLED=0 GOOS=linux go build -a -o app .FROM registry.access.redhat.com/ubi8/ubi-microWORKDIR /COPY --from=builder /go/src/github.com/alexellis/href-counter/app /EXPOSE 8080CMD ["./app"]

Here we are using a multi-stage build, which is a popular way to build a container image from a Dockerfile. The first section of the Dockerfile brings in the official Golang image, and the second section brings in the official UBI image. This Dockerfile demonstrates that UBI images work well with other base images

Now, to pack our sample Golang app into a UBI image, we need to use the FROM command to specify the base image. Here, the base image is the official Red Hat UBI micro image. The WORKDIR command specifies the directory where the app is located inside the container image. The COPY command copies the Golang single binary app to the UBI image and the EXPOSE command specifies the port that the app will be listening on. Finally, the CMD command specifies the command that will be executed when the container runs.

Great—we now have the Dockerfile. Let's build the image and run it:

git clone ksingh7/dockerfile-hub.gitcd dockerfile-hub/go-hello-world-app-ubidocker build -t go-hello-world-app-ubi .docker images | grep -i go-hello-world-app-ubidocker run -it -p 8080:8080 -d go-hello-world-app-ubicurl http://localhost:8080

The cURL command should return the following response:

Hello OpenShift!

Upon checking the final image size, we see it's just 42.8MB:

$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEgo-hello-world-app-ubi latest ac7f4c163f5c 6 hours ago 42.8MB

Build and run a Python application with UBI

Now, let's do the same process we did for Golang for the Python runtime, just for fun. Here's the Dockerfile:

FROM registry.access.redhat.com/ubi8/ubi-minimalRUN microdnf install -y python3WORKDIR /appCOPY ./requirements.txt ./app ./RUN python3 -m pip install -r /app/requirements.txtEXPOSE 8080CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

I intentionally choose to use a classic Dockerfile for the Python example because it is easier to understand. The base image used here is the official Red Hat UBI minimal image. The microdnf command installs the Python runtime. The WORKDIR command specifies the directory where the app is located inside the container image. The COPY command copies the Python requirements file to the UBI image, which will then be used by RUN command to install the dependencies. The EXPOSE command specifies the port that the app will listen on. Finally, the CMD command specifies the command that will be executed when the container runs.

Here's our build and run:

# Clone the git repo if you have not already donegit clone ksingh7/dockerfile-hub.gitcd dockerfile-hub/python-hello-world-app-ubidocker build -t python-hello-world-ubi .docker images | grep -i python-hello-world-ubidocker run -it -p 8080:8080 -d python-hello-world-ubicurl http://localhost:8080

The cURL command should return the following response:

{"Hello":"World"}

The final image size here is impressively just 169MB:

$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEpython-hello-world-ubi latest 50c12e1ca549 55 minutes ago 169MB

Conclusion

If you are serious about containerizing your application and running in production, you should consider using Red Hat Universal Base Image. UBI gives you greater reliability, security, and peace of mind for your containerized applications. And, you can freely distribute UBI-based containerized applications with your friends (and enemies) on both Red Hat and non-Red Hat platforms.

Happy UBI-ing.

Last updated: November 17, 2023

Build lightweight and secure container images using RHEL UBI | Red Hat Developer (2024)

References

Top Articles
Latest Posts
Article information

Author: Van Hayes

Last Updated:

Views: 6296

Rating: 4.6 / 5 (46 voted)

Reviews: 93% of readers found this page helpful

Author information

Name: Van Hayes

Birthday: 1994-06-07

Address: 2004 Kling Rapid, New Destiny, MT 64658-2367

Phone: +512425013758

Job: National Farming Director

Hobby: Reading, Polo, Genealogy, amateur radio, Scouting, Stand-up comedy, Cryptography

Introduction: My name is Van Hayes, I am a thankful, friendly, smiling, calm, powerful, fine, enthusiastic person who loves writing and wants to share my knowledge and understanding with you.