Serverless computing has revolutionized application deployment by abstracting infrastructure management and enabling automatic scaling. This comprehensive guide demonstrates setting up a production-ready Knative serverless platform on Rocky Linux, covering serving, eventing, and advanced deployment patterns.
Understanding Knative Architecture
Knative extends Kubernetes to provide a serverless platform with two main components:
Knative Serving
- Request-driven compute: Scale to zero and from zero
- Automatic scaling: Based on concurrent requests or CPU
- Traffic management: Blue-green deployments and canary releases
- Revision management: Immutable snapshots of code and configuration
Knative Eventing
- Event sources: Connect to various event producers
- Event delivery: Reliable event routing and delivery
- Event processing: Filter, transform, and route events
- CloudEvents: Standard event format support
Prerequisites
Before deploying Knative:
- Rocky Linux 9 with Kubernetes 1.24+
- kubectl and helm installed
- Minimum 4 nodes with 8GB RAM each
- LoadBalancer or Ingress controller
- Container registry access
- DNS configuration for custom domains
Setting Up Kubernetes Cluster
Installing Kubernetes with kubeadm
# Disable swap and SELinux
sudo swapoff -a
sudo sed -i '/swap/d' /etc/fstab
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
# Configure kernel modules
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# Configure sysctl
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
# Install container runtime (containerd)
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y containerd.io
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
sudo systemctl restart containerd
sudo systemctl enable containerd
# Install Kubernetes components
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el9-x86_64
enabled=1
gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
sudo dnf install -y kubelet kubeadm kubectl
sudo systemctl enable --now kubelet
# Initialize cluster (on master node)
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
# Configure kubectl
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# Install Calico network plugin
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.0/manifests/tigera-operator.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.0/manifests/custom-resources.yaml
Installing Istio (Required for Knative)
# Download and install Istio
curl -L https://istio.io/downloadIstio | sh -
cd istio-*
export PATH=$PWD/bin:$PATH
# Install Istio with Knative configuration
istioctl install --set values.pilot.env.PILOT_ENABLE_WORKLOAD_ENTRY_AUTOREGISTRATION=true \
--set values.gateways.istio-ingressgateway.runAsRoot=true \
--set values.global.proxy.clusterDomain="cluster.local" \
--set values.global.proxy.autoInject=disabled
# Label default namespace for injection
kubectl label namespace default istio-injection=enabled
# Verify Istio installation
kubectl get pods -n istio-system
Installing Knative Serving
Core Components
# Install Knative Serving CRDs
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.12.0/serving-crds.yaml
# Install Knative Serving core
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.12.0/serving-core.yaml
# Verify installation
kubectl get pods -n knative-serving
# Install Knative Istio controller
kubectl apply -f https://github.com/knative/net-istio/releases/download/knative-v1.12.0/net-istio.yaml
# Configure DNS for Knative
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.12.0/serving-default-domain.yaml
Configuring Knative Serving
# knative-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: config-domain
namespace: knative-serving
data:
# Configure your domain
example.com: |
selector:
app: my-app
# Default domain template
_example: |
{{.Name}}.{{.Namespace}}.{{.Domain}}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: config-autoscaler
namespace: knative-serving
data:
# Autoscaling configuration
container-concurrency-target-default: "100"
container-concurrency-target-percentage: "0.7"
enable-scale-to-zero: "true"
max-scale-up-rate: "1000"
max-scale-down-rate: "2"
panic-window-percentage: "10"
panic-threshold-percentage: "200"
scale-to-zero-grace-period: "30s"
scale-to-zero-pod-retention-period: "0s"
stable-window: "60s"
target-burst-capacity: "200"
requests-per-second-target-default: "200"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: config-deployment
namespace: knative-serving
data:
# Revision timeout configuration
revision-timeout-seconds: "300"
max-revision-timeout-seconds: "600"
revision-cpu-request: "25m"
revision-memory-request: "50Mi"
revision-cpu-limit: "1000m"
revision-memory-limit: "1024Mi"
Installing Knative Eventing
Core Components
# Install Knative Eventing CRDs
kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.12.0/eventing-crds.yaml
# Install Knative Eventing core
kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.12.0/eventing-core.yaml
# Install default channel (InMemoryChannel)
kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.12.0/in-memory-channel.yaml
# Install MT-Channel-Based Broker
kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.12.0/mt-channel-broker.yaml
# Verify installation
kubectl get pods -n knative-eventing
Configuring Event Sources
# kafka-source.yaml
apiVersion: sources.knative.dev/v1beta1
kind: KafkaSource
metadata:
name: kafka-source
namespace: default
spec:
consumerGroup: knative-group
bootstrapServers:
- kafka-broker-1.kafka:9092
- kafka-broker-2.kafka:9092
- kafka-broker-3.kafka:9092
topics:
- events-topic
sink:
ref:
apiVersion: eventing.knative.dev/v1
kind: Broker
name: default
---
# github-source.yaml
apiVersion: sources.knative.dev/v1alpha1
kind: GitHubSource
metadata:
name: github-source
namespace: default
spec:
eventTypes:
- push
- pull_request
ownerAndRepository: myorg/myrepo
accessToken:
secretKeyRef:
name: github-secret
key: token
secretToken:
secretKeyRef:
name: github-secret
key: webhook-secret
sink:
ref:
apiVersion: eventing.knative.dev/v1
kind: Broker
name: default
Deploying Serverless Applications
Basic Knative Service
# hello-service.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: hello
namespace: default
spec:
template:
metadata:
annotations:
# Autoscaling annotations
autoscaling.knative.dev/target: "50"
autoscaling.knative.dev/metric: "concurrency"
autoscaling.knative.dev/scale-down-delay: "0s"
autoscaling.knative.dev/window: "60s"
spec:
containerConcurrency: 100
timeoutSeconds: 300
containers:
- image: gcr.io/knative-samples/helloworld-go
ports:
- containerPort: 8080
env:
- name: TARGET
value: "Knative"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 1000m
memory: 512Mi
livenessProbe:
httpGet:
path: /healthz
initialDelaySeconds: 5
readinessProbe:
httpGet:
path: /ready
initialDelaySeconds: 5
Advanced Service with Traffic Splitting
# canary-deployment.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: canary-app
namespace: default
spec:
template:
metadata:
name: canary-app-v2
spec:
containers:
- image: myregistry/app:v2
env:
- name: VERSION
value: "v2"
traffic:
- latestRevision: false
revisionName: canary-app-v1
percent: 80
tag: stable
- latestRevision: true
percent: 20
tag: canary
Event-Driven Architecture
Creating Event Flows
# event-display-service.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: event-display
namespace: default
spec:
template:
spec:
containers:
- image: gcr.io/knative-samples/event-display
---
# broker-trigger.yaml
apiVersion: eventing.knative.dev/v1
kind: Broker
metadata:
name: default
namespace: default
annotations:
eventing.knative.dev/broker.class: MTChannelBasedBroker
spec:
config:
apiVersion: v1
kind: ConfigMap
name: config-br-defaults
namespace: knative-eventing
---
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
name: event-display-trigger
namespace: default
spec:
broker: default
filter:
attributes:
type: dev.knative.samples.helloworld
source: dev.knative.samples/helloworldsource
subscriber:
ref:
apiVersion: serving.knative.dev/v1
kind: Service
name: event-display
Complex Event Processing
# sequence-example.yaml
apiVersion: flows.knative.dev/v1
kind: Sequence
metadata:
name: event-processing-sequence
namespace: default
spec:
channelTemplate:
apiVersion: messaging.knative.dev/v1
kind: InMemoryChannel
steps:
- ref:
apiVersion: serving.knative.dev/v1
kind: Service
name: event-transformer
- ref:
apiVersion: serving.knative.dev/v1
kind: Service
name: event-filter
- ref:
apiVersion: serving.knative.dev/v1
kind: Service
name: event-logger
reply:
ref:
apiVersion: serving.knative.dev/v1
kind: Service
name: event-display
Creating Custom Functions
Go Function Example
// main.go
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type EventData struct {
Message string `json:"message"`
Time string `json:"time"`
}
func handler(ctx context.Context, event cloudevents.Event) (*cloudevents.Event, error) {
log.Printf("Received event: %s", event)
var data EventData
if err := event.DataAs(&data); err != nil {
return nil, fmt.Errorf("failed to parse event data: %w", err)
}
// Process the event
response := EventData{
Message: fmt.Sprintf("Processed: %s", data.Message),
Time: event.Time().String(),
}
responseEvent := cloudevents.NewEvent()
responseEvent.SetType("com.example.processed")
responseEvent.SetSource("knative-function")
responseEvent.SetData(cloudevents.ApplicationJSON, response)
return &responseEvent, nil
}
func main() {
ctx := context.Background()
p, err := cloudevents.NewHTTP()
if err != nil {
log.Fatalf("failed to create protocol: %s", err.Error())
}
c, err := cloudevents.NewClient(p)
if err != nil {
log.Fatalf("failed to create client: %s", err.Error())
}
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("Starting server on port %s", port)
if err := c.StartReceiver(ctx, handler); err != nil {
log.Fatalf("failed to start receiver: %s", err.Error())
}
}
Dockerfile for Function
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o function .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /
COPY --from=builder /app/function .
EXPOSE 8080
CMD ["./function"]
Advanced Autoscaling Configuration
Custom Metrics Autoscaling
# hpa-custom-metrics.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: function-hpa
namespace: default
spec:
scaleTargetRef:
apiVersion: serving.knative.dev/v1
kind: Service
name: my-function
minReplicas: 1
maxReplicas: 100
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000"
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
- type: Pods
value: 4
periodSeconds: 15
selectPolicy: Max
Configuring Class-Based Autoscaling
# autoscaling-class.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: config-autoscaler
namespace: knative-serving
data:
# Class-specific configuration
autoscaling-class-config: |
performance:
container-concurrency-target-default: "50"
container-concurrency-target-percentage: "0.5"
max-scale-up-rate: "1000"
panic-window-percentage: "5"
panic-threshold-percentage: "150"
target-burst-capacity: "300"
economy:
container-concurrency-target-default: "200"
container-concurrency-target-percentage: "0.8"
max-scale-up-rate: "10"
panic-window-percentage: "20"
panic-threshold-percentage: "300"
target-burst-capacity: "50"
Security Best Practices
Network Policies
# knative-network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: knative-serving-ingress
namespace: knative-serving
spec:
podSelector:
matchLabels:
app: activator
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: istio-system
ports:
- protocol: TCP
port: 8012
- protocol: TCP
port: 9090
egress:
- to:
- podSelector: {}
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
Service Authentication
# auth-policy.yaml
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: default
spec:
mtls:
mode: STRICT
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: require-jwt
namespace: default
spec:
selector:
matchLabels:
serving.knative.dev/service: my-secure-service
action: ALLOW
rules:
- from:
- source:
requestPrincipals: ["*"]
when:
- key: request.auth.claims[iss]
values: ["https://auth.example.com"]
Monitoring and Observability
Prometheus Configuration
# prometheus-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: knative-monitoring
data:
prometheus.yml: |
global:
scrape_interval: 30s
evaluation_interval: 30s
scrape_configs:
- job_name: 'knative-serving'
kubernetes_sd_configs:
- role: pod
namespaces:
names:
- knative-serving
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
action: keep
regex: (activator|autoscaler|controller|webhook)
- source_labels: [__meta_kubernetes_pod_container_port_name]
action: keep
regex: metrics
- job_name: 'knative-eventing'
kubernetes_sd_configs:
- role: pod
namespaces:
names:
- knative-eventing
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
action: keep
regex: (eventing-controller|eventing-webhook|imc-controller)
Custom Dashboards
{
"dashboard": {
"title": "Knative Serverless Metrics",
"panels": [
{
"title": "Request Rate",
"targets": [
{
"expr": "sum(rate(activator_request_count[5m])) by (namespace, service_name)"
}
]
},
{
"title": "Request Latency P95",
"targets": [
{
"expr": "histogram_quantile(0.95, sum(rate(activator_request_latencies_bucket[5m])) by (namespace, service_name, le))"
}
]
},
{
"title": "Active Instances",
"targets": [
{
"expr": "sum(autoscaler_actual_pod_count) by (namespace, service_name)"
}
]
},
{
"title": "Scale to Zero Events",
"targets": [
{
"expr": "sum(rate(activator_request_count{response_code=\"503\"}[5m])) by (namespace, service_name)"
}
]
}
]
}
}
Performance Optimization
Cold Start Optimization
# optimized-service.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: optimized-function
namespace: default
annotations:
# Keep minimum instances warm
autoscaling.knative.dev/min-scale: "1"
spec:
template:
metadata:
annotations:
# Faster pod startup
autoscaling.knative.dev/initial-scale: "5"
# Reduce cold start impact
autoscaling.knative.dev/scale-down-delay: "15m"
autoscaling.knative.dev/scale-to-zero-pod-retention-period: "15m"
spec:
# Init containers for pre-warming
initContainers:
- name: warm-up
image: busybox
command: ['sh', '-c', 'echo "Warming up..." && sleep 2']
containers:
- image: myregistry/optimized-function:latest
ports:
- containerPort: 8080
# Startup probe for readiness
startupProbe:
httpGet:
path: /ready
periodSeconds: 1
failureThreshold: 30
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 1000m
memory: 512Mi
Resource Optimization
# Create priority classes
cat <<EOF | kubectl apply -f -
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: knative-high-priority
value: 1000
globalDefault: false
description: "High priority class for Knative services"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: knative-low-priority
value: 100
globalDefault: false
description: "Low priority class for batch Knative services"
EOF
# Configure node affinity for Knative workloads
kubectl label nodes node1 node2 node3 workload-type=serverless
Multi-Tenancy Configuration
Namespace Isolation
# tenant-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: tenant-a
labels:
serving.knative.dev/release: v1.12.0
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: tenant-quota
namespace: tenant-a
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
services.knative.dev: "50"
persistentvolumeclaims: "0"
---
apiVersion: v1
kind: LimitRange
metadata:
name: tenant-limits
namespace: tenant-a
spec:
limits:
- max:
cpu: "2"
memory: 2Gi
min:
cpu: 50m
memory: 64Mi
default:
cpu: 100m
memory: 128Mi
type: Container
Conclusion
Setting up a serverless platform with Knative on Rocky Linux provides a powerful foundation for modern cloud-native applications. The combination of automatic scaling, event-driven architecture, and seamless Kubernetes integration enables developers to focus on business logic while the platform handles infrastructure concerns.
By following the configurations and best practices outlined in this guide, you can build a production-ready serverless platform that scales efficiently, maintains security, and provides excellent observability for your applications.