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)
  • kubectl configured 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.