Introduction to Cloud Native Buildpacks with Kubernetes

Reading Time: 4 minutes


What Are Buildpacks?

Buildpacks provide a higher-level abstraction for building apps compared to Dockerfiles.

Specifically, buildpacks:

  • Provide a balance of control that reduces the operational burden on developers and supports enterprise operators who manage apps at scale.
  • Ensure that apps meet security and compliance requirements without developer intervention.
  • Provide automated delivery of both OS-level and application-level dependency upgrades, efficiently handling day-2 app operations that are often difficult to manage with Dockerfiles.
  • Rely on compatibility guarantees to safely apply patches without rebuilding artifacts and without unintentionally changing application behavior.

Buildpacks were first conceived by Heroku in 2011. Since then, they have been adopted by Cloud Foundry and other PaaS such as Gitlab, Knative, Deis, Dokku, and Drie.

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 embrace modern container standards, such as the OCI image format. They take advantage of the latest capabilities of these standards, such as cross-repository blob mounting and image layer “rebasing” on Docker API v2 registries.


How Cloud Native Buildpack Works?

Buildpacks are responsible for taking app code and create a OCI compatible docker image using it.

Lifecycle of this conversion includes multiple phases

  • Detection – Finds an ordered group of buildpacks to use during the build phase.
  • Analysis – Restores files that buildpacks may use to optimize the build and export phases.
  • Build – Transforms application source code into runnable artifacts that can be packaged into a container.
  • Export – Creates the final OCI image.

How to use Cloud Native Buildpacks?

Cloud native buildpacks can be consumed using pack cli, which helps application developer to create Docker images, without going through hassle of creating Dockerfile.

You must be wondering, where to define the base image for Docker image or where to define all the fancy bash scripting or how to copy code, well buildpack take care of all those steps for us.

Before we can start creating our awesome Docker images, we need to install the Pack cli. Follow the instructions from below link depending on your OS.

Installing Pack cli https://buildpacks.io/docs/install-pack/


Prerequisites

Before we start doing all the fancy Docker & Kubernetes & Buildpack stuff, lets ensure we have the stack in place

  • Docker Daemon is running
  • Docker cli is available on system
  • Kubernetes access if you want to try to k8s deployment


Building first Docker image with CNB

let’s start by cloning the application repository, this sample app is written in Go and dep is used for vendor management.

git clone https://github.com/ronakbanka/cnb-goapp

We will be using the cloudfoundry builder to build our application docker image. To know more about builders check CNB builders page.

To build our docker image, follow the steps

cd cnb-goapp
pack build cnb-goapp --builder cloudfoundry/cnb:tiny

We have used the tag tiny, which under the hood uses scratch image to run the application with lower footprint. There are multiple tags available which you can find on docker hub cnb images.

Once you run the above command, pack cli will go ahead and build the image by binding application along with dependencies.

