This is the third 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
Let’s expose our Knative routing to the external world.
Knative Serving subsystem coordinates autoscaling of a service and its pods, and its routing. It maintains automatically generated internal routes for containers to use to communicate with Knative services, plus ingress routes — traffic that comes in from the external world.
But to make this work we need to external users’ traffic to find its way into your Knative Kubernetes cluster.
In this article we set up DNS and allow public HTTP traffic into our Knative applications. We will deploy a fresh GKE cluster, install Knative, deploy an application, and then move to adding a static IP to our Knative gateway, setting up public DNS with CloudFlare, configuring Knative for our public domain, and redeploying our application. Exciting times. Let’s do things on the internet.
Fresh Knative on GKE
Create a Google Kubernetes cluster (GKE) and install Knative (following along to the documentation instructions that are similar to below):
gcloud components update
gcloud auth login
gcloud config set project knative-experiments
export CLUSTER_NAME=knative
export CLUSTER_REGION=us-west1
export CLUSTER_ZONE=us-west1-c
gcloud container clusters create $CLUSTER_NAME \
--region=$CLUSTER_ZONE \
--cluster-version=latest \
--machine-type=n1-standard-2 \
--enable-autoscaling --min-nodes=1 --max-nodes=5 \
--enable-autorepair \
--scopes=service-control,service-management,compute-rw,storage-ro,cloud-platform,logging-write,monitoring-write,pubsub,datastore \
--num-nodes=3
Your local kubectl
will be automatically configured for your new cluster:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-knative-default-pool-db8990a0-3bls Ready <none> 57s v1.10.7-gke.6
gke-knative-default-pool-db8990a0-hf7t Ready <none> 57s v1.10.7-gke.6
gke-knative-default-pool-db8990a0-t2hz Ready <none> 57s v1.10.7-gke.6
Grant cluster-admin permissions to current user:
kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole=cluster-admin \
--user=$(gcloud config get-value core/account)
Install Knative (which includes Istio):
knctl install --exclude-monitoring
This command will block until all istio/knative pods are running successfully.
Deploy an application
We can quickly check that our GKE Knative is operational by deploying a pre-built Docker image:
knctl namespace create -n helloworld
knctl deploy \
--namespace helloworld \
--service hello \
--image gcr.io/knative-samples/helloworld-go \
--env TARGET=Rev1
Since we have not yet setup public DNS into our Knative/Istio/Kubernetes cluster, we are limited to interacting with our application via knctl curl
or its equivalent curl
command:
$ knctl curl --service hello -n helloworld
Running: curl '-sS' '-H' 'Host: hello.helloworld.example.com' 'http://104.198.109.254:80'
Hello World: Rev1!
By default Knative uses a dummy example.com base domain for all routes. We’ll replace this with your own working domain soon.
In the rest of this article we will add a static IP into our Knative routing, set up DNS to the static IP, and redeploy our application with its public route.
Allocating Static IP to Knative
At the time of this article, instructions for creating a GCP IP address and mapping it to our Knative cluster is located at https://github.com/knative/docs/blob/master/serving/gke-assigning-static-ip-address.md
GCP automatically allocates public IP addresses to each node in our GKE cluster, so the Knative ingress gateway (where we want our inbound traffic to arrive) already appears to have an External IP:
$ kubectl get svc knative-ingressgateway --namespace istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
knative-ingressgateway LoadBalancer 10.19.248.251 104.198.109.254 80:32380/TCP,443:32390/TCP,32400:32400/TCP 2m
$ knctl ingress list
Ingresses
Name Addresses Ports Age
knative-ingressgateway 104.198.109.254 80 2m
443
32400
The External IP value 104.198.109.254
matches the IP from the knctl curl
output above.
But we cannot use this dynamic IP for our DNS as GCP makes no promises that the IP address will be the same over time.
Instead, we need to create and allocate a static persistent IP address to our Knative ingress gateway.
First, create a regional IP address into the same region as your GKE cluster.
$ gcloud compute addresses create --region $CLUSTER_REGION knative-ingress
$ gcloud compute addresses list
NAME REGION ADDRESS STATUS
knative-ingress us-west1 35.233.215.214 RESERVED
We now want to explicitly configure GCP to bind this static IP to the node running the Knative ingress gateway.
kubectl patch svc knative-ingressgateway --namespace istio-system --patch \
$(gcloud compute addresses describe \
knative-ingress --region $CLUSTER_REGION --format json | \
jq -cr "{spec: {loadBalancerIP: .address}}")
Confirm that the gateway is bound and curl example traffic routes successfully (in my example the expected IP is 35.233.215.214):
$ kubectl describe svc knative-ingressgateway --namespace istio-system
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal EnsuringLoadBalancer 62s (x2 over 3m45s) service-controller Ensuring load balancer
Normal LoadbalancerIP 62s service-controller -> 35.233.215.214
Normal EnsuredLoadBalancer 13s (x2 over 3m5s) service-controller Ensured load balancer
$ gcloud compute addresses list
NAME REGION ADDRESS STATUS
knative-ingress us-west1 35.233.215.214 IN_USE
$ knctl ingress list
Ingresses
Name Addresses Ports Age
knative-ingressgateway 35.233.215.214 80 13m
443
32400
$ knctl curl --service hello -n helloworld
Running: curl '-sS' '-H' 'Host: hello.helloworld.example.com' 'http://35.233.215.214:80'
Hello World: Rev1!
GCP/GKE now promises that this static 35.233.215.214 will always be bound to the Knative ingress gateway. We can now try out public routing through a hostname.
Knative Domains
Before we setup our custom domain, we can use free services like https://sslip.io. Any subdomains of 35.233.215.214.sslip.io will always resolve to 35.233.215.214.
Update Knative to use your own
knctl domain create -d 35.233.215.214.sslip.io --default
Or try the following to dynamically construct the .sslip.io hostname:
knctl domain create --default \
-d "$(gcloud compute addresses describe knative-ingress --region $CLUSTER_REGION --format json | jq -cr .address).sslip.io"
Your Knative applications do not need to be redeployed. Since we updated the default base domain, our "hello" application’s route is updated automatically:
$ knctl routes list -n helloworld
Routes in namespace 'helloworld'
Name Traffic All Traffic Assigned Ready Domain Age
hello 100% -> hello: true true hello.helloworld.35.233.215.214.sslip.io 21m
We can now view our application in a web browser or via normal curl
commands:
$ curl hello.helloworld.35.233.215.214.sslip.io
Hello World: Rev1!
Custom DNS
You can now setup your own bespoke DNS using the static IP.
In my example below I’ve created an A record for knative-ingress.starkandwayne.com, and then a wildcard CNAME to the A name. This allows me to change the knative-ingress record once (for example, to a new static IP or a different load balancer) and all application CNAMEs will be automatically adjusted.
I can update my Knative default domain:
knctl domain create --default --domain knative.starkandwayne.com
Once the DNS has propogated, I can now reach my application by my custom base domain:
$ curl hello.helloworld.knative.starkandwayne.com
Hello World: Rev1!
Summary
In this article we deployed a new Kubernetes cluster on GKE, installed Knative with default load balancer support, associated a static IP with Knative’s ingress gateway, and set up our own DNS to reference the static IP.
This gives us the magical:
$ curl hello.helloworld.knative.starkandwayne.com
Hello World: Rev1!
In the next article we will craft beautiful domains, such as hello.starkandwayne.com
, to replace the programmatically generated (read: ugly) domain hello.helloworld.knative.starkandwayne.com
above.