部署在 Kubernetes 集群中的应用,在升级发布时可能会存在的问题:
1,由于 Kuberneter 底层 Pod 容器生命周期与网络组件生命周期是异步管理的,在升级时如果没有处理好应用优雅退出的问题,就很容易导致 http 访问请求 5xx
2,原生 Deployment 应用的滚动发布功能是一把梭的全量发布模式,没有灰度和分批控制发布的概念,一旦出现问题,故障影响范围就会迅速扩大
这也是为什么需要灰度发布,蓝绿发布,彩虹发布,金丝雀发布、A/B Test等多样化形式发布的重要原因,核心目标只有一个,就是为了确保服务的稳定性,减少或避免因变更带来的不稳定因素
今天我们主要来聊下,如何在不引入第三方插件的情况下,来实现简单的灰度发布和蓝绿发布功能
在 Kubernetes 里面 Pod 的网络通信都是借助 Service 实现的,Service 的底层是依赖 Iptables 或者 eBPF 加上 dns 技术实现的,具体细节感兴趣可自行探索,这里不在展开,那么我们来看下如何借助 Service 实现灰度发布和蓝绿发布。
依赖资源:1 个 Service 对象,两个 Deployment 对象,一般称为 blue 和 green
原理:每次升级发布都会额外拉起一个 Deployment ,然后通过 Service 的 selector 来绑定 Deployment,通过在旧的 Deployment上缩容一个副本,新的 Deployment 上扩容一个副本类似这样的一缩一扩的方式实现灰度发布。
假设:blue_deployment.yaml 是 v1 版本,注意我们设置了 selector label
apiVersion: apps/v1
kind: Deployment
metadata:
name: py-hello-blue
spec:
selector:
matchLabels:
app: hello
color: blue
replicas: 1
template:
metadata:
labels:
app: hello
color: blue
spec:
terminationGracePeriodSeconds: 30
containers:
- name: hello
imagePullPolicy: Always
image: localhost:5001/py-http:1
ports:
- containerPort: 8888
resources:
requests:
memory: "50Mi"
limits:
memory: "200Mi"
lifecycle:
preStop:
exec:
command: ["sleep", "5"]
# command: ["/usr/bin/tini", "--", "bash", "-c"]
command: ["sh", "-c"]
args:
- |
python app.py
现在要灰度升级,我们新建一个 green_deployment.yaml 是 v2 版本,注意也设置了 label 标签:
apiVersion: apps/v1
kind: Deployment
metadata:
name: py-hello-green
spec:
selector:
matchLabels:
app: hello
color: green
replicas: 1
template:
metadata:
labels:
app: hello
color: green
spec:
terminationGracePeriodSeconds: 30
containers:
- name: hello
imagePullPolicy: Always
image: localhost:5001/py-http:2
ports:
- containerPort: 8888
resources:
requests:
memory: "50Mi"
limits:
memory: "200Mi"
lifecycle:
preStop:
exec:
command: ["sleep", "5"]
# command: ["/usr/bin/tini", "--", "bash", "-c"]
command: ["sh", "-c"]
args:
- |
python app.py
ok,现在我们通过 Service 来绑定流量:
apiVersion: v1
metadata:
name: py-hello-service
kind: Service
spec:
selector:
app: hello
#color: green
#color: blue
ports:
- name: web
port: 8888
protocol: TCP
targetPort: 8888
type: ClusterIP
在这个 Service 中,我们如果设置 color 标签,则可以把流量全部导入v1 或者 v2,如果我们不设置 color 标签,那么默认会把流量请求在两个 Deployment 中轮询,为了实现灰度发布,我们需要通过缩扩副本来实现:我们例子都只有一个副本,所以灰度起来也很简单:
# blue 旧服务
kubectl scale deployment/py-hello-blue --replicas=1
# green 新服务扩容 1,就相当于 50% 流量进来,如果想要 10% 流量,则可以把 bluee 扩容到 9
kubectl scale deployment/py-hello-green --replicas=1
注意这里面的流量调控并不是精准的,只能实现简单的灰度发布
接着灰度发布的例子,我们蓝布发布的实现就非常简单了,直接在 Service 里面增加 color 标签为 green 就可以把全部流量切换到新拉起的服务中,当然如果测试失败了,也可以快速进行回滚,同理将 color 改回为 blue 即可
apiVersion: v1
metadata:
name: py-hello-service
kind: Service
spec:
selector:
app: hello
color: green #如果蓝布切换后,测试失败,可以快速把 color 改为 blue 进行回滚
ports:
- name: web
port: 8888
protocol: TCP
targetPort: 8888
type: ClusterIP
通过 service 进行灰度发布和蓝绿发布虽然功能比较简单,但是对于大部分普通的发布业务也足够用了,这种方案可以结合发布平台稍做封装,比如一键创建新克隆版本,切换流量,扩缩容等,有了这些基础功能后,使用起来会更加流畅