- 文档
- 教程
- 云原生
【免费下载链接】docs
User documentation for Knative components.
本文以 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=Knativekn会在 CLI 中打印出有用的信息,预期输出:
Service 'hello' created to latest revision 'hello-00002' is available at URL: http://hello.default.${LOADBALANCER_IP}.sslip.io使用 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: "Knative"- 应用更新:
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 | 固定到指定名称的 Revision | revisionName: hello-00001 |
latestRevision | 指向最新 Revision(true时通常无需revisionName) | latestRevision: true |
percent | 分配给该目标的流量百分比 | percent: 50 |
tag | 为该流量目标创建命名的可访问 URL | tag: staging |
configurationName | 固定到某 Configuration 的最新就绪 Revision | configurationName: 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 建立测试端点 → 逐步放量 → 全部切换”。步骤如下:
- 部署应用为 Knative Service,并找到第一个 Revision 名称:
kubectl get configurations <service-name> -o=jsonpath='{.status.latestCreatedRevisionName}'- 定义 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- 验证旧版本可访问:
kubectl get route <route-name>查看输出 URL。 - 部署第二个 Revision:修改 Service
templatespec 中至少一个字段(例如image或env环境变量),然后通过kubectl apply或kn service update重新部署。 - 再次查询
latestCreatedRevisionName拿到第二个 Revision 名称。此时两个 Revision 都已部署并运行。 - 更新 Route:主 URL 仍把 100% 流量导向第一 Revision,同时给第二 Revision 打 tag
v2、流量 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。
- 获取新 Route 的 URL用于在放量前验证新版本行为:
kubectl get route <route-name> --output jsonpath="{.status.traffic[*].url}"- 50/50 放量:
spec: traffic: - revisionName: <first-revision-name> percent: 50 - revisionName: <second-revision-name> percent: 50 tag: v2- 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)。
- 访问第一 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.
相关推荐
Mermaid ELK 布局完整指南:改一个词让流程图快速不再乱线
Mermaid ELK 布局完整指南:改一个词让流程图快速不再乱线 如果你的 Mermaid 流程图节点叠在一起、连线交叉成一团,这篇文章教你把布局算法切换为
图表库前端数据可视化从NOT_A_JPEG到PARSING_ERROR:node-exif三类错误码的完整处理方案
从NOT_A_JPEG到PARSING_ERROR:node exif三类错误码的完整处理方案 node exif 是一个 Node.js 库,用于从 JPEG
后端免费开源直播聚合工具 Simple Live:一个 App 装下 4 大平台,源码 5 分钟编出自己的跨平台客户端
免费开源直播聚合工具 Simple Live:一个 App 装下 4 大平台,源码 5 分钟编出自己的跨平台客户端 你切到第五个直播 App,点开才发现主播刚下
直播音视频移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考