🌹🌹 如果您觉得我的文章对您有帮助的话,记得在 GitHub 上 star 一波哈 🌹🌹
0 酷划商业平台容器化历程
本文会先介绍酷划商业平台容器化前的主链路业务架构及技术架构,然后根据现有的架构,设计具体的容器化实施方案。
PS:本文不会讨论容器化本身的东西,只讨论将应用从阿里云 ECS 迁移到 k8s 中的实施方案。
1 酷划商业平台主链路业务架构及技术架构
本次迁移过程只涉及业务服务的迁移,所以只涉及具体的业务以及服务间的 RPC。不涉及持久层等其他组件和中间件。
如下图所示:
由 nginx 做负载均衡,到接入层(web 接口),下面在通过 motan 串联各个服务。
2 容器化目的
- 降低成本
- 提升产品迭代以及运维效率
- CI/CD,通过镜像持续交付,统一环境,降低因环境不同而导致的问题
- 规范产品迭代交付流程(主要规范测试流程)
3 容器化整体流程
- 全链路迁移预发布环境,并在预发布环境将全链路跑通。
- 全链路迁移到生产环境,通过 nginx 放小流量进来,观察是否有问题。
- nginx 增加放量到 50%,至此,ECS 保留 50% 的量,k8s 有 50% 的量,此时要观察是否有报错,并需要对比 ECS 和 k8s 中的服务性能差异(主要是接口延迟及资源消耗)。
- nginx 增加放量到 90%,保持最少一周(依据服务的流量变化周期而定)的观察。
- nginx 全部切到 k8s 中。
4 细节
4.1 预发布环境的迁移
预发布环境指的是上线前,测试同学可以在这个环境上做生产环境验证。
在预发布环境下,DB、Cache、MQ、Config 等跟生产环境用的是同一个,能够验证在生产环境的配置下,服务是否正常。
预发布环境只有公司内部网络环境可以访问(或挂 VPN),依据这个特点,我们优先动预发环境也不会对线上产生影响。
预发布环境的迁移方案比较简单粗暴,直接在 k8s 中将全链路的预发环境部署,然后从 nginx 层直接转发过去。如果有异常再及时切回来,保证环境的可用。
大概是下面这样的部署方式(假设把上面的架构图变成一个调用时序的纵向结构图):
在 ECS 和在 k8s 中的服务同时注册到注册中心中,所以这里呈现出交叉调用的关系图。
验证通过后,直接将 ECS 中的服务下线即可。
4.2 生产环境的迁移
生产环境与预发环境不同,不能存在交叉调用的情况。因为,交叉调用的情况下,在大流量的情况下,万一有异常,不能很好的控制,另外,也不好观察 ECS 和 k8s 的性能差异。
所以,我们需要将流量完全切开。即,流量从 nginx 进来后,根据比例进入到 ECS 环境和 k8s 环境中。
也就是下图这个样子:
流量如何切分?
这需要先了解 motan 的服务注册发现机制。在 motan 中,使用 group 来标识一个服务,即,如果服务提供方的 group 相同,即可注册为一个服务组,调用方使用这个 group 就能调用这个服务。
这个 group 其实是一个配置。目前这个 group 的配置是在 Apollo 中管理的。PS:这里只讨论 group,motan 还有其他的相关配置,这里不讨论。
由于 ecs 和 k8s 用的注册中心是同一个,如果某一个 k8s 的服务上线后,就会注册到注册中心,此时 ecs 的流量也会访问进来。
如何避免这种情况呢?
我们基于 ecs 和 k8s 的 IP 的特点,以及配置中心(Apollo)的灰度功能,得出一套解决方案。
核心思路: 利用 Apollo 的灰度功能,将 group 拆分为主版本配置和灰度配置,指定 IP 的机器走灰度配置,其他机器走主配置。例如,我们现在需要将 group 拆分为"service-ecs"和"service-k8s",ECS 的机器统一走 service-ecs 这个 group 服务发现,k8s 的统一走 service-k8s 服务发现,这样就将两个环境的流量完全拆开了。
现在的一个问题是,如何能让 ecs 和 k8s 走到指定的配置中(主版本 or 灰度)。
首先,Apollo 可以指定多个机器 IP 来走灰度配置,但这些 IP 必须是固定不能变的。然后考虑 k8s 和 ecs 的 IP 特点,ECS 每次重启 IP 都不会改变,但 k8s 的 pod 每次重启 IP 都会变化,所以我们必须将 ecs 的 IP 配置到灰度中,然后灰度的 group 配置为 service-ecs,主版本的 group 配置为 service-k8s,这样一来,容器中的 IP 不管怎么变化,都会走到主版本配置的 service-k8s 中,而 ECS 也肯定会走到 service-ecs 中了。
下面来看下 Apollo 中具体的配置方式:
假设我们要提供 ad-common 这个服务,ecs 的服务要走到 ad-common 这个 group 中,k8s 要走到 ad-common-k8s 这个 group 中。
Apollo 中主版本的配置:
下面是灰度版本配置:
还有灰度版本的指定 IP 配置:
最后,调用方的配置,同理,有主版本和灰度两个版本的配置。灰度版本的配置为 ad-common,主版本的配置为 ad-common-k8s,这样一来 k8s 中的调用方就肯定能走到 ad-common-k8s 中,而 ecs 的调用方,由于命中了灰度,所以肯定会走到 ad-common 中。
至此完成流量的切分。然后要做的就是逐渐从 nginx 控制放量,观察 bug 以及性能问题等。
4.3 全量切生产环境
基于上述方案,切全量是否可以直接把 nginx 流量全部切到 k8s 里就好了?答案是不行的。
为啥呢?因为 k8s 中的 motan 的 group 用的是 ad-common-k8s,有很多服务可能还被其他业务线调用,而其他业务线调用的依然是 ad-common,如果直接全量切到 k8s,并将 ecs 全部下线,其他业务线就挂了。
所以我们现在要想办法将 k8s 中的服务的 group 改回 ad-common。
咋改呢?首先将流量全部切回 ECS,保证 k8s 中没有流量。
然后自底向上开始切(基于刚才那个从上到下的调用时序图)。
操作配置:将灰度版本删除,将主版本的 group 改为 ad-common。
目前正在运行的 ECS,注册的 group 是 ad-common,灰度删除后,即使重启,也会命中到主版本的 ad-common 上,保持不变,所以 ECS 是没问题的。
k8s 中,重启后,同样走到主版本的 ad-common 上。
这样一来,ECS 和 k8s 就同时在 ad-common 上提供服务了。
然后按照这个方案,继续向上操作即可。
切完 ad-common 后,服务状态如图所示:
总结
首先,在生产中,小流量接入的测试是很有必要的。在这个阶段,我们遇到并解决了一些在 ECS 中遇不到的问题。
其次,Ecs 和 k8s 等分流量的测试也是很有必要的,这可以较好的对比两个环境的性能差异。本次容器化过程中遇到了如下一些问题:
-
所有接口响应延迟整体变慢。
- 经验证,与阿里云可用区有关,同可用区下的服务之间访问延迟要远远优于跨可用区情况。
- 其次,与流量进入 k8s 和 ecs 走过的路径不同有关。下面是 ecs 和 k8s 流量路径对比:
-
服务启动瞬间,接口响应延迟飙升,最高可达 10S。
- 首先定位到是阿里的 Sentinal 影响,此服务在应用启动时同步获取配置,而且响应比较慢,导致进来的流量全部 hang 住。
解决方案是,等 Sentinal 初始化完成,再接入流量。 - 预热:包括各种连接池的预热(Redis、Motan、MQ、DB、Tomcat 等),以及代码预热(JIT)。经验证,对于我们这种重度依赖 Redis 的服务来说,预热 Redis 连接池收益是很大的。
- 首先定位到是阿里的 Sentinal 影响,此服务在应用启动时同步获取配置,而且响应比较慢,导致进来的流量全部 hang 住。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于