Kubernetes实战指南(三十一):零宕机无缝迁移Spring Cloud至k8s

1. 项目迁移背景

1.1 为何要在“太岁”上动土?

目前公司的测试环境、UAT环境、生产环境均已经使用k8s进行维护管理,大部分项目均已完成容器化,而且已经在线上平稳运行许久。在咱们将大大小小的项目完成容器化之后,测试、UAT、生产环境的发版工具以及CICD流程慢慢的实现统一化管理,而且基于k8s开发了内部的发版审核平台,同时接入了Jira等项目管理工具。 前端

在自研平台进行发版时,可以自动关联项目的开发进度以及Release版本,最重要的是其能够控制发版权限、统一发版工具及发版模式,而且支持一键式发版多个项目的多个模块,同时也包括了发版失败应用的统一回滚及单个应用的回滚。 安全

由于该项目从始至今一直在使用GitRunner进行发版,而且基于虚机部署,因此一直没有集成到发版审核平台,可是因为项目比较重要,而且涉及的服务和机器较多,因此必需要把这个项目进行容器化而且统一发版工具才能更好的适应公司的环境,以及更好的应对下一代云计算的发展。服务器

1.2 为何要弃用Git Runner?

首先咱们看一下Git Runner发版的页面,虽然看起来很简洁清爽,可是也不免不了会遇到一些问题。
在这里插入图片描述网络

1.2.1 多分支并行开发问题

当多分支并行开发或者可以发版到生产环境的分支较多时,很容易在手动部署的阶段点错,或者看串行,固然这种几率很小。并且在一个项目的微服务拆分红多个Git的Project时,发版时须要先找到对应的Project,而后在找到对应的发版分支,而后再点击发版,若是有二十个微服务须要发版,那么你须要重复二十次这个流程。 架构

咱们还能够看到另一个问题,每次提交或者合并,都会触发构建,当咱们使用Git Flow分支流时,可能同时有不少分支都在并行开发、并行测试、并行构建,若是Git Runner是基于虚机建立的,颇有可能会出现构建排队的状况,固然这个排队的问题,也是能解决的。less

1.2.2 多微服务配置维护问题

其次,若是一个项目稍微大一些,维护起来也不是很方便。好比这个准备要迁移的项目,一个前端和二十多个业务应用,在加上Zuul、ConfigServer、Eureka将近三十个服务,每一个服务对应一个Git仓库,而后每一个服务同时在开发的分支又有不少,若是想要升级GitLab CI脚本或者微服务的机器想要添加节点,这将是一个枯燥乏味的工做。运维

1.2.3 安全问题

最后,还有一个安全的问题,GitLab的CI脚本通常都是内置在代码仓库里面的,这就意味着任何有Push或者Merge权限的人均可以随意的修改CI脚本,这会致使意想不到的结果,同时也会威胁到服务器和业务安全,
针对发版而言,可能任何的开发者均可以点击发版按钮,这些可能一直都是一个安全隐患。 tcp

可是这些并不意味着Git Runner是一个不被推荐的工具,新版的GitLab内置的Auto DevOps和集成Kubernetes依旧很香。可是可能对于咱们而言,使用Git Runner进行发版的项目并很少,因此咱们想要统一发版工具、统一管理CI脚本,因此可能其它的CI工具更为合适。ide

1.3 为何要容器化?

1.3.1 端口冲突问题

容器化以前这个项目采用虚机部署的,每一个虚拟机交叉的启动了两个或者三个微服务,这会遇到一个问题,就是端口冲突的问题,在项目加入新应用时,须要考虑服务器之间端口冲突问题的,还要考虑每一个微服务的端口不能同样,由于使用虚拟机部署应用时,可能会有机器节点故障须要手动迁移应用的状况,若是部分微服务端口同样,迁移的过程可能会受阻。 微服务

另外,当一个项目只有几个应用时,端口维护起来可能没有什么问题,像本项目,涉及三十多个微服务,这就会成为一件很痛苦的事情。而使用容器部署时,每一个容器相互隔离,全部应用能够采用一样的端口,就无需再去关心端口的问题。

