Balance innovation and agility with security and compliance
risks using a 3-step process across all cloud infrastructure.
Step up business agility without compromising
security or compliance
Everything you need to become a Kubernetes expert.
Always for free!
Everything you need to know about Magalix
culture and much more
Containers have changed the way of application development. Developers are not only building applications, but they often perform the task of application containerization by using Docker files. Building containers from the start often helps the team package the application and its dependencies, resulting in a more reliable product and improved development velocity.
As the project reaches its rollout milestone, enterprise operation teams start collaborating with development teams. Operation teams have a different perspective on the application container images. The ops team aims for security and standardization of the application’s various facets.
Their evaluations are around the following elements:
All the above decisions are aimed to optimize operational efficiency and enterprise security. Development teams need to update Docker files to attain the above goals. Often the operations team is also responsible for maintaining the deployed application containers. If there are CVE fixes in the base image, then ops need to update the Docker files with the newer version of the base images. To summarize, Docker files are owned by the development team, but there are continuous demands from the operations team. The development team must be well versed with Docker practices and syntax to support these needs.
Cloud Native Buildpacks (CNB) is an alternative to Docker files. CNB provides a reliable, safe, modular, and fast way to build Open Container Initiative (OCI) spec-based images. These images can be deployed to any open container runtime like Docker, RKT, etc. CNB tool enables decoupling between the development and operations teams. Its focus is to provide container maintenance and security without compromising developer flexibility and productivity.
This post demonstrates CNB’s focus on operational governance while building application containers. It starts by building an example project, followed by enforcing different operational practices.
If you'd like to follow along with this tutorial on your machine, you'll need a few things first.
fakeuserapplication provides an API to generate random usernames. It is a web application created using Java and Spring. The application is built using maven. All code and configuration accompanying the article are available on GitHub.
./mvnw clean install
You can run the application and generate a username by using
The fakeuser is a simple application, but it provides the test-bed for working with CNB. You can build the container by using the following command :
pack build fakeapp
If you look at the generated output, you can see pack initiates a Docker build in the following steps.
buildpackis applied as an abstract layer.
buildpacks, executing launch mode, are copied to a different base image. CNB calls this base image a runtime image.
The image is named
fakeapp. You can execute it using the
docker runcommand. CNB pushes the image to your local registry. Alternatively, there is
--publishoption to send the generated image to a remote registry.
CNB provides good support for major languages like Java, .NET Core, NodeJS, Go, Ruby, Python. There are
buildpacksfor diverse needs like SDK setup, Application packaging, configuring the environment, etc. These
buildpackshave been published by enterprises like Heroku and Pivotal.
Operations teams strive for standardization across their tools and techniques. These standardizations help in delivering reliability and security. They are often termed as a set of best practices followed across the organization. CNB can be used to enforce these practices for container builds by using the principle of policy-as-code.
CNB also helps in delivering application security. Some of the policies enforced by CNB are motivated by Docker CIS principles. Let's look at how to build policies for the most common use case.
All application containers are built using a base image. CNB calls this base image the
run imagefor the application. Additionally, CNB compiles the application code in a container, instead of compiling it locally on the workstation. This container is known as the
build imagefor the application. These two images, the
run imageand the
build image, in a pair are known as CNB
stack. A CNB
stackis the foundation for all operations.
The above component diagram shows the components of the CNB container. It starts a Build Image and then applies the related
buildpackslike Bellsoft Bulidpack, Maven Buildpack and Springboot Buildpack. Each of these buildpacks performs their operation, which may or may not be copied to the run image. CNB
stack, build and run image pair, along with the associated
buildpacksis also known as CNB
stackcan be enforced for the project by using the
--buildercommand-line option. This would select the corresponding run image for generating the application container. CNB registry publishes builders which can be applied to your application.
pack build fakeapp --builder paketobuildpacks/builder:full
The above command will select Ubuntu Bionic for application build and
≈runtime. CNB registry contains Ubuntu and Alpine stacks, so organizations may not find a suitable CNB builder for their needs. In such cases, you can also create a custom stack that best fits.
The CNB stack specification enforces that all operations are performed as a configured user. All default builders use the cnb user and cnb group for performing operations. Custom CNB stack creators are enforced to configure the
CNB_GROUP_IDvariables for their images.
The pack command generated the application container automatically. It discovered that you have a Java application. All related buildpacks were configured automatically. CNB buildpack is responsible for executing a task. The task can compile your code, install dependencies, configure environment variables, etc.
A CNB builder has a default set of buildpacks. But every buildpack has a concept of auto-detection followed by execution. Thus only a subset of the default buildpacks are executed for a particular build.
CNB allows you to provide your own set of buildpacks. In such cases, it does not pick the superset of buildpacks available with the CNB builder. Thus there is fine control on the process of image creation. The buildpack override is accomplished by using the
--buildpackcommand-line option. Alternatively, you can create a descriptor file in toml format, as shown below.
uri = "java-maven"
The above file contains which buildpacks apply to your project. The
uriidentifies any of the existing build packs, or it can also define a custom build pack. The
java-mavenis a custom buildpack that enforces Azul open JDK version.
As per CNB specification, a buildpack consists of two scripts. Each of these scripts is executed for the following two phases of the buildpack :
The detect script for java-maven buildpack validated if the project contains
pom.xml. On the other hand, the build script downloads Azul JDK 11 build and triggers a maven build. It also creates the launcher process configuration. You can build the project by using the above-created descriptor.
pack build fakeapp -d ../buildpacks/descriptor.toml --builder paketobuildpacks/builder:full
As discussed previously, CNB can also alter the runtime environment by setting up environment variables. This behavior can be used to enforce various practices like application ports. Spring boot applications like fakeuser expose the application on 8080 port. But you can change the port by configuring the
SERVER_PORTenvironment variable. The build variable
BPE_OVERRIDE_SERVER_PORTaccomplished the same. It is picked by
paketo-buildpacks/environment-variablesto configure the SERVER_PORT environment variable in the runtime container image.
[[build.env]] name = "BPE_OVERRIDE_SERVER_PORT" value = "8000" [[build.buildpacks]] uri = "java-maven" [[build.buildpacks]] uri = "paketo-buildpacks/environment-variables"
It is important to note that the buildpacks are executed in the order specified in the descriptor file.
Logging is another practice that is standardized by operation teams. Often application logs are ingested in a monitoring tool. The monitoring tool can build dashboards and alerts based on this information. Thus these logs must adhere to a particular format.
Application logging is often accomplished by various language-specific logging libraries. These libraries require a log configuration file. Logging for the fakeuser application can be controlled by adding a
logback.xmlto the application classpath. You can create a custom buildpack to generate the required logback.xml. The buildpack would also set the required environment variables.
[[build.env]] name = "BPE_OVERRIDE_SERVER_PORT" value = "8000" [[build.buildpacks]] uri = "java-maven" [[build.buildpacks]] uri = "paketo-buildpacks/environment-variables" [[build.buildpacks]] uri = "log-format"
CNB offers a rich capability for building OCI images. The article only scratches the CNB surface. There are various layer caching practices, image inspection, and runtime rebasing, which are helpful in day-to-day operations.
CNB allows decoupling of application development from security and operational concerns. This way, developers can focus on writing code. On the other hand, DevOps teams can enforce their policies for container generation. The different phases provide the necessary support to customize policies for the varying needs of an enterprise.