Knative Installation for Serverless on Kubernetes
Knative is a Kubernetes-based platform for deploying and managing serverless workloads, providing automatic scaling including scale-to-zero, traffic splitting for canary deployments, and an event-driven architecture through Knative Eventing. This guide covers installing Knative Serving and Eventing, configuring scale-to-zero, splitting traffic between revisions, and integrating with CI/CD pipelines.
Prerequisites
- Kubernetes cluster 1.28+ (minikube, k3s, or managed cluster)
kubectlconfigured with cluster-admin permissions- At least 3 nodes with 2 CPU and 4GB RAM each (for production)
- Helm 3.x installed
# Verify cluster is ready
kubectl get nodes
kubectl version --short
# Check available resources
kubectl describe nodes | grep -E "cpu|memory" | grep -v "#"
Installing Knative Serving
# Install Knative Serving CRDs
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.13.0/serving-crds.yaml
# Install Knative Serving core
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.13.0/serving-core.yaml
# Verify installation
kubectl get pods -n knative-serving
# Wait for all pods to be Running
kubectl wait --for=condition=Ready pod --all -n knative-serving --timeout=300s
Configure the domain for Knative services:
# Set your domain (replace example.com with your domain)
kubectl patch configmap/config-domain \
--namespace knative-serving \
--type merge \
--patch '{"data":{"example.com":""}}'
# Verify
kubectl get configmap config-domain -n knative-serving -o yaml
Configuring a Networking Layer
Knative requires a networking layer. Kourier is the simplest option:
# Install Kourier (lightweight Knative ingress)
kubectl apply -f https://github.com/knative/net-kourier/releases/download/knative-v1.13.0/kourier.yaml
# Configure Knative to use Kourier
kubectl patch configmap/config-network \
--namespace knative-serving \
--type merge \
--patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}'
# Get the Kourier external IP
kubectl get service kourier -n kourier-system
# Use this IP to configure DNS or /etc/hosts for testing
For Istio (more features but heavier):
# Install Istio
curl -L https://istio.io/downloadIstio | sh -
cd istio-*/
sudo mv bin/istioctl /usr/local/bin/
istioctl install --set profile=minimal -y
# Install Knative Istio controller
kubectl apply -f https://github.com/knative/net-istio/releases/download/knative-v1.13.0/net-istio.yaml
Deploying Knative Services
A Knative Service (ksvc) automatically creates Configurations, Revisions, and Routes:
cat > /tmp/hello-ksvc.yaml << 'EOF'
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: hello
namespace: default
spec:
template:
metadata:
annotations:
# Scale to zero after 30 seconds of inactivity
autoscaling.knative.dev/initial-scale: "1"
autoscaling.knative.dev/min-scale: "0"
autoscaling.knative.dev/max-scale: "10"
autoscaling.knative.dev/target: "100" # Target concurrent requests
spec:
# Timeout for pod to start and serve
timeoutSeconds: 60
containers:
- image: gcr.io/knative-samples/helloworld-go
ports:
- containerPort: 8080
env:
- name: TARGET
value: "World"
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "1000m"
memory: "256Mi"
EOF
kubectl apply -f /tmp/hello-ksvc.yaml
# Check the service
kubectl get ksvc hello
# NAME URL LATESTCREATED LATESTREADY READY
# hello http://hello.default.example.com hello-00001 hello-00001 True
# Test the service
SERVICE_URL=$(kubectl get ksvc hello -o jsonpath='{.status.url}')
curl "$SERVICE_URL"
View revisions:
# Each deployment creates a new revision
kubectl get revisions
kubectl describe revision hello-00001
# View the route (traffic distribution)
kubectl get routes
Scale-to-Zero Configuration
# Configure global scale-to-zero settings
kubectl edit configmap config-autoscaler -n knative-serving
apiVersion: v1
kind: ConfigMap
metadata:
name: config-autoscaler
namespace: knative-serving
data:
# Time after last request to scale to zero
scale-to-zero-grace-period: "30s"
# Cooldown period after scaling up from zero
scale-to-zero-pod-retention-period: "0s"
# Stable window for autoscaling decisions
stable-window: "60s"
# Panic window (rapid scale-up threshold)
panic-window-percentage: "10.0"
panic-threshold-percentage: "200.0"
# Target utilization (scale up when this is exceeded)
container-concurrency-target-default: "100"
Per-service scale-to-zero annotations:
# In the ksvc template.metadata.annotations:
autoscaling.knative.dev/min-scale: "0" # Allow scale to zero
autoscaling.knative.dev/max-scale: "20" # Maximum replicas
autoscaling.knative.dev/initial-scale: "1" # Start with 1 pod
autoscaling.knative.dev/target: "50" # Target concurrent requests per pod
autoscaling.knative.dev/scale-down-delay: "60s" # Wait before scaling down
Test scale-to-zero:
# Watch pod scaling
kubectl get pods -w -l serving.knative.dev/service=hello
# Send requests to see scale-up
for i in {1..10}; do
curl -s "$SERVICE_URL" &
done
wait
# Stop requests and watch scale-down
kubectl get pods -l serving.knative.dev/service=hello
# After 30 seconds, pods should disappear
Traffic Splitting and Canary Deployments
# Deploy a new revision
kubectl patch ksvc hello \
--type merge \
-p '{"spec":{"template":{"metadata":{"name":"hello-v2"},"spec":{"containers":[{"image":"gcr.io/knative-samples/helloworld-go","env":[{"name":"TARGET","value":"Knative v2"}]}]}}}}'
Split traffic between revisions:
cat > /tmp/traffic-split.yaml << 'EOF'
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: hello
spec:
template:
metadata:
name: hello-v2
spec:
containers:
- image: gcr.io/knative-samples/helloworld-go
env:
- name: TARGET
value: "Knative v2"
traffic:
- tag: current
revisionName: hello-v2
percent: 80 # 80% to new version
- tag: previous
revisionName: hello-v1
percent: 20 # 20% to old version (canary)
- tag: latest
latestRevision: true
percent: 0 # 0% to latest (staging)
EOF
kubectl apply -f /tmp/traffic-split.yaml
# Check traffic configuration
kubectl get ksvc hello -o jsonpath='{.status.traffic}'
# Access specific revision directly by tag
curl http://current-hello.default.example.com
curl http://previous-hello.default.example.com
Gradual rollout:
# Migrate 100% traffic to new version after validation
kubectl patch ksvc hello \
--type json \
-p '[{"op":"replace","path":"/spec/traffic/0/percent","value":100},{"op":"replace","path":"/spec/traffic/1/percent","value":0}]'
Installing Knative Eventing
# Install Knative Eventing CRDs
kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.13.0/eventing-crds.yaml
# Install Knative Eventing core
kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.13.0/eventing-core.yaml
# Install in-memory channel (for development; use Kafka for production)
kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.13.0/in-memory-channel.yaml
# Install MT Channel Based Broker
kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.13.0/mt-channel-broker.yaml
kubectl get pods -n knative-eventing
Create a default broker:
kubectl create namespace events-demo
# Create default broker
kubectl apply -f - << 'EOF'
apiVersion: eventing.knative.dev/v1
kind: Broker
metadata:
name: default
namespace: events-demo
annotations:
eventing.knative.dev/broker.class: MTChannelBasedBroker
spec:
config:
apiVersion: v1
kind: ConfigMap
name: config-br-defaults
namespace: knative-eventing
EOF
kubectl get broker -n events-demo
Event Sources and Triggers
Create a Trigger to route events to a service:
# Deploy an event consumer service
kubectl apply -f - << 'EOF' -n events-demo
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: event-consumer
spec:
template:
spec:
containers:
- image: gcr.io/knative-releases/knative.dev/eventing/cmd/event_display
ports:
- containerPort: 8080
EOF
# Create a trigger: route all events to the consumer
kubectl apply -f - << 'EOF' -n events-demo
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
name: all-events-trigger
spec:
broker: default
subscriber:
ref:
apiVersion: serving.knative.dev/v1
kind: Service
name: event-consumer
EOF
# Create a trigger with filter (only order.created events)
kubectl apply -f - << 'EOF' -n events-demo
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
name: order-created-trigger
spec:
broker: default
filter:
attributes:
type: com.example.order.created
source: /orders/service
subscriber:
ref:
apiVersion: serving.knative.dev/v1
kind: Service
name: order-processor
EOF
Send a test event:
# Port-forward the broker
kubectl port-forward -n events-demo service/broker-ingress 8080:80 &
# Send a CloudEvent to the broker
curl -v http://localhost:8080/events-demo/default \
-H "Content-Type: application/json" \
-H "Ce-Id: 12345" \
-H "Ce-Specversion: 1.0" \
-H "Ce-Type: com.example.order.created" \
-H "Ce-Source: /orders/service" \
-d '{"orderId": "123", "userId": "456", "amount": 99.99}'
# Check event consumer logs
kubectl logs -n events-demo -l serving.knative.dev/service=event-consumer -f
Integrating with CI/CD
# Tekton pipeline for Knative deployment
# Install Tekton
kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
# Create a Tekton Task for Knative deployment
cat > /tmp/knative-deploy-task.yaml << 'EOF'
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: knative-deploy
spec:
params:
- name: app-name
type: string
- name: image
type: string
- name: namespace
type: string
default: default
steps:
- name: deploy
image: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
script: |
#!/bin/bash
cat <<KSVC | kubectl apply -f -
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: $(params.app-name)
namespace: $(params.namespace)
spec:
template:
spec:
containers:
- image: $(params.image)
KSVC
EOF
kubectl apply -f /tmp/knative-deploy-task.yaml
Troubleshooting
Service stuck in "Unknown" state:
kubectl describe ksvc hello
kubectl get conditions -n knative-serving
# Check controller logs
kubectl logs -n knative-serving -l app=controller -f
Cold start latency too high:
# Prevent scale-to-zero by setting min-scale > 0
kubectl annotate ksvc hello autoscaling.knative.dev/min-scale="1" --overwrite
# Or pre-warm pods before traffic
Pods not scaling up under load:
# Check autoscaler logs
kubectl logs -n knative-serving -l app=autoscaler -f
# Verify HPA is configured
kubectl get hpa -n default
Kourier ingress not routing:
kubectl get svc kourier -n kourier-system
# Check DNS or /etc/hosts entry points to Kourier LB IP
dig hello.default.example.com
Conclusion
Knative transforms Kubernetes into a serverless platform by adding automatic scaling (including scale-to-zero), traffic splitting for safe deployments, and an event-driven eventing layer. Knative Serving is the core component for HTTP workloads, while Knative Eventing enables loosely coupled event-driven microservices. Start with scale-to-zero for cost efficiency, use traffic splitting for gradual rollouts, and integrate with Tekton or your existing CI/CD pipeline for automated deployments to production.


