SendGrid 是一个基于云的电子邮件服务,它的后端架构从小型 Postfix 演变为托管在自有数据中心以及公共云上的系统。使用 Go 语言重写服务,逐步迁移到 AWS,使用基于 Ceph 的分布式队列,团队因此每月可以处理超过 400 亿封电子邮件。
SendGrid 的初始架构由边缘节点组成,这些边缘节点通过 HTTP 和 SMTP API 摄取用户请求,并将它们发送到基于磁盘的队列中。这些请求在到达实际发送电子邮件的邮件传输代理(MTA)之前,会先经过一系列处理器。这种架构的潜在缺点——处理延迟以及由于节点故障导致的数据丢失——让工程团队转向使用由分布式文件系统提供支持的基于拉的模型。随着 AWS 基础设施的采用,他们更多地使用 AWS 的托管服务。InfoQ 采访了 SendGrid 首席软件工程师 Seth Ammons,了解到更多的信息。
他们在演化系统方面做了很多工作,包括使用 Go 语言和 Python 重写了很多系统、迁移到 AWS 托管服务、使用分布式队列,并采用日志聚合和监控系统来实现可见性。分布式队列系统是一种由 Ceph 提供支持的定制解决方案。 Ceph 是一个开源的存储平台,通过复制实现内置的容错机制。Ammons 表示,SendGrid 目前在自有数据中心运行 Ceph,他们“正处于评估这些 AWS 工作负载的早期阶段”,因此将在未来面临 HA 方面的挑战。
SendGrid 的监控系统由 Graphite (作为指标收集引擎)和 Grafana (作为查看指标的前端)组成。他们使用工具将它们推送到 Kafka 和 Splunk 来实现日志聚合。Pagerduty 负责基于来自 Splunk Alerts 和 Sensu 检查的事件发送警报。Graphite 存在规模问题,那么 SendGrid 是否面临伸缩性问题?Ammons 说:
我们遇到了一些与伸缩 Graphite 有关的问题。我们首先想到的是调整保留策略,并确保能够正确地聚合指标。我们采取的其他措施包括用更高性能的 carbon-c-relay 替换 carbon_relay。度量指标通过 carbon-c-relay 服务器池前面的负载均衡器进入管道,这些服务器可以在后端存储出现问题的情况下将指标保存在内存中。我们还在后端存储服务器池中复制指标并对它们进行分片。
SendGrid 在测试方面面临的挑战随着架构演变而加剧。因为变更导致的生产中断可能会导致电子邮件丢失,因此团队的目标是提高单元测试和集成测试的全面性。由于电子邮件可传递性是成功的关键指标,SendGrid 的系统集成测试会验证端到端的可传递性。由于存在大量的电子邮件客户端,而且每个客户端都有许多不断变化的版本,因此保持测试更新是一项巨大的挑战。Ammons 阐述了他们解决这个问题的策略:
我的团队专注于 SMTP 状态码,我们对错误代码和错误消息文本进行了扩展。由于这些响应会随着时间的推移而发生变化,因此我们成立了一个可交付性顾问团队,以确保我们能够以最有效的方式处理电子邮件。该团队密切监控主要收件箱的响应趋势,并维护了一个响应表,用于映射处理这些响应的规则。在处理来自收件箱的响应时,MTA 代码将引用这个表。
他们的单元集成测试使用了 Docker。开发环境也使用了 Docker,其中 Docker 的 compose 文件被用于管理所需的整个容器集和依赖项。然而,该团队“目前处于容器采用的过渡状态”,Ammons 说:
对于遗留应用程序,我们在开发和 CI 过程中使用了 Docker。在大多数情况下,我们的 CI 系统会基于容器化的环境生成工件,这些工件可用作要部署的 RPM。我们在自己的数据中心运行新服务,并开始使用 Kubernetes 作为容器编配器。声明式的配置更为简单,一些团队已开始利用 Helm 来管理在数据中心或环境之间的配置变化。我们正处于评估如何在 AWS 中解决这些问题的早期阶段。
大量的应用程序指标和日志记录以及基于这两者的警报减轻了 dev 和 prod 之间的差异所带来的挑战。对于配置管理,SendGrid 的遗留代码使用由 Chef 管理的环境变量——团队已经使用了很长时间——以及他们的配置库。代码部署通常需要经过审查、签名、合并到 Github 的过程,然后将二进制文件推送到他们的仓库服务器,然后再推送到生产环境。
评论