Introduction to Cloud Native Buildpacks with Kubernetes
What Are Buildpacks?
Cloud Native Buildpacks provides 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
- Docs for CNB are on https://buildpacks.io/docs/
- Pivotal Blog on CNB https://content.pivotal.io/blog/cloud-native-buildpacks-for-kubernetes-and-beyond
- Using Buildpacks with Azure https://docs.microsoft.com/en-us/azure/container-registry/container-registry-tasks-pack-build