使用Knative Serving构建Serverless应用平台
本文介绍如何在CCE集群上部署Knative Serving v1.19,构建具备按需弹性伸缩、灰度发布、Scale to Zero等能力的Serverless应用平台。内容涵盖从安装、配置域名、部署服务、验证Scale to Zero到灰度发布的完整上线流程,并给出KPA调优、资源配额、SWR镜像迁移等生产环境配置建议。
概述
Knative是一款基于Kubernetes的Serverless框架,旨在提供跨平台的Serverless编排标准。它通过整合容器构建(或函数)、工作负载管理及事件驱动能力,在Kubernetes之上提供“声明式 API + 自动编排 + 流量控制”的Serverless体验。
Knative的核心价值:
- 请求驱动的自动扩缩容(KPA):不同于HPA基于容器指标(CPU/内存),Knative Pod Autoscaler(KPA)基于请求并发数或QPS动态调整Pod数量,支持缩容到0,无流量时不占用计算资源。
- 零冷启动兜底:Pod缩容到0后,Activator组件拦截请求、触发扩容、排队等待,Pod就绪后转发,保证请求不丢失。
- 版本管理与灰度发布:Revision不可变,Route通过流量权重实现蓝绿发布与金丝雀发布,无需额外工具链。
- 标准化:基于Kubernetes CRD,与云原生生态无缝集成。
前提条件
- 已创建1.28及以上版本(推荐1.30+)的CCE Standard或Turbo集群,至少2个节点,节点规格不低于4核8GB。Turbo集群(独享节点池)适合对隔离性有要求的AI推理场景。
- 已创建弹性负载均衡实例,用于Kourier网关对外暴露服务。
- 已开通容器镜像服务。
- 节点可访问公网,或已将Knative组件镜像迁移至SWR。
约束与限制
- 本实践仅部署Knative Serving(请求驱动的工作负载管理),暂不涉及Knative Eventing(事件驱动模型)。若需事件驱动模型(如IoT、消息队列),可在Serving之上叠加Knative Eventing组件。
- CCE不会为type: LoadBalancer的Service自动创建和绑定ELB,需手动创建并通过kubernetes.io/elb.id注解关联。
- 网络插件需支持NetworkPolicy,CCE默认容器网络(Cilium/Tunnel)均支持。
- Scale to Zero冷启动耗时取决于镜像拉取速度与应用启动时长(通常1~3秒),对冷启动敏感的业务建议设置min-scale≥1。
- Knative Service的services.serving.knative.dev与core Service的services名称冲突,在部分CCE版本下需调整service-webhook的apiGroups配置,详见创建Knative Service时报错Forbidden?。
资源规划
- 节点规划
节点池
规格
数量
用途
系统节点池
4C8G
2
运行Knative控制面(knative-serving命名空间)+ Kourier网关。
业务节点池
按业务峰值估算(如8C16G)
≥ 2
运行Knative Service业务Pod;启用Scale to Zero后,节点资源可被其他工作负载复用。
GPU节点池(可选)
Ascend 910 / NVIDIA GPU
≥ 1
AI推理场景(如vLLM部署大模型),需安装对应设备插件。
- 网络规划
资源
规划
说明
ELB(公网型)
1 个
Kourier Gateway的LoadBalancer服务将绑定此ELB,对外提供Knative Service访问入口。
域名
1 个(生产环境)
如“*.knative.example.com”,泛域名解析至ELB绑定的EIP;测试环境可用sslip.io自动解析。
命名空间
knative-serving(控制面)、default或业务命名空间
控制面命名空间由安装YAML自动创建。
- ELB规划
CCE的hws-cloudprovider不会为type: LoadBalancer的Service自动创建和绑定ELB,必须手动创建ELB实例,然后通过Service注解显式关联。
- ELB类型选择
类型
elb.class
适用场景
配额
共享型 ELB
union
测试环境、低流量场景
监听器/后端服务器组配额较小,多个Service共享时易触发Quota exceeded for resources: pool。
独享型 ELB
performance
生产环境、高流量场景
独立资源配额,性能隔离,推荐Knative生产部署使用。
- 创建ELB。
- 类型选择:测试环境用共享型,生产环境用独享型。
- 网络配置:选择与当前CCE集群相同的VPC。
- 记录ELB ID。
无需手动配置监听器与后端服务器组,hws-cloudprovider会根据Service的spec.ports自动创建。若之前apply失败导致ELB残留了监听器或Pool,需在ELB控制台手动清理后重新 apply。
- 绑定ELB到Kourier Service
- 在Kourier Gateway Service(kourier-system命名空间下的kourier Service)上添加注解:
apiVersion: v1 kind: Service metadata: name: kourier namespace: kourier-system annotations: kubernetes.io/elb.id: <your-elb-id> # 替换为实际ELB ID kubernetes.io/elb.class: union # 共享型填union,独享型填performance spec: type: LoadBalancer # ... 其余字段保持不变 - 通过kubectl命令:
kubectl annotate svc kourier -n kourier-system \ kubernetes.io/elb.id=<your-elb-id> \ kubernetes.io/elb.class=union # 独享型改为performance
注意:若绑定失败,请检查以下常见报错:
- “listener is empty”或“elb.id is not defined, skip”:Service缺少elb.id注解,hws-cloudprovider无法创建监听器。
- “Quota exceeded for resources: pool(ELB.8905)”:共享型ELB的后端服务器组配额已满,需清理或更换独享型ELB。
- 在Kourier Gateway Service(kourier-system命名空间下的kourier Service)上添加注解:
- ELB类型选择
- 镜像仓库规划
Knative组件镜像默认来自gcr.io/knative-releases与ghcr.io/knative,CCE节点若无法直接拉取公网镜像,需将镜像迁移至SWR。镜像迁移详情请参见上传镜像。
组件
默认镜像
SWR 目标
activator / autoscaler / controller / webhook
gcr.io/knative-releases/knative.dev/serving/cmd/*
swr.<region>.myhuaweicloud.com/<project>/knative-*:v1.19.6
net-kourier-controller / kourier-gateway
gcr.io/knative-releases/knative.dev/net-kourier/cmd/*
swr.<region>.myhuaweicloud.com/<project>/kourier-*:v1.19.0
操作流程
步骤一:安装kn CLI客户端
kn是Knative的命令行客户端,用于创建、更新、删除Knative Service与管理Revision。
- 使用kubectl连接集群,详情请参见通过kubectl连接集群。
- 执行以下命令,安装kn CLI客户端。本文以knative-v1.19.0为例。
# Linux amd64 curl -Lo ./kn https://github.com/knative/client/releases/download/knative-v1.19.0/kn-linux-amd64 chmod +x ./kn sudo mv ./kn /usr/local/bin/
- 验证安装:
kn version
预期输出:
Version: v1.19.0 Build Date: 2026-xx-xx Git Revision: xxxxxxx Supported APIs: * Serving v1 (knative-serving v1.19.x)
步骤二:安装Knative Serving CRD与核心组件
若节点无法拉取公网镜像,请先完成SWR镜像迁移,并将下方YAML中的镜像地址替换为SWR地址。
- 安装CRD。
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.19.6/serving-crds.yaml kubectl wait --for=condition=Established --all crd --timeout=120s
预期输出信息包含以下内容。
customresourcedefinition.apiextensions.k8s.io/services.serving.knative.dev created customresourcedefinition.apiextensions.k8s.io/configurations.serving.knative.dev created customresourcedefinition.apiextensions.k8s.io/revisions.serving.knative.dev created customresourcedefinition.apiextensions.k8s.io/routes.serving.knative.dev created ...
- 安装核心组件。
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.19.6/serving-core.yaml
该YAML部署以下组件到knative-serving命名空间:
组件
说明
controller
监听Service/Configuration CRD变化,协调生成Revision与Deployment。
webhook
校验CRD合法性,注入默认值。
autoscaler
收集Pod并发指标,调整Deployment的replicas,支持缩容到0。
activator
Pod缩容到0时拦截请求,触发扩容并排队转发。
- 执行以下命令,确认knative-serving命名空间下所有Pod状态均为Running。
kubectl get pods -n knative-serving
预期输出:
NAME READY STATUS RESTARTS AGE activator-xxxxx-xxxxx 1/1 Running 0 60s autoscaler-xxxxx-xxxxx 1/1 Running 0 60s controller-xxxxx-xxxxx 1/1 Running 0 60s webhook-xxxxx-xxxxx 1/1 Running 0 60s
若webhook处于CrashLoopBackOff,多为TLS证书未就绪,等待1~2分钟后自动恢复。Knative v1.19自带证书签发,无需额外部署cert-manager。
步骤三:部署Kourier网关并配置Ingress
Knative支持多种Ingres网关(Kourier/Istio/Contour)。Kourier是基于Envoy的轻量级网关,依赖少、启动快,推荐作为入门与生产默认选型。
- 部署Kourier。
kubectl apply -f https://github.com/knative/net-kourier/releases/download/knative-v1.19.0/kourier.yaml
Kourier部署到kourier-system命名空间,包含3scale-kourier-gateway(数据面Envoy)。其中Gateway通过“type: LoadBalancer”暴露,CCE不会自动绑定ELB并分配外部IP,需要在Kourier Service上通过注解手动关联已创建的ELB实例。未配置ELB注解时,hws-cloudprovider会报“listener is empty”或“elb.id is not defined, skip”错误。
- 配置Kourier为默认Ingress。
kubectl patch configmap/config-network \ --namespace knative-serving \ --type merge \ --patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}' - 验证网关就绪并获取入口IP地址。
kubectl get pods -n kourier-system kubectl get svc kourier -n kourier-system
预期输出:
NAME READY STATUS RESTARTS AGE 3scale-kourier-control-xxxxx 1/1 Running 0 60s 3scale-kourier-gateway-xxxxx 1/1 Running 0 60s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kourier LoadBalancer 10.x.x.x 121.xxx.xxx.xxx 80:3xx/TCP,443:3xx/TCP 90s
EXTERNAL-IP为ELB绑定的公网/私网IP地址,需提前通过kubernetes.io/elb.id注解关联ELB。请记录该IP地址,后续配置域名解析时使用。
步骤四:配置域名与DNS
Knative默认域名后缀由config-domain ConfigMap决定,所有Knative Service的访问地址为<service-name>.<namespace>.<domain>。
- 测试环境:使用sslip.io自动解析
sslip.io会将{ip}.sslip.io解析为{ip},无需自建DNS。将上一步获取的ELB IP地址拼接为域名:
INGRESS_IP=$(kubectl get svc kourier -n kourier-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') KNATIVE_DOMAIN="${INGRESS_IP}.sslip.io" kubectl patch configmap/config-domain \ --namespace knative-serving \ --type merge \ --patch "{\"data\":{\"${KNATIVE_DOMAIN}\":\"\"}}" echo "Knative domain: ${KNATIVE_DOMAIN}"预期输出以下内容。
Knative domain: 121.xxx.xxx.xxx.sslip.io
- 生产环境:使用自定义域名
- 将泛域名“*.knative.<your-domain>”解析至ELB绑定的EIP。
- 配置config-domain:
kubectl patch configmap/config-domain \ --namespace knative-serving \ --type merge \ --patch '{"data":{"knative.example.com":""}}' - (可选)为Kourier Gateway的443端口配置TLS证书,建议通过CCE的证书管理或Secret注入实现HTTPS访问。
步骤五:部署首个Knative Service
- 使用kn部署helloworld。
kn service create helloworld \ --image ghcr.io/knative/helloworld-go:latest \ --port 8080 \ --env TARGET=World
若节点无法拉取ghcr.io镜像,请先迁移至SWR并替换为swr.<region>.myhuaweicloud.com/<project>/helloworld-go:latest。
预期输出:
Creating service 'helloworld' in namespace 'default': 0.030s The Route is still working to reflect the latest desired specification. 0.062s ... 0.080s Configuration "helloworld" is waiting for a Revision to become ready. 8.156s ... 8.178s Ingress has not yet been reconciled. 8.298s Ready to serve. Service 'helloworld' created with latest revision 'helloworld-00001' and URL: http://helloworld.default.121.xxx.xxx.xxx.sslip.io
- 查看服务与底层资源。
kn service list kubectl get configuration,revision,route,deployment -l serving.knative.dev/service=helloworld
预期输出:
NAME URL LATEST CREATED LATEST READY READY REASON helloworld http://helloworld.default.121.xxx.xxx.xxx.sslip.io helloworld-00001 helloworld-00001 True NAME READY STATUS configuration.serving.knative.dev/helloworld True revision.serving.knative.dev/helloworld-00001 True route.serving.knative.dev/helloworld True deployment.apps/helloworld-00001-deployment 1/1
根据Knative Serving的自动伸缩机制,当无流量进入时,Revision对应的Deployment副本数将自动缩容至0(即0/0)。
Knative Service自动创建了Configuration、Revision、Route、Deployment四级资源,对应CRD层级关系:Service →Configuration → Revision → Deployment。
- 访问服务。在任意能访问公网的节点或机器上测试helloworld服务。
curl http://helloworld.default.${KNATIVE_DOMAIN}返回“Hello World!”即表示服务部署成功。
步骤六:验证Scale to Zero
Scale to Zero是Knative的核心特性:无流量时Autoscaler将Deployment replicas缩为0,请求到达时由Activator拦截并触发扩容。
- 观察缩容到0。
服务无请求一段时间后(默认30秒空闲窗口),Pod自动终止:
# 持续观察Pod状态 kubectl get pod -l serving.knative.dev/service=helloworld -w
预期结果:约30~60秒后,Pod数量变为0。
NAME READY STATUS RESTARTS AGE helloworld-00001-deployment-xxxxx-xxxxx 2/2 Running 0 60s helloworld-00001-deployment-xxxxx-xxxxx 2/1 Terminating 0 90s helloworld-00001-deployment-xxxxx-xxxxx 0/1 Terminating 0 95s
- 触发冷启动。
Pod数为0时发起请求,Activator拦截、触发扩容、转发:
curl -w "\nTotal time: %{time_total}s\n" http://helloworld.default.${KNATIVE_DOMAIN}预期输出:
Hello World! Total time: 1.2s
首次冷启动耗时取决于镜像拉取与应用启动时长(通常1~3秒)。再次请求会直接命中已运行的Pod,耗时降至毫秒级。
- (可选)调整缩容窗口。
Knative默认在空闲30秒后将Pod缩容到 0,可通过Revision注解调整该窗口,例如延长至1分钟。
kn service update helloworld \ --annotation autoscaling.knative.dev/scale-to-zero-pod-retention-period=1m
预期输出:
Updating service 'helloworld' in namespace 'default': 0.030s The Route is still working to reflect the latest desired specification. 0.062s ... 0.080s Configuration "helloworld" is waiting for a Revision to become ready. 1.156s ... 1.178s Ingress has not yet been reconciled. 1.298s Ready to serve. Service 'helloworld' updated to latest revision 'helloworld-00003' and URL: http://helloworld.default.121.xxx.xxx.xxx.sslip.io
修改注解会触发Knative创建新的Revision(如上例中的helloworld-00003),旧Revision将逐步停止流量,新Revision承接后续请求。
步骤七:验证灰度发布(蓝绿/金丝雀)
Knative通过Revision不可变快照 + Route流量权重实现灰度发布,无需引入额外工具链。
- 更新镜像(自动生成新Revision)。
kn service update helloworld \ --image ghcr.io/knative/helloworld-go:latest \ --env TARGET=Knative
该操作会自动创建新Revision(helloworld-00002),默认Route将100%流量切到新版本。旧Revision(helloworld-00001)保留但不接流量。
- 金丝雀发布:按权重分流。
kn service update helloworld \ --traffic helloworld-00001=90 \ --traffic helloworld-00002=10
--traffic参数格式为“Revision名称=百分比”,多个Revision的权重总和应为100。
- 验证流量分配。多次请求服务,观察返回内容的比例分布。
for i in $(seq 1 10); do curl -s http://helloworld.default.${KNATIVE_DOMAIN} done | sort | uniq -c预期输出(约9:1比例):
9 Hello World! 1 Hello Knative!
- 查看路由详情。
kn route describe helloworld
预期输出:
Name: helloworld Namespace: default Age: 10m URL: http://helloworld.default.121.xxx.xxx.xxx.sslip.io Traffic Targets: 90% @latest (helloworld-00001) # still old URL: http://helloworld.default.../helloworld-00001 10% helloworld-00002 URL: http://helloworld.default.../helloworld-00002 - 版本回滚。
Revision为不可变快照,回滚操作即将流量权重重新指向旧Revision:
kn service update helloworld --traffic helloworld-00001=100
执行后流量立即切回helloworld-00001,无需重新部署。
配置优化
KPA弹性伸缩调优
Knative Pod Autoscaler(KPA)默认基于并发数(concurrency)进行弹性伸缩。生产环境需根据业务特征调整关键参数。
关键参数通过Revision的template.metadata.annotations注解配置:
| 注解 | 默认值 | 说明 | 调优建议 |
|---|---|---|---|
autoscaling.knative.dev/metric | concurrency | 伸缩指标 | 高QPS短任务用rps;长连接或重计算任务用concurrency。 |
autoscaling.knative.dev/target | 100(concurrency)/ 200(rps) | 单Pod目标并发/速率 | 按单Pod承载能力设定,过低会过度扩容,过高会触发限流。 |
autoscaling.knative.dev/max-scale | 0(无上限) | 最大Pod数 | 生产环境必设,防止流量突增打满节点池资源。 |
autoscaling.knative.dev/min-scale | 0 | 最小Pod数 | 对冷启动敏感的场景设为1~2,保留温实例。 |
autoscaling.knative.dev/scale-to-zero-pod-retention-period | 0s | 缩容到0前的保留时长 | 设30s~1m,平滑应对间歇性流量。 |
autoscaling.knative.dev/window | 60s | 伸缩计算窗口 | 突发流量场景缩短至30s;平稳流量延长至120s减少抖动。 |
该表注解作用于单个Revision。如需为所有Service设置全局默认值,请编辑“config-autoscaler ConfigMap:kubectl edit configmap/config-autoscaler -n knative-serving”。注解优先级高于全局默认。
示例:为高QPS Web服务配置KPA
以下以helloworld服务为例,演示如何为高QPS场景配置KPA参数。
kn service create web-api \ --image swr.cn-north-7.myhuaweicloud.com/<project>/web-api:latest \ --port 8080 \ --annotation autoscaling.knative.dev/metric=rps \ --annotation autoscaling.knative.dev/target=200 \ --annotation autoscaling.knative.dev/max-scale=20 \ --annotation autoscaling.knative.dev/min-scale=2 \ --annotation autoscaling.knative.dev/window=30s
资源配额与限制
为避免控制面过载、Pod调度失败或集群资源耗尽,建议按以下策略配置资源配额。
| 配置项 | 建议值 | 说明 |
|---|---|---|
| 控制面Pod resources requests | controller/autoscaler 500m/512Mi,webhook 100m/50Mi | 默认值偏低,在生产负载下建议适当上调。 |
| 业务Pod requests | 按单实例承载能力设定 | 设置过低可能导致节点资源超卖;设置过高可能导致调度失败。Knative默认按requests计算并发数。 |
| 业务Pod limits | requests的2~4倍 | 预留突发流量的资源余量,避免突发流量OOM。在GPU场景下,limits应等于requests。 |
| namespace ResourceQuota | 按业务命名空间实际需要进行设定 | 限制单个命名空间的资源配额,防止Knative Service无限扩容导致集群资源耗尽或服务不可用。 |
通过config-resource ConfigMap全局调整默认资源策略:
kubectl patch configmap/config-resource \
--namespace knative-serving \
--type merge \
--patch '{"data":{"pod-container-default-requests":"{\"cpu\":\"500m\",\"memory\":\"512Mi\"}"}}'