news 2026/10/16 2:51:13

Knative Serving 流量拆分实战:从 Revision 机制到蓝绿发布与金丝雀部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Knative Serving 流量拆分实战:从 Revision 机制到蓝绿发布与金丝雀部署
  • 文档
  • 教程
  • 云原生

【免费下载链接】docs

User documentation for Knative components.

项目地址:https://gitcode.com/gh_mirrors/docs1/docs
点击查看免费下载

本文以 Knative Serving 的traffic规范为主线,完整演示如何通过kn与kubectl两种方式在 Service 的不同 Revision 之间拆分流量,并结合本仓库的文档与源码深入讲解@latest语义、流量标签(tag)、渐进式发布(rollout-duration)以及蓝绿/金丝雀部署的完整落地流程。读完本文,你将掌握创建 Revision、查看流量分布、按百分比切流、验证拆分结果,以及在生产中安全发布新版本的全部关键操作与底层原理。

什么是 Revision:一次配置更新的“时间快照”

流量拆分是 蓝绿部署 与金丝雀部署(canary deployment)的核心手段。要理解它,首先必须理解 Knative 中的核心资源Revision。

根据本仓库 Revisions 概念文档 的定义:

Revision 是 Knative Serving 资源,包含每次对 Knative Service 做出更改时,应用代码与配置的“时间点快照”(point-in-time snapshot)。

Knative 的资源模型中,service.serving.knative.dev会全权管理工作负载的生命周期:它自动创建 Route、Configuration,并在每次 Service 更新时生成新的 Revision。正如 Serving 概述 所述,Service 可以被定义为“始终把流量路由到最新 Revision”,也可以“固定到某个 Revision”。

几个关键事实(来自 about-revisions.md):

  • Revision 是自动生成的:你无法直接创建 Revision,也无法直接修改 Revision 的 spec;Revision 总是在响应 Configuration spec 的更新时被创建。
  • Revision 基本不可变(immutable):仅在引用 ConfigMap、Secret 等可变 Kubernetes 资源时例外,或受 Revision 默认值变更影响(多为语法层面而非语义层面)。
  • Revision 可以保留任意时间:在 Knative 中,一个 Service 的每次配置变更都会生成新的 Revision,例如hello-00001、hello-00002。

也就是说:当你在 Knative Service 上修改任何配置(镜像、环境变量等)并重新部署时,Knative 不会原地覆盖,而是冻结当前配置生成一个新 Revision。流量拆分,就是在这些新旧 Revision 之间按百分比分配请求。

准备:部署一个 Hello World Service

本教程以 Knative 官方 Go 示例应用helloworld-go为蓝本。该应用读取环境变量TARGET,输出Hello ${TARGET}!;若TARGET未设置则默认输出Hello World!。完整源码见 helloworld.go:

func handler(w http.ResponseWriter, r *http.Request) { log.Print("helloworld: received a request") target := os.Getenv("TARGET") if target == "" { target = "World" } fmt.Fprintf(w, "Hello %s!\n", target) }

该示例的官方 Service 定义见 service.yaml,构建与部署细节见 README。

首先部署第一个版本(环境变量设为World),详见 部署第一个 Service 教程:

使用knCLI:

kn service create hello \ --image ghcr.io/knative/helloworld-go:latest \ --port 8080 \ --env TARGET=World

或使用 YAML 文件(hello.yaml):

apiVersion: serving.knative.dev/v1 kind: Service metadata: name: hello spec: template: spec: containers: - image: ghcr.io/knative/helloworld-go:latest ports: - containerPort: 8080 env: - name: TARGET value: "World"
kubectl apply -f hello.yaml

注意:${LOADBALANCER_IP}的值取决于集群类型——对于kind集群通常为127.0.0.1,对于minikube则取决于本地 tunnel 配置。

部署完成后,第一次部署会生成名为hello-00001的 Revision。

创建新 Revision:修改 TARGET 并重新部署

现在把环境变量TARGET从World改为Knative,让应用改为输出 “Hello Knative!”。这会产生一个新的 Revisionhello-00002。

使用kn:

