Digital Developer Conference: Hybrid Cloud 2021. On Sep 21, gain free hybrid cloud skills from experts and partners. Register now

Develop Knative Serving on IBM Cloud

This tutorial guides developers who want to use IBM Cloud to develop and test Knative, a serverless platform that runs on Kubernetes. It helps you start contributing to the Knative open source project by showing you how to set up a development environment on IBM Cloud, and make and test changes to the code base.

Prerequisites

You must also install the following tools on your local system:

  • Go, which is the language that Knative is written in (version 1.14 or later)
  • Git for version control
  • ko container image builder for Go applications
  • kubectl to interact with your Kubernetes cluster

If you use a Mac, you must update your Bash to version 4 or later (the default Bash is too old). You can use Homebrew to install a later version.

Estimated time

Completing this tutorial should take about 20 minutes.

Steps

  1. Set up IBM Cloud
  2. Install Knative from source
  3. Test your development environment
  4. Make a code change to Knative

1. Set up IBM Cloud

After you log into or register for your IBM Cloud account, install the IBM Cloud Command Line Interface (CLI) to create and manage your Kubernetes cluster and deploy Knative to your cluster.

After you install the CLI, you must also install the IBM Cloud Kubernetes Service and IBM Cloud Container Registry plugins with the following commands:

ibmcloud login
ibmcloud plugin install container-service
ibmcloud plugin install container-registry

After you set up the CLI and plugins, you must create the following resources in IBM Cloud:

After your cluster is created, connect to it by running the following commands:

ibmcloud ks cluster ls #this will provide the name of your cluster
ibmcloud ks cluster config -c <cluster-name>

Note: This tutorial assumes that your registry namespace is in the Dallas (us-south) region, which means that it uses the us.icr.io endpoint in the following steps. If your namespace is in a different region, you must use the appropriate endpoint for your location.

To create your namespace, run the following commands:

ibmcloud cr login
ibmcloud cr region-set us-south
ibmcloud cr namespace-add <your-namespace>

You must also configure the ko container image builder to push images to your Container Registry namespace. This is done by setting the KO_DOCKER_REPO variable to the repository where images should be pushed. For example, you can set this value in your .bashrc file by adding the following command:

export KO_DOCKER_REPO=us.icr.io/<your-namespace>

2. Install Knative from source

Get the code

To set up your Knative development environment, start by cloning the knative/serving repo in GitHub.

To submit pull requests, you must first fork the repo.

After you made your fork, you must then clone it to your machine as follows:

git clone git@github.com:<your-github-id>/serving.git

To keep your fork in sync with the main Knative branch, set an upstream remote as follows:

cd serving
git remote add upstream https://github.com/knative/serving.git

Note: To learn more about the GitHub workflow, see the Knative contributor guidelines. More generally, the Kubernetes team put together a nice GitHub workflows guide.

Deploy to Kubernetes

After you clone the repo to your machine, you’re ready to install Knative on your cluster.

When you build Knative from source, use the ko tool to build and push several images to your Container Registry namespace, and then deploy them to your cluster in the knative-serving namespace. Because Container Registry is a private registry, you must configure the credentials so that your images can be deployed from Container Registry to your cluster.

To do this, you first create the knative-serving namespace:

kubectl create namespace knative-serving

Then, copy the Container Registry secrets from the default namespace to knative-serving:

kubectl get secret all-icr-io -n default -o yaml | sed "s/default/knative-serving/g" | kubectl create -n knative-serving -f -

The last step is to update the controller service account so that it can use these credentials. You can do this by adding an imagePullSecrets section to the YAML file for the controller service account as follows:

apiVersion: v1
kind: ServiceAccount
imagePullSecrets:
  - name: all-icr-io
metadata:
  name: controller
  namespace: knative-serving
  labels:
    serving.knative.dev/release: devel

After the service account is configured, you can then proceed with the standard Knative setup.

First, deploy cert-manager on your cluster:

kubectl apply -f ./third_party/cert-manager-latest/cert-manager.yaml
kubectl wait --for=condition=Established --all crd
kubectl wait --for=condition=Available -n cert-manager --all deployments

