Ceryx:一個動態 NGINX
反向代理包含數百甚至上千個微服務是一個很有意思的事情,也是我們在 Sourcelair 每天都要面對的事情。這也是為什么我們今天要很高興地宣布 Ceryx,一個動態反向代理,使用 OpenResty,Lua 和 Flask,可以代理主機上任意多的服務。它的配置是即時生效。在過去幾個月里我們一直在做 Ceryx 項目。現在我們將它開源。
摘要
在 SourceLair, 我們快速提供開發環境并努力讓 web 開發可以無障礙,并使用云的力量。我們提供的一個優秀服務之一是每個項目有一個公共的 URL,它始終可用,并自動刷新你的代碼。這樣我們需要每小時啟動和停止多用戶容器,并在不停機的情況下路由每個用戶的公共 URL 到當前容器。
以前的解決方案
Ceryx 是去年我們開發的內部項目。為了找到解決我們問題的最佳方案,我們嘗試了各種技術。在這條路上,我們保持 API 的穩定不受變化的影響。有一個很好的話題沒有放到這里,你可以在 API Meetup Athens 找到我們的 slides。
Twisted,MongoDB 和 Redis
首先,我們使用基于 Twisted 自定義反向代理,Twisted 非常好用,是使用 Python 寫的事件驅動的網絡引擎。如果在 Redis 緩存中沒有查到,服務就以 MongoDB 查 詢做為路由。不管是使用數據庫查詢,還是調用 API 添加,更新或刪除路由,緩存都是流行的方式。對我們來說,它工作得很好,很快 Twisted 有了一個很好用的反向代理 API 可以使用。這個應用讓我們錯失的一件事情是,Twisted 在默認情況下,每個反向代理頭部不能設置成你想要的,在某些情況會導致一些無效的轉發。但是用它工作是很方便的。
tproxy和Redis
在 Twisted 之后,我們又嘗試了 tproxy。tproxy 是一個使用 Gevent 創建的 TCP 路由代理(第七層),Gevent 深受 Ruby 的 ProxyMachine 影響由著名的 Gunicorn 創建。我們創建了一個查找層用來替代靜態路由和文件,它會查找后端的 Redis。我們從 MongoDB 徹底地分離了服務,因為路由是短暫且易于重建的。同樣我們將 API 分離出來用 Flask 寫了獨立的服務。主要的問題是 tproxy 的開發有點被遺棄。最后一次提交是一年前。我們需要重新做一些性能優化,還有一個有趣的 bug,響應沒有包含響應長度,并保持開放直到超時。
NGINX 和 etcd
我們已經決定將代理作為一個服務而不是一個盒子,我們調研了 NGINX 和 HAProxy。 因為我們非常熟悉前者并且對它的表現很滿意。我們所有的前端都是一直使用 NGINX。我們創建了一個監控腳本,它監控etcd 的重要變更,并自動加載 NGINX 的配置。我們還修改了我們的 API,讓它和 etcd 一起做為后端工作。這大大改善了性能。但過了幾周我們發現配置的變更沒有我們想像得快。NGINX 的重新加載幾乎是瞬間的。但配置要花一段時間接收人工配置才能生效。導致路由更新變得緩慢。對我們來說最重要的問題是這種重新加載時間超過10秒,導致多 次出現“服務已停止"頁面,直到新的配置生效。
OpenResty 和 Lua 就是我們的救星
當前,Github 記錄關于他們在 NGINX 使用 Lua 腳本對 GitHub pages 進行更頻繁的重載 - 之前他們大概 30 分鐘重載一次。
當我們在尋找另外一個替代品的時候,對這篇博文非常感興趣,就開始深入研究 OpenResty - NGINX 的風情版本,使用 Lua JIT 編譯,還有其他第三方 NGINX 模塊。
我們決定繼續用回 Redis 作為后端,因為我們已經準備好 API ,Redis 也已經在內存中,也因此查詢速度非常快。我們同時使用 Redis 進行其他服務和緩存,所以不需要再考慮其他集群。
解決方案就是 Ceryx,現在已經開源,提供給每個人使用。這包括了 NGINX lua 腳本和 API,可以輕松的使用 Docker Compose 部署。
把所有的一切都縫合在一起
NGINX 提供幾個鉤子,可以在請求的幾個階段執行 Lua 代碼。Ceryx 只會在代理階段之前采取行動 -在 "access_by_lua_file" 階段,即是 Lua 路由器。
此路由器會查詢 NGINX 和 Redis 后端的本地內存緩存,以此確定目標主機和將會到來的主機端口,如果沒找到就返回一個通配符目標。一個 Redis 查詢返回一個結果時,這個結果會被緩存 5 秒,所以后續請求不會影響 Redis - 當需要靜態文件(比如 CSS,JavaScript 和圖像)的時候這會是一個很好的改進,這時候會同時拋出多個請求。可以通過增加緩存超時來對 Ceryx 進行量身定做,適應各個應用的需求。
與此同時,提供一個簡單的 Flask 服務,為路由和使用新路由更新 Redis 后端提供一個 CRUD API。代理和 API 服務共享相同的環境變量,用來配置 Redis,保持一致性。
第一印象
最新的 Ceryx 發布第一周后,我們看到了其在反向代理和大幅減少休眠服務頁上的巨大改進。在更多地細節上,升級前的 Ceryx,每個開發會話平均有 10 頁的視圖,而現在我們僅僅需要 2.5 個。
下一步
Ceryx 是在 MIT 協議下的開源項目,因此我們樂見用戶貢獻或者提出 bug 以及新功能請求。我們計劃添加 StatsD 測量功能到 Ceryx,這樣我們可以更好改進和優化一些相應的部分。
Ceryx Github項目期待你的想法和貢獻。