kn service update hello \ --env TARGET=Knative

kn会在 CLI 中打印出有用的信息,预期输出:

Service 'hello' created to latest revision 'hello-00002' is available at URL: http://hello.default.${LOADBALANCER_IP}.sslip.io

使用 YAML:

  1. 编辑已有的hello.yaml,使其包含以下内容:
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: hello spec: template: spec: containers: - image: ghcr.io/knative/helloworld-go:latest ports: - containerPort: 8080 env: - name: TARGET value: "Knative"
  1. 应用更新:
kubectl apply -f hello.yaml

预期输出:

service.serving.knative.dev/hello configured

由于是更新已有 Knative Service,URL 不会改变,但新 Revision 的名称变为hello-00002。

底层机制:为什么 URL 不变、Revision 却变了?

从本仓库 Serving 概述 可以确认:使用Service资源部署时,服务会自动拥有对应的 Route 和 Configuration;每次 Service 更新都会创建一个新的 Revision。URL 属于 Route 层,Route 仍然指向同一个 Service 域名,因此对外 URL 不变;而 Configuration 每次变更都会冻结出新的 Revision 快照。这也解释了kn service update与kubectl apply两种方式殊途同归的原因——它们最终都修改了同一个Service资源的 spec。

一个容易被忽略的细节:镜像标签解析

本仓库 Tag resolution 文档指出:Knative Serving 在创建 Revision 时,会把镜像标签(tag)解析为摘要(digest),以保证 Deployment 的一致性。也就是说ghcr.io/knative/helloworld-go:latest在 Revision 快照中会被固定到当时的镜像 digest——这正是 Revision 作为“不可变快照”能稳定回滚的前提之一。当然,该功能要求 Serving controller 有权限访问容器镜像仓库。

访问新 Revision 验证变更

在浏览器中再次访问该 Knative Service,或在终端中使用curl:

echo "Accessing URL $(kn service describe hello -o url)" curl "$(kn service describe hello -o url)"

预期输出:

Hello Knative!

此时hello-00002是新 Revision,Knative 默认把 100% 流量都切到了它上面。

查看现有 Revisions:kn 与 kubectl 两种视角

使用 Knative CLI(kn)或kubectl查看已有 Revision 列表。

使用kn:

kn revisions list

预期输出:

NAME SERVICE TRAFFIC TAGS GENERATION AGE CONDITIONS READY REASON hello-00002 hello 100% 2 30s 3 OK / 4 True hello-00001 hello 1 5m 3 OK / 4 True

使用kubectl:

kubectl get revisions

预期输出:

NAME CONFIG NAME K8S SERVICE NAME GENERATION READY REASON ACTUAL REPLICAS DESIRED REPLICAS hello-00001 hello 1 True 0 0 hello-00002 hello 2 True 0 0

使用kn命令时,需要重点关注TRAFFIC列:可以看到 100% 的流量都指向最新 Revisionhello-00002(即GENERATION最大的那一行),而hello-00001的流量为 0%。

要点:当创建一个新 Revision 时,Knative 默认将 100% 的流量指向该最新 Revision。你可以通过为每个 Revision 指定流量百分比来改变这一默认行为。

补充:从输出还能看到ACTUAL REPLICAS为 0、DESIRED REPLICAS为 0。这正是 Knative Serving 按需伸缩(scale to zero)的体现——没有流量时副本数可以缩到 0,这与 Serving 概述 中“Autoscaling, including scaling pods down to zero”的定位一致。同时,长时间无流量的非活跃 Revision 会被自动垃圾回收以释放集群资源(详见 about-revisions-garbage-collection.md)。

在 Revisions 之间拆分流量:50/50 实战

现在把流量在hello-00001与最新 Revision 之间按 50/50 拆分。

使用kn:

kn service update hello \ --traffic hello-00001=50 \ --traffic @latest=50

使用 YAML:在hello.yaml末尾添加traffic段:

apiVersion: serving.knative.dev/v1 kind: Service metadata: name: hello spec: template: spec: containers: - image: ghcr.io/knative/helloworld-go:latest ports: - containerPort: 8080 env: - name: TARGET value: "Knative" traffic: - latestRevision: true percent: 50 - latestRevision: false percent: 50 revisionName: hello-00001