Then, deploy Knative serving (including a couple of optional components):

ko apply --selector knative.dev/crd-install=true -Rf config/core/
kubectl wait --for=condition=Established --all crd
ko apply -Rf config/core/
ko delete -f config/post-install/default-domain.yaml --ignore-not-found
ko apply -f config/post-install/default-domain.yaml

You can monitor the status of your Knative installation by checking that all pods deployed in the knative-serving namespace are either running or completed:

kubectl get pods --n knative-serving

After everything is running, the next step is to install a networking layer. Kourier is probably the simplest to get up and running:

kubectl apply -f ./third_party/kourier-latest/kourier.yaml
kubectl patch configmap/config-network -n knative-serving --type merge -p '{"data":{"ingress.class":"kourier.ingress.networking.knative.dev"}}'

There are other alternatives you could use, such as Istio or Contour.

Similar to Knative serving, you can monitor the status of the Kourier deployment by running the following command:

kubectl get pods --n kourier-serving

After all of the pods are either running or completed, you can move on to the last step. Collect the info you need to configure DNS by running the following command:

kubectl get svc kourier -n kourier-system

Make a note of the EXTERNAL-IP and PORT that are listed, as you’ll need them when you deploy a Knative service.

3. Test your development environment

To ensure that your development environment is working properly, deploy a sample Knative application. Create a file called hello-knative.yaml and add the following code:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
 name: helloworld-go
 namespace: default
spec:
 template:
  spec:
   containers:
    - image: gcr.io/knative-samples/helloworld-go
      env:
        - name: TARGET
          value: "Go Sample v1"

Deploy this sample application by running the following command:

kubectl apply -f hello-knative.yaml

It will take a few seconds for the service to deploy. After it’s ready, use the DNS info that you looked up earlier to access the application by running the following command:

curl -H "Host: helloworld-go.default.example.com" http://<EXTERNAL-IP>:<PORT>

If it’s successful, you’ll see output similar to the following:

Hello Go Sample v1!

That’s it! You now have a Knative development environment up and running.

4. Make a code change to Knative

Of course, the reason why you set up a Knative development environment is so that you can make changes to the code base. In this step, you make a small change to see what the process is to iterate on your installation.

Make a trivial change to the cmd/activator/main.go file as follows:

// Lines 91-95, before your change...
cfg := injection.ParseAndGetRESTConfigOrDie()

log.Printf("Registering %d clients", len(injection.Default.GetClients()))
log.Printf("Registering %d informer factories", len(injection.Default.GetInformerFactories()))
log.Printf("Registering %d informers", len(injection.Default.GetInformers()))
// ... and after your change
cfg := injection.ParseAndGetRESTConfigOrDie()

log.Println("this is running from our development environment") // This is the change we made

log.Printf("Registering %d clients", len(injection.Default.GetClients()))
log.Printf("Registering %d informer factories", len(injection.Default.GetInformerFactories()))
log.Printf("Registering %d informers", len(injection.Default.GetInformers()))

After you make the change, you can redeploy your changes by running the following command:

ko apply -f config/core/deployments/controller.yaml

Changing the code of the activator pod will destroy and recreate it automatically. If you check its logs:

k logs -n knative-serving activator-7bb6c56899-vqzht | head -n 5

You see the new line that you added in results similar to the following:

2021/06/22 15:22:57 this is running from our development environment
2021/06/22 15:22:57 Registering 2 clients
2021/06/22 15:22:57 Registering 3 informer factories
2021/06/22 15:22:57 Registering 3 informers
{"severity":"INFO","timestamp":"2021-06-22T15:22:57.151203591Z","caller":"logging/config.go:116","message":"Successfully created the logger."}

Notice that your update shows up in the second line of the output. That means you successfully set up your Knative development environment on IBM Cloud. Congratulations!

Start contributing

In this tutorial, you learned how to use IBM Cloud to create a development environment for Knative. Now that it’s working, join the community and start contributing. Take a look at the good first issues and attend a working group session.