The OpenWhisk community began prototyping OpenWhisk runtime environments, specifically Node.js, so OpenWhisk actions can be deployed and run on Knative. You can read more about running OpenWhisk actions with Docker for Desktop and Minikube on my blog post.

In addition, Doug Davis announced managed Knative on the IBM Cloud Kubernetes service in early 2019. He explains how to enable the managed Knative add-on in your Kubernetes cluster.

This tutorial shows how to experiment with the IBM Cloud Kubernetes Service to see how it differs from other environments.

Learning objectives

Understand how to run OpenWhisk actions on managed Knative on a Kubernetes cluster. Experiment with examples using the free IBM Cloud Kubernetes Service.

Estimated time

This tutorial takes about 10-15 minutes after installing and configuring the prerequisites.

Prerequisites

  • Create a standard Kubernetes cluster with version 1.12.7 of IBM Cloud Kubernetes Service in Single Zone/Multi Zone with a flavor of 4 cores, 16 GB RAM, and 3 worker nodes. (Feel free to grab some lunch while the standard cluster is created!)

  • Configure IBM Cloud CLI. To download the kubeconfig files for your cluster using the following command:

     ibmcloud ks cluster-config <cluster-name>
    
  • Configure kubectl to access the cluster on IBM Cloud Kubernetes Service.

    You can verify with the following command:

     kubectl cluster-info
    
  • Enable the Knative add-on:

     ibmcloud ks cluster-addon-enable knative <cluster-name>
    

    (Now is a good time to grab some coffee!)

  • Verify that all pods are Running under istio-system, knative-build, knative-serving, knative-eventing, and knative-monitoring.

      kubectl get pods --all-namespaces
      NAMESPACE            NAME                                            READY   STATUS              RESTARTS   AGE
      istio-system         cluster-local-gateway-6d8585d55d-jmmwn          1/1     Running             0          79s
      ...
      knative-build        build-controller-865d99747c-lj9sh               1/1     Running             0          109s
      ...
      knative-eventing     eventing-controller-64bf9bbf79-ccq5p            2/2     Running             0          79s
      ...
      knative-monitoring   elasticsearch-logging-0                         1/1     Running             0          108s
      ...
      knative-serving      activator-58fb6bdff4-5tsjh                      0/2     PodInitializing     0          78s
      ...
      knative-sources      controller-manager-0                            1/1     Running             0          106s
    

Steps

Complete the following six steps to build and serve OpenWhisk actions on Knative:

Architecture flow diagram for building and serving an OpenWhisk runtime environment on Knative

Step 1. Register secrets for your container registry on Knative

Knative needs access to your container registry in order to push locally built container image. The examples here use Docker Hub, but you can use any registry of your choice by changing the registry name under annotation.

Save this file as docker-secret.yaml after replacing DOCKERHUB_USERNAME_BASE64_ENCODED and DOCKERHUB_PASSWORD_BASE64_ENCODED with your Docker Hub user name and password:

apiVersion: v1
kind: Secret
metadata:
    name: dockerhub-user-pass
    annotations:
        build.knative.dev/docker-0: https://index.docker.io/v1/
type: kubernetes.io/basic-auth
data:
    # use `echo -n "username" | base64 -b 0` to generate this value
    username: ${DOCKERHUB_USERNAME_BASE64_ENCODED}
    # use `echo -n "password" | base64 -b 0` to generate this value
    password: ${DOCKERHUB_PASSWORD_BASE64_ENCODED}

Apply the secret resource manifest for Docker Hub:

 $ kubectl apply -f docker-secret.yaml
secret/dockerhub-user-pass created

Verify that the secret exists:

$ kubectl get secret dockerhub-user-pass
NAME                    TYPE                                  DATA      AGE
dockerhub-user-pass     kubernetes.io/basic-auth              2       21s

Step 2: Create Service Account for our Knative Builds

Create a service account to link the build process with the registry secret that you created in step 1 so that the Knative build system can push container images to the registry using these credentials.

apiVersion: v1
kind: ServiceAccount
metadata:
    name: openwhisk-runtime-builder
secrets:
    - name: dockerhub-user-pass
$ kubectl apply -f service-account.yaml
serviceaccount/openwhisk-runtime-builder created

Verify the service account exists:

$ kubectl get serviceaccount/openwhisk-runtime-builder
NAME                        SECRETS   AGE
openwhisk-runtime-builder   2         3m46s

Step 3: Install the build template for the Node.js runtime environment

Deploy the OpenWhisk build template:

$ kubectl apply -f https://raw.githubusercontent.com/apache/incubator-openwhisk-runtime-nodejs/master/core/nodejsActionBase/buildtemplate.yaml
buildtemplate.build.knative.dev/openwhisk-nodejs-runtime created

Verify that the build template exists:

$ kubectl get buildtemplate openwhisk-nodejs-runtime
NAME                       AGE
openwhisk-nodejs-runtime   2m

Step 4: Deploy the Node.js runtime environment with the Hello World action code

Configure the build file to point to your Docker Hub repo by replacing DOCKER_USERNAME with your user name:

apiVersion: build.knative.dev/v1alpha1
kind: Build
metadata:
  name: nodejs-10-helloworld-with-params
spec:
  serviceAccountName: openwhisk-runtime-builder
  source:
    git:
      url: "https://github.com/apache/incubator-openwhisk-runtime-nodejs.git"
      revision: "master"
  template:
    name: openwhisk-nodejs-runtime
    arguments:
      - name: TARGET_IMAGE_NAME
        value: "docker.io/${DOCKER_USERNAME}/nodejs-10-helloworld-with-params"
      - name: DOCKERFILE
        value: "./core/nodejs10Action/knative/Dockerfile"
      - name: OW_ACTION_NAME
        value: "nodejs-helloworld-with-params"
      - name: OW_ACTION_CODE
        value: "function main(params) { return { payload: 'Hello ' + params.name + ' from ' + params.place +  '!' }; }"

Deploy the Node.js runtime environment with the following action code:

$ kubectl apply -f build.yaml
build.build.knative.dev/nodejs-10-helloworld-with-params created

Verify that the build pod exists:

$ kubectl get build.build.knative.dev/nodejs-10-helloworld-with-params
NAME                              SUCCEEDED   REASON   STARTTIME   COMPLETIONTIME
nodejs-10-helloworld-with-params   True                 8m

Step 5: Serve the Node.js runtime environment as a Knative service

Now that you built the OpenWhisk Node.js runtime image with the helloworld function included in it, you can deploy that image as a Knative service.

Configure the service template to point to the Docker Hub repo where the OpenWhisk runtime environment (that you built in step 4) is pulled from. Replace ${DOCKER_USERNAME} and create service.yaml:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: nodejs-helloworld-with-params
  namespace: default
spec:
  runLatest:
    configuration:
      revisionTemplate:
        spec:
          container:
            image: docker.io/${DOCKER_USERNAME}/nodejs-10-helloworld-with-params

Deploy the runtime environment:

 $ kubectl apply -f service.yaml
service.serving.knative.dev/nodejs-helloworld-with-params created

Step 6: Run the Hello World action

This step differs in the way you run the Knative service on IBM Kubernetes or Docker for Desktop or Minikube using the JSON payload:

{
  "activation": {
    "namespace": "default",
    "action_name": "nodejs-helloworld-with-params",
    "api_host": "",
    "api_key": "",
    "activation_id": "",
    "deadline": "4102498800000"
  },
  "value": {
    "name" : "Jill",
    "place" : "OK"
  }
}
kubectl get ksvc nodejs-helloworld-with-params
NAME                            DOMAIN                                                                                  LATESTCREATED                         LATESTREADY                           READY   REASON
nodejs-helloworld-with-params   nodejs-helloworld-with-params.default.<cluster-name>.<region>.containers.appdomain.cloud   nodejs-helloworld-with-params-f2w4p   nodejs-helloworld-with-params-f2w4p   True
curl nodejs-helloworld-with-params.default.<cluster-name>.<region>.containers.appdomain.cloud -H "Content-Type: application/json" -d '@params.json'
{"payload":"Hello Jill from OK!"}

Hooray! You have your hello world action running on Knative on IBM Cloud Kubernetes Service.

Summary

This example can help you understand how to run OpenWhisk actions on managed Knative on a Kubernetes cluster. I hit a memory limitation when I installed Knative on the free IBM Cloud Kubernetes cluster. In a production environment, you might need to plan for more memory.

Try it out for yourself at IBM Cloud Kubernetes Service.