Knative Tutorial

At the end of this chapter you will be able to :

  • Deploy a Knative service.

  • Deploy multiple revisions of a service.

  • Run different revisions of a service via traffic definition.

Prerequisite

The following checks ensure that each chapter exercises are done with the right environment settings.

  • Kubernetes

  • OpenShift

  • Set your local docker to use minikube docker daemon

eval $(minikube docker-env)

  • Kubernetes should be v1.12+

kubectl version

  • OpenShift CLI should be v4.1+

oc version

The output should be like

oc version
Client Version: version.Info{Major:"4", Minor:"1+", GitVersion:"v4.1.0+b4261e0", GitCommit:"b4261e07ed", GitTreeState:"clean", BuildDate:"2019-07-06T03:16:01Z", GoVersion:"go1.12.6", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"14+", GitVersion:"v1.14.6+73b5d76", GitCommit:"73b5d76", GitTreeState:"clean", BuildDate:"2019-09-23T16:18:51Z", GoVersion:"go1.12.8", Compiler:"gc", Platform:"linux/amd64"}
  • Make sure to be on knativetutorial OpenShift project

oc project -q

If you are not on knativetutorial project, then run following command to change to knativetutorial project:

oc project knativetutorial

Deploy Service

Navigate to the tutorial chapter’s knative folder:

cd $TUTORIAL_HOME/02-basics/knative

The following snippet shows what a Knative service YAML looks like:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: greeter
spec:
  template:
    metadata:
      name: greeter-v1
      annotations:
        # disable istio-proxy injection
        sidecar.istio.io/inject: "false"
    spec:
      containers:
      - image: quay.io/rhdevelopers/knative-tutorial-greeter:quarkus
        livenessProbe:
          httpGet:
            path: /healthz
        readinessProbe:
          httpGet:
            path: /healthz

The service can be deployed using the following command:

  • kubectl

  • oc

kubectl apply -n knativetutorial -f service.yaml

oc apply -n knativetutorial -f service.yaml

After successful deployment of the service we should see a Kubernetes Deployment named similar to greeter-nsrbr-deployment available:

  • kubectl

  • oc

kubectl get deployments -n knativetutorial

oc get deployments -n knativetutorial

  • The actual deployment name may vary in your setup

Invoke Service

  • Kubernetes

  • OpenShift

IP_ADDRESS="$(minikube ip):$(kubectl get svc istio-ingressgateway --namespace istio-system --output 'jsonpath={.spec.ports[?(@.port==80)].nodePort}')"

http $IP_ADDRESS 'Host:greeter.knativetutorial.example.com'

export SVC_URL=`oc get rt greeter -o yaml | yq read - 'status.url'` && \
http $SVC_URL

The http command should return a response containing a line similar to Hi greeter ⇒ '6fee83923a9f' : 1

Sometimes the response might not be returned immediately especially when the pod is coming up from dormant state. In that case, repeat service invocation.

See what you have deployed

The service-based deployment strategy that we did now will create many Knative resources, the following commands will help you to query and find what has been deployed.

service

  • kubectl

  • oc

kubectl --namespace knativetutorial  get services.serving.knative.dev greeter

oc --namespace knativetutorial  get services.serving.knative.dev greeter

configuration

  • kubectl

  • oc

kubectl --namespace knativetutorial get configurations.serving.knative.dev greeter

oc --namespace knativetutorial get configurations.serving.knative.dev greeter

routes

  • kubectl

  • oc

kubectl --namespace knativetutorial get routes.serving.knative.dev greeter

oc --namespace knativetutorial get routes.serving.knative.dev greeter

When the service was invoked with http $IP_ADDRESS 'Host:greeter.knativetutorial.example.com', you noticed that we added a Host header to the request with value greeter.knativetutorial.example.com. This FQDN is automatically assigned to your Knative service by the Knative Routes and uses the following format: <service-name>.<namespace>.<domain-suffix>.

  • The domain suffix in this case example.com is configurable via the config map config-domain of knative-serving namespace.

revisions

  • kubectl

  • oc

kubectl --namespace knativetutorial get rev \
--selector=serving.knative.dev/service=greeter \
--sort-by="{.metadata.creationTimestamp}"

oc --namespace knativetutorial get rev \
 --selector=serving.knative.dev/service=greeter \
 --sort-by="{.metadata.creationTimestamp}"

  • add -oyaml to the commands above to see more details

