讲句实话,我刚开始用 Kubernetes 的时候,对“发布”这件事是没有太强烈概念的。不就是代码改完,打包,推镜像,kubectl apply 一下么,然后看 Pod 都起来了,心里基本就觉得差不多了。
那时候甚至以为:Kubernetes 已经帮我把发布这件事解决了。
直到有一次线上出问题。
一个小改动,上去之后 CPU 开始飙高,接口时好时坏,最烦的不是全挂,而是那种“有的正常、有的 500”。群里一堆人问你,压力你,你这边还在一行行翻日志找问题,真的压力山大。那一刻才明白一件事:Kubernetes 只是帮你“发上去”,但怎么发、发得稳不稳,是另一回事。
后来踩坑多了,才慢慢把这几种发布方式搞清楚。今天就不讲那些太虚太大的概念,咱们直接从运维视角,把常见几种发布方式一次性讲明白。
滚动发布
先从最基础的说起。其实大部分人平时用的 Deployment,默认就是滚动发布,只不过很多时候大家没认真看过配置,一直忽略了它的存在。
下面是一个最常用的模板:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: demo-app
template:
metadata:
labels:
app: demo-app
spec:
containers:
- name: demo-app
image: nginx:1.27
ports:
- containerPort: 80
这里面最关键的其实就两个参数:
maxSurge 表示发布过程中,最多可以比原来多出来多少个 Pod。
maxUnavailable 表示发布过程中,最多允许多少个 Pod 不可用。
比如你现在有 4 个 Pod,配置成:
maxSurge: 1
maxUnavailable: 1
意思就是:更新的时候,最多先多起 1 个新 Pod,同时最多允许 1 个旧 Pod 不可用。
这个配置的好处是比较稳,不会一下子把服务搞空。但问题也很明显,如果新版本本身有问题,它还是会一点点替换进去,所以它适合日常更新,不适合那种你心里都没底的大版本上线。
蓝绿发布
蓝绿发布别看名字高大上,本质上真没多复杂。 说白了就是:一套旧版本在跑、一套新版本提前准备好、最后靠 Service 切 selector。
先来两个 Deployment。
旧版本 Blue
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app-blue
spec:
replicas: 3
selector:
matchLabels:
app: demo-app
version: blue
template:
metadata:
labels:
app: demo-app
version: blue
spec:
containers:
- name: demo-app
image: nginx:1.26
ports:
- containerPort: 80
新版本 Green
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app-green
spec:
replicas: 3
selector:
matchLabels:
app: demo-app
version: green
template:
metadata:
labels:
app: demo-app
version: green
spec:
containers:
- name: demo-app
image: nginx:1.27
ports:
- containerPort: 80
然后 Service 先指向 blue
apiVersion: v1
kind: Service
metadata:
name: demo-app-svc
spec:
selector:
app: demo-app
version: blue
ports:
- port: 80
targetPort: 80
等你确认 green 没问题了,再把 selector 改成 green
spec:
selector:
app: demo-app
version: green
这就是蓝绿发布最核心的动作。这个方案最大的优点就是:简单、直接、回滚快。
回滚怎么办?再把 selector 切回 blue 就完了。
但他也有他的痛点:得准备两套资源。
如果你的服务本来就吃资源,那蓝绿发布在很多公司里就不是“技术问题”,而是“老板批不批机器”的问题。
金丝雀发布
金丝雀发布和蓝绿发布最大的区别,不在于是不是两个版本同时存在,而在于:它不是一刀切,而是一点点放量。
如果你用的是 Nginx Ingress,可以这么搞。
先准备一个稳定版 Service 和一个金丝雀版 Service。 Ingress 主规则走稳定版
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-app
spec:
ingressClassName: nginx
rules:
- host: demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-app-stable
port:
number: 80
然后再单独配一个 canary Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-app-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
ingressClassName: nginx
rules:
- host: demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-app-canary
port:
number: 80
这里最关键的是这两个 annotation
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
第一个表示这是一个金丝雀规则。
第二个表示把 10% 流量 导给 canary 版本。
这个方式的好处就是非常直观。你可以先给 10%,看监控、看日志、看告警,觉得稳了再往上调成 30%、50%,最后再全量切换。
但这里有个很容易被忽略的点:金丝雀发布不是配完就完了,它依赖监控。
你没有这些东西,其实很难做得稳:
不然你只是“把流量分过去了”,但到底有没有出问题,你心里根本没数。
灰度发布
金丝雀是按流量切,灰度是按规则切。
比如你想让公司内部测试同学先访问新版本,或者只让带特定 Header 的请求走新版本,那就可以用灰度规则。
还是以 Nginx Ingress 为例,最常见的是按 Header 灰度。
主 Ingress 还是走稳定版,这里就不重复了。新版本单独搞一个带 Header 判断的 canary Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-app-gray
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "X-Gray"
nginx.ingress.kubernetes.io/canary-by-header-value: "true"
spec:
ingressClassName: nginx
rules:
- host: demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-app-v2
port:
number: 80
这个配置是什么意思?
意思就是:当请求头里带上
X-Gray: true
这个请求才会打到 demo-app-v2。否则还是走旧版本。
这种方式特别适合做几类事情:内部测试先试、指定用户先试、某个终端先试、某个区域先试。
这个玩法比金丝雀更精细,因为你不是随机给 10% 流量,而是明确规定“谁能进新版本”。
但你也得知道: 灰度发布不是纯运维配置,它很容易和业务耦合起来。
因为你得跟前端、后端、网关约定好:
- Header 叫什么
- 谁来加
- 哪类用户带这个 Header
- 怎么撤掉
所以灰度发布做得好,看起来很高级;做不好,就会变成一坨谁也说不清的流量规则。
A/B 测试
如果从配置层面看,A/B 测试跟灰度真的很像。都是把不同请求导向不同版本。
比如你完全可以用 Header 或 Cookie 来做
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-app-ab
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-cookie: "ab-test"
spec:
ingressClassName: nginx
rules:
- host: demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-app-b
port:
number: 80
比如当用户带上 ab-test 这个 Cookie,就让它走 B 版本。
但 A/B 测试和灰度发布最大的区别,是目的不一样。
灰度发布更关心的是:新版本稳不稳。
A/B 测试更关心的是:哪个版本效果更好。
比如:哪个按钮点击率更高、哪个页面停留时间更长、哪个推荐策略转化更高。
所以这一块你可以在文章里顺手点一句:A/B 测试看起来是发布,实际上更偏产品实验。
总结与建议
很多人一看到这些发布方式,就想一步到位:金丝雀、灰度、Service Mesh...
但我干了这么几年下来,反而越来越觉得:
大部分团队,其实不需要那么复杂。
如果你现在:
那你就算上 Istio,也一样会炸,技术这东西,真不是越复杂越牛。把这些基础发布策略的原理、配置和适用场景搞清楚,结合自己团队的实际情况去选择,往往比盲目追求新技术更有用。
能稳住业务的,才是好方案。 本文提到的几种策略各有优缺点,滚动发布适用于常规迭代,蓝绿发布适合简单粗暴的切换,金丝雀和灰度发布则通过精细化流量控制来保障上线安全,而 A/B 测试则超越了发布本身,服务于产品和业务实验。在实际的云原生运维实践中,不妨从滚动发布开始,逐步根据需求引入更高级的策略,稳扎稳打地构建起自己的发布体系。如果你想了解更多相关的技术实践和社区讨论,也可以访问 云栈社区 来获取更多资源。