jardim do jojo

Three Solutions to Run Knative Locally

// Publicado em: 31 de março de 2020

Knative is a set of building blocks to run Serverless workloads on top of Kubernetes. You can also use those building blocks to build your own Serverless platform.

One problem with Knative is running it locally. Kubernetes is quite heavy, running yet another platform on top of it is a killer. Today we will take a look at some different approaches to test Knative without burning down your computer.

Some notes before proceeding:


k3s

k3s is a lite version of Kubernetes, made for IoT & Edge computing – perfect for your laptop. k3s is packaged as a single binary (!!!), making it very easy to run. It only works on Linux :( Mac and Windows users will need a virtual machine – take a look at multipass or Digital Ocean.

Let’s do it!

export KNATIVE_VERSION="0.13.0"
export KUBECONFIG="/var/lib/rancher/k3s/server/cred/admin.kubeconfig"

# Since Knative has its own network layer, we need to disable k3s' Traefik during its installation
# to make sure Kourier proxy gets a LoadBalancer IP
curl -sfL https://get.k3s.io | sh -s - --disable traefik

# Install Knative Serving
kubectl apply --filename "https://github.com/knative/serving/releases/download/v$KNATIVE_VERSION/serving-crds.yaml"
kubectl apply --filename "https://github.com/knative/serving/releases/download/v$KNATIVE_VERSION/serving-core.yaml"

# Configure the magic xip.io DNS name
kubectl apply --filename "https://github.com/knative/serving/releases/download/v$KNATIVE_VERSION/serving-default-domain.yaml"

# Install and configure Kourier
kubectl apply --filename https://raw.githubusercontent.com/knative/serving/v$KNATIVE_VERSION/third_party/kourier-latest/kourier.yaml
kubectl patch configmap/config-network --namespace knative-serving --type merge --patch '{"data":{"ingress.class":"kourier.ingress.networking.knative.dev"}}'

Alright, the installation is done! Make sure all the Pods are running (if not, wait a bit):

kubectl get pod -A

And that k3s gave Kourier a LoadBalancer IP address:

kubectl get svc kourier -n kourier-system

                                       # yay!
NAME      TYPE           CLUSTER-IP    EXTERNAL-IP       PORT(S)                      AGE
kourier   LoadBalancer   10.43.94.13   172.17.255.1      80:31619/TCP,443:30649/TCP   80s

Let’s deploy the Knative Service sample:

kubectl apply -f https://gist.githubusercontent.com/jonatasbaldin/bc04de2e376be23f75bb5815041fdd61/raw/d2345ac9aa01d0f3c771e9b3d4a1421dd766e0f9/service.yaml

service.serving.knative.dev/helloworld-go created

Make sure it is running and find its “public” address:

kubectl get ksvc

NAME            URL                                                   LATESTCREATED         LATESTREADY           READY   REASON
helloworld-go   http://helloworld-go.default.172.17.255.1.xip.io      helloworld-go-2xvpx   helloworld-go-2xvpx   True    

Finally:

curl http://helloworld-go.default.172.17.255.1.xip.io

Hello Go Sample v1!

✨🎉🍾

✨BONUS ✨

If you run the same commands in a Digital Ocean droplet, you will get a public IP address! Quick and easy to test a web facing Service:

kubectl get ksvc

NAME                   URL                                                       LATESTCREATED                LATESTREADY                  READY   REASON
helloworld-go          http://helloworld-go.default.<my-public-ip>.xip.io        helloworld-go-7ejj4          helloworld-go-7ejj4          True    

kind

kind runs Kubernetes inside a Docker container. As crazy as it seems, it works amazingly for testing and local development with a small resource footprint.

kind do works on Docker for Mac, but getting the LoadBalancer is not trivial :( With Linux, we can get a LB using MetalLB – I used this incredible tutorial to learn about it. Again, you can totes use a virtual machine here.

Let’s start with kind and Knative:

# Getting kind
curl -Lo kind https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-$(uname)-amd64
chmod +x kind
mv kind /usr/local/bin

# Getting kubectl
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
chmod +x kubectl
mv kubectl /usr/local/bin

# Installing Docker (just copied from the Docker docs)
apt-get update
apt-get install apt-transport-https ca-certificates  curl gnupg-agent software-properties-common -y 
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io -y

# Create a kind cluster
kind create cluster

# Install the Knative Serving
export KNATIVE_VERSION="0.13.0"
kubectl apply --filename "https://github.com/knative/serving/releases/download/v$KNATIVE_VERSION/serving-crds.yaml"
kubectl apply --filename "https://github.com/knative/serving/releases/download/v$KNATIVE_VERSION/serving-core.yaml"

# Configure the magic xip.io DNS name
kubectl apply --filename "https://github.com/knative/serving/releases/download/v$KNATIVE_VERSION/serving-default-domain.yaml"

# Install and configure Kourier
kubectl apply --filename https://raw.githubusercontent.com/knative/serving/v$KNATIVE_VERSION/third_party/kourier-latest/kourier.yaml
kubectl patch configmap/config-network --namespace knative-serving --type merge --patch '{"data":{"ingress.class":"kourier.ingress.networking.knative.dev"}}'

Now, some MetalLB steps:

# https://mauilion.dev/posts/kind-metallb/

# First, find the network CIDR used by the Docker bridge network
# MetalLB will use some IPs from there
docker inspect network bridge

[
    {
        "Name": "bridge",
        # ...
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    # this is what we want
                    # we will give the 10 addresses to MetalLB
                    # let's say, 172.17.255.1-172.17.255.250
                    "Subnet": "172.17.0.0/16"
                }
            ]
        },
        # ...
    }
]