tiny: Pulling from cloudfoundry/cnb
Digest: sha256:2f8c44c4ca7cb5e8f7c7bd61a279152d3341953694af0be4b05a58785f9e547c
Status: Image is up to date for cloudfoundry/cnb:tiny
tiny-cnb: Pulling from cloudfoundry/run
Digest: sha256:8984f81bc66488f583526f688f321ee54bddff885683bc42d186a837de75247c
Status: Image is up to date for cloudfoundry/run:tiny-cnb
===> DETECTING
[detector] org.cloudfoundry.go-compiler 0.0.55
[detector] org.cloudfoundry.dep         0.0.64
===> RESTORING
[restorer] Restoring cached layer 'org.cloudfoundry.go-compiler:go'
[restorer] Restoring cached layer 'org.cloudfoundry.go-compiler:692d17071736f74be04a72a06dab9cac1cd759377bd85316e52b2227604c004c'
[restorer] Restoring cached layer 'org.cloudfoundry.dep:dep'
[restorer] Restoring cached layer 'org.cloudfoundry.dep:79b3ab9e67bf51bae787faaa5c78782752d0e39ea7b0d99e485a181b63a49559'
===> ANALYZING
[analyzer] Using cached layer 'org.cloudfoundry.go-compiler:692d17071736f74be04a72a06dab9cac1cd759377bd85316e52b2227604c004c'
[analyzer] Using cached layer 'org.cloudfoundry.go-compiler:go'
[analyzer] Using cached layer 'org.cloudfoundry.dep:79b3ab9e67bf51bae787faaa5c78782752d0e39ea7b0d99e485a181b63a49559'
[analyzer] Using cached layer 'org.cloudfoundry.dep:dep'
[analyzer] Writing metadata for uncached layer 'org.cloudfoundry.dep:app-binary'
===> BUILDING
[builder]
[builder] Go Compiler Buildpack 0.0.55
[builder]   Go 1.13.4: Reusing cached layer
[builder]
[builder] Dep Buildpack 0.0.64
[builder]   Dep 0.5.4: Reusing cached layer
[builder]   Dep Packages 4c8b8a6098d59d5c4ab3c766b1f32edd53e8f4046f99c04fd403626403ca8a17: Contributing to layer
[builder]     Fetching any unsaved dependencies (using `dep ensure`)
[builder]   App Binary 30e4162a0e6eb9721efaf5f65c9d464e5c01e561f9bf505f7173a094492daebe: Contributing to layer
[builder]     Running `go install`
[builder]   Process types:
[builder]     web: /layers/org.cloudfoundry.dep/app-binary/bin/cnb-goapp
===> EXPORTING
[exporter] Reusing layer 'app'
[exporter] Reusing layer 'config'
[exporter] Reusing layer 'launcher'
[exporter] Reusing layer 'org.cloudfoundry.dep:app-binary'
[exporter] *** Images (c60a0eb28fc8):
[exporter]       index.docker.io/library/cnb-goapp:latest
===> CACHING
[cacher] Reusing layer 'org.cloudfoundry.go-compiler:692d17071736f74be04a72a06dab9cac1cd759377bd85316e52b2227604c004c'
[cacher] Reusing layer 'org.cloudfoundry.go-compiler:go'
[cacher] Reusing layer 'org.cloudfoundry.dep:79b3ab9e67bf51bae787faaa5c78782752d0e39ea7b0d99e485a181b63a49559'
[cacher] Reusing layer 'org.cloudfoundry.dep:dep'
Successfully built image cnb-goapp

We can run the container with newly created docker image using

docker run -d -p 8080:8080 cnb-goapp

Verify that container is running successfully

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
e4e5b18b4fb4        cnb-goapp           "/cnb/lifecycle/laun…"   3 seconds ago       Up 1 second         0.0.0.0:8080->8080/tcp   priceless_shockley

Open the browser to access the application on http://localhost:8080


Deploy on Kubernetes (k8s)

Assuming you have kubernetes cluster access, we will use deployment definition to deploy our above docker image.

Before deploying the application, we need to push the above image to Docker hub, I have pushed mine on ronakbanka/cnb-goapp. you can use it too for testing the k8s deployment.

You can use the below cnb-goapp.yml file to create k8s deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: cnb-goapp
  name: cnb-goapp
spec:
  replicas: 1
  selector:
    matchLabels:
      run: cnb-goapp
  template:
    metadata:
      labels:
        run: cnb-goapp
    spec:
      containers:
      - image: ronakbanka/cnb-goapp
        name: cnb-goapp
        ports:
        - containerPort: 8080

Create the deployment using

kubectl apply -f cnb-goapp.yml

Pod will be created if no issues on cluster

$ kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
cnb-goapp-68f4db8c98-9s8qn   1/1     Running   0          7s

To access the application, let’s expose the application using k8s service, I tested this on k8s cluster on AWS, so depending on your infrastructure you can create the LoadBalancer.

kubectl expose deploy cnb-goapp --type LoadBalancer

Let’s check our service which was created by kubernetes, thus creating LoadBalancer on AWS

kubectl get svc
NAME            TYPE           CLUSTER-IP       EXTERNAL-IP                                                                   PORT(S)          AGE
cnb             LoadBalancer   10.100.200.19    a9c4733e9de35483588c18cdebcf2705-408330440.ap-southeast-1.elb.amazonaws.com   8080:30167/TCP   20m

Now to access the application, all we have to do is curl the endpoint

$ curl http://a9c4733e9de35483588c18cdebcf2705-408330440.ap-southeast-1.elb.amazonaws.com:8080

Hello world!

So you don’t have to manage your Dockerfiles anymore, just use Cloud Native Buildpacks.

Next in the series will be a blog on Kpack, which allows us to build the image on Kubernetes itself using the same contracts from buildpacks.

Stay tuned!!

References

Ronak Banka

Ronak works as a Senior Solution architect with VMware, based out of Singapore.

Leave a Reply

Your email address will not be published. Required fields are marked *