Ruby 又拍云 Redis 的改进之路

upyun · 2022年06月29日 · 229 次阅读

作为推出国内首创可编程 CDN 服务的专业云服务提供商,又拍云利用 CDN 边缘网络规模和性能,允许客户自定义编写规则来满足常用业务场景。而为了保证这些源数据,如边缘重定向、请求限速、自定义错误页面、访问防盗链控制、 HTTP 头部管理等,能快速同步到边缘的节点服务器,在对比了多个方案以后,又拍云于 2014 年初开始使用 Redis2.8 版本作为数据同步的解决方案。

最初的架构如下:

在继续谈 Redis 改进前,我们要先了解一下技术债。这里说的技术债指的是技术负债,通常开发人员为了加速软件开发,在应该采用最佳方案时可能进行妥协,改用短期内能加速软件开发的方案。而这种方案在未来给自己带去了额外开发负担。这种虽然眼前看起来可以得到好处,但必须在未来偿还的选择,就像债务一样,所以被叫做技术债。

而我们上面说到的这个方案就埋下了技术债的引子。在过去的几年里它虽然起到重要的作用,但架构的缺点明显,而且随着边缘服务器数量和同步数据量的增加,再加上服务器硬件的老化故障等等原因,造成了很多问题,比如如下问题:

  • 出于安全考虑,相互 Redis 之间的通信数据都需要加密,但 Redis 本身不支持 SSL 加密。因此所有的边缘服务器都必须通过 stunnel 套接做中转服务器。然而实际工作状态下,stunnel 的性能不足,导致服务器 CPU 负载过高。

  • Redis 的数据主从都是长连接且尽量保持从同一源做同步,因此早期边缘服务器都是通过域名解析的方式来获取源服务器的 IP 地址。这样的好处是实施部署简单,缺点是 DNS 无法获知后端服务器的处理能力,造成每台机器上的长连接负载不均衡。而且后端服务出故障后 DNS 也无法自动处理, 即便及时对 DNS 进行了切换解析,也会因为 TTL 生效前的真空期引起数据不一样, 导致只能使用旧数据应急。

  • 因为历史遗留原因, 边缘 Redis 版本大都是 2.x 低版本,而低版本只能通过 sync 做全量同步。因此中转服务器和主服务器的异常都会造成全网的雪崩效应,从而同步阻塞,无法快速同步元数据到边缘。

  • 因为早期 Redis 只有主从模式可以采用,也没有实现哨兵和集群改造。所以让如今主服务器成为了单点风险,很容易造成源头上的重大故障。

因为之前妥协导致的问题和副作用,以至于我们现在必须要付出额外的时间和精力进行重构,把架构改善为最佳实现方式。

我们把改造过程分成几个步骤:

加强 SSL 的安全防护,尽可能升级到 OpenSSL 最新的稳定版本

SSL 可能是大家接触比较多的互联网安全协议之一,一般网站地址用了 “https://” 开头,就是采用了 SSL 安全协议。OpenSSL 是一种开放源码的 SSL 实现,用来实现网络通信的高强度加密,现在被广泛地用于各种网络应用程序中。如此重要的项目多年来始终面临着资金和人手不足的窘境,多数工作都要由为数不多的黑客和爱好者及志愿者来完成。幸好现在纳入 Linux 基金会资金资助对象,不过依然有新漏洞不断暴露,需要及时关注和跟进。

参考最新的 OpenSSL 漏洞危险等级报告:

鉴于 RC4 算法安全漏洞太多,建议编译时选择禁用。

使用最新的 stunnel 版本,优化性能,基于安全 OpenSSL 依赖库,支持 TLSv1.2+ 以上

从下图的红色框中可以看出,stunnel 在某些算法下的性能是最强的,所以在配置文件中推荐优先使用:

./configure --prefix=/opt/stunnel --with-ssl=/opt/openssl

来看一下推荐配置中的优化选项:

verify = 3
sslVersionMax = TLSv1.3
sslVersionMin = TLSv1.2
options = NO_SSLv2
options = NO_SSLv3
.......
ciphers = ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE

可以通过亚洲诚信的网站来做 HTTPS 的可信等级检测和验证。

编译最新的 Redis-6.2.x 稳定版,功能强大丰富且无需依赖高版本的 GCC

Redis6.2 与 7.0 对比来看的话,肯定是 7.0 版本更为强大一点。Redis7.0 几乎包括了对各个方面的增量改进,其中最值得注意的是 Redis Functions、ACLv2、command introspection 和 Sharded Pub/Sub。7.0 版添加了近 50 个新命令和选项来支持这种演变并扩展 Redis 的现有功能。

