什么是金丝雀发布
既然要聊具体的实现,那么在开始之前,先科普下什么是“金丝雀发布”。金丝雀发布也叫**“灰度发布”**,具体来说就是在发布线上版本时,先将少量的生产流量打到服务的新版本,以验证新版本的准确性和可靠性,待发布的新版本得到线上流量的全面验证后,在逐步将所有流量放入新版本,以实现生产服务版本的稳定更新。
为什么叫金丝雀发布呢,是因为金丝雀对矿场中的毒气比较敏感,所以在矿场开工前工人们会放一只金丝雀进去,以验证矿场是否存在毒气,这便是金丝雀发布名称的由来。
在不同技术栈场景中,金丝雀发布的实现方式也不尽相同:有通过nginx实现的、也有借助A/B测试实现的。而随着以Kubernetes为代表的云原生基础设施的普及,金丝雀发布作为一项基本的服务发布功能,其实现方式也有了一些新的趋势——那就是逐步与云原生基础设施融为一体,成为基础设施服务的一部分。
Kubernetes中的金丝雀(灰度)发布
接下来,先看看在Kubernetes中是如何实现版本更新的。以下内容假设你已经有了一套可用的Kubernetes环境,如果没有可以查看文末推荐阅读的文章链接,参考相关分享自行部署。
1.滚动更新
在介绍Kubernetes中的金丝雀(灰度)发布之前,先来了解下Kubernetes中最重要的应用部署方式——“滚动升级”。
所谓“滚动升级”:是指当更新了Kubernetes中Deployment编排资源的Pod模版(例如更新镜像版本号)之后,Deployment就需要遵循一种叫做**“滚动更新(rolling update)”的方式,来升级现有的容器,从而实现应用对外服务的“不中断更新部署”。**Kubernetes实现“滚动升级”的示意图如下:
如上图所示,滚动升级的过程为:
1)当容器开始升级时,集群中会先启动一个新版本的Pod,并终止一个旧版本的Pod。
2)如果此时,新版本的Pod有问题启动不了,那么“滚动升级”就会停止,并允许开发和运维人员介入。而在这个过程中,由于应用本身还有两个旧版本的Pod在线,所以服务并不会受到太大的影响。
3)而如果新版本的Pod启动成功,且服务访问正常,则继续滚动升级,直至按照Deployment编排器设置的副本数量,完成后续旧版本Pod的升级。
在Kubernetes中Deployment还可以通过相应地“滚动升级”策略,来控制Pod的滚动升级行为,以进一步保证服务的连续性。例如:“在任何时间窗口内,只有指定比例的Pod处于离线状态;在任何时间窗口内,只有指定比例的新Pod被创建出来"。可以通过相应地控制参数进行设置,如下:
...
spec:
selector:
matchLabels:
app: micro-api
replicas: 3
#设置滚动升级策略
#Kubernetes在等待设置的时间后才开始进行升级,例如5秒
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
#升级过程中最多可以比原先设置多出的Pod数量
maxSurge: 1
#升级过程中Deployment控制器最多可以删除多少个旧Pod,主要用于提供缓冲时间
maxUnavailable: 1
...
在上面RollingUpdate Strategy(滚动升级策略)的配置中:
-
maxSurge:指定的是,除了设定的Pod副本数量之外,在一次“滚动”中,Deployment控制器还可以创建多少个新的Pod。
-
maxUnavailable:指的是,在一次“滚动”中,Deployment控制器可以删除多少个旧Pod。
通过这种精确的“滚动升级”策略,可以使得Kubernetes服务版本发布的过程更加平滑。此外,这两个配置还可以通过百分比的方式来表示,比如**“maxUnavailable=50%”,**指的是Deployment控制器最多可以一次删除“50%*设定Pod副本数”个Pod。
接下来具体演示下在Kubernetes中进行服务滚动升级的详细过程。
使用的示例代码说明:
项目以Spring Boot编写的Java服务为主,在体验上更接近真实的项目开发场景。项目的结构如下:
该项目所在的GitHub地址为:
https://github.com/manongwudi/istio-micro-service-demo
“滚动升级”演示:
这里先借助示例项目中的“micro-api”服务来演示其在Kubernetes中进行“滚动升级”的过程,步骤如下:
(1)首先准备“micro-api”服务的k8s发布文件(如:micro-api.yaml)。代码如下:
apiVersion: v1
kind: Service
metadata:
name: micro-api
spec:
type: ClusterIP
ports:
- name: http
port: 19090
targetPort: 9090
selector:
app: micro-api
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: micro-api
spec:
selector:
matchLabels:
app: micro-api
replicas: 3
#设置滚动升级策略
#Kubernetes在等待设置的时间后才开始进行升级,例如5秒
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
#升级过程中最多可以比原先设置多出的Pod数量
maxSurge: 1
#升级过程中Deployment控制器最多可以删除多少个旧Pod
maxUnavailable: 1
template:
metadata:
labels:
app: micro-api
spec:
#设置的阿里云私有镜像仓库登陆信息的secret(对应2.1.2的设置)
imagePullSecrets:
- name: regcred
containers:
- name: micro-api
image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.0-SNAPSHOT
imagePullPolicy: Always
tty: true
ports:
- name: http
protocol: TCP
containerPort: 19090
上述部署文件设置了“micro-api”服务的Pod副本个数为“3”,并且设置了相应地滚动升级策略。
(2)接下来执行k8s部署命令如下:
$ kubectl apply -f micro-api.yaml
成功后,查看Deployment创建后的状态信息,命令效果如下:
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
micro-api 3/3 3 3 190d
从上述命令的返回结果中,可以看到三个状态字段,它们的含义如下所示:
-
READY:表示用户期望的Pod副本个数,以及当前处于Running状态的Pod个数。
-
UP-TO-DATE:当前处于最新版本的Pod个数。所谓最新版本,指的是Pod的Spec部分与Deployment中Pod模版里定义的完全一致。
-
AVAILABLE:当前已经可用的Pod的个数——既是Running状态,又是最新版本,并且已经处于Ready(监控检查正确)状态的Pod个数。
(3)模拟服务版本升级,触发滚动升级。
接下来重新构建“micro-api”服务的版本,并将其上传至私有镜像仓库。之后,