应用更新:

kubectl apply -f hello.yaml

理解@latest:@latest始终指向“最新”的 Revision,在本例中即hello-00002。使用--traffic @latest=50可以避免硬编码 Revision 名称,下次创建新 Revision 时@latest会自动指向它。等价地,在 YAML 中通过latestRevision: true表达“指向最新 Revision”,而latestRevision: false配合revisionName则用于固定到指定 Revision。

验证流量拆分结果

再次列出 Revisions 验证流量拆分是否正确:

kn revisions list

预期输出:

NAME SERVICE TRAFFIC TAGS GENERATION AGE CONDITIONS READY REASON hello-00002 hello 50% 2 10m 3 OK / 4 True hello-00001 hello 50% 1 36m 3 OK / 4 True

在浏览器中多次访问该 Service,可以看到每个 Revision 提供不同的输出。同样地,可以在终端多次访问 Service URL 来观察流量在 Revision 之间的拆分:

echo "Accessing URL $(kn service describe hello -o url)" curl "$(kn service describe hello -o url)"

预期输出(交替出现):

Hello Knative! Hello World! Hello Knative! Hello World!

这就完成了一次最基本的金丝雀式流量分发:新旧两个版本各承担 50% 请求,方便你在生产流量中观察新版本表现。

深入:traffic 规范的完整语义

本教程所用的traffic段是 Knative Service 流量管理的核心。结合 traffic-management.md,我们总结其完整语义:

  • 创建 Knative Service 时没有默认的trafficspec;通过设置trafficspec,你可以把流量拆分到任意数量的固定 Revision,或通过latestRevision: true把流量发送到最新 Revision。
  • 每个 traffic 条目是一个“流量目标(target)”,常用字段组合如下:
字段含义示例
revisionName固定到指定名称的 RevisionrevisionName: hello-00001
latestRevision指向最新 Revision(true时通常无需revisionName)latestRevision: true
percent分配给该目标的流量百分比percent: 50
tag为该流量目标创建命名的可访问 URLtag: staging
configurationName固定到某 Configuration 的最新就绪 RevisionconfigurationName: config

用 tag 创建目标 URL

当traffic条目带有tag属性时,Route 会为这个特定流量目标生成独立地址。例如 traffic-management.md 中的示例:

apiVersion: serving.knative.dev/v1 kind: Service metadata: name: example-service namespace: default spec: traffic: - percent: 0 revisionName: example-service-1 tag: staging - percent: 40 revisionName: example-service-2 - percent: 60 revisionName: example-service-3
  • 打上tag: staging的 Revision 可以通过staging-<route name>.<namespace>.<domain>访问;
  • 未打 tag 的example-service-2、example-service-3只能通过主路由<route name>.<namespace>.<domain>访问;
  • 被 tag 的流量目标会创建一个新的 Kubernetes Service(名为staging-<route name>),供集群内其他 Service 访问;可通过给它打上networking.knative.dev/visibility: cluster-local标签来限制可见性(详见 private-services)。

这个机制正是金丝雀发布中“先上线 0% 流量 + 打 tag 内部验证,再逐步放量”的基础。

通过 kn CLI 管理流量

kn提供了与 YAML 等价的流量管理命令:

kn service update <service-name> --traffic <revision-name>=<percent>

参数说明:

  • <service-name>:要配置流量路由的 Knative Service 名称;
  • <revision-name>:要接收一定百分比流量的 Revision 名称;
  • <percent>:要发送给该 Revision 的流量百分比。

例如,将名为example-service的 Service 流量按 80% 到 Revisiongreen、20% 到 Revisionblue拆分:

kn service update example-service --traffic green=80 --traffic blue=20

也可以先给 Revision 打 tag,再按 tag 拆分流量:

kn service update example --tag revision-0001=green --tag @latest=blue

@latesttag 表示blue解析到该 Service 的最新 Revision。下面的命令将 80% 流量发送到最新 Revision,20% 发送到名为v1的 Revision:

kn service update example-service --traffic @latest=80 --traffic v1=20

常见 traffic 配置模式(来自官方示例)

以下示例均出自 traffic-management.md,可直接套用:

模式一:100% 流量到最新 Revision(status中可看到latestRevision解析出的实际 Revision 名称):

spec: traffic: - latestRevision: true percent: 100 status: traffic: - percent: 100 revisionName: example-service-1

模式二:100% 固定到 current Revision,同时保留最新 Revision 备用于回滚(最新就绪 Revision 即使无流量也保持可用):

spec: traffic: - tag: current revisionName: example-service-1 percent: 100 - tag: latest latestRevision: true percent: 0

模式三:双 Revision 各 50%(蓝绿/金丝雀的典型形态):

spec: traffic: - tag: current revisionName: example-service-1 percent: 50 - tag: candidate revisionName: example-service-2 percent: 50 - tag: latest latestRevision: true percent: 0

深入:渐进式发布(gradual rollout)

如果你在traffic中通过configurationName或latestRevision指向“配置目标”而非固定 Revision,那么当新 Revision 创建并就绪后,目标流量会立刻 100% 切到新 Revision,这可能导致请求在队列代理(queue proxy)或 Activator 处堆积、过期或被拒绝。

Knative 为此提供了rollout-duration参数,可将流量渐进式地切到最新 Revision:先切 1% 的流量,随后按等量递增步长逐步放量到目标值。详见 rolling-out-latest-revision.md 与 gradual-rollout-intro.md。

配置方式是在 Service(或 Route)上加注解:

apiVersion: serving.knative.dev/v1 kind: Service metadata: name: helloworld-go namespace: default annotations: serving.knative.dev/rollout-duration: "380s"

值得注意的限制(来自官方文档):

  • rollout-duration是基于时间的,不参与自动伸缩(autoscaling)子系统的交互;
  • 该特性同时适用于带 tag 和不带 tag 的流量目标,也适用于独立 Route(无 Service 包裹的情况)。

Rollout 期间,Route 与 Service 的status.conditions会显示Reason: RolloutInProgress、Status: Unknown,消息为 “A gradual rollout of the latest revision(s) is in progress.”(详见 route-status-updates.md)。以 55% 配比为例,rollout 会先让新 Revision 拿到 1%,再按约 18% 的步长递增,直至到达目标 55%。

深入:蓝绿部署的完整流程

traffic-management.md 给出了基于独立 Route 资源的蓝绿部署完整流程,核心思路是“先上线新版本但不放流量 → 打 tag 建立测试端点 → 逐步放量 → 全部切换”。步骤如下:

  1. 部署应用为 Knative Service,并找到第一个 Revision 名称:
kubectl get configurations <service-name> -o=jsonpath='{.status.latestCreatedRevisionName}'
  1. 定义 Route把所有流量发送到该 Revision:
apiVersion: serving.knative.dev/v1 kind: Route metadata: name: <route-name> namespace: default spec: traffic: - revisionName: <first-revision-name> percent: 100 # All traffic goes to this revision
  1. 验证旧版本可访问:kubectl get route <route-name>查看输出 URL。
  2. 部署第二个 Revision:修改 Servicetemplatespec 中至少一个字段(例如image或env环境变量),然后通过kubectl apply或kn service update重新部署。
  3. 再次查询latestCreatedRevisionName拿到第二个 Revision 名称。此时两个 Revision 都已部署并运行。
  4. 更新 Route:主 URL 仍把 100% 流量导向第一 Revision,同时给第二 Revision 打 tagv2、流量 0%,建立测试端点:
spec: traffic: - revisionName: <first-revision-name> percent: 100 # All traffic is still being routed to the first revision - revisionName: <second-revision-name> percent: 0 # 0% of traffic routed to the second revision tag: v2 # A named route

重新应用后,第二 Revision 处于“staged”状态,主 URL 无流量进入,而 Knative 为它创建了名为v2的独立 Route。

  1. 获取新 Route 的 URL用于在放量前验证新版本行为:
