• 从一次API Gateway升级来谈单体应用走向微服务架构的转变
  • 码农007 发表于 2017/3/3 11:01:00 | 分类标签: 网站架构 架构设计 分布式
  • 大量技术团队面临将历史单体应用升级迁移到微服务的需求,HelloFresh 是一家国外投递新鲜食材的电商公司,他们最近完成了这一升级过程并分享了系列文章,本文介绍其关键的一步,升级 API Gateway。

    HelloFresh 的规模每一天都在不断增长,产品在持续改进,新的想法不断涌现,供应链也已经完全实现自动化,所有这一切都让我们惊讶。另外一方面业务的持续增长也给我们带来了许多技术挑战。

    今天给大家介绍基础架构中的一次 API Gateway 升级过程,这次改造的目的是给微服务铺路,使得我们将来可以更快、更灵活和更安全的方式快速迭代。

    技术的挑战

    我们最近完成开发了一个新的 API Gateway(网关) [1],因此面临着将老的主  API 单体应用迁移到其下层的复杂挑战,理想情况下当然是升级过程中没有停机,迁移后可以使我们能够开发更多的微服务,并容易地将它们连接到我们的基础设施中,而无需额外的代价。

    HelloFresh 的架构

    我们的 API 网关位于基础设施的上层,它每天接收大量的请求,我们在开发它时选择 Go 语言,因为它的性能、简单性和优雅的高并发解决方案。借助我们其他已有的系统或组件,使得这种改造变得简单:

    服务发现和客户端负载均衡

    我们使用 Consul 作为我们的服务注册发现工具。连同 HAProxy 一起,使我们能够解决进微服务架构的两个主要问题:服务注册发现和客户端负载均衡。

    自动化

    对于我们的基础设施,最有用的工具是我们的自动化工具。 在云环境中我们使用 Ansible 进行配置服务,从单个机器到处理网络、DNS、CI 服务等。 重要的是,我们团队达成了一个约定:当开发一个新的服务,工程师的第一件事是为这个服务创建 Ansible 脚本。

    日志和监控

    在我们的基础设施中的任何系统、组件、数据都应该被监控。我们有一些关于如何正确记录日志和监控应用程序的最佳实践:

    •对于任何给定时间点,仪表板(dashboard)都能够显示系统的状态;
    •对于记录日志,我们使用 ELK,借助它我们可以快速分析服务的详细行为;
    •对于监控,我们喜欢 Statsd + Grafana 的组合。 它非常简单易用并且足够强大;
    理解老的架构

    即使借助这些工具,我们依然面临一个难题:了解老的架构,并且尽可能平滑的升级它。在这个阶段,我们花了一些时间和精力来重构我们的老系统,以支持我们的新 API 网关和身份验证服务,这也是本文将要介绍的。

    我们遇到的一些问题:

    •虽然我们可以更改我们的移动客户端,但没法保证用户会很快更新。因此我们不得不保持向后兼容性,比如通过 DNS 来确保旧版本依然可以正常使用;

    •我们必须分析公开和私有 API 中的所有可访问地址,并自动在网关中注册它们;
    •我们必须禁用原有 API 中的身份验证,并将此职责交给新的身份验证服务;
    •我们必须确保 API 网关和微服务之间的通信安全、可靠;

    为了解决导入问题,我们使用 Go 语言写了一个脚本先来读取我们的 OpenAPI 规范(Swagger 生成的 API DOC),再为我们 原有 API 的每个资源(Restful 中的 resource)创建一个代理,同时需要包含正确的规则(如限速,配额,CORS等)。为了测试服务之间的通信,我们在一个 staging 环境中搭建了一整套基础设施,并开始执行我们的自动化测试集。我们维护了一套大型自动化功能测试集,帮助我们保障原 API 到移动客户端和 Web 客户端的服务质量。在我们确信上述事情在 staging 环境中正确工作后,我们开始考虑如何将其应用到生产环境。

    初次升级尝试

    必须承认第一次尝试(生产环境)完全是个灾难,即使我们有一个相当不错的计划:

    •将最新版本的 API 网关部署到 staging 环境
    •将最新版本的主 API 部署到 staging 环境
    •在 staging 环境执行自动化功能测试集合
    •在 staging 环境针对移动客户端和 Web 客户端执行手工测试
    •部署 API 网关到生产环境
    •部署主 API 到生产环境
    •执行自动化功能测试
    •针对移动客户端和 Web 客户端执行手工测试
    •啤酒 🍺 :)


    在 staging 环境上一切都进展顺利(至少根据我们的测试结果来看),但当我们决定上线生产环节,开始有一些问题:
    1.auth 数据库上的负载过高:我们低估了请求量,导致我们的数据库开始拒绝连接并最终不可用
    2.错误的 CORS 配置:对于一些 API 地址,CORS规则不正确,导致浏览器的请求失

    这样的情况下,我们不得不立即回滚。 幸运的是,我们的监控在一开始就捕获到授权服务在处理请求新令牌时出现的问题。

    再次尝试

    第一次部署我们没有做好充分准备,所以不得不在出现问题后进行回滚。但在再次尝试前我们改进了一些方面:

    •准备并采用蓝-绿灰度发布流程。我们创建了一个生产环境的完全副本/镜像,并部署了 API 网关,所以我们可以在必要时通过配置使此环境来服务线上请求,也可以快速的回滚。

    •收集更多的监控指标,以帮助我们正确决策应对负载所需要的机器规模。我们使用首次尝试时的数据作为预期流量的基准,并使用 Gatling 进行压力测试,以确保我们可以应对。

    •修复认证授权服务上次暴露的一些问题。包括一个愚蠢的区分大小写(case-sensitivity)问题,为 JWT 应用签名时的性能问题,以及(常规的)添加更多日志输出。

    我们花了大约一个星期来完成上面所有任务,最终我们的线上部署顺利完成,并且没有产生停机时间。在这个过程中,我们发现了一些自动化测试集没有覆盖的潜在问题,好在我们修复了它们,并且没有产生影响。

    结果

    最终,我们的整体架构如下:
    主 API

    •10+个部署在高性能 CPU 机器上的实例
    •MySQL 以 3 副本的主-从方式部署

    认证授权服务

    •4 个应用服务器实例
    •PostgreSQL 以 2 副本的主-从方式部署
    •RabbitMQ 集群用来异步用户更新请求

    API 网关

    •4 个应用服务器实例
    •MongoDB 以 4 副本的主-从方式部署

    其他

    •Ansible 用来并行执行远程任务,通常是秒级任务
    •Amazon CloudFront 作为 CDN/WAF
    •Consul+HAProxy 用于服务注册发现和请求侧/客户端负载均衡
    •Statsd+Grafana 用于监控收集、展示和告警
    •ELK 栈用于集中日志收集和处理
    •Concourse CI 作为我们的持续集成工具

  • 请您注意

    ·自觉遵守:爱国、守法、自律、真实、文明的原则

    ·尊重网上道德,遵守《全国人大常委会关于维护互联网安全的决定》及中华人民共和国其他各项有关法律法规

    ·严禁发表危害国家安全,破坏民族团结、国家宗教政策和社会稳定,含侮辱、诽谤、教唆、淫秽等内容的作品

    ·承担一切因您的行为而直接或间接导致的民事或刑事法律责任

    ·您在编程中国社区新闻评论发表的作品,本网站有权在网站内保留、转载、引用或者删除

    ·参与本评论即表明您已经阅读并接受上述条款

  • 感谢本文作者
  • 作者头像
  • 昵称:码农007
  • 加入时间:2014/6/1 14:39:00
  • TA的签名
  • 这家伙很懒,虾米都没写
  • +进入TA的空间
  • 以下内容也很赞哦
分享按钮