实现云容器引擎的灰度发布,常规做法需要在集群中部署如Nginx Ingress或Traefik这样的开源工具,或者依赖服务网格的功能。这些方案在操作上可能较为复杂,对于只需简单灰度发布且不希望引入过多额外插件或复杂流程的用户来说,可以考虑利用Kubernetes自带的特性来达成目标。这样,我们不仅能实现简单的灰度发布和蓝绿发布,还能保持系统的简洁和高效。
原理介绍
用户在进行业务部署时,通常会选择利用Kubernetes中的无状态负载Deployment和有状态负载StatefulSet等对象,这些对象各自负责管理一组Pod。以Deployment为例,示意图如下:
在Kubernetes中,为了使得工作负载能够被外部访问,用户通常会为每个工作负载创建一个对应的Service。这个Service通过selector机制来匹配后端Pod,从而建立起访问路径。无论是集群内部的其他服务还是集群外部的客户端,只需访问这个Service,就能间接访问到后端Pod所提供的服务。若希望将服务对外暴露,用户只需将Service的类型设置为LoadBalancer,此时,一个弹性负载均衡器(ELB)将作为流量入口,负责将外部请求转发到后端Pod。
灰度发布原理
以Deployment为例,按照上述的方式每个Deployment创建一个Service,Service通过selector匹配后端Pod,通过Service最终访问到业务Pod。使不同Deployment的Pod被同一Service的selector选中,即表示同一Service可以访问不同Deployement的Pod。调整不同版本Deployment的副本数,即可调整路由到不同版本负载的流量比例,实现灰度发布。示意图如下:
蓝绿发布原理
以Deployment为例,假设在集群中已经部署了两个不同版本的Deployment,这些Pod都拥有一些共同的标签,但其中有一个标签的值是不同的,这个值用于区分它们的版本。Service在选择后端Pod时,会依据这些标签。如果想要更改Service后端对应的Pod,即实现服务从一个版本切换到另一个版本,我们只需修改Service的selector中那个用于区分版本的标签的值。这样,Service就会自动将流量转发到新版本的Pod上,从而实现了版本的平滑切换。示意图如下:
示例说明
前提条件
已上传测试镜像至容器镜像服务,镜像包含v1和v2两个版本。
资源创建方式
本文提供以下两种方式使用YAML部署Deployment和Service:
- 方式1:在无状态工作负载页面,单击上方的“新增YAML”,再将本文示例的YAML文件内容输入编辑窗中。
- 方式2:将本文的示例YAML保存为文件,再使用kubectl指定YAML文件进行创建。例如:kubectl create -f xxx.yaml。
步骤1:部署两个版本的服务
在集群中部署两个版本的服务。
1、创建第一个版本的Deployment,名称以app-v1为例。YAML示例如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v1
spec:
replicas: 2
selector:
matchLabels:
app: myapp
version: v1
template:
metadata:
labels:
app: myapp
version: v1
spec:
containers:
- image: {repository_url}/myapp:v1
name: container
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
2、创建第二个版本的Deployment,名称以app-v2为例。YAML示例如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v2
spec:
replicas: 2
selector:
matchLabels:
app: myapp
version: v2
template:
metadata:
labels:
app: myapp
version: v2
spec:
containers:
- image: {repository_url}/myapp:v2
name: container
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
您可以登录云容器引擎控制台查看部署情况。
步骤2:实现灰度发布
1、为了将部署的Deployment服务暴露给外部访问,我们为其创建一个类型为LoadBalancer的Service。在这个Service的配置中,不会在其selector中指定特定的版本标签,这样做的目的是让Service能够同时选中并暴露两个不同版本Deployment的Pod。通过这种方式,外部流量可以通过这个Service访问到这两个版本的Pod,从而实现了服务的对外暴露和版本共存。YAML示例如下:
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/ctyun-loadbalancer-id: 586c97da-a47c-467c-a615-bd25a20de39c # ELB实例的ID,请替换为实际取值
name: app-service
spec:
ports:
- name: service0
port: 80
protocol: TCP
targetPort: 80
selector: # selector中不包含version信息
app: myapp
type: LoadBalancer # 类型为LoadBalancer
2、没有内置的机制来按权重分配流量,因此流量可能会随机分配到这两个版本的Pod上。要验证这一点,你可以多次访问Service并观察响应是否来自不同的版本。访问若干次,结果一半为v1的响应,一半为v2的响应。
3、通过控制台或kubectl方式调整Deployment的副本数,将v1版本调至4个副本,v2版本调至1个副本。
kubectl scale deployment/app-v1 --replicas=4
kubectl scale deployment/app-v2 --replicas=1
4、再次访问若干次,结果中v1与v2版本的响应比例与其副本数比例一致,为4:1。通过控制不同版本服务的副本数就实现了灰度发布。
步骤3:实现蓝绿发布
1、为部署的Deployment创建LoadBalancer类型的Service对外暴露服务,指定使用v1版本的服务。YAML示例如下:
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/ctyun-loadbalancer-id: 586c97da-a47c-467c-a615-bd25a20de39c # ELB实例的ID,请替换为实际取值
name: app-service
spec:
ports:
- name: service0
port: 80
protocol: TCP
targetPort: 80
selector: # selector中指定version为v1
app: myapp
version: v1
type: LoadBalancer # 类型为LoadBalancer
2、访问若干次,结果均为v1版本的响应。
3、通过控制台或kubectl方式修改Service的selector,使其选中v2版本的服务。
kubectl patch service nginx -p '{"spec":{"selector":{"version":"v2"}}}'
4、再次访问若干次,均为v2版本的响应,成功实现了蓝绿发布。