Installing MetalLB:

# god bless the yaml
kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.9.3/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.9.3/manifests/metallb.yaml
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"

# Creating a MetalLB configuration
# It states that we will use those IP addresses for the LoadBalancer
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      # the range from before!
      - 172.17.255.1-172.17.255.250
EOF

Alright, the installation is done! Make sure all the Pods are running (if not, wait a bit):

kubectl get pod -A

After that, take a look at Kourier:

kubectl get svc kourier -n kourier-system

                                       # yes!
NAME      TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)                      AGE
kourier   LoadBalancer   10.96.7.120   172.17.255.1   80:30201/TCP,443:30909/TCP   110s

Let’s deploy the Knative Service sample:

kubectl apply -f https://gist.githubusercontent.com/jonatasbaldin/bc04de2e376be23f75bb5815041fdd61/raw/d2345ac9aa01d0f3c771e9b3d4a1421dd766e0f9/service.yaml

service.serving.knative.dev/helloworld-go created

Make sure it is running and find its “public” address:

kubectl get ksvc

NAME                   URL                                                       LATESTCREATED                LATESTREADY                  READY   REASON
helloworld-go          http://helloworld-go.default.172.17.255.1.xip.io          helloworld-go-7sttx          helloworld-go-7sttx          True    

Finally:

curl http://helloworld-go.default.172.17.255.1.xip.io

Hello Go Sample v1!

Docker for Mac

Might be my favorite 😅 The Kubernetes shipped with Docker for Mac is a very simple way to use the platform locally: click on the button on your Docker configurations and you are good to go. Unfortunately, not that easy for Knative, but let’s fix that.

First, Knative only supports K8S v1.15.0+ and the default Docker for Mac comes with v1.14.6, without an upgrade path. BUT, you can get v1.16.0+ with the Edge release. It incorporates experimental features and, as such, might not be 100% stable. Whatever, we like living on the Edge. Read the Switch between Stable and Edge versions before proceeding to the download page.

Once installed, let’s do the Knative dance:

# Install the Knative Serving
export KNATIVE_VERSION="0.13.0"
kubectl apply --filename "https://github.com/knative/serving/releases/download/v$KNATIVE_VERSION/serving-crds.yaml"
kubectl apply --filename "https://github.com/knative/serving/releases/download/v$KNATIVE_VERSION/serving-core.yaml"

# Install and configure Kourier
kubectl apply --filename https://raw.githubusercontent.com/knative/serving/v$KNATIVE_VERSION/third_party/kourier-latest/kourier.yaml
kubectl patch configmap/config-network --namespace knative-serving --type merge --patch '{"data":{"ingress.class":"kourier.ingress.networking.knative.dev"}}'

If we inspect the Kourier LoadBalancer, it gets a localhost address 🤔

kubectl get svc kourier -n kourier-system

