Tanzu Build Service uses Cloud Native Buildpacks (CNBs) to turn source code into OCI (Open Container Initiative) compatible container images, no manual compilation or Dockerfiles. This is something developers using CloudFoundry/PAS/TAS loved, and this bring this same experience to application running on Kubernetes.
Build Service also solves some of the biggest operational and security challenges that come with maintaining software over time by removing the need for a human to intervene when there are updates to your software or its dependencies. Automating the maintenance of your containers is especially important because it drastically reduces the risk of critical security vulnerabilities being left unpatched.
We will be doing a lab where we install Tanzu Build Service (beta) version v0.2.0 and deploy a Go application.
You will need to have the following requirements:
- An Kubernetes cluster (v1.14 or later) with
PersistentVolumes
. I’m using an Azure AKS cluster (2 nodes) so I have multipleStorageClasses
that can be used to provision volumes.
> kubectl get nodes NAME STATUS ROLES AGE VERSION aks-agentpool-20474252-vmss000000 Ready agent 8d v1.16.9 aks-agentpool-20474252-vmss000001 Ready agent 8d v1.16.9 > kubectl get sc NAME PROVISIONER AGE azurefile kubernetes.io/azure-file 8d azurefile-premium kubernetes.io/azure-file 8d default (default) kubernetes.io/azure-disk 2d1h managed-premium kubernetes.io/azure-disk 2d1h
- An OCI container registry. This can be public or private registry. I will be using Docker hub
- Download the
duffle
executable for your operating system from the Tanzu Build Service page on Tanzu Network. - Download the
kp
binary from the Tanzu Build Service page on Tanzu Network. - Download the Build Service Bundle from the Tanzu Build Service page on Tanzu Network.
Create the Tanzu Build Service credentials file
The credentials file tells Tanzu Build Service where your Kubernetes config file is in order to connect to it. It also defines credentials for Tanzu Build Service to connect to your image repository in order to complete the install. Since this guide uses Docker Hub for the install, we don’t need to define credentials for the registry in this step.
name: build-service-credentials credentials: - name: kube_config source: path: "/home/jfcoronado/.kube/config" destination: path: "/root/.kube/config"
Push the Tanzu Build Service bundle to Docker Hub
The bundle (build-service-0.2.0.tgz) contains many images for the buildpack runtime packages Tanzu Build Service will use to build your images. It also contains the install image for Tanzu Build Service. For Tanzu Build Service to have access to these images, they need to be pushed to the image registry.
First, log in to your Docker Hub account. This command will ask you for your username and password.
>docker login Authenticating with existing credentials... WARNING! Your password will be stored unencrypted in /home/jfcoronado/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded
Then use Duffle to push the bundle to the repo:
>duffle relocate -f ./build-service-0.2.0.tgz -m /tmp/relocated.json -p jfcoronado
Install Tanzu Build Service
Now it’s time to install Tanzu Build Service. This command will utilize many of the environment variables we previously defined.
The kubernetes_env
attribute is the name of your Kubernetes cluster. You can get it by running “kubectl config get-clusters”
that will give you a list of your configured clusters.
>kubectl config get-clusters NAME aks-cluster
custom_builder_image
is the fully-qualified tag where Build Service will create a default cluster-wide builder.
>duffle install my-build-service -c /tmp/credentials.yml \ --set kubernetes_env=aks-cluster \ --set docker_registry=index.docker.io \ --set docker_repository=jfcoronado \ --set registry_username="jfcoronado" \ --set registry_password="XXXXXXXXX" \ --set custom_builder_image="jfcoronado/default-builder" \ -f /home/jfcoronado/Downloads/build-service-0.2.0.tgz \ -m /tmp/relocated.json
The docker_
and registry_
flags you set the registry information, in my case my Docker Hub account.
The installation will take a couple of minutes. After that you can test that your installation was successful running kp command:
>kp custom-cluster-builder list NAME READY STACK IMAGE base true io.buildpacks.stacks.bionic jfcoronado/default-builder:base@sha256:f6df65162effe938e00b83557d052827f4d1bd3b47bbdf765e6a2b4c4267b4b2 default true org.cloudfoundry.stacks.cflinuxfs3 jfcoronado/default-builder:default@sha256:df185b47e0a725260a27d5bb5160779ac0dfe729d8e3fa51cf43dc5baa17e7d2 full true org.cloudfoundry.stacks.cflinuxfs3 jfcoronado/default-builder:full@sha256:df185b47e0a725260a27d5bb5160779ac0dfe729d8e3fa51cf43dc5baa17e7d2 tiny true io.paketo.stacks.tiny jfcoronado/default-builder:tiny@sha256:20b3e0c56596e9d45c7f0aea10ade3afa3226e14054b4172d22c49053f1a1508
Playing with Build Service
Let’s build an application. For this I’m going to use a demo app written on Go that was used in the SpringOne Tour 2020. I will start by cloning the repo locally.
>git clone https://github.com/springone-tour-2020-cicd/go-sample-app Cloning into 'go-sample-app'... remote: Enumerating objects: 111, done. remote: Total 111 (delta 0), reused 0 (delta 0), pack-reused 111 Receiving objects: 100% (111/111), 13.40 KiB | 4.47 MiB/s, done. Resolving deltas: 100% (39/39), done.
It have already a Docker file that we will not need so let’s go and delete it to make sure we are not using it:
>cd go-sample-app >ls Dockerfile go.mod hello-server.go README.md >rm Dockerfile
For Build Service to work we need to create a secret with our registry credentials. You can create it using the kp tool this way:
>kp secret create docker-hub --dockerhub jfcoronado dockerhub password: "docker-hub" created >kp secret list NAME TARGET default-token-xzcrd docker-hub https://index.docker.io/v1/
Now we can generate an image also using kp image command. This will generate a pod that will build the image and push it to our registry. You can monitor the pod until it get completed.
>kp image create go-sample-app jfcoronado/go-sample-app --local-path . >kubectl get pods NAME READY STATUS RESTARTS AGE go-sample-app-build-1-qft69-build-pod 0/1 Init:1/6 0 12s >kubectl get pods NAME READY STATUS RESTARTS AGE go-sample-app-build-1-qft69-build-pod 0/1 Completed 0 4m5s
Now you can go ahead and try your app
>kubectl run go-sample-app --image=jfcoronado/go-sample-app:latest pod/go-sample-app created >kubectl get pods NAME READY STATUS RESTARTS AGE go-sample-app 1/1 Running 0 54s go-sample-build-1-qft69-build-pod 0/1 Completed 0 12m >kubectl port-forward go-sample-app 8080:8080 Forwarding from 127.0.0.1:8080 -> 8080 Forwarding from [::1]:8080 -> 8080 >curl localhost:8080 Hello, world!
And vuala! We have our Go app running. We went from source code to container, no compiling, no Dockerfile creation, following cloud native best practices. Easy!