但是尽管 Redis7.0 更加强大,可是综合考虑到与原来的 Redis 代码的完全兼容性,以及生产环境的稳定,我们最终选择了 Redis6.2。因为 Redis6.2 的优点也足够多,功能也很强大,而且更能满足我们生产环境的要求,比如:

  • 多线程 IO(Threaded I/O)

  • 众多新模块(modules)API

  • 更好的过期循环(expire cycle)

  • 支持 SSL

  • ACLs 权限控制

  • RESP3 协议

  • 客户端缓存(Client side caching)

  • 无盘复制&PSYNC2

  • Redis-benchmark 支持集群

  • Redis-cli 优化、重写 Systemd 支持

  • Redis 集群代理与 Redis6 一同发布(但在不同的 repo)

  • RDB 更快加载

  • SRANDMEMBER 和类似的命令具有更好的分布

  • STRALGO 命令

  • 带有超时的 Redis 命令更易用

重点介绍一下 PSYNC2 的特性,这也是我们架构改进升级的重点特性之一。

在 Redis cluster 的实际生产运营中,实例的维护性重启、主实例的故障切换(如 cluster failover)操作都是比较常见的 (如实例升级、rename command 和释放实例内存碎片等)。而在 Redis4.0 版本前,这类维护性的处理 Redis 都会发生全量重新同步,导到性能敏感的服务有少量受损。而 PSYNC2 主要让 Redis 在从实例重启和主实例故障切换场景下,也能使用部分重新同步。

直接下载源代码编译:

# make BUILD_TLS=no

推荐配置,添加以下选项增强性能:

io-threads-do-reads yes
io-threads 8
aof-use-rdb-preamble yes

在我们的测试过程中,发现 Redis+TLS 有几个问题:

  • Redis 开启 TLS 后,性能下降 30%。

  • Redis 对 OpenSSL 的强依赖性. 考虑到 OpenSSL 的过往高危漏洞不断, 如果要不断修复漏洞要重新编译 Redis,导致运维更新成本过高。

  • Redis 升级后, 要重新同步数据, 增加了出故障的机率或让生产停摆。

所以, 我们还是决定使用第三方程序 stunnel 来加固安全,方便升级和修复漏洞。又不影响后端连接,从而保障了 Redis 的工作连续性和稳定可靠性。

基于 APISIX+TLS 托管,使用 TCP 的哈希一致性做负载均衡来替换 DNS 的轮询,效能显著

APISIX 使用 TCP 代理, 这部分直接配置后就可以使用,和 Redis 改造关系不大,我们就直接略过,大家可以直接看一下改造后的连接数统计截图。从实际的 APISIX 的连接数可以看出负载被数量均衡地分摊到了不同的后端,而且边缘服务器重启也利用 PSYNC2 做了快速的增量同步。

使用 Redis-shake 做定制化的数据同步

在架构改进的过程中,我们也看了 redis-shake 这个工具,它是阿里云 Redis&MongoDB 团队开源的用于 Redis 数据同步的工具。它支持 解析、恢复、备份、同步 四个功能。给大家主要介绍同步 sync:

恢复 restore:将 RDB 文件恢复到目的 Redis 数据库。

备份 dump:将源 Redis 的全量数据通过 RDB 文件备份起来。

解析 decode:对 RDB 文件进行读取,并以 json 格式解析存储。

同步 sync:支持源 Redis 和目的 Redis 的数据同步,支持全量和增量数据的迁移。

同步 rump:支持源 Redis 和目的 Redis 的数据同步,仅支持全量的迁移。采用 scan 和 restore 命令进行迁移,支持不同云厂商不同 Redis 版本的迁移。

我们原来有一个做过源代码修改过的 Redis,只会同步想要的空间。虽然好用,但还是需要在新代码上重新编译一个,可是原来的负责人已经找不到了。这也是很多年久失修项目的通病, 但通过 redis-shake 这样的开源工具,只要通过它简单配置一下就可以实现我们想要的功能:

- filter.db.whitelist / blacklist
- filter.key.whitelist / blacklist
- filter.command.whitelist / blacklist

现在的架构及未来的展望

在现在的架构中,我们在原来的三层架构基础上,又拆分和强化了三层架构:

  • DNS 层解析到 VIP,VIP 利用了 BGP/OSPF 的动态网关路由协议,对应后面一组服务器集群服务。

  • 负载均衡层:利用 “apisix ”+ “tls1.2+ ”+ “tcp 的哈希一致性连接”,把 Redis 的主从连接均衡,故障转移。

  • 边缘 CDN 节点,利用 Redis 高版本所带来的技术红利,psync 的增量同步,加上 stunnel+tls1.2 实现了加密传输。

下一个阶段, 还要继续把数据中心的 Redis 主改造成 Redis 哨兵模式(考虑到程序代码要对哨兵模式做兼容性改造, 第一阶段先不上, 一切都为了生产环境中的稳定性)。

参考文档:

如何检查网站的 TLS 版本:https://wentao.org/post/2020-11-29-ssl-version-check/

Redis 特性之复制增强版 PSYNC2:https://www.modb.pro/db/79478

通俗易懂的 Redis 架构模式详解:https://www.cnblogs.com/mrhelloworld/p/redis-architecture.html

推荐阅读

【实操干货】做好这 16 项优化,你的 Linux 操作系统焕然一新

Golang 常见设计模式之单例模式

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号