持续部署入门:基于 Kubernetes 实现蓝绿发布
前言
软件世界比以往任何时候都更快。为了保持竞争力,需要尽快推出新的软件版本,而不会中断活跃用户访问,影响用户体验。越来越多企业已将其应用迁移到 Kubernetes。
在 Kubernetes 中有几种不同的方式发布应用,所以为了让应用在升级期间依然平稳提供服务,选择一个正确的发布策略就非常重要了,本篇文章将讲解在 Kubernetes 使用蓝绿更新的方式更新镜像。
原理
蓝绿发布是版本 1 与版本 2 会同时存在,通过控制 Service 来决定使用具体哪一个版本,也称为红黑部署。蓝绿发布与滚动更新不同,版本 2 (绿
) 与版本 1(蓝
)一起部署,在测试新版本满足要求后,然后更新 Service 对象,通过替换 label selector 中的版本标签来将流量发送到新版本,更新过程如下图所示
实践
使用 Kubernetes 原生方式升级应用
准备
image
bebullish/demo:v1bebullish/demo:v2
deployment-v1
apiVersion: apps/v1kind: Deploymentmetadata: name: demo-dp-v1spec: selector: matchLabels: app: demo version: v1 replicas: 3 template: metadata: labels: app: demo version: v1 spec: containers: - name: demo image: bebullish/demo:v1 ports: - containerPort: 8080
deployment-v2
apiVersion: apps/v1kind: Deploymentmetadata: name: demo-dp-v2spec: selector: matchLabels: app: demo version: v2 replicas: 3 template: metadata: labels: app: demo version: v2 spec: containers: - name: demo image: bebullish/demo:v2 ports: - containerPort: 8080
service
apiVersion: v1kind: Servicemetadata: name: demo-servicespec: selector: app: demo version: v1 # 通过更改 version 来控制流量走向 type: LoadBalancer ports: - port: 80 targetPort: 8080 protocol: TCP
将上述 deployment-v1
以及 service
保存为 yaml 文件,使用 kubectl apply -f
命令创建 yaml 资源,等待创建成功后,使用 kubectl get svc
获取 EXTERNAL-IP。
测试
如果使用浏览器测试的话,你会发现每次调用都会返回同一个 pod 的名字,那是因为浏览器发出的请求包含 keepAlive,所以需要使用 curl 来保证每次发出的请求都是重新创建的。
curl -X GET http://${EXTERNAL-IP}
升级
将上述 deployment-v2
保存为 yaml 文件,使用 kubectl apply -f
命令创建 yaml 资源,切换流量之前先执行命令,以便查看镜像更新过程
while true; do curl -X GET http://${EXTERNAL-IP} ; done
等待 deployment-v2
创建成功后,通过将 service 的 version 值改为 v2 来切换流量
kubectl edit service demo-service
查看日志
请求流量
结论
首先可以发现在更新过程中,程序保持一直可用的状态,v2 版本部署成功之后,所有请求还是 v1 版本,当流量切换后,立刻出现 v2 版本的日志,并且不会出现 v1 版本的日志,说明流量是一次性切换的,如果需要回滚只需要将流量切回 v1 版本即可。
使用 CODING CD 方式升级应用
创建服务
service
apiVersion: v1kind: Servicemetadata: name: demo-servicespec: selector: createBy: demo-service # 这里填写的标签,会被添加到对应的 ReplicaSet 中 type: LoadBalancer ports: - port: 80 targetPort: 8080 protocol: TCP
这里注意,service 创建之后应不会匹配到任何资源,即 endpoint 为空,而在后面执行部署流程时会为 ReplicaSet 添加 label createBy: demo-service
,从而决定流量走向。
部署成功之后可以看到 demo-service
配置制品
使用 docker 官方镜像需要以 docker.io
开头
配置 yaml 及绑定制品
replicaSet
apiVersion: apps/v1kind: ReplicaSetmetadata: name: demo-rsspec: replicas: 3 selector: matchLabels: app: demo template: metadata: labels: app: demo spec: containers: - image: docker.io/bebullish/demo name: demo ports: - containerPort: 8080
阶段中选择 部署(Manifest)
,输入上述 yaml 文件(目前发布策略选项仅支持 ReplicaSet),这里需要把镜像的版本删除掉,在需要绑定的制品选择之前配置的制品。这样配置之后,每次执行的时候版本是动态传入的。
蓝绿(红黑)发布配置
在下方勾选让 CODING 部署控制台管理入口流量,然后选择 demo-service
所在的命名空间(我这里是在 marlon
这个命名空间下),然后选择 demo-service
,策略选择 Red/Black(Blue/Green),保存即可。
发布制品
选择应用和部署流程,输入版本 v1。
查看结果
等待一小段时间后,就可以在部署控制台中看到发布的资源了。
更新镜像版本
再次执行发布,版本输入 v2。
更新原理
基于 CODING CD 的蓝绿发布和一般的蓝绿发布略有不同,一旦 v2 版本的 pod 处于就绪状态后,他就会立即获得流量,而当所有的 v2 版本的 pod 处于就绪状态后,会禁用 v1 版本的 pod,此时所有流量会打到 v2 版本上,从而完成更新。
注意:基于 CODING CD 的蓝绿发布会出现 v1 版本和 v2 版本同时获得流量的情况,具体取决于 pod 的就绪探针,v2 版本的 pod 一旦就绪,那么它就会获得流量,所以需要合理设计就绪探针,尽量减少 v1 版本和 v2 版本同时存在的时间差。
总结
使用 Kubernetes 原生方式实现蓝绿更新步骤较多,但也容易出错,推荐使用 coding.net 提供的 CD 功能,配置一次,永久使用。不仅降低了人工成本,提高容错率,还提供了非常丰富的 CD 功能,推荐使用哦~
参考文章
TrafficManagement:https://spinnaker.io/guides/user/kubernetes-v2/traffic-management/
RolloutStrategies:https://spinnaker.io/guides/user/kubernetes-v2/rollout-strategies/
CODING 持续部署:https://help.coding.net/docs/cd/overview.html
- 利用Spring中同名Bean相互覆盖的特性,定制平台的类内容。
- 关于java传参
- 浅析ButterKnife
- SQL Server常用语句
- pyecharts(一):Python可视化利器
- 如何在Python中保存ARIMA时间序列预测模型
- SVN+Apache域用户认证配置方法_Windows(转,重新排版,部分内容更新优化)
- sum(x) over( partition by y ORDER BY z ) 分析
- Android DataBinding 数据绑定
- Flask拾遗笔记之上下文
- switch.....case....使用最容易犯错的地方
- electron 构建跨平台桌面应用
- 两个关于字符串的经典例子
- Android Wear 开发初探
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- SpringBoot对全局异常的处理封装
- 自定义springboot-starter揭秘自动配置骚操作
- 【大厂面试题】Redis中是如何实现分布式锁的?
- 最近公司招人,研发组商量了下,暂时定下这么多java面试题!
- 市面上数据库种类那么多,如何选择?
- 玩转正则!推荐一个速查、调试、验证、可视化工具
- 当一个http请求来临时,SpringMVC究竟偷偷帮你做了什么?
- Js实现文本复制
- 当一个http请求来临时,SpringMVC究竟偷偷帮你做了什么?处理器映射器与处理器篇
- anetTcpGenericConnect 详解
- 详解 MySQL 基准测试和sysbench工具
- 第六天:网络处理(anet部分)-- redis源码慢慢学,慢慢看【redis6.0.6】
- python爬王者荣耀壁纸
- 搞定三大神器之 Python 装饰器
- 当一个http请求来临时,SpringMVC究竟偷偷帮你做了什么?请求映射器篇