酷划商业平台容器化历程

本贴最后更新于 1677 天前,其中的信息可能已经时移俗易

🌹🌹 如果您觉得我的文章对您有帮助的话,记得在 GitHub 上 star 一波哈 🌹🌹

🌹🌹GitHub_awesome-it-blog 🌹🌹


0 酷划商业平台容器化历程

本文会先介绍酷划商业平台容器化前的主链路业务架构及技术架构,然后根据现有的架构,设计具体的容器化实施方案。

PS:本文不会讨论容器化本身的东西,只讨论将应用从阿里云 ECS 迁移到 k8s 中的实施方案。

1 酷划商业平台主链路业务架构及技术架构

本次迁移过程只涉及业务服务的迁移,所以只涉及具体的业务以及服务间的 RPC。不涉及持久层等其他组件和中间件。

如下图所示:

酷划商业平台架构.png

由 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 层直接转发过去。如果有异常再及时切回来,保证环境的可用。

大概是下面这样的部署方式(假设把上面的架构图变成一个调用时序的纵向结构图):

预发布环境方案.png

在 ECS 和在 k8s 中的服务同时注册到注册中心中,所以这里呈现出交叉调用的关系图。

验证通过后,直接将 ECS 中的服务下线即可。

4.2 生产环境的迁移

生产环境与预发环境不同,不能存在交叉调用的情况。因为,交叉调用的情况下,在大流量的情况下,万一有异常,不能很好的控制,另外,也不好观察 ECS 和 k8s 的性能差异。

所以,我们需要将流量完全切开。即,流量从 nginx 进来后,根据比例进入到 ECS 环境和 k8s 环境中。

也就是下图这个样子:

容器化生产环境流量切分.png

流量如何切分?

这需要先了解 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 中主版本的配置:

容器化 Apollo 主版本配置.png

下面是灰度版本配置:

容器化 Apollo 灰度版本配置.png

还有灰度版本的指定 IP 配置:

容器化 Apollo 灰度 IP 配置.png

最后,调用方的配置,同理,有主版本和灰度两个版本的配置。灰度版本的配置为 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 后,服务状态如图所示:

容器化全量上线.png

总结

首先,在生产中,小流量接入的测试是很有必要的。在这个阶段,我们遇到并解决了一些在 ECS 中遇不到的问题。

其次,Ecs 和 k8s 等分流量的测试也是很有必要的,这可以较好的对比两个环境的性能差异。本次容器化过程中遇到了如下一些问题:

  • 所有接口响应延迟整体变慢。

    • 经验证,与阿里云可用区有关,同可用区下的服务之间访问延迟要远远优于跨可用区情况。
    • 其次,与流量进入 k8s 和 ecs 走过的路径不同有关。下面是 ecs 和 k8s 流量路径对比:
      容器化前后流量路径对比.png
  • 服务启动瞬间,接口响应延迟飙升,最高可达 10S。

    • 首先定位到是阿里的 Sentinal 影响,此服务在应用启动时同步获取配置,而且响应比较慢,导致进来的流量全部 hang 住。
      解决方案是,等 Sentinal 初始化完成,再接入流量。
    • 预热:包括各种连接池的预热(Redis、Motan、MQ、DB、Tomcat 等),以及代码预热(JIT)。经验证,对于我们这种重度依赖 Redis 的服务来说,预热 Redis 连接池收益是很大的。
  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    108 引用 • 54 回帖 • 1 关注
  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    472 引用 • 892 回帖 • 1 关注
  • 容器化
    2 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...