Ruby 又拍云 Redis 的改进之路

upyun · June 29, 2022 · 404 hits

作为推出国内首创可编程 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 常见设计模式之单例模式

No Reply at the moment.
You need to Sign in before reply, if you don't have an account, please Sign up first.