1.3.2 程序健康问题

使用过Java程序的人大部分都遇到过程序假死的状况,好比端口明明是通的,可是请求就是不处理,这就是一种程序假死的现象。而咱们在使用虚机部署时,每每不能把健康检查作的很好,或许在虚机上面并无作接口级的健康检查,这就会形成程序假死没法自动处理的问题,而且在虚机上面作一些接口级的健康检查及处理操做并非一件简单的事情,一样也是一件枯燥乏味的事情,尤为是当一个项目微服务过多,健康检查接口不一致时更为痛苦。

但在k8s上面,自带的Read和Live探针用以处理上面的问题就极其简单,如图所示,咱们能够看到目前支持三种方式的健康检查:
healthCheck

  • tcpSocket: 端口健康检查
  • exec: 根据指定命令的返回值
  • httpGet: 接口级健康检查

同时这些健康检查的灵活性也很高,能够自定义检查间隔、错误次数、成功次数、检查Host等参数,并且上面提到的接口级健康检查httpGet也支持自定义主机名、请求头、检查路径以及HTTP或者HTTPS等配置,能够看到用k8s自带的健康检查能够省去咱们很大一部分工做,不用再去维护很是多使人讨厌的脚本。

1.3.3 故障恢复问题

在使用虚机部署应用时,有时可能会碰到宿主机故障,单节点的应用没法使用,或者多节点部署的应用因为其余副本不可用,致使自身压力大出现服务延迟的状况。而偏偏宿主机没法很快恢复,这时可能就须要手动添加节点或者须要新加服务器才能解决这类问题,这个过程可能会很漫长,或许也很痛苦。由于须要去准备依赖环境,而后才能去部署本身的应用,而且有时候你可能还须要更改CI脚本。。。

而使用k8s编排时,咱们无需关心这类问题,一切的故障恢复、容灾机制都由强大的k8s负责,你能够去喝杯咖啡,或者你刚打开电脑去处理这个问题时,一切都已经恢复如初。

1.3.4 其余小问题

固然k8s给咱们带来的便利性和解决的问题远不止上面所说的,容器镜像帮咱们解决了依赖环境的问题,服务编排帮咱们解决了故障容灾的问题,咱们可使用k8s的包管理工具一键建立一套新的环境,咱们可使用k8s的服务发现让开发人员无需再关注网络部分的开发,咱们可使用k8s的权限控制让运维人员无需再去管理每台服务器的权限,咱们可使用k8s强大的应用程序发布策略让咱们无需过多的考虑如何实现零宕机发布应用及应用回滚,等等,这一切的便利性正在悄悄的改变着咱们的行为。

2. 迁移计划

2.1 蓝绿迁移

首先来看一下迁移以前的架构
vmStart
和大多数SpringCloud架构同样,使用NodeJS做为前端,Eureka用做服务发现,Zuul进行路由分发,ConfigServer做为配置中心。这种架构也是SpringCloud在企业中最广泛的架构,没有使用更多额外的组件,因此咱们在第一次迁移时,也没有考虑太多,仍是按照迁移其余项目时用的方案,即在k8s上新建一套环境(本次迁移没有涉及到中间件),也就是容器化环境,配置一个一样的域名,而后添加hosts解析进行测试,没有问题的话直接进行域名切换便可完成迁移。这种方式是最简单也是最经常使用的方式,相似于程序发版的蓝绿部署,此时在k8s新建一套环境对应的架构图以下:
blueGreen
在进行测试时,此项目同时并行了两套环境,一套虚机环境,一套容器环境,容器环境只接收测试人员的流量,两套环境链接的是同一套中间件服务,由于其余项目大部分也是按照这种方式迁移的,而且该项目在测试环境也进行过一样的流程,没有出现什么问题,因此也一样认为这种方式在本项目也不会出现什么问题。但每每现实总会与预期有所差别,在测试过程当中因为两套环境并存,致使了部分生产数据出现问题,因为容器环境没有通过完整性测试,也没有强制切换域名,后来紧急关停了全部的容器问题才得以恢复。因为时间比较紧迫,咱们并无仔细排查问题所在,只是修复了部分数据,后来咱们认为多是迁移过程当中部分微服务master分支和生产代码不一致形成的,固然也可能并非这么简单。为了规避这类问题再次发生只能去修改迁移方案。

