12.3 Serverless, FaaS & Event-Driven Architecture
Key Takeaways
- Serverless means the developer does not provision or manage capacity; the platform scales on demand and can scale to zero, so idle workloads cost nothing.
- Knative Serving adds request-driven autoscaling and scale-to-zero to Kubernetes, using a pluggable networking layer such as Kourier, Contour, or Istio.
- Knative Eventing routes events between producers and consumers through Brokers and Triggers using the CloudEvents specification.
- CloudEvents is a CNCF specification that standardises event metadata so producers and consumers from different vendors can interoperate.
- Scale-to-zero trades idle cost against cold-start latency, which is why serverless suits spiky, event-driven, and batch workloads more than steady low-latency traffic.
12.3 Serverless, FaaS & Event-Driven Architecture
Quick Answer: Serverless does not mean there are no servers — it means the developer never provisions or manages them. The platform allocates capacity on demand, bills for actual use, and can scale to zero when idle. On Kubernetes, Knative provides this: Knative Serving for request-driven autoscaling with scale-to-zero, and Knative Eventing for routing events through Brokers and Triggers, using the CNCF CloudEvents specification as the wire format.
Section 12.2 listed serverless among the cloud native architecture patterns. This section explains how it actually works on Kubernetes and where its limits are.
1. What Makes a Workload Serverless
Four properties, all of which must hold:
- No capacity management. You never choose an instance count or a machine type.
- Event- or request-driven execution. Code runs in response to something: an HTTP request, a queue message, a storage upload, a timer.
- Scale to zero. Nothing runs, and nothing is billed, when there is no work.
- Sub-second elasticity. Load arrives and instances appear in seconds, not minutes.
Function-as-a-Service (FaaS) — AWS Lambda, Azure Functions, Knative functions, OpenFaaS — is the narrowest form, where the deployable unit is a single function. But serverless is broader than FaaS: a container that scales to zero on a serverless platform is equally serverless.
| Standard Deployment | Serverless workload | |
|---|---|---|
| Minimum instances | At least 1 (an HPA cannot go below 1) | 0 |
| Scaling signal | CPU, memory, or custom metrics | Concurrent requests, queue depth, events |
| Cost when idle | Full reserved capacity | Zero |
| First request after idle | Immediate | Cold start |
| Best fit | Steady traffic, latency-critical paths | Spiky, bursty, event-driven, batch |
2. Knative Serving
Knative Serving layers request-aware autoscaling onto Kubernetes. Its object model nests:
Service ──► Configuration ──► Revision (immutable snapshot of code + config)
│
└────► Route ──► traffic split across Revisions (e.g. 90/10)
| Object | Purpose |
|---|---|
Service | The top-level object developers write; manages Configuration and Route together |
Configuration | Desired state of the code and its settings; each change produces a new Revision |
Revision | An immutable snapshot. Revisions are never modified, only created — immutability made concrete |
Route | Maps traffic to Revisions by percentage, enabling canary and instant rollback |
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: image-resizer
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/minScale: "0"
autoscaling.knative.dev/maxScale: "100"
autoscaling.knative.dev/target: "50" # concurrent requests per pod
spec:
containers:
- image: registry.example.com/resizer:1.4.0
The Knative Pod Autoscaler (KPA)
The KPA scales on request concurrency rather than CPU — the correct signal for a request/response workload, because a Pod handling 200 concurrent requests may still show modest CPU while queueing badly. It also supports zero, which a standard HPA cannot.
When a Revision is scaled to zero, an Activator component holds incoming requests, signals the autoscaler to start a Pod, and forwards the buffered request once it is ready — so the caller experiences latency, not an error.
Networking is pluggable. Knative Serving requires a networking layer but does not mandate a particular one: Kourier (a lightweight Envoy-based default), Contour, or Istio are all supported. Early Knative required Istio; that has not been true for a long time, and "Knative requires Istio" is a stale claim.
Cold Starts
The honest cost of scale-to-zero:
request → activator buffers → pod scheduled → image pulled (maybe) →
container starts → runtime initialises → app ready → response
That can be tens of milliseconds for a small Go binary or many seconds for a JVM application on a node that must first pull the image. Mitigations: keep the image small, pre-pull it onto nodes, choose a fast-starting runtime, or set minScale: 1 — which trades away the zero-cost idle you adopted serverless for. This trade-off is the whole design decision.
3. Knative Eventing and CloudEvents
Serving handles requests; Eventing handles asynchronous events.
[ Source ]──► [ Broker ]──► [ Trigger (filter) ]──► [ Sink: Knative Service ]
Kafka an event attribute-based or any addressable
S3 upload mesh / filtering endpoint
cron router
webhook
| Object | Role |
|---|---|
| Source | Adapts an external system (Kafka, GitHub, cron, cloud storage) into CloudEvents |
| Broker | An event mesh that receives events and holds them for delivery |
| Trigger | A subscription with a filter on event attributes, pointing at a sink |
| Sink | Any addressable target — usually a Knative Service |
| Channel / Subscription | The lower-level primitive underneath Brokers |
The decoupling is the point: producers do not know their consumers exist. Adding a fraud-check consumer to order.created means creating one Trigger — the order service is never modified, redeployed, or even informed.
CloudEvents
CloudEvents is a CNCF specification that standardises event metadata, so an event emitted by one system is intelligible to a consumer built by another:
{
"specversion": "1.0",
"type": "com.example.order.created",
"source": "/services/checkout",
"id": "A234-1234-1234",
"time": "2026-08-07T12:34:56Z",
"datacontenttype": "application/json",
"data": { "orderId": "9912", "total": 42.50 }
}
Required attributes are specversion, type, source, and id. Before CloudEvents every platform invented its own envelope, and every integration needed a bespoke translator. It has bindings for HTTP, Kafka, AMQP, MQTT, and NATS, and it is what Knative Triggers filter on.
4. Event-Driven vs Request/Response
| Synchronous request/response | Asynchronous event-driven | |
|---|---|---|
| Coupling | Caller must know the callee | Producer knows nothing about consumers |
| Failure behaviour | Callee down ⇒ caller fails | Events queue and are delivered later |
| Latency | Immediate answer | Eventual |
| Consistency | Easier to reason about | Eventual consistency |
| Adding a consumer | Modify the caller | Add a subscription |
| Debugging | Follow the call stack | Requires distributed tracing and correlation IDs |
Event-driven design buys resilience and evolvability at the cost of reasoning difficulty. "Where did this event go?" is a genuinely harder question than "what did this function return?", which is why the observability material in section 12.1 is not optional in an event-driven system.
5. The Serverless Landscape on Kubernetes
| Project | Character |
|---|---|
| Knative | The de facto standard serverless layer for Kubernetes; Serving plus Eventing |
| KEDA | Event-driven autoscaling for ordinary Deployments, including to zero (section 4.3). Lighter than Knative when you only need scaling |
| OpenFaaS, Fission, Nuclio | FaaS platforms with their own function packaging and runtimes |
| Dapr | CNCF graduated; portable building blocks — pub/sub, state, bindings, service invocation — for distributed applications |
KEDA or Knative? If you have an existing Deployment and only need it to scale on queue depth and reach zero, KEDA is the smaller change. If you want request-driven autoscaling, revisions, traffic splitting, and an eventing mesh, Knative is the platform.
6. When Serverless Is the Wrong Answer
- Sustained high traffic — always-warm capacity is cheaper than per-request pricing, and there is no idle to eliminate.
- Strict low-latency paths — cold starts are unacceptable on a checkout endpoint.
- Long-running or stateful processes — serverless units are short-lived and stateless by design.
- Heavy local state or large in-memory caches — every cold start rebuilds them.
Serverless is a tool for spiky, event-driven, and intermittent workloads. Applying it uniformly is as much a mistake as never applying it.
Which capability distinguishes a Knative Service from a standard Kubernetes Deployment fronted by a Horizontal Pod Autoscaler?
What problem does the CloudEvents specification solve?
What is the fundamental trade-off a team accepts when adopting scale-to-zero?