Deploy a New Revision of a Service

As Knative follows 12-Factor application principles, any configuration change will trigger creation of a new revision of the deployment.

To deploy a new revision of the greeter service, we will add an environment variable to the existing service as shown below:

Service revision 2

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: greeter
spec:
  template:
    metadata:
      name: greeter-v2
      annotations:
        # disable istio-proxy injection
        sidecar.istio.io/inject: "false"
    spec:
      containers:
      - image: quay.io/rhdevelopers/knative-tutorial-greeter:quarkus
        env:
        - name: MESSAGE_PREFIX
          value: Namaste
        livenessProbe:
          httpGet:
            path: /healthz
        readinessProbe:
          httpGet:
            path: /healthz

Adding an environment variable that will be used as the message prefix

Let us deploy the new revision using the command:

  • kubectl

  • oc

kubectl apply -n knativetutorial -f service-env.yaml

oc apply -n knativetutorial -f service-env.yaml

After successful deployment of the service we should see a Kubernetes deployment called greeter-v2-deployment.

Now if you list revisions, you will see two of them, named similar to greeter-v1 and greeter-v2.

revisions

  • kubectl

  • oc

kubectl --namespace knativetutorial get rev \
--selector=serving.knative.dev/service=greeter \
--sort-by="{.metadata.creationTimestamp}"

oc --namespace knativetutorial get rev \
 --selector=serving.knative.dev/service=greeter \
 --sort-by="{.metadata.creationTimestamp}"

Invoking Service will now show an output like Namaste greeter ⇒ '6fee83923a9f' : 1, where Namaste is the value we configured via environment variable in the Knative service resource file.

Pinning Service to a Revision

As you noticed, Knative service always routes traffic to the latest revision of the service deployment. It is possible to split the traffic amongst the available revisions.

As we already know the at got two revisions namely greeter-v1 and greeter-v2. The traffic block in the Knative service specification helps in pinning a service to a particular revision or split traffic among multiple revisions.

You can use the show Knative service revisions command to find the available revisions for the greeter service.

Service pinned to first revision

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: greeter
spec:
  template:
    metadata:
      name: greeter-v2
      annotations:
        # disable istio-proxy injection
        sidecar.istio.io/inject: "false"
    spec:
      containers:
      - image: quay.io/rhdevelopers/knative-tutorial-greeter:quarkus
        env:
        - name: MESSAGE_PREFIX
          value: Namaste
        livenessProbe:
          httpGet:
            path: /healthz
        readinessProbe:
          httpGet:
            path: /healthz
  traffic:
  - tag: current
    revisionName: greeter-v1
    percent: 100
  - tag: prev
    revisionName: greeter-v2
    percent: 0
  - tag: latest
    latestRevision: true
    percent: 0

The above service definition creates three sub-routes(named after traffic tags) to existing greeter route.

  • current - The revision is going to have all 100% traffic distribution

  • prev - The previously active revision, which will now have zero traffic

  • latest - The route pointing to any latest service deployment, by setting to zero we are making sure the latest revision is not picked up automatically.

Let us redeploy the greeter service by pinning it to the greeter-v1:

  • kubectl

  • oc

kubectl -n knativetutorial apply -f service-pinned.yaml

oc -n knativetutorial  apply -f service-pinned.yaml

Let us list the available sub-routes:

  • kubectl

  • oc

kubectl -n knativetutorial get ksvc greeter -oyaml \
 | yq r - 'status.traffic[*].url'

oc -n knativetutorial get ksvc greeter -oyaml \
  | yq r - 'status.traffic[*].url'

The above command should return you three sub-routes for the main greeter route:

1 the sub route for the traffic tag current
2 the sub route for the traffic tag prev
3 the sub route for the traffic tag latest

Invoking Service will produce output similar to Hi greeter ⇒ '6fee83923a9f' : 1

As per the current traffic distribution, the greeter route will always return response from revision greeter-v1 (traffic tag current)

  • If you are in vanilla Kubernetes cluster you can also try the using the above urls in the Host header of the http command to see them scaling the respective deployments to get the result.

  • If you are on OpenShift then you can directly invoke the urls to see the response of those respective revision

Cleanup

  • kubectl

  • oc

kubectl -n knativetutorial delete services.serving.knative.dev greeter

oc -n knativetutorial delete services.serving.knative.dev greeter