Stark & Wayne

Kpack, oh kpack, wherefore art thou build logs?

Any CI/CD is just like a serverless platform or PaaS: you run other people's code. The difference? With a platform you expect the code to work.

With CI/CD you're waiting for things to fail. So you can fix them. Until they work. Hopefully.

And to fix them you need logs.

kpack is a new open source build system from Pivotal that specializes in applying Cloud Native Buildpacks to my apps and pushing out easy to use OCI/docker images. kpack runs entirely within Kubernetes, and allows me to build OCIs from a git repo branch. New commit results in new OCI image.

That is unless the buildpacks fail upon my heathen code. Then I must debug thy code, reduce my mistakes to distant memories, and request forgiveness from my build system overlords.

So kpack, where are my logs?

In this article we will look at both kpack's own logs CLI, and how you can find the raw logs from the Kubernetes init containers used to run Cloud Native Buildpacks. I learned about init containers and you can too.

First, let's setup kpack and build something

To install kpack v0.0.4 into a clean kpack namespace (there is a Cleanup section at the end):

kubectl apply -f

We will build my sample NodeJS application using a collection of new buildpacks from the Cloud Foundry Buildpacks team, which includes buildpacks for NodeJS applications:

kubectl create ns $ns
kubectl apply -n $ns -f

Now we need a serviceaccount that includes permissions to publish our OCI/docker images to a registry.

Find a sample serviceaccount YAML at Make your own YAML file, and install it:

kubectl apply -n $ns -f my-serviceaccount.yaml

Finally, to ask kpack to continously watch and build my sample NodeJS application, create a kpack Image file kpack-image.yamlwith the name you wish to publish the Docker image:

kind: Image
  name: sample-app-nodejs
    name: cflinuxfs3-builder
    kind: Builder
  serviceAccount: service-account
  cacheSize: "1.5Gi"
      revision: master
  tag: <my organization>/<my image name>:latest

Apply this file to your namespace and you're done:

kubectl apply -n $ns -f kpack-image.yaml

Your kpack image will automatically detect the latest Git commit on my repository, will create a kpack Build and will start doing its Cloud Native Buildpacks magic.

Unless it doesn't. You have no idea. kpack is "native to Kubernetes" which I think means "no UI" and "figure out for yourself if it works".

Logs, damn it

Latest kpack releases include a logs CLI to allow you to watch or replay the logs for a build (git rep + builder/buildpacks -> docker image). Download the one for your OS, put it in your $PATH, make it executable, and we can see the logs from our first build:

logs -image sample-app-nodejs -namespace $ns -build 1

The output will include the magic of Cloud Native Buildpacks applied to our sample NodeJS app:

-----> Node Engine Buildpack 0.0.49
  Node Engine 10.16.3: Contributing to layer
    Downloading from
-----> Yarn Buildpack 0.0.28
  Yarn 1.17.3: Contributing to layer
*** Images:
      starkandwayne/sample-app-nodejs:latest - succeeded

From where doth logs cometh?

So we have a kpack logs CLI, but what does it do? Where are these logs?

Take a moment to brush up on init containers. You are now qualified to understand how kpack implements each Build - it creates a pod with a long ordered sequence of init containers. Each step of the Cloud Native Buildpack lifecycle (detect, build, export, etc) is implemented as an independent init container.

Init containers for a pod are run one at a time, until they complete, and pods are only run once all init containers are run. A kpack Build is implemented as a pod whose container does nothing; its all implemented with init containers.

The STDOUT/STDERR of each init container are the logs we are looking for.

To see the logs for an init container we use the kubectl logs -c <container> flag.

For example, to see the build stage logs (most likely where you will find bugs in how buildpacks are running against your application source code) we'd run:

kubectl logs <build-pod> -c build

The kpack logs CLI is simply discovering the build pod, and displaying the logs for each init container in the correct order. Neat.

The init containers map to the buildpack lifecycle steps:

$ kubectl get pods -n $ns
NAME                                        READY   STATUS      RESTARTS   AGE
sample-app-nodejs-build-1-wnlxs-build-pod   0/1     Completed   0          2m38s

$ pod=sample-app-nodejs-build-1-wnlxs-build-pod
$ kubectl get pod $pod -n $ns -o json | jq -r ".spec.initContainers[].name"

So to get the logs for a complete kpack Build, we just look up the logs for each init container in order.

Enter  xargs to allow us to invoke kubectl logs -c <init-container> on each named container above:

kubectl get pod $pod -n $ns -o json | \
  jq -r ".spec.initContainers[].name" | \
  xargs -L1 kubectl logs $pod -n $ns -c

stern shows all the logs

Another way to view the logs then is the stern cli, which is very handy way to view logs of pods with multiple containers:

stern $pod -n $ns --container-state terminated

One current downside of stern for this task is that it does not show init container logs first, in correct order, so it may be confusing debugging them.


Delete our demo namespace to remove the kpack image, builds, and pods:

kubectl delete ns demo

To remove kpack namespace and custom resource definitions:

kubectl delete -f