作为推出国内首创可编程 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 漏洞危险等级报告:
./config --prefix=/opt/openssl -fPIC enable-tls1_3 enable-shared enable-zlib no-asm no-rc4
鉴于 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