This is the first in a collection of articles as I figure out what’s what with Knative for Kubernetes. The full set of articles are:
- Deploying 12-factor apps to Knative
- Building and deploying applications to Knative
- Adding public traffic to Knative on Google Kubernetes Engine
- Adding a custom hostname domain for Knative services
- Build Docker images inside your Kubernetes with Knative Build
- Binding secrets to Knative services
At Google Next18, Google announced that the “serverless add-on” to GKE would be open sourced as Knative. Described as the building blocks for serverless platforms, it infers that Knative might not “just work”, and you might need Google, Pivotal, or RedHat in order to use Knative. Maybe. These are strange times in open source. From my initial exploration, Knative works; and when we add Heroku/Cloud Foundry buildpacks it starts to feel more and more like the Heroku/Cloud Foundry we know and love than raw Kubernetes.
In this article you and I will install Knative into your own Kubernetes (knctl install
), and then we will have a lot of fun with Knative (knctl deploy
).
Knative can do many tricks for operators running their stateless apps on Kubernetes. The most enticing feature for me is autoscaling: scaling up when more load appears, and fabulously scaling down to zero when no one loves your application anymore.
Download and install the handy knctl
Knative CLI that will both install Knative into your Kubernetes, and deploy your applications.
On MacOS you can fetch it with our homebrew tap:
brew install starkandwayne/kubernetes/knctl
In this article I will assume you’re using Minikube. With Minikube you can configure with node ports rather than a load balancer:
minikube start --memory=8192 --cpus=3 \
--kubernetes-version=v1.11.3 \
--vm-driver=hyperkit \
--bootstrapper=kubeadm
knctl install --node-ports --exclude-monitoring
See Knative docs for tips on installing to different Kubernetes.
The knctl install
command will take a few minutes. Or ten minutes. Or longer. It is downloading a dozen or so container images. Neither your Internet bandwidth nor the size of the container images are easily changeable, so sit back and relax. Or go for a 10 minute walk.
If knctl install
fails, it is likely that your Internet is slow and the Docker images had not finished downloading before the command timed out. Run kubectl get pods --all-namespaces
until you see all pods running, then run the knctl install
command again to continue.
Your Kubernetes cluster is now “serverless”. Sweet.
Run kubectl get pods --all-namespaces
to see all the pods that make up bare Knative.
$ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
istio-system istio-citadel-7d8f9748c5-zgm9x 1/1 Running 0 21m
istio-system istio-cleanup-secrets-j4pkx 0/1 Completed 0 21m
istio-system istio-egressgateway-676c8546c5-dnwsd 1/1 Running 0 21m
istio-system istio-galley-5669f7c9b-g774b 1/1 Running 0 21m
istio-system istio-ingressgateway-5475685bbb-q5f2x 1/1 Running 0 21m
istio-system istio-pilot-5795d6d695-9klrz 2/2 Running 0 21m
istio-system istio-policy-7f945bf487-2wh88 2/2 Running 0 21m
istio-system istio-sidecar-injector-d96cd9459-7knkm 1/1 Running 0 21m
istio-system istio-statsd-prom-bridge-549d687fd9-lcmb7 1/1 Running 0 21m
istio-system istio-telemetry-6c587bdbc4-t4jql 2/2 Running 0 21m
istio-system knative-ingressgateway-7f4477dd99-n9wz2 1/1 Running 0 4m
knative-build build-controller-7dcc4b7544-rkgwb 1/1 Running 0 4m
knative-build build-webhook-fb6484576-sr4fk 1/1 Running 0 4m
knative-serving activator-77d46b585d-b6g8n 2/2 Running 0 4m
knative-serving controller-85768cfd45-t8ktc 1/1 Running 0 4m
knative-serving webhook-56dd548f8-hjw44 1/1 Running 0 4m
...
Deploy pre-built application
Let’s deploy an existing public Docker image as an autoscaling stateless application to Knative, in your current Kubernetes namespace:
kubectl create ns helloworld
knctl deploy \
--namespace helloworld \
--service hello \
--image gcr.io/knative-samples/helloworld-go \
--env TARGET=Rev1
The output will show that a new service hello
is created, and its first revision hello-00001
is created and tagged as latest
, and previous
(being the first revision).
Name hello
Waiting for new revision to be created...
Tagging new revision 'hello-00001' as 'latest'
Tagging new revision 'hello-00001' as 'previous'
Succeeded
We can construct a curl
request into the Knative routing layer to our hello
service:
$ knctl curl --namespace helloworld --service hello
Running: curl '-H' 'Host: hello.helloworld.example.com' 'http://192.168.64.8:32380'
Hello World: Rev1!
Succeeded
If this does not immediately print Hello World: Rev1!
it may be because the system is still downloading the gcr.io/knative-samples/helloworld-go
image. Wait and try again.
I’m using minikube with node ports ingress, which means I cannot setup nice DNS routing. I’ll save discussion of using a public load balancer, DNS, Knative Routing, and https://github.com/cppforlife/kwt for a future post.
After knctl deploy
our Kubernetes will be running a single pod of our service:
$ kubectl get pods --namespace helloworld
NAME READY STATUS RESTARTS AGE
hello-00001-deployment-5864685cbc-v8r7n 3/3 Running 0 15s
Revisions of our service is a first-class feature of Knative. We can see that we have our single initial revision, which is receiving 100% of all traffic:
$ knctl revisions list --namespace helloworld --service hello
Revisions for service 'hello'
Name Tags Allocated Traffic % Annotations Age
hello-00001 previous 100% - 1m
latest
1 revisions
The next time we knctl deploy
will create a new revision and allocate all traffic to it:
$ knctl deploy \
--namespace helloworld \
--service hello \
--image gcr.io/knative-samples/helloworld-go \
--env TARGET=Rev2
Name hello
Waiting for new revision (after revision 'hello-00001') to be created...
Tagging new revision 'hello-00002' as 'latest'
Tagging older revision 'hello-00001' as 'previous'
Succeeded
Requests now go to our new revision:
$ knctl revisions list --namespace helloworld --service hello
Revisions for service 'hello'
Name Tags Annotations Conditions Age Traffic
hello-00002 latest - 4 OK / 4 20s 100% -> hello.helloworld.example.com
hello-00001 previous - 4 OK / 4 1m -
$ knctl curl --namespace helloworld --service hello
Running: curl '-H' 'Host: hello.helloworld.example.com' 'http://192.168.64.8:32380'
Hello World: Rev2!
After deploying the second revision, initially both revisions’ pods will continue running:
$ kubectl get pods --namespace helloworld
NAME READY STATUS RESTARTS AGE
hello-00001-deployment-5864685cbc-v8r7n 3/3 Running 0 1m
hello-00002-deployment-7874bf89b8-4b4k5 3/3 Running 0 29s
We’ll see later that Knative automatically shuts down pods under zero load or if no routes point to a revision.
Routing
An overview of Knative Serving Module includes this diagram of routes to revisions of the service:
Revisions are an immutable snapshot of code, dependencies, and configuration. Revisions that are not referenced by routes are retired and their Kubernetes resources are deleted.
We can view the current routing of domains:
$ knctl routes list --namespace helloworld
Routes in namespace 'helloworld'
Name Traffic All Traffic Assigned Ready Domain Age
hello 100% -> hello: true true hello.helloworld.example.com 2h
Scale to zero
If you walk away for five minutes and come back, you will discover you have no hello-00001
pods running, or they are being terminated:
$ kubectl get pods --namespace helloworld
NAME READY STATUS RESTARTS AGE
hello-00001-deployment-5c79d4f99b-h622d 2/3 Terminating 0 6m
hello-00002-deployment-7ff7bf89d9-f72mn 3/3 Running 0 5m
And later, hello-00002
will terminate:
$ kubectl get pods --namespace helloworld
NAME READY STATUS RESTARTS AGE
hello-00001-deployment-5c79d4f99b-h622d 0/3 Terminating 0 6m
hello-00002-deployment-7ff7bf89d9-f72mn 3/3 Terminating 0 5m
And then:
$ kubectl get pods --namespace helloworld
No resources found.
The next time you knctl curl
the service Knative will dynamically spin up a single pod to service the request.
$ knctl curl --namespace helloworld --service hello
There will be a multiple second lag on this first request as Knative spins up your revision again.
$ kubectl get pods --namespace helloworld
NAME READY STATUS RESTARTS AGE
hello-00002-deployment-7ff7bf89d9-f8755 3/3 Running 0 56s
I’m not yet sure why hello-00001-deployment-...
pod does not get scaled down and terminated.
Future articles
In future articles I’ll explore:
- Knative Build to automatically build container images from your own bespoke code (either sourced locally or from a Git repository) using
Dockerfile
or Cloud Foundry buildpacks - Setting up DNS into a Kubernetes Load Balancer to provide normal public URLs into Knative routes and services
- Options for traffic splitting between different revisions (say 10% to latest revision, and 90% to previous revision; and then changing that thru to 100% for the latest and 0% for the previous revisions)
- Knative Eventing for binding and delivery of CloudEvents within applications to provide even more serverlessness atop your servers
Community
The Knative core team hang out in their own Knative Slack. Request to join at https://slack.knative.dev
The knative-dev Google Group has summaries and proposals.
Thanks
Thanks to Google’s Mark Chmarny for being the first person to answer my barage of "But, why?" questions on Knative and Kubernetes during Spring One Platform 2018. He was a good sport.
Thanks to Pivotal’s Dmitriy Kalinin for spending time with me to bring Knative to life, and showing me his knctl
Knative CLI and kwt
Kubernetes Workstation Tool tools. A beautiful CLI for Knative helped bring the promise of Knative to life much more than a barrage of YAML files and low-level kubectl
commands.