diff --git a/katacoda.yaml b/katacoda.yaml new file mode 100644 index 00000000000..65f212bde98 --- /dev/null +++ b/katacoda.yaml @@ -0,0 +1 @@ +scenario_root : tutorials/katacoda diff --git a/tutorials/katacoda/1-serving-intro/finish.md b/tutorials/katacoda/1-serving-intro/finish.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tutorials/katacoda/1-serving-intro/index.json b/tutorials/katacoda/1-serving-intro/index.json new file mode 100644 index 00000000000..3e03465c02e --- /dev/null +++ b/tutorials/katacoda/1-serving-intro/index.json @@ -0,0 +1,35 @@ +{ + "title": "Getting Started with Knative Serving (kn cli)", + "description": "Introduction to Knative by installing knative serving and deploying an application (If you want to script your workflow, or really don't like yaml)", + "difficulty": "Beginner", + "time": "20", + "details": { + "steps": [ + { + "title": "Step 1", + "text": "step1.md" + }, + { + "title": "Step 2", + "text": "step2.md" + }, + { + "title": "Step 3", + "text": "step3.md" + } + ], + "intro": { + "code": "scripts/install-dependencies.sh", + "text": "intro.md" + }, + "finish": { + "text": "finish.md" + } + }, + "environment": { + "uilayout": "terminal" + }, + "backend": { + "imageid": "minikube-running" + } +} diff --git a/tutorials/katacoda/1-serving-intro/intro.md b/tutorials/katacoda/1-serving-intro/intro.md new file mode 100644 index 00000000000..cc12c134f6d --- /dev/null +++ b/tutorials/katacoda/1-serving-intro/intro.md @@ -0,0 +1,7 @@ +## What is Knative? +Knative brings the "serverless" experience to kubernetes. It also tries to codify common patterns and best practices for running applications while hiding the complexity of doing so on kubernetes. It does so by providing two components: +- Eventing - Management and delivery of events +- Serving - Request-driven compute that can scale to zero + +## What will we learn in this tutorial? +This tutorial will serve as an introduction to Knative. Here we will install Knative (Serving only), deploy an application, watch Knative's "scale down to zero" feature then deploy a second version of the application and watch traffic split between the two versions. diff --git a/tutorials/katacoda/1-serving-intro/scripts/install-dependencies.sh b/tutorials/katacoda/1-serving-intro/scripts/install-dependencies.sh new file mode 100755 index 00000000000..f73241d74be --- /dev/null +++ b/tutorials/katacoda/1-serving-intro/scripts/install-dependencies.sh @@ -0,0 +1,12 @@ +echo "Installing kn cli..." +wget https://storage.googleapis.com/knative-nightly/client/latest/kn-linux-amd64 -O kn +chmod +x kn +mv kn /usr/local/bin/ +echo "Done" + +echo "Waiting for Kubernetes to start. This may take a few moments, please wait..." +while [ `minikube status &>/dev/null; echo $?` -ne 0 ]; do sleep 1; done +echo "Kubernetes Started" + +export latest_version=$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/knative/serving/tags?per_page=1 | jq -r .[0].name) +echo "Latest knative version is: ${latest_version}" diff --git a/tutorials/katacoda/1-serving-intro/step1.md b/tutorials/katacoda/1-serving-intro/step1.md new file mode 100644 index 00000000000..586e9ac31a3 --- /dev/null +++ b/tutorials/katacoda/1-serving-intro/step1.md @@ -0,0 +1,20 @@ +## Installation +> The startup script running on the right will install the `kn` cli and wait for kubernetes to start. Once you see a prompt, you can click on the commands below at your own pace, and they will be copied and run for you in the terminal on the right. + +1. Install Knative Serving's core components + ``` + kubectl apply --filename https://github.com/knative/serving/releases/download/${latest_version}/serving-crds.yaml + kubectl apply --filename https://github.com/knative/serving/releases/download/${latest_version}/serving-core.yaml + ```{{execute}} +1. Install contour as the networking layer. (Knative also supports Courier, Gloo, Istio and Kourier as options) + ``` + kubectl apply --filename https://github.com/knative/net-contour/releases/download/${latest_version}/contour.yaml + kubectl apply --filename https://github.com/knative/net-contour/releases/download/${latest_version}/net-contour.yaml + ```{{execute}} +1. Configure Knative Serving to use Contour by default + ``` + kubectl patch configmap/config-network \ + --namespace knative-serving \ + --type merge \ + --patch '{"data":{"ingress.class":"contour.ingress.networking.knative.dev"}}' + ```{{execute}} diff --git a/tutorials/katacoda/1-serving-intro/step2.md b/tutorials/katacoda/1-serving-intro/step2.md new file mode 100644 index 00000000000..c0bda8883d6 --- /dev/null +++ b/tutorials/katacoda/1-serving-intro/step2.md @@ -0,0 +1,26 @@ +## Deploy and autoscale application + +We are going to deploy the [Hello world sample web application](https://knative.dev/docs/serving/samples/hello-world/helloworld-go/). This basic web application reads in an env variable TARGET and prints `Hello ${TARGET}!`. If TARGET is not specified, it will use `World` as the TARGET. + +We will now deploy the application by specifying the image location and the `TARGET` env variable set to `blue`. + +Knative defines a `service.serving.knative.dev` CRD to control the lifecycle of the application (not to be confused with kubernetes service). We will use the `kn` cli to create the Knative service: (This may take up to a minute) + +``` +kn service create demo --image gcr.io/knative-samples/helloworld-go --env TARGET=blue --autoscale-window 15s +```{{execute}} + +We can now invoke the application using `curl`. We first need to figure out the IP address of minikube and the ingress port. +``` +MINIKUBE_IP=$(minikube ip) +INGRESS_PORT=$(kubectl get svc envoy --namespace contour-external --output 'jsonpath={.spec.ports[?(@.port==80)].nodePort}') +```{{execute}} +Then invoke the application using curl: +``` +curl http://$MINIKUBE_IP:$INGRESS_PORT/ -H 'Host: demo.default.example.com' +```{{execute T1}} + +### Scale down to zero +You can run `watch kubectl get pods`{{execute T2}} (may need two clicks) in a new Terminal tab to see a pod created to serve the requests. Knative will scale this pod down to zero if there are no incoming requests, we have configured this window to be 15 seconds above. + +You can wait for the pods to scale down to zero and then issue the above `curl` again to see the pod spin up and serve the request. diff --git a/tutorials/katacoda/1-serving-intro/step3.md b/tutorials/katacoda/1-serving-intro/step3.md new file mode 100644 index 00000000000..7c251c5cb78 --- /dev/null +++ b/tutorials/katacoda/1-serving-intro/step3.md @@ -0,0 +1,47 @@ +## Blue/Green deploy +The Knative `service` resource creates additional resources "route, configuration and revision" to manage the lifecycle of the application. +- revision: just like a git revision, any change to the Service's `spec.template` results in a new revision +- route: control traffic to several revisions + +You can list those resources by running `kubectl get ksvc,configuration,route,revision` or by using the `kn` cli + +We will now update the service to change the `TARGET` env variable to `green`. + +But, before we do that, let us update the revision name to "hello-v1", so that we can direct traffic to it. +``` +kn service update demo --revision-name="demo-v1"```{{execute T1}} + +Now, let's update the env variable to `green`, but let's do it as a dark launch i.e. zero traffic will go to this new revision: +``` +kn service update demo --env TARGET=green --revision-name="demo-v2" --traffic demo-v1=100,demo-v2=0 +```{{execute T1}} + +This will result in a new `revision` being created. Verify this by running `kn revision list`{{execute T1}}. + +All these revisions are capable of serving requests. Let's tag the `green` revision, so as to get a custom hostname to be able to access the revision. +``` +kn service update demo --tag demo-v2=v2 +```{{execute T1}} + +You can now test the `green` revision like so: (This hostname can be listed with `kn route describe demo` command). +``` +curl http://$MINIKUBE_IP:$INGRESS_PORT/ -H 'Host: v2-demo.default.example.com' +```{{execute T1}} + +We now need to split our traffic between the two revisions. +``` +kn service update demo --traffic demo-v1=50,@latest=50 +```{{execute T1}} +Then proceed by issuing the curl command multiple times to see that the traffic is split between the two revisions. +``` +curl http://$MINIKUBE_IP:$INGRESS_PORT/ -H 'Host: demo.default.example.com' +```{{execute T1}} + +We can now make all traffic go to the `green` revision: +``` +kn service update demo --traffic @latest=100 +```{{execute T1}} +Then proceed by issuing the curl command multiple times to see that all traffic is routed to `green` revision. +``` +curl http://$MINIKUBE_IP:$INGRESS_PORT/ -H 'Host: demo.default.example.com' +```{{execute T1}} diff --git a/tutorials/katacoda/2-serving-intro-yaml/finish.md b/tutorials/katacoda/2-serving-intro-yaml/finish.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tutorials/katacoda/2-serving-intro-yaml/index.json b/tutorials/katacoda/2-serving-intro-yaml/index.json new file mode 100644 index 00000000000..85e9fc2e868 --- /dev/null +++ b/tutorials/katacoda/2-serving-intro-yaml/index.json @@ -0,0 +1,35 @@ +{ + "title": "Getting Started with Knative Serving (yaml)", + "description": "Introduction to Knative by installing knative serving and deploy an application", + "difficulty": "Beginner", + "time": "20", + "details": { + "steps": [ + { + "title": "Step 1", + "text": "step1.md" + }, + { + "title": "Step 2", + "text": "step2.md" + }, + { + "title": "Step 3", + "text": "step3.md" + } + ], + "intro": { + "code": "scripts/install-dependencies.sh", + "text": "intro.md" + }, + "finish": { + "text": "finish.md" + } + }, + "environment": { + "uilayout": "terminal" + }, + "backend": { + "imageid": "minikube-running" + } +} diff --git a/tutorials/katacoda/2-serving-intro-yaml/intro.md b/tutorials/katacoda/2-serving-intro-yaml/intro.md new file mode 100644 index 00000000000..cc12c134f6d --- /dev/null +++ b/tutorials/katacoda/2-serving-intro-yaml/intro.md @@ -0,0 +1,7 @@ +## What is Knative? +Knative brings the "serverless" experience to kubernetes. It also tries to codify common patterns and best practices for running applications while hiding the complexity of doing so on kubernetes. It does so by providing two components: +- Eventing - Management and delivery of events +- Serving - Request-driven compute that can scale to zero + +## What will we learn in this tutorial? +This tutorial will serve as an introduction to Knative. Here we will install Knative (Serving only), deploy an application, watch Knative's "scale down to zero" feature then deploy a second version of the application and watch traffic split between the two versions. diff --git a/tutorials/katacoda/2-serving-intro-yaml/scripts/install-dependencies.sh b/tutorials/katacoda/2-serving-intro-yaml/scripts/install-dependencies.sh new file mode 100755 index 00000000000..4218b23bad9 --- /dev/null +++ b/tutorials/katacoda/2-serving-intro-yaml/scripts/install-dependencies.sh @@ -0,0 +1,6 @@ +echo "Waiting for Kubernetes to start. This may take a few moments, please wait..." +while [ `minikube status &>/dev/null; echo $?` -ne 0 ]; do sleep 1; done +echo "Kubernetes Started" + +export latest_version=$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/knative/serving/tags?per_page=1 | jq -r .[0].name) +echo "Latest knative version is: ${latest_version}" diff --git a/tutorials/katacoda/2-serving-intro-yaml/step1.md b/tutorials/katacoda/2-serving-intro-yaml/step1.md new file mode 100644 index 00000000000..d1e4c31411f --- /dev/null +++ b/tutorials/katacoda/2-serving-intro-yaml/step1.md @@ -0,0 +1,20 @@ +## Installation +> The startup script running on the right will wait for kubernetes to start. Once you see a prompt, you can click on the commands below at your own pace, and they will be copied and run for you in the terminal on the right. + +1. Install Knative Serving's core components + ``` + kubectl apply --filename https://github.com/knative/serving/releases/download/${latest_version}/serving-crds.yaml + kubectl apply --filename https://github.com/knative/serving/releases/download/${latest_version}/serving-core.yaml + ```{{execute}} +1. Install contour as the networking layer. (Knative also supports Courier, Gloo, Istio and Kourier as options) + ``` + kubectl apply --filename https://github.com/knative/net-contour/releases/download/${latest_version}/contour.yaml + kubectl apply --filename https://github.com/knative/net-contour/releases/download/${latest_version}/net-contour.yaml + ```{{execute}} +1. Configure Knative Serving to use Contour by default + ``` + kubectl patch configmap/config-network \ + --namespace knative-serving \ + --type merge \ + --patch '{"data":{"ingress.class":"contour.ingress.networking.knative.dev"}}' + ```{{execute}} diff --git a/tutorials/katacoda/2-serving-intro-yaml/step2.md b/tutorials/katacoda/2-serving-intro-yaml/step2.md new file mode 100644 index 00000000000..0a34acce10a --- /dev/null +++ b/tutorials/katacoda/2-serving-intro-yaml/step2.md @@ -0,0 +1,43 @@ +## Deploy and autoscale application + +We are going to deploy the [Hello world sample application](https://knative.dev/docs/serving/samples/hello-world/helloworld-go/). This application reads in an env variable TARGET and prints `Hello ${TARGET}!`. If TARGET is not specified, it will use `World` as the TARGET. + +We will now deploy the application by specifying the image location and the `TARGET` env variable. + +Knative defines a `service.serving.knative.dev` CRD to control the lifecycle of the application (not to be confused with kubernetes service). We will create the Knative service using the yaml below: + +``` +cat </dev/null; echo $?` -ne 0 ]; do sleep 1; done +echo "Kubernetes Started." + +export latest_version=$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/knative/serving/tags?per_page=1 | jq -r .[0].name) +echo "Latest knative version is: ${latest_version}" + +echo "Installing Knative Serving..." +kubectl apply --filename https://github.com/knative/serving/releases/download/${latest_version}/serving-crds.yaml +kubectl apply --filename https://github.com/knative/serving/releases/download/${latest_version}/serving-core.yaml +kubectl apply --filename https://github.com/knative/net-contour/releases/download/${latest_version}/contour.yaml +kubectl apply --filename https://github.com/knative/net-contour/releases/download/${latest_version}/net-contour.yaml +kubectl patch configmap/config-network \ + --namespace knative-serving \ + --type merge \ + --patch '{"data":{"ingress.class":"contour.ingress.networking.knative.dev"}}' +echo "Knative Serving Installed." diff --git a/tutorials/katacoda/3-eventing-intro-channels/step1.md b/tutorials/katacoda/3-eventing-intro-channels/step1.md new file mode 100644 index 00000000000..ca92ede7a2c --- /dev/null +++ b/tutorials/katacoda/3-eventing-intro-channels/step1.md @@ -0,0 +1,19 @@ +## Installation +> The startup script running on the right will wait for kubernetes to start and knative serving to be installed. (Although Serving is not required for Eventing to work, we install it here for creating consumers succinctly). +> Once you see a prompt, you can click on the commands below at your own pace, and they will be copied and run for you in the terminal on the right. + +1. Install Knative Eventing's core components + ``` + kubectl apply --filename https://github.com/knative/eventing/releases/download/${latest_version}/eventing-crds.yaml + kubectl apply --filename https://github.com/knative/eventing/releases/download/${latest_version}/eventing-core.yaml + ```{{execute}} + +## Event Driven Architecture +In an event driven architecture, microservice and functions execute business logic when they are triggered by an event. +The source that generates the event is called the "Producer" of the event and the microservice/function is the "consumer". +The microservices/functions in an event-driven architecture are constantly reacting to and producing events themselves. + +Producers should send their event data in a specific format, like [Cloud Events](https://cloudevents.io/), to make it easier +for consumers to handle the data. By default, Knative Eventing works with Cloud Events as a standard format for event data. + +In the next section we will look at various eventing topologies. diff --git a/tutorials/katacoda/3-eventing-intro-channels/step2.md b/tutorials/katacoda/3-eventing-intro-channels/step2.md new file mode 100644 index 00000000000..543b0212e2e --- /dev/null +++ b/tutorials/katacoda/3-eventing-intro-channels/step2.md @@ -0,0 +1,93 @@ +## Event Topologies +We will walk through the simplest to more complicated topologies + +### 1:1 Event Delivery +The most straightforward use case is that whenever events are produced, you want some code to handle that event. + +![1to1](./assets/1to1.png) +Looking at the diagram above, we’ll create the components in the reverse order. +Let's create a consumer that will display the events that are sent to it: + +``` +cat < Write programs that do one thing and do it well. Write programs to work together. + +In an event driven architecture too, it is useful to split up functionality into smaller chunks. These chunks can then be implemented as either microservices +or functions. We then need to pass the events from the producer through a series of consumers. This time rather than creating several channels and subscriptions, we +will create a `Sequence` instead. A Sequence lets us define an in-order list of functions that will be invoked. Sequence creates `Channel`s and `Subscription`s under the hood. + +Let's see this in action now. We will create the following topology where the event is passed from Producer to Consumer 1, who transforms the event, and then passes it +along to Consumer 2, which displays the event. + +![seq](./assets/sequence.png) + +First, let's create the final consumer: + +``` +cat </dev/null; echo $?` -ne 0 ]; do sleep 1; done +echo "Kubernetes Started." + +export latest_version=$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/knative/serving/tags?per_page=1 | jq -r .[0].name) +echo "Latest knative version is: ${latest_version}" + +echo "Installing Knative Serving..." +kubectl apply --filename https://github.com/knative/serving/releases/download/${latest_version}/serving-crds.yaml +kubectl apply --filename https://github.com/knative/serving/releases/download/${latest_version}/serving-core.yaml +kubectl apply --filename https://github.com/knative/net-contour/releases/download/${latest_version}/contour.yaml +kubectl apply --filename https://github.com/knative/net-contour/releases/download/${latest_version}/net-contour.yaml +kubectl patch configmap/config-network \ + --namespace knative-serving \ + --type merge \ + --patch '{"data":{"ingress.class":"contour.ingress.networking.knative.dev"}}' +echo "Knative Serving Installed." diff --git a/tutorials/katacoda/4-eventing-intro-broker/step1.md b/tutorials/katacoda/4-eventing-intro-broker/step1.md new file mode 100644 index 00000000000..4bd7061053a --- /dev/null +++ b/tutorials/katacoda/4-eventing-intro-broker/step1.md @@ -0,0 +1,28 @@ +## Installation +> The startup script running on the right will wait for kubernetes to start and knative serving to be installed. (Although Serving is not required for Eventing to work, we install it here for creating consumers succinctly). +> Once you see a prompt, you can click on the commands below at your own pace, and they will be copied and run for you in the terminal on the right. + +1. Install Knative Eventing's core components + ``` + kubectl apply --filename https://github.com/knative/eventing/releases/download/${latest_version}/eventing-crds.yaml + kubectl apply --filename https://github.com/knative/eventing/releases/download/${latest_version}/eventing-core.yaml + ```{{execute}} +1. Install an in-memory channel. (Knative also supports Apache Kafka Channel, Google Cloud Pub/Sub Channel and NATS Channel as options) + ``` + kubectl apply --filename https://github.com/knative/eventing/releases/download/${latest_version}/in-memory-channel.yaml + ```{{execute}} +1. Install a Broker + ``` + kubectl apply --filename https://github.com/knative/eventing/releases/download/${latest_version}/mt-channel-broker.yaml + ```{{execute}} + + +## Event Driven Architecture +In an event driven architecture, microservice and functions execute business logic when they are triggered by an event. +The source that generates the event is called the "Producer" of the event and the microservice/function is the "consumer". +The microservices/functions in an event-driven architecture are constantly reacting to and producing events themselves. + +Producers should send their event data in a specific format, like [Cloud Events](https://cloudevents.io/), to make it easier +for consumers to handle the data. By default, Knative Eventing works with Cloud Events as a standard format for event data. + +In the next section we will look at the broker-trigger model. diff --git a/tutorials/katacoda/4-eventing-intro-broker/step2.md b/tutorials/katacoda/4-eventing-intro-broker/step2.md new file mode 100644 index 00000000000..8d33c87925d --- /dev/null +++ b/tutorials/katacoda/4-eventing-intro-broker/step2.md @@ -0,0 +1,111 @@ +## Use Case +The broker and trigger model is useful for complex event delivery topologies like N:M:Z, i.e. there are a multitude of Sources sending events, Functions +consuming/transforming which are then processed by even more functions and so on. It can get a bit unwieldy to keep track of which Channel is having which events. Also, sometimes you might only want to +consume specific types of events. You would have to receive all the events, and throw out the ones you’re not interested in. To make these kinds of interactions easier and allow the +user to only focus on declaring which events they are interested in and where to send them is an easier way to reason about them. This is where Broker and Trigger are meant +to provide a straightforward user experience. + +### Broker +Producers POST events to the Broker. Once an Event has entered the Broker, it can be forwarded to event Channels by using Triggers. This event delivery mechanism hides +details of event routing from the event producer and event consumer. + +### Trigger +A Trigger describes a filter on event attributes which should be delivered to Consumers. This allows the consumer to only receive a subset of the events. + +### Example +Suppose we want to implement the following workflow for events +![broker-eg](./assets/broker-eg.png) + +We can implement this with Channel and Subscription, but in order for that we’d need 6 Channels and have two instances of Consumer1 (because we would need to have it output +to two different channels to separate Red and Yellow without Consumer2 and Consumer4 having to filter out the events they didn’t want). We would furthermore need to have 6 +Subscription objects. By using the Broker / Trigger we only have to declare that Consumer1 is interested in Blue and Orange events, Consumer2 is interested in Red events and so forth. The new topology now becomes: +![broker](./assets/broker.png) + +To see this in action let us first create a Broker: + +``` +kubectl create -f - <