3. Knative 核心概念:Serving 与 Eventing 架构、Serverless 容器模型、Revision 与 Route 流量管理

Knative 是 Kubernetes 原生的 Serverless 框架,其核心由 Serving 和 Eventing 两大组件构成。Serving 负责无状态服务的自动伸缩与流量管理,Eventing 负责事件驱动架构的构建与事件路由。理解这两个组件的架构以及 Revision、Route 等核心资源,是掌握 Knative 弹性伸缩能力的基础。

3.1 Knative Serving 架构

Knative Serving 定义了一套 Kubernetes 自定义资源(CRD),用于管理 Serverless 工作负载的生命周期。其核心目标是实现“从零到 N”的自动伸缩,即当服务没有请求时缩容到零副本,有请求时快速启动并扩容。

3.1.1 核心资源模型

资源名称 缩写 职责描述
Service ksvc 用户面向的顶层资源,自动管理 Configuration 和 Route 的生命周期。用户只需定义 Service,Knative 自动创建后续资源。
Configuration config 描述服务的期望配置(镜像、环境变量、资源限制等)。每次配置更新都会生成一个新的 Revision。
Revision rev Configuration 的不可变快照,代表一个特定版本的代码和配置。每个 Revision 对应一个独立的 Deployment 和 KPA(Knative Pod Autoscaler)实例。
Route route 负责流量分发,可以将流量按比例分配到不同的 Revision 上,支持灰度发布、蓝绿部署等场景。
关键理解: Knative Service 是用户操作的唯一入口,它内部自动串联了 Configuration 和 Route。用户修改 Service 的 spec 时,Knative 会创建一个新的 Revision,并自动将 Route 的流量指向最新的 Revision(默认行为)。

3.1.2 Serving 架构与请求流

下图展示了 Knative Serving 的核心组件以及一个外部请求如何被路由到实际的用户容器中。

Knative Serving 请求流架构图 Knative Serving 请求流架构 外部请求 Ingress / Kourier Activator Queue-Proxy + User Container (Revision Pod) 说明: 当 Revision 缩容到 0 时,Activator 会接管流量并缓存请求,同时启动新 Pod;Pod 就绪后,流量直接转发至 Queue-Proxy。

3.2 Knative Eventing 架构

Knative Eventing 提供了一套事件驱动的基础设施,使得不同服务之间可以通过事件进行松耦合通信。它定义了事件的生产者(Source)、消费者(Sink)以及事件路由(Broker/Trigger)等概念。

3.2.1 核心资源模型

资源名称 职责描述
Event Source 事件的生产者,例如 ApiServerSource(监听 K8s 事件)、PingSource(定时触发)、KafkaSource(从 Kafka 消费事件)。
Broker 事件网格的核心,负责接收事件并根据 Trigger 的规则将事件路由到对应的 Sink。Broker 通常与 InMemoryChannel 或 KafkaChannel 配合使用。
Trigger 定义事件过滤规则(如事件类型、来源)以及事件的目标 Sink。一个 Broker 可以绑定多个 Trigger。
Sink 事件的最终消费者,通常是一个 Knative Service、Kubernetes Service 或 URI。

3.2.2 Eventing 事件流

下图展示了一个典型的事件从产生到被消费的完整流程。

Knative Eventing 事件流架构图 Knative Eventing 事件流 Event Source (PingSource / KafkaSource) Broker (事件路由与过滤) Trigger A → Sink A Trigger B → Sink B 事件过滤示例: Trigger A 过滤条件:type: "dev.knative.source.ping" AND source: "cron-job-1" Trigger B 过滤条件:type: "dev.knative.source.ping" AND source: "cron-job-2" Broker 根据 Trigger 的过滤规则,将事件精准路由到不同的 Sink(Knative Service)。

3.3 Serverless 容器模型

Knative 的 Serverless 容器模型基于“请求驱动”的理念。与传统 Deployment 始终运行固定数量的 Pod 不同,Knative 的 Revision Pod 在没有流量时可以缩容到零,仅在收到请求时才被唤醒。

3.3.1 Pod 内部结构

每个 Knative Revision 的 Pod 内部包含两个容器:

  • User Container: 用户定义的业务容器,运行实际的应用程序代码。
  • Queue-Proxy: Knative 自动注入的 Sidecar 容器。它负责:
    • 收集请求并发指标(并发数、QPS)。
    • 将指标上报给 KPA(Knative Pod Autoscaler)用于决策伸缩。
    • 在 Pod 缩容到零时,将请求转发给 Activator 进行缓存。

3.3.2 自动伸缩机制

伸缩模式 触发条件 行为描述
缩容到零 无请求持续一段时间(默认 30s) KPA 将 Deployment 的副本数设置为 0,释放所有 Pod 资源。
从零启动 新请求到达,Activator 拦截 Activator 缓存请求,同时 KPA 快速启动新 Pod(冷启动)。Pod 就绪后,请求转发至 Queue-Proxy。
基于并发扩容 Queue-Proxy 上报并发数超过目标值 KPA 根据并发目标值(如 concurrency-target: 10)计算所需副本数,并调整 Deployment 的 replicas。
弹性伸缩核心参数:
  • container-concurrency-target:每个 Pod 的目标并发请求数(默认 100)。
  • stable-window:稳定窗口期,用于判断是否缩容(默认 60s)。
  • panic-window:恐慌窗口期,用于快速扩容应对突发流量(默认 6s)。

3.4 Revision 与 Route 流量管理

Revision 是 Knative 实现版本管理和灰度发布的核心。Route 则负责将流量按规则分发到不同的 Revision 上。

3.4.1 Revision 的不可变性与生命周期

  • 不可变性: 每个 Revision 一旦创建,其 spec(镜像、环境变量等)就不可修改。任何配置变更都会生成一个新的 Revision。
  • 生命周期: Revision 创建后,默认处于 Active 状态。当不再需要时,可以手动标记为 Retired 或直接删除。
  • 版本追溯: 由于 Revision 是不可变的,因此可以轻松回滚到任意历史版本。

3.4.2 Route 流量分发策略

Route 通过 traffic 字段定义流量分配规则。以下是一个典型的灰度发布配置示例:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: my-service
spec:
  template:
    spec:
      containers:
        - image: my-image:v2
  traffic:
    - revisionName: my-service-00001
      percent: 90
    - revisionName: my-service-00002
      percent: 10
      tag: canary
字段 说明
revisionName 指定目标 Revision 的名称。
percent 分配给该 Revision 的流量百分比,所有 Revision 的百分比之和必须为 100。
tag 可选字段,为 Revision 分配一个标签名。通过标签名可以生成一个独立的访问 URL(如 canary-my-service.default.example.com),方便内部测试。
latestRevision 布尔值,设置为 true 时,流量自动指向最新的 Revision,无需指定 revisionName。

3.4.3 流量管理场景

  • 蓝绿部署: 将 100% 流量从旧 Revision 切换到新 Revision。
  • 金丝雀发布: 将 10% 流量分配给新 Revision,90% 保留在旧 Revision,验证新版本稳定性后再逐步增加比例。
  • A/B 测试: 通过 tag 为不同 Revision 生成独立 URL,供内部测试团队或特定用户群体访问。
注意: 当使用 latestRevision: true 时,如果 Configuration 更新生成了新的 Revision,Route 会自动将流量指向最新的 Revision。如果希望手动控制流量切换,应避免使用 latestRevision,而是显式指定 revisionName。