又拍雲 Redis 的改進之路

2022-06-29 12:01:03

作為推出國內首創可程式化 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 常見設計模式之單例模式