NAME      TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
kourier   LoadBalancer   10.110.153.164   localhost     80:31650/TCP,443:30315/TCP   34m

The LB is being exposed to our localhost, on ports 80 and 443. This is important and will affect the way we communicate with the Knative Service.

Let’s deploy the Knative Service sample:

kubectl apply -f https://gist.githubusercontent.com/jonatasbaldin/bc04de2e376be23f75bb5815041fdd61/raw/d2345ac9aa01d0f3c771e9b3d4a1421dd766e0f9/service.yaml

service.serving.knative.dev/helloworld-go created

Make sure it is running and find its “public” address:

kubectl get ksvc

NAME            URL                                        LATESTCREATED         LATESTREADY           READY   REASON
helloworld-go   http://helloworld-go.default.example.com   helloworld-go-9qsq7   helloworld-go-9qsq7   True    

Differently from the other steps, we didn’t apply the Magic xip.io DNS Knative file because we don’t work when the LoadBalancer is pre-configured with a domain (which, in this case, is localhost).

Finally, to access the Service, hit the LoadBalancer on localhost:80 and pass the Service domain as the Host header:

curl localhost:80 -H "Host: helloworld-go.default.example.com"

Hello Go Sample v1!

Awesome, right!?

PS: I didn’t dive too much into this solution. The localhost LoadBalancer might have its quirks that I haven’t encountered. I’ll keep this article updated if I see anything suspicious.

minikube

The Minikube section is a contribution by Jonathan Beri, you can find his original gist here. Thanks a lot!

Minikube is perfectly described in their GitHub repository:

minikube implements a local Kubernetes cluster on macOS, Linux, and Windows. minikube’s primary goals are to be the best tool for local Kubernetes application development and to support all Kubernetes features that fit.

# Get a Minikube VM up and running, with enough resources
minikube start --cpus=4 --memory=4096 --addons=ingress

# Install the Knative Serving
export KNATIVE_VERSION="0.13.0"
kubectl apply --filename "https://github.com/knative/serving/releases/download/v$KNATIVE_VERSION/serving-crds.yaml"
kubectl apply --filename "https://github.com/knative/serving/releases/download/v$KNATIVE_VERSION/serving-core.yaml"

# Configure the magic xip.io DNS name
kubectl apply --filename "https://github.com/knative/serving/releases/download/v$KNATIVE_VERSION/serving-default-domain.yaml"

# Install and configure Kourier
kubectl apply --filename https://raw.githubusercontent.com/knative/serving/v$KNATIVE_VERSION/third_party/kourier-latest/kourier.yaml
kubectl patch configmap/config-network --namespace knative-serving --type merge --patch '{"data":{"ingress.class":"kourier.ingress.networking.knative.dev"}}'

Alright, the installation is done! Make sure all the Pods are running (if not, wait a bit):

kubectl get pod -A

Open a new terminal console and start the Minikube LoadBalancer:

minikube tunnel

Status:
        machine: minikube
        pid: 13391
        route: 10.96.0.0/12 -> 192.168.64.6
        minikube: Running
        services: [kourier]
    errors: 
                minikube: no errors
                router: no errors
                loadbalancer emulator: no errors

After that, get the public IP from Kourier:

kubectl get svc kourier -n kourier-system

                                        # yes!
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)                      AGE
kourier   LoadBalancer   10.107.29.82   10.107.29.82   80:30354/TCP,443:30261/TCP   2m47s

Let’s deploy the Knative Service sample:

kubectl apply -f https://gist.githubusercontent.com/jonatasbaldin/bc04de2e376be23f75bb5815041fdd61/raw/d2345ac9aa01d0f3c771e9b3d4a1421dd766e0f9/service.yaml

service.serving.knative.dev/helloworld-go created

Make sure it is running and find its “public” address:

kubectl get ksvc

NAME                URL                                                    LATESTCREATED             LATESTREADY               READY   REASON
helloworld-go       http://helloworld-go.default.10.107.29.82.xip.io       helloworld-go-xqlxs       helloworld-go-xqlxs       True    

Finally:

curl http://helloworld-go.default.10.107.29.82.xip.io

Hello Go Sample v1!

✨ Bonus: inlets-operator ✨

Imagine if you could easily expose all these solutions to the public Internet? Well, inlets-operator does just that. I’ll leave you with this article for your next adventure.