2.2 灰度迁移

因为上面的迁移方案出了点问题,就从新定了一个方案,较上次略微麻烦,采用逐个微服务迁移至k8s,相似于应用程序发版的灰度发布。

单个应用迁移时,须要确保容器环境和虚机环境的代码一致,在迁移时微服务采用域名注册的方式。也就是每一个微服务都配置一个内部域名,经过域名去注册到Eureka,而不是采用容器的IP和端口去注册(由于k8s内部的IP和虚拟机未打通),此时的环境以下图所示:
canary
此时有一个域名service-c.interservice.k8s指向ServiceC,而后ServiceC注册到Eureka时修改本身的地址为该域名(默认是宿主机IP+端口),以后别的应用经过该地址调用ServiceC,当ServiceC测试无问题后,下线虚拟机里面的ServiceC,最后的架构如图所示:
canaryAll
除了Zuul、前端UI和Eureka,其余服务都使用灰度的方式迁移到k8s,比蓝绿的形式更为复杂,须要为每一个微服务单首创建Service、域名,在迁移完成以后还须要删除。到这一步后,除了Eureka其余服务都已经部署在k8s上,而对于Eureka的迁移,涉及的细节更多。

2.3 Eureka迁移

到这一步后,服务访问没有出现其余问题,除了Eureka以外的服务,都已经部署在k8s,而Eureka的过分型迁移设计的问题可能会更多。由于咱们不能直接在k8s上部署一套高可用的Eureka集群,而后直接把ConfigServer里面的微服务注册地址改为k8s中的Eureka地址,由于此时两个Eureka集群都是独立的Zone,注册信息并不会共享,这种会在更改配置的过程当中丢失注册信息,此时架构图可能会出现以下状况:
eureka
也就是在替换配置的过程当中,可能会有ServiceA注册到了以前的Eureka上,ServiceB注册到了k8s中的Eureka,就会致使ServiceA找不到ServiceB,反过来也是一样的问题。

因此在k8s搭建Eureka的集群后,须要给每一个Eureka实例配置一个临时域名,而后更改以前的Eureka集群和k8s里面的Eureka集群的zone配置,让k8s里面的Eureka和虚机里面的Eureka组成一个新的集群,这样注册信息就会被同步,不管注册到Eureka都不会形成服务找不到,此时的架构图以下(此时全部的服务仍是注册到原来的Eureka集群中):
eurekaVMK8s
接下来须要作的事情,就是更改微服务的配置,此时须要更改地方有三处:

  1. 微服务注册到Eureka的地址更为容器IP和端口,再也不使用域名注册,由于此时微服务都已经在k8s中,直接经过内部Pod IP便可链接;
  2. 更改服务注册的Eureka地址为k8s Eureka的service地址,Eureka使用StatefulSet部署,直接经过eureka-0/1/2.eureka-headless-svc就能够链接;
  3. 待全部的微服务都已经迁移完毕后,更改k8s的Eureka集群的zone为:eureka-0/1/2.eureka-headless-svc,并删除其余微服务的Service和域名。

最终的架构图如图:
end

3. 总结

为了保证服务的可用性,咱们无奈的采用灰度的方式进行迁移,比蓝绿的方式麻烦了不少,并且须要考虑的问题也有不少。在程序没有任何问题的前提下,仍是建议采用蓝绿的方式进行迁移,不只遇到的问题少,迁移也比较方便快捷。固然采用灰度的方式对于大型的项目或者不能中断服务的项目可能更为稳妥,由于一次性所有切换可能会有遗漏的须要测试的地方。固然不管哪一种方式,对应用的容器化、迁移至Kubernetes才是比较重要的事情,毕竟云计算才是将来,而Kubernetes是云计算的将来。

广告时间
有须要学习k8s的能够关注下这个视频教程:https://edu.51cto.com/sd/518e5

相关文章
相关标签/搜索