kubectl get route <route-name> --output jsonpath="{.status.traffic[*].url}"
  1. 50/50 放量:
spec: traffic: - revisionName: <first-revision-name> percent: 50 - revisionName: <second-revision-name> percent: 50 tag: v2
  1. 100% 切换到新版本:
spec: traffic: - revisionName: <first-revision-name> percent: 0 - revisionName: <second-revision-name> percent: 100 tag: v2

提示:如果确定不再需要回滚,可以直接删除第一 Revision,而不必保留 0% 流量的条目。不可路由(non-routeable)的 Revision 对象随后会被自动垃圾回收(详见 about-revisions-garbage-collection.md)。

  1. 访问第一 Revision 的 URL 验证旧版本已不再接收流量。

小结与延伸阅读

本教程展示了 Knative 流量拆分的完整链路:更新 Service 生成新 Revision → 查看 Revision 与流量分布 → 通过--traffic或 YAMLtraffic段按百分比拆分 → 验证新旧版本交替响应。在此基础上,tag 机制与rollout-duration分别解决了“独立验证端点”与“平滑放量”两个生产级问题,配合蓝绿部署流程即可形成一套完整的发布体系。

进一步深入可参考本仓库的以下资料:

  • 流量管理官方指南:tag URL、traffic 示例与蓝绿部署细节
  • Revisions 概念与配置:Revision 的不可变性与自动伸缩
  • 渐进式发布指南:rollout-duration 注解配置
  • Serving 概述:Service/Route/Configuration/Revision 四类资源的关系
  • 示例应用源码与部署文件:可自行构建镜像复现本教程
  • Serving 代码示例索引:更多进阶样例
  • 文档
  • 教程
  • 云原生

【免费下载链接】docs

User documentation for Knative components.

项目地址:https://gitcode.com/gh_mirrors/docs1/docs
点击查看免费下载

相关推荐

上一篇:Ruff 之 ty:类型可赋值性诊断错误上下文(error context)机制全解析
下一篇:Sim React 组件编写规范:结构顺序、列表渲染性能陷阱与 react-doctor 误报甄别

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 2:50:38

NLP文本情感分类实战:从数据清洗到宏平均F1的完整项目链路

1. 这份小考的题目设定与破题思路1.1 为什么我把一份小考当成一个完整的NLP项目来做事情是这样的。前阵子某内部讨论群里丢出来一份自然语言处理方向的考核题目&#xff0c;要求在三周内完成文本情感二分类任务&#xff0c;数据大概四千条短评语料&#xff0c;没有预训练权重&a…

作者头像 李华
网站建设 2026/10/16 2:50:38

《自然本源》如何实现广义相对论的代数化

摘要&#xff1a;本文介绍张祥前与徐玉川合作提出的“自然本源”框架&#xff0c;尝试用更简洁的代数形式改写广义相对论&#xff0c;将天体运动、光线弯曲、引力红移、黑洞和引力波等现象统一纳入一个更基础的尺度和中心源关系之中&#xff0c;目标是把天文观测、工程计算和物…

作者头像 李华
网站建设 2026/10/16 2:50:35

Python实现M-QAM仿真:从Gray映射到BER曲线全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 2:49:56

CodeBuddy IDE实测:AI Agent如何重塑开发工作流

打开下载页&#xff0c;看到 CodeBuddy IDE 的状态已经是"可下载"&#xff0c;再扫一眼社区里关于 WorkBuddy 的讨论&#xff0c;大家都管它叫"腾讯版小龙虾"&#xff0c;说实话我的第一反应是&#xff1a;腾讯这次是真的下场比赛了。2025 年的 AI 编程赛道…

作者头像 李华
网站建设 2026/10/16 2:49:36

老校区的机械表,不拆不改怎么走进数字化

一所学校的新旧之差&#xff0c;有时候站在校门口就能看出来。新楼那一片&#xff0c;智能电表、智慧教室、能源管理系统一应俱全&#xff0c;数据自己跑&#xff0c;不需要人管。拐进老校区就是另一回事了。配电柜还是十几年前那批&#xff0c;里面装的机械表还在转&#xff0…

作者头像 李华