精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Dropbox是如何將接入層從Nginx遷移到Envoy的

開發(fā) 前端
這篇文章里講述了 Dropbox 如何將他們的接入層從 Nginx 遷移到了 Envoy,就兩個軟件之間基于多個維度進行了對比,并介紹了他們的遷移流程、遷移后的現(xiàn)狀以及這個過程中遇到的一些問題。

【編者的話】這篇文章里講述了 Dropbox 如何將他們的接入層從 Nginx 遷移到了 Envoy,就兩個軟件之間基于多個維度進行了對比,并介紹了他們的遷移流程、遷移后的現(xiàn)狀以及這個過程中遇到的一些問題。

在這篇文章里,我們將會介紹 Dropbox 之前曾經(jīng)使用過的一套基于 Nginx 的流量基礎設施,它的一些痛點,以及遷移到 Envoy 之后帶來的一些好處。我們將會針對 Nginx 和 Envoy 就多個軟件工程及運維方面進行比較。我們還將簡單地介紹一下我們的遷移流程,遷移后的現(xiàn)狀,以及在這個過程中遇到的一些問題。

在我們大部分的 Dropbox 流量轉到 Envoy 后,我們還必須無縫地將一個已經(jīng)處理了數(shù)千萬個建立的連接,每秒數(shù)百萬個請求,并擁有數(shù)個 TB 帶寬的復雜系統(tǒng)遷移到上面。這實際上已經(jīng)讓我們成為了世界上最大的 Envoy 用戶之一。

免責聲明:盡管我們試圖保持客觀性,但是本文中有很多對比是針對 Dropbox 以及我們軟件開發(fā)的方式進行的:我們的技術棧選型是 Bazel,gRPC 和 C++/Golang。

另外請注意,下文提到的 Nginx 指的是其開源版本,而不是具有附加功能的商業(yè)版本。

舊的一套基于 Nginx 的流量基礎設施

我們的 Nginx 配置絕大部分都是靜態(tài)的,然后通過結合 Python2、Jinja2 以及 YAML 等渲染。任意一處變動都需要一次完整的重新部署才能生效。所有動態(tài)的部分,比如 upstream 管理以及 stats exporter,這些是用 Lua 編寫的。更復雜的邏輯放到了用 Go 編寫的,下面一層的代理層。

Nginx 在我們的環(huán)境里良好運行了近十年。但是它已經(jīng)無法再適應我們現(xiàn)在的開發(fā)優(yōu)秀實踐:

  • 我們內(nèi)部和(私有)外部的 API 逐漸從 REST 遷移到 gRPC,這需要各種代理層面的轉碼功能支持;
  • Protocol buffer 已經(jīng)成為服務定義和配置的事實標準;
  • 所有軟件,無論使用哪種語言,均是通過 Bazel 構建和測試;
  • 我們的工程師開始重度參與開源社區(qū)里一些重要的基礎設施項目。

此外,在運維方面,Nginx 的維護成本非常高:

  • 配置生成邏輯太過靈活,而且拆分到了 YAML、Jinja2 和 Python等多處;
  • 監(jiān)控是一個 Lua 腳本、日志解析以及基于系統(tǒng)的基礎監(jiān)控的組合;
  • 越來越依賴于第三方模塊,而這會帶來穩(wěn)定性、性能以及后續(xù)升級成本等多方面的影響;
  • Nginx 的部署及流程管理同其他服務完全不同。它完全依賴于其他一些系統(tǒng)的配置:syslog、logrotate 等,并沒有和基礎系統(tǒng)完全分離。

正是出于上述種種原因,10 年來我們首次開始尋找 Nginx 的潛在替代產(chǎn)品。

為什么不用 Bandaid?

正如我們上面提到的,在 Dropbox 內(nèi)部,我們嚴重依賴一個 Golang 實現(xiàn)的代理(稱為 Bandaid)。之所以它可以和 Dropbox 的整個基礎設施很好地集成,原因是在于它可以訪問到我們內(nèi)部 Golang 庫的廣闊生態(tài):監(jiān)控、服務發(fā)現(xiàn)、限流等。我們考慮過從 Nginx 遷移到 Bandaid 的方案,但是這里面有一些問題阻礙了我們這樣做:

  • Golang 比 C/C++ 占用更多的資源。資源的低使用率對于跑在邊緣端的我們至關重要,因為我們無法輕易地“自動擴容”那里的部署。
  • CPU 開銷主要來自 GC、HTTP 解析器和 TLS ,后者的優(yōu)化程度低于 Nginx/Envoy 使用的 BoringSSL。
  • “每個請求一個 goroutine”的模型和 GC 開銷大大增加了像我們這樣高連接服務的內(nèi)存需求。
  • Go 的 TLS 棧不支持 FIPS(譯者注:見 go #11658)。
  • 除了 Dropbox,Bandaid 沒有一個社區(qū)的支持,這意味著我們只能依靠我們自己來開發(fā)功能。

綜合上述原因,我們決定將所有流量基礎設施遷移到 Envoy。

全新的基于 Envoy 的流量基礎設施

讓我們逐一看看開發(fā)和運維幾個主要的維度,從中了解為什么我們認為 Envoy 對我們來說是一個更好的選擇,以及我們從 Nginx 遷移到 Envoy 之后獲得了哪些收益。

性能

Nginx 的架構是一個事件驅動和多進程的模式。它支持 SO_REUSEPORT,EPOLLEXCLUSIVE 以及 Worker 綁核。盡管它是基于事件循環(huán)的,但它并不是完全非阻塞的。這意味著某些操作(例如打開一個文件或記錄 access/error 日志)可能會導致事件循環(huán)中止(即使啟用了 aio,aio_write 和線程池),這樣會使得尾部延遲增加,從而導致旋轉磁盤驅動器上出現(xiàn)幾秒鐘的延遲。

Envoy 是一個類似的事件驅動式的架構,只是它使用的是線程而不是進程。它也同樣支持 SO_REUSEPORT(自帶一個 BPF 過濾器的支持),并且依賴 libevent 實現(xiàn)事件循環(huán)(換句話說,沒有用到像 EPOLLEXCLUSIVE 這樣的 epoll(2) 功能)。Envoy 在事件循環(huán)里并沒有任何阻塞 IO 的操作。甚至日志記錄也是以非阻塞方式實現(xiàn)的,因此它不會出現(xiàn)停頓的現(xiàn)象。

從理論上講,Nginx 和 Envoy 應該具有相似的性能特征。但是“希望”不是我們的做事風格,因此我們的第一步便是針對經(jīng)過類似調整的 Nginx 和 Envoy 設置運行各種工作負載測試。

我們的測試結果表明,在大多數(shù)測試工作負載下,Nginx 和 Envoy 擁有相近的性能表現(xiàn):單秒高請求(RPS),高帶寬以及混合的低延遲/高帶寬的 gRPC 代理。

可以說,進行一個良好的性能測試非常困難。Nginx 有提供一個用于性能測試的準則,但是這些準則尚未形成規(guī)范。Envoy 也有提供一個基準測試指南,甚至在 envoy-perf 項目下也提供了一些工具,但遺憾的是后者似乎不怎么維護了。

我們轉而使用內(nèi)部的測試工具。之所以稱其為“綠巨人(hulk)”,是因為它在粉碎我們的服務方面享有盛譽。

這么說吧,我們發(fā)現(xiàn)測試結果里有幾處顯著的差異:

  • Nginx 表現(xiàn)出更高的長尾延遲。這主要是在遭遇繁重的 I/O 時事件循環(huán)停滯所致,尤其是與 SO_REUSEPORT 一起使用的情況,因為在這種情況下,它可以代表當前被阻塞的 Worker 程序繼續(xù)接受連接請求;
  • 在沒有開啟統(tǒng)計數(shù)據(jù)收集的情況下,Nginx 的功能和 Envoy 相當,但是我們的 Lua 統(tǒng)計數(shù)據(jù)收集模塊在高 RPS 測試中拖慢了 Nginx 3 倍。考慮到我們對 lua_shared_dict 的依賴,它在 Nginx 的各個 worker 之間是以一個互斥鎖進行同步,性能方面的損失也是意料之中的情況。

我們也知道統(tǒng)計數(shù)據(jù)收集模塊效率很低下。我們有考慮過在用戶空間里實現(xiàn)一個類似于 FreeBSD 的 counter(9) 的功能:綁定 CPU 核心,為每個 worker 分配一個無鎖的計數(shù)器以及一個取值的例程,該例程會循環(huán)遍歷所有 worker 并聚合匯總他們各自的統(tǒng)計信息。但是我們最終放棄了這個想法,因為如果我們想要監(jiān)控 Nginx 內(nèi)部狀態(tài)(比如所有錯誤情況),那就意味著我們要維護一個巨大的補丁程序,這將使得后續(xù)升級變成一個真正的地獄。

由于 Envoy 不會受到這兩個問題的困擾,因此在遷移到 Envoy 之后,我們可以釋放多達 60% 的服務器資源(之前被 Nginx 獨占)。

可監(jiān)察性

可監(jiān)察性是任何產(chǎn)品的最基本的運維需求,尤其是對于像代理這樣的基礎設施而言。在遷移期間,這一點尤為關鍵,這樣一來任何問題都可以被監(jiān)控系統(tǒng)檢測到,而不用等到沮喪的用戶報障時才發(fā)現(xiàn)。

非商業(yè)版本的 Nginx 自帶一個“stub status”模塊,提供了 7 個統(tǒng)計信息: 

  1. Active connections: 291  
  2. server accepts handled requests 
  3. 16630948 16630948 31070465  
  4. Reading: 6 Writing: 179 Waiting: 106 

這當然是不夠的,因此我們添加了一個簡單的 log_by_lua 處理程序,該處理程序根據(jù) Lua 中可用的 HTTP header 和變量添加每個請求的統(tǒng)計信息:狀態(tài)代碼,大小,緩存命中率等。以下是一個簡單的吐出統(tǒng)計數(shù)據(jù)的函數(shù)的例子: 

  1. function _M.cache_hit_stats(stat) 
  2. if _var.upstream_cache_status then 
  3.     if _var.upstream_cache_status == "HIT" then 
  4.         stat:add("upstream_cache_hit"
  5.     else 
  6.         stat:add("upstream_cache_miss"
  7.     end 
  8. end 
  9. end 

除了每個請求的 Lua 統(tǒng)計信息外,我們還有一個非常脆弱的 error.log 解析器,它負責 upstream,http,Lua 和 TLS 的錯誤分類。

在這些之上,我們有一個單獨的 exporter ,用于收集 Nginx 的內(nèi)部狀態(tài):自上次 reload 的時間,worker 數(shù)量,RSS/VMS 大小,TLS 證書使用期限等。

典型的 Envoy 配置為我們提供了數(shù)千種不同的指標(采用 Prometheus 格式),用于描述代理流量和服務器的內(nèi)部狀態(tài): 

  1. $ curl -s http://localhost:3990/stats/prometheus | wc -l  
  2. 14819 

這里面囊括了五花八門的來自不同地方匯總的大量統(tǒng)計信息:

  • 每個集群/每個 upstream /每個 vhost 的 HTTP 統(tǒng)計信息,包括連接池信息和各種時序直方圖;
  • 每個監(jiān)聽器的 TCP/HTTP/TLS 下游的連接統(tǒng)計信息;
  • 從基本版本信息和正常運行時間到內(nèi)存分配統(tǒng)計以及棄用功能使用情況計數(shù),各種內(nèi)部/運行時狀態(tài)的統(tǒng)計信息。

Envoy 的管理接口真心贊。它不僅通過 /certs,/clusters 和 /config_dump 端點提供額外的結構化的統(tǒng)計信息,而且還提供了非常重要的運維功能:

  • 通過 /logging 提供即時更改錯誤日志配置的能力。這讓我們能夠在幾分鐘內(nèi)排查相當模糊的問題;
  • /cpuprofiler,/heapprofiler,/contention 在不可避免的性能排障期間肯定會有用武之地;
  • /runtime_modify 端點允許我們更改配置參數(shù)集而不用推送新的配置,而這些配置可用于功能開關等。

除了統(tǒng)計數(shù)據(jù)外,Envoy 還支持可插拔的 tracing 實現(xiàn)。這不僅對擁有多個負載均衡層的接入層團隊有用,對于希望從邊緣到應用服務器端到端跟蹤請求延遲的應用程序開發(fā)人員也很有用。

從技術上講,Nginx 也支持通過第三方的 OpenTracing 集成進行跟蹤,但是該功能開發(fā)尚不成熟。

最后,同等重要的是,Envoy 能夠通過 gRPC 流式傳輸 access 日志。這減輕了我們接入層團隊不得不支持打通 syslog 到 hive 的負擔。此外,在 Dropbox 生產(chǎn)中啟動通用 gRPC 服務比添加自定義的 TCP/UDP 監(jiān)聽器更容易(也更安全!)。

和其他所有操作類似,Envoy 里面 access 日志的配置是通過 gRPC 管理服務,即訪問日志服務(ALS)設置。管理服務是將 Envoy 數(shù)據(jù)平面與生產(chǎn)中的各種服務集成的標準方式。這也是我們下一節(jié)要介紹的內(nèi)容。

集成

Nginx 提供的集成方案,一個最佳描述便是 “Unix-ish”。配置是非常靜態(tài)的。而且它嚴重依賴于文件(例如配置文件本身,TLS 證書和 Ticket 、白名單/黑名單等)以及一些知名的行業(yè)協(xié)議(記錄日志到 syslog 以及通過 HTTP 發(fā)送認證子請求)。對于小型部署而言,這樣做的簡單性和向后兼容性是一件好事,因為我們可以通過編寫幾個 Shell 腳本輕松實現(xiàn) Nginx 的自動化。但是隨著系統(tǒng)規(guī)模的擴大,可測試性和標準化變得更加重要。

Envoy 對于是否應該將接入層數(shù)據(jù)平面和它的控制平面,以及因此也即是與其他基礎設施集成在一起,持更加堅定的態(tài)度。它通過提供一個穩(wěn)定的 API(通常稱為 xDS ),鼓勵用戶使用 protobuf 和gRPC。Envoy 通過查詢一個或多個這樣的 xDS 服務來發(fā)現(xiàn)其動態(tài)資源。

如今,xDS API 的發(fā)展已然超越 Envoy:通用數(shù)據(jù)平面 API(UDPA)的宏偉目標是“成為事實上的 L4/L7 負載均衡器標準”。

根據(jù)我們的經(jīng)驗,這一雄心壯志是靠譜的。我們已經(jīng)在內(nèi)部負載測試中使用了開放請求成本匯總(ORCA),并且正在考慮將 UDPA 用于非 Envoy 的負載均衡,例如我們基于 Katran 的 eBPF/XDP 4層負載均衡代理。

這對于 Dropbox 尤其適合,Dropbox 的所有服務都已經(jīng)實現(xiàn)了在內(nèi)部通過基于 gRPC 的 API 進行交互。我們已經(jīng)實現(xiàn)了自主版本的 xDS 控制平面,它將 Envoy 同我們的配置管理、服務發(fā)現(xiàn)、私密信息管理以及路由信息集成在一起。

以下是一些可用的 xDS 服務,它們的 Nginx 替代品以及我們?nèi)绾问褂盟鼈兊囊恍┦纠?/p>

  • 如上所述,訪問日志服務(ALS)讓我們可以動態(tài)地配置訪問日志目標,編碼和格式。試想一下 Nginx 的 log_format 和 access_log 的動態(tài)版本;
  • 端點發(fā)現(xiàn)服務(EDS)提供相關集群成員的信息。這類似于 Nginx 配置中動態(tài)更新 upstream 里的 server 條目列表(例如,對于 Lua 來說便是 balancer_by_lua_block)。在這里,我們將其代理到內(nèi)部的服務發(fā)現(xiàn);
  • 私密信息發(fā)現(xiàn)服務(SDS)提供了各種與 TLS 相關的信息,這些信息將涵蓋各種 ssl_ * 指令(以及相應的ssl_*_by_lua_block)。我們將這個接口調整為適用于我們場景的私密信息分發(fā)服務;
  • 運行時發(fā)現(xiàn)服務(RTDS)提供了運行時標志。我們在 Nginx 中實現(xiàn)這一功能的方式很不可靠,它是通過在 Lua 里檢查各種文件是否存在來實現(xiàn)的。這種方法很快會在各個服務器之間變得不一致。 Envoy 的默認實現(xiàn)也是基于文件系統(tǒng)的,但是我們將 RTDS xDS API 指向了我們的分布式配置存儲。這樣一來,我們便可以一次控制整個集群(通過帶有類似 sysctl 界面的工具),并且在不同服務器之間不會出現(xiàn)意外不一致的情況;
  • 路由發(fā)現(xiàn)服務(RDS)將路由映射到虛擬主機,并允許對 HTTP 頭部和過濾器進行額外的配置。用 Nginx 術語來說,它們類似于帶有 set_header/proxy_set_header 和 proxy_pass 的動態(tài) location 配置。在更底下的代理層,我們直接在我們的服務定義配置中自動生成這些配置;

關于 Envoy 如何與現(xiàn)有生產(chǎn)系統(tǒng)集成的示例,這里有一個將 Envoy 與自定義服務發(fā)現(xiàn)集成的一個經(jīng)典例子。還有一些開源的 Envoy 控制面板實現(xiàn),例如 Istio 和更少復雜度的 go-control-plane。

我們自己的 Envoy 控制平面實現(xiàn)了越來越多的 xDS API。它在生產(chǎn)中以普通的 gRPC 服務的形式部署,并充當我們基礎設施構建塊的一個適配器。它通過一組通用的 Golang 庫來實現(xiàn)與內(nèi)部服務進行對話,并通過穩(wěn)定的 xDS API 向 Envoy 公開它們。整個過程不涉及任何文件系統(tǒng)調用,信號量,cron,logrotate,syslog,日志解析器等。

配置

Nginx 在配置方面具有簡單易讀這樣一項不可否認的優(yōu)勢。但是,隨著配置變得越來越復雜,并且配置開始變成是代碼生成式的,這項優(yōu)勢就沒了。

正如上面所提到的,我們的 Nginx 配置是通過 Python2,Jinja2 和 YAML 的混合生成的。 你們中的一些人可能已經(jīng)在 erb,pug,Text::Template 甚至 m4 里面看到或甚至編寫了這樣類似的變體: 

  1. {% for server in servers %} 
  2. server { 
  3. {% for error_page in server.error_pages %} 
  4. error_page {{ error_page.statuses|join(' ') }} {{ error_page.file }}; 
  5. {% endfor %} 
  6. ... 
  7. {% for route in service.routes %} 
  8. {% if route.regex or route.prefix or route.exact_path %} 
  9. location {% if route.regex %}~ {{route.regex}}{% 
  10.         elif route.exact_path %}= {{ route.exact_path }}{% 
  11.         else %}{{ route.prefix }}{% endif %} { 
  12.     {% if route.brotli_level %} 
  13.     brotli on
  14.     brotli_comp_level {{ route.brotli_level }}; 
  15.     {% endif %} 
  16.     ... 

我們的 Nginx 配置的生成方式有一個大問題:配置生成中涉及的所有語言都允許代入 和/或 邏輯。YAML 有 anchor,Jinja2 有 loop/ifs/macroses,然后,Python 當然是圖靈完備的。如果沒有一個干凈的數(shù)據(jù)模型,復雜性會迅速擴散到這三者。

這個問題自然是可以解決的,但是有兩個基本問題:

  • 這里沒有關于配置格式的聲明性描述。如果我們想通過編程的方式生成和驗證配置,那么需要自己重新造輪子;
  • 語法上有效的配置,從 C 代碼的角度來看仍然可能是無效的。例如,某些與緩沖區(qū)相關的變量具有值限制,對齊限制以及與其他變量的相互依賴性。為了從語義上驗證配置,我們需要運行 nginx -t 來校驗。

另一方面,Envoy 自帶一套用于配置的統(tǒng)一數(shù)據(jù)模型:它的所有配置都放到 protobuffer 中定義。這不僅解決了數(shù)據(jù)建模問題,而且還將輸入的信息添加到了配置值里。鑒于 protobuf 是Dropbox 生產(chǎn)環(huán)境里的頭等公民,并且是描述/配置服務的通用方式,因此,集成就變得更加簡單了。

我們針對 Envoy 設計的新的配置生成器是基于 protobuf 和 Python3 實現(xiàn)的。所有數(shù)據(jù)建模均在原始文件中完成,而所有邏輯均在 Python 中執(zhí)行。下面是一個例子: 

  1. from dropbox.proto.envoy.extensions.filters.http.gzip.v3.gzip_pb2 import Gzip 
  2. from dropbox.proto.envoy.extensions.filters.http.compressor.v3.compressor_pb2 import Compressor 
  3.  
  4. def default_gzip_config( 
  5. compression_level: Gzip.CompressionLevel.Enum = Gzip.CompressionLevel.DEFAULT
  6. ) -> Gzip: 
  7.     return Gzip( 
  8.         # Envoy's default is 6 (Z_DEFAULT_COMPRESSION). 
  9.         compression_level=compression_level, 
  10.         # Envoy's default is 4k (12 bits). Nginx uses 32k (MAX_WBITS, 15 bits). 
  11.         window_bits=UInt32Value(value=12), 
  12.         # Envoy's default is 5. Nginx uses 8 (MAX_MEM_LEVEL - 1). 
  13.         memory_level=UInt32Value(value=5), 
  14.         compressor=Compressor( 
  15.             content_length=UInt32Value(value=1024), 
  16.             remove_accept_encoding_header=True
  17.             content_type=default_compressible_mime_types(), 
  18.         ), 
  19.     ) 

注意上述代碼里的 Python3 類型注解!結合 mypy-protobuf protoc 插件,它們可以在 config 生成器里提供端到端的輸入。如果你用的 IDE 支持自動檢查的話,它將會立即高亮顯示輸入信息不匹配。

在某些情況下,經(jīng)過類型檢查的 protobuf 在邏輯上仍然可能是無效的。在上面的示例中,gzip window_bits 只能取 9 到 15 之間的值。這類限制可以在 protoc-gen-validate protoc 插件的幫助下輕松完成定義:

  1. google.protobuf.UInt32Value window_bits = 9 [(validate.rules).uint32 = {lte: 15 gte: 9}]; 

最后,使用官方定義的配置模型的一個潛在好處是,它有機地引領了文檔與配置定義并排配置。 以下是一個 gzip.proto 的示例: 

  1. // Value from 1 to 9 that controls the amount of internal memory used by zlib. Higher values.            
  2. // use more memory, but are faster and produce better compression results. The default value is 5.             
  3. google.protobuf.UInt32Value memory_level = 1 [(validate.rules).uint32 = {lte: 9 gte: 1 

可擴展性

要想讓 Nginx 提供超出標準配置所提供的功能范疇之外的特性的話,通常需要編寫一個 C 模塊。 Nginx 的開發(fā)指南對于可用的構建塊提供了詳盡的介紹。這也就是說,這種方式是相對重量級的。實際上,要想安全地編寫一個 Nginx 模塊的話,需要一位相當資深的軟件工程師。

關于可供模塊開發(fā)人員選用的基礎設施的話,他們可以期待一些像哈希表、隊列、rb樹這樣的基礎容器,(非RAII)內(nèi)存管理、以及可用于所有請求處理階段的鉤子。另外,還有一些外部庫,比如 pcre、zlib、openssl,當然,還有l(wèi)ibc。

為了提供更輕量級的功能擴展,Nginx 提供了 Perl 和 JavaScript 的接口。可悲的是,它們所提供的功能都相當有限,絕大部分都局限于請求處理的內(nèi)容階段。

社區(qū)采納的最常用的擴展方式是基于第三方的 lua-nginx 模塊和各種 Openresty 類庫。這一方案幾乎可以外掛到請求處理的任意階段。我們使用 log_by_lua 進行統(tǒng)計信息的收集,然后使用 balancer_by_lua 進行動態(tài)地后端再配置。

從理論上講,Nginx 提供了使用 C++ 開發(fā)模塊的能力。然而實際上,對于所有原語,它都缺少適當?shù)?C++ 接口/包裝器,這樣就顯得不太值得了。盡管如此,仍有一些社區(qū)對此進行嘗試。這些還遠遠沒有達到生產(chǎn)就緒。

Envoy 的主要擴展機制是通過 C++ 插件。這個流程在文檔方面不如 Nginx,但是它更為簡單。部分原因是:

  • 整潔且擁有良好注釋的界面。C++ 類充當自然的擴展和文檔點。比如,看看 HTTP 過濾接口。
  • C++14 語言和標準庫。從 template 和 lambda 函數(shù)等這些基本的語言功能,到類型安全的容器和算法。通常,編寫現(xiàn)代 C++14 與使用 Golang 并沒有多大區(qū)別,或者有些人甚至會說 Python。
  • C++14 及其標準庫以外的功能。由 abseil 庫提供的,其中包括來自較新版本的 C++ 標準的直接替換,內(nèi)置了靜態(tài)的死鎖檢測和 debug 支持的互斥鎖,以及額外的/更有效的容器,等等。

具體地,這里有一份 HTTP 過濾模塊的經(jīng)典例子。

通過簡單地實現(xiàn) Envoy stats 接口,我們僅用200行代碼就可以將 Envoy 與 Vortex2(我們的監(jiān)視框架)集成在一起。

Envoy 還通過 moonjit 添加了對 Lua 的支持,moonjit 是一個對 Lua 5.2 做了諸多改進支持的 LuaJIT fork。與 Nginx 的第三方 Lua 集成相比,它所提供的功能和開放的鉤子要少得多。由于在開發(fā)、測試和解釋后代碼的排障等諸多方面存在的額外復雜性成本,這使得 Lua 對 Envoy 的吸引力大大降低。專門從事 Lua 開發(fā)的公司可能會不同意這一觀點,但是在我們的案例中,我們決定避免使用它,而只是將 C++ 用于 Envoy 的可​​擴展性。

Envoy 與其他 Web 服務器的區(qū)別就在于它提供了對 WebAssembly(WASM)的新興支持 —— 一種快速的,可移植且安全的擴展機制。WASM 不能直接使用,但是可以作為任何通用編程語言的編譯目標。Envoy 實現(xiàn)了一個適用于代理的 WebAssembly 規(guī)范(還包括相關的 Rust 和 C++ SDK),該規(guī)范描述了 WASM 代碼和通用的 L4/L7 代理之間的邊界。代理和擴展之間代碼的分隔提供了一個安全的沙箱,而 WASM 低級凝練的二進制格式又為之提供了接近原生的效率。在此之上,在 Envoy 里,proxy-wasm 擴展是和 xDS 集成在一起的。這樣便可以進行動態(tài)地更新,甚至可以進行潛在的 A/B 測試。

在 KubeCon’s 19 上,《通過 WebAssembly 擴展 Envoy》這個演講很好地概述了 Envoy 里集成的 WASM 及其潛在用途。它還暗示已經(jīng)達到了原生 C++ 代碼性能水平的 60-70%。

使用 WASM,服務提供方可以安全有效地在其邊緣環(huán)境運行客戶的代碼。客戶獲益的則是可移植性:他們的擴展可以在實現(xiàn) proxy-wasm ABI 的任何云上運行。此外,它允許用戶使用任意一種語言,只要它可以編譯為 WebAssembly。這使得他們能夠安全有效地使用更廣泛的非 C++ 類庫。

Istio 正在向 WebAssembly 的開發(fā)傾注大量資源:他們已經(jīng)有了基于 WASM 的遙測擴展的實驗版本以及用于共享擴展的 WebAssemblyHub 社區(qū)。

當前,Dropbox 并未用到 WebAssembly。但是,等到 proxy-wasm 的 GO SDK 可用時,這一情況也許會發(fā)生變化。

構建和測試

默認情況下,Nginx 使用的是一套自定義的基于 shell 的配置系統(tǒng)和基于 make 的構建系統(tǒng)構建的。這是簡單而優(yōu)雅的,但是將其集成到 Bazel 構建的 monorepo 的話需要花費大量的精力才能獲得增量、分布式、封閉和可重現(xiàn)構建這些所有優(yōu)點。

Google 開源了他們用 Bazel 構建的 Nginx 版本,該版本由 Nginx,BoringSSL,PCRE,ZLIB 和 Brotli 庫/模塊組成。

在測試方面,Nginx 在一個單獨的倉庫里有一組 Perl 驅動的集成測試,沒有任何單元測試。

鑒于我們對 Lua 的大量使用以及缺乏內(nèi)置的單元測試框架,我們求助于使用模擬配置和基于 Python 的簡單測試驅動程序進行測試: 

  1. class ProtocolCountersTest(NginxTestCase): 
  2. @classmethod 
  3. def setUpClass(cls): 
  4.     super(ProtocolCountersTest, cls).setUpClass() 
  5.     cls.nginx_a = cls.add_nginx( 
  6.         nginx_CONFIG_PATH, endpoint=["in"], upstream=["out"], 
  7.     ) 
  8.     cls.start_nginxes() 
  9.  
  10. @assert_delta(lambda d: d == 0, get_stat("request_protocol_http2")) 
  11. @assert_delta(lambda d: d == 1, get_stat("request_protocol_http1")) 
  12. def test_http(self): 
  13.     r = requests.get(self.nginx_a.endpoint["in"].url("/")) 
  14.     assert r.status_code == requests.codes.ok 

最重要的是,我們通過預處理所有生成的配置(例如用 127/8 替換所有 IP 地址,切換到自簽名 TLS 證書等)并在結果上運行 nginx -c 來驗證所有語法的語法正確性。

在 Envoy 方面,其主流的構建系統(tǒng)已經(jīng)是 Bazel。因此,將它和我們的 monorepo 集成起來并不繁瑣:Bazel 支持輕松添加外部依賴項。

我們還使用 copybara 腳本來同步 Envoy 和 udpa 的 protobuf。當你需要進行簡單的轉換而無需永遠維護大型補丁集時,Copybara 十分方便。

通過 Envoy,我們可以靈活地選擇使用一組預先編寫好的 mock 來進行單元測試(基于 gtest/gmock ),也可以選擇使用 Envoy 的集成測試框架,或者同時使用兩者。至此,針對每一項細小的改動,我們不再需要依靠緩慢的端到端集成測試了。

gtest 是 Chromium 和 LLVM 等項目所采用的一套相當有名的單元測試框架。如果你想進一步了解 googletest,這里有兩份不錯的介紹資料:googletest 和 googlemock。

開源的 Envoy 開發(fā)要求每次更改達到 100% 的單元測試覆蓋率。它會通過 Azure CI 流水線針對每個 PR 自動觸發(fā)測試。

使用 google/becnhmark 對性能敏感的代碼進行微基準測試也是一種常見做法: 

  1. $ bazel run --compilation_mode=opt test/common/upstream:load_balancer_benchmark -- --benchmark_filter=".*LeastRequestLoadBalancerChooseHost.*" 
  2. BM_LeastRequestLoadBalancerChooseHost/100/1/1000000          848 ms          449 ms            2 mean_hits=10k relative_stddev_hits=0.0102051 stddev_hits=102.051 
  3. ... 

改用 Envoy 之后,我們開始完全依賴單元測試來進行內(nèi)部模塊開發(fā): 

  1. TEST_F(CourierClientIdFilterTest, IdentityParsing) { 
  2. struct TestCase { 
  3. std::vector<std::string> uris; 
  4. Identity expected; 
  5. }; 
  6. std::vector<TestCase> tests = { 
  7. {{"spiffe://prod.dropbox.com/service/foo"}, {"spiffe://prod.dropbox.com/service/foo""foo"}}, 
  8. {{"spiffe://prod.dropbox.com/user/boo"}, {"spiffe://prod.dropbox.com/user/boo""user.boo"}}, 
  9. {{"spiffe://prod.dropbox.com/host/strange"}, {"spiffe://prod.dropbox.com/host/strange""host.strange"}}, 
  10. {{"spiffe://corp.dropbox.com/user/bad-prefix"}, {""""}}, 
  11. }; 
  12. for (auto& test : tests) { 
  13. EXPECT_CALL(*ssl_, uriSanPeerCertificate()).WillOnce(testing::Return(test.uris)); 
  14. EXPECT_EQ(GetIdentity(ssl_), test.expected); 
  15. }  

實施亞秒級的往返測試在生產(chǎn)力方面會帶來復合效果。它讓我們能夠把更多精力放在增加測試覆蓋范圍。由于可以在單元測試和集成測試之間進行自由選擇,這使得我們能夠在 Envoy 測試的覆蓋范圍、速度和成本之間獲得一個平衡。

學習上手 Bazel 對我們開發(fā)人員來說是一次絕佳的經(jīng)歷。它的學習曲線非常陡峭,而且前期需要付出大量的投資,但是它卻有很高的回報:增量式構建,遠程緩存,分布式構建/測試等。

Bazel 很少討論的好處之一是,它讓我們能夠查詢甚至擴展依賴圖譜。依賴關系圖的編程接口,以及跨語言的通用構建系統(tǒng),這是一項非常強大的功能。它可以用作代碼提示器,代碼生成,漏洞跟蹤以及部署系統(tǒng)等這類服務的基礎構建塊。

安全性

Nginx 的代碼面非常小,它的外部依賴很少。通常,對生成的二進制文件來說只能看到 3 個外部依賴項:zlib(或者更快的變體之一),一個 TLS 庫以及 PCRE。Nginx 自研實現(xiàn)了所有相關的協(xié)議解析器、事件庫,甚至還重新實現(xiàn)了某些 libc 函數(shù)。

在某些情況下,Nginx 被認為非常安全,以至于它被用作 OpenBSD 里默認的 Web 服務器。后來,兩個開發(fā)社區(qū)發(fā)生了沖突,也因此有了 httpd。您可以在 BSDCon《介紹OpenBSD的新 httpd 》中了解此舉背后的動機。

這種極簡主義在實踐中得到了回報。Nginx 在11年多的時間里僅報告了30個安全漏洞。

另一方面,Envoy 的代碼量更大,尤其是考慮到 C++ 代碼比用于 Nginx 的基本 C 代碼更加密集時,這一點更為明顯。它還包含來自外部依賴項的數(shù)百萬行代碼。從事件通知到協(xié)議解析器的所有內(nèi)容都推給了第三方庫。這會增加攻擊面并造成最終產(chǎn)出的可執(zhí)行文件愈加膨脹。

為了解決這個問題,Envoy 高度依賴現(xiàn)代安全實踐。它使用 AddressSanitizer,ThreadSanitizer 和 MemorySanitizer。它的開發(fā)人員甚至超綱地采用了模糊測試。

任何對于全球IT基礎架構至關重要的開源項目都可以接入到 OSS-Fuzz,這是一個用于自動化模糊測試的免費平臺。要了解更多信息,請參閱“OSS-Fuzz/架構”。

實際上,盡管如此,所有這些預防措施都不能完全抵消增加的代碼所留下的痕跡。結果便是,在過去的兩年里,Envoy 已經(jīng)發(fā)布了 22 條安全公告。

Envoy 的《安全發(fā)布策略》一文對此進行了詳細描述,針對某些指定的漏洞還有詳細的檢查報告。Envoy 還是 Google 漏洞獎勵計劃(VRP)的成員之一。VRP 向所有安全研究人員開放,根據(jù)它們設計的規(guī)則,對發(fā)現(xiàn)和報告的漏洞提供獎勵。

為了應對不斷增長的漏洞風險,我們使用了來自上游發(fā)行版供應商 Ubuntu 和 Debian 的最佳可執(zhí)行文件來強化安全措施。我們?yōu)樗羞吘壄h(huán)境曝光的可執(zhí)行文件定義了特殊加固后的構建配置文件。它包括 ASLR,堆棧保護器以及符號表的加固: 

  1. build:hardened --force_pic 
  2. build:hardened --copt=-fstack-clash-protection 
  3. build:hardened --copt=-fstack-protector-strong 
  4. build:hardened --linkopt=-Wl,-z,relro,-z,now 

在絕大多數(shù)環(huán)境里,fork 的 Web 服務器(如 Nginx)在堆棧保護器方面會存在問題。由于主進程和工作進程共享同一套堆棧來進行灰度,而在灰度驗證失敗時,工作進程會被殺死,因此,我們可以通過大約 1000 次嘗試逐位對灰度的實例進行暴力破解。使用線程作為并發(fā)原語的 Envoy 不受此攻擊的影響。

我們還希望盡可能的給第三方依賴的安全性加固。我們在 FIPS 模式下使用 BoringSSL,該模式包括啟動自檢和可執(zhí)行文件的完整性檢查。我們還考慮在某些邊緣環(huán)境的灰度服務器上運行啟用 ASAN 的可執(zhí)行文件。

功能性

這是帖子中最主觀的部分,請做好準備。

Nginx 最初是設計成一個 Web 服務器,致力于以最少的資源消耗來提供靜態(tài)文件服務。它的功能性在這里是最重要的:靜態(tài)文件服務,緩存(包括驚群保護)以及 range 緩存。

但是,作為代理的話,Nginx 缺乏現(xiàn)代基礎架構所需的功能。它的后端沒有 HTTP/2。盡管可以代理 gRPC 服務,但是它不支持連接多路復用。而且不支持 gRPC 轉碼。最重要的是,Nginx的“核心開放(open-core)”模型限制了可以納入開源版本代理服務的功能集。結果便是,某些重要功能(如統(tǒng)計信息)在它的“社區(qū)”版本里是不可用的。

相比之下,Envoy 已經(jīng)演變?yōu)橐粋€ ingress/egress 代理,經(jīng)常用于以 gRPC 為主要負載的環(huán)境。盡管它的 Web 服務功能還是相當初級的:沒有文件服務,而且緩存功能仍然在開發(fā)中,brotli或者預壓縮這些功能也都不支持。針對這些場景,我們?nèi)匀惶峁┝艘粋€小的 fallback Nginx 實例,Envoy 會把它當成上游集群。

等到 Envoy 的 HTTP 緩存功能生產(chǎn)就緒時,我們便可以將大多數(shù)靜態(tài)服務用例遷移過去,使用 S3 取代文件系統(tǒng)作為長期存儲。

Envoy 還提供了許多 gRPC 相關能力的原生支持:

  • gRPC 代理。這是一項基本功能,允許我們在應用程序里端到端地使用 gRPC(比如 Dropbox 桌面客戶端)
  • HTTP/2 到后端。這項功能使得我們可以大大減少流量層之間的 TCP 連接數(shù),從而減少內(nèi)存消耗和 keepalive 的流量。
  • gRPC -> HTTP 的橋接(+反向代理)這些讓我們能夠使用現(xiàn)代的 gRPC 棧對外暴露舊版的 HTTP/1 應用程序服務。
  • gRPC-WEB。這項功能讓我們即使在中間層(防火墻,IDS等)尚不支持 HTTP/2 的環(huán)境里也可以端對端地使用gRPC。
  • gRPC JSON 轉碼器。這讓我們能夠將所有入站流量(包括 Dropbox 公共 API )從 REST 轉到 gRPC。

此外,Envoy 還可以用作一個出站代理。我們通過它統(tǒng)一了另外幾個用例場景:

  • Egress 代理:自從 Envoy 添加了對 HTTP CONNECT 方法的支持以后,它便可以用作 Squid 代理的替代產(chǎn)品。我們已經(jīng)開始用 Envoy 替換出站的 Squid 實例。這樣做不僅極大地提高了可見性,而且還通過統(tǒng)一使用通用的數(shù)據(jù)平面和可觀察性技術棧省掉了一攬子運維方面的糟心事(不再需要為了統(tǒng)計信息而去解析日志)
  • 第三方軟件服務發(fā)現(xiàn):我們并沒有使用 Envoy 來構建服務網(wǎng)格,取而代之的是,我們依靠的是軟件里的 Courier gRPC 庫。但是,當遇到需要一次性花費最小的精力將開源服務與我們的服務發(fā)現(xiàn)連接的場景時,我們實際還是選擇了 Envoy。比如,Envoy 在我們的分析棧里用作服務發(fā)現(xiàn)的輔助工具。Hadoop 可以動態(tài)地發(fā)現(xiàn)它的 name 及 journal 節(jié)點。 Superset 可以發(fā)現(xiàn)airflow,presto 以及 hive 的后端。Grafana 可以發(fā)現(xiàn)它的 MySQL 數(shù)據(jù)庫。

社區(qū)

Nginx 的開發(fā)是相當中心化的。它的絕大部分開發(fā)活動都是核心團隊內(nèi)部進行的。nginx-devel 郵件列表上有一些外部活動,然后官方 Bug 跟蹤上偶爾有一些與開發(fā)相關的討論。

FreeNode 上有一個 #nginx 頻道。我們可以隨時加入到里面進行更多互動性質的社區(qū)對話。

Envoy 的開發(fā)則是開放和去中心化的:通過 GitHub issue/pull request,郵件列表和社區(qū)會議進行協(xié)同。

Slack 上的社區(qū)活動也很活躍。你可以在這里收到邀請。

開發(fā)風格和工程社區(qū)這些方面很難量化定性,因此,我們不妨一起來看一個開發(fā) HTTP/3 的特定例子。

F5 最近發(fā)表了 Nginx QUIC 和 HTTP/3 實現(xiàn)。這塊代碼是干凈的,沒有任何外部依賴。但是開發(fā)過程本身并不透明。在此之前的半年,Cloudflare 提出了自己的 Nginx HTTP/3 實現(xiàn)。結果便是,該社區(qū)現(xiàn)在擁有兩套單獨的用于 Nginx 的 HTTP/3 實驗版本。

再來看看 Envoy ,HTTP/3 的實現(xiàn)這塊也正在開發(fā)中,它是基于 chromium 的 “quiche”(QUIC,HTTP等)庫。該項目的進展可以通過 這個 GitHub issue 跟蹤。在補丁開發(fā)完成前,設計文檔便已經(jīng)對外公布了。剩下的工作里,想要從社區(qū)參與中獲益的部分會帶有 “help wanted” 標簽。

如你所見,后者顯然更加開放透明,而且非常鼓勵協(xié)作開發(fā)。對我們來說,這意味著我們能夠從上游獲得大量的 Envoy 相關的大大小小的變動 —— 包括從運維改進和性能調優(yōu)到新的 gRPC 轉碼功能以及負載均衡功能的一些變動。

遷移后的現(xiàn)狀

我們已經(jīng)讓 Nginx 和 Envoy 并排運行了半年多,然后通過 DNS 逐步將流量從一個切換到另一個。到目前為止,我們已經(jīng)成功將許多各式各樣的工作負載遷移到了 Envoy:

  • Ingress 高吞吐量服務。Dropbox 桌面客戶端的所有文件數(shù)據(jù)都是通過 Envoy 背后端到端 的 gRPC 提供服務。改用 Envoy 后由于在邊緣環(huán)境的連接復用效果更好,我們還略微提高了用戶的性能。
  • Ingress 高RPS服務。這是 Dropbox 桌面客戶端的所有文件元數(shù)據(jù)。同樣地,我們獲得了端到端的 gRPC 帶來的好處,而且去掉了連接池,這意味著我們不受每個連接一次請求的限制。
  • 通知和遙測服務。這里是用來處理所有實時通知的服務,因此這些服務器一般會有數(shù)百萬個 HTTP 連接(每個活躍的客戶端會有一個連接)。如今我們不再需要使用昂貴的長輪詢方式來實現(xiàn)通知服務,取而代之的是,我們可以通過 streaming gRPC 來實現(xiàn)。
  • 兼有高吞吐量/高RPS的混合服務。API 流量(元數(shù)據(jù)和數(shù)據(jù)本身)。這使得我們開始考慮一些公共的 gRPC API。我們甚至可以直接在邊緣環(huán)境對現(xiàn)有的基于 REST 的 API 進行轉碼。
  • Egress 高吞吐量代理。在我們的使用場景,Dropbox 和 AWS 的通信主要是 S3 這塊。最終我們會在生產(chǎn)網(wǎng)絡里下線所有的 Squid 代理,然后只留下一個 L4/L7 的數(shù)據(jù)平面。

遷移的最后一件事便是 www.dropbox.com 自己。在完成遷移后,我們便可以開始停用我們在邊緣環(huán)境部署的 Nginx 服務。一個時代即將結束。

我們遇到的一些問題

當然,整個遷移的過程并非完美無瑕。不過至少沒有導致任何明顯的中斷。整個遷移過程中最困難的部分是我們的 API 服務這塊。大量不同的設備通過我們的公共 API 和 Dropbox 進行通信——從 curl/wget 驅動的 Shell 腳本,以及具有自定義 HTTP/1.0 堆棧的嵌入式設備,到每個可能訪問到這里的 HTTP 庫。 Nginx 是經(jīng)過實踐檢驗的行業(yè)事實標準。可以理解,大多數(shù)庫都隱式地依賴于它的某些行為。除了我們的 api 用戶在依賴的 Nginx 行為這塊,在轉到 Envoy 后遇到了一些行為不一致的地方以外,我們在使用 Envoy 和它提供的類庫的過程中還發(fā)現(xiàn)了許多 bug。在社區(qū)的幫助下,所有這些問題都很快得到了解決并且反饋到了上游。

下面只是其中一些“異常的”/沒有出現(xiàn)在 RFC 里的行為的摘要:

  • 合并 URL 路徑里的斜杠。URL 規(guī)范化及斜杠合并在 Web 代理里是一項非常常見的功能。Nginx 默認啟用了斜杠歸一和斜線合并,但是 Envoy 不支持后者。我們向上游提交了一個補丁,添加了該項功能,并允許用戶通過merge_slashes 選項選擇性地加入。
  • 虛擬主機名字里的端口。Nginx 允許接收兩種形式的 Host header:要么是 example.com,又或者是 example.com:port。我們有幾個曾經(jīng)依賴于這一行為的 API 用戶。首先,我們通過在配置里復制虛擬主機(帶和不帶端口)來解決此問題,但是后來我們又在 Envoy 端添加了一個忽略匹配端口的選項:strip_matching_host_port。
  • 傳輸編碼區(qū)分大小寫。出于某種不知名的原因,一小部分子集的 API 客戶端使用了 Transfer-Encoding:Chunked(請注意大寫的“C”)header。這在技術上是可行的,因為 RFC7230 聲明了 Transfer-Encoding/TE header 不區(qū)分大小寫。修復辦法也很簡單,然后也已經(jīng)提交到了上游的 Envoy 。
  • 一個請求里同時帶有 [Content-Length](https://github.com/envoyproxy/envoy/issues/11398) 和 [Transfer-Encoding:chunked](https://github.com/envoyproxy/envoy/issues/11398) 這兩個 header。有些請求在 Nginx 下面是好的,但是遷移到 Envoy 以后就炸了。 RFC7230 有點棘手,通常大家會覺得 Web 服務器的確應該在處理這些請求時報錯,因為它們很可能是“走私的(smuggled)”。另一方面,可能又會有人說,代理應該去掉 Content-Length header 然后再轉發(fā)該請求。對此,我們的做法是:我們擴展了 http-parse 然后讓使用該庫的用戶來自主選擇是否要支持這類請求,然后目前我們正在努力為 Envoy 自身引入這項功能的支持。

另外,值得一提的是,我們還遇到了一些常見的配置問題:

  • 熔斷器配置錯誤。根據(jù)我們的經(jīng)驗,如果入站代理采用 Envoy 的話,尤其是工作在 HTTP/1 以及 HTTP/2 這樣的混合環(huán)境里,那么熔斷器的配置不當可能會在流量高峰或后端中斷期間導致意外停機。如果不使用 Envoy 作為 mesh 代理,不妨考慮放松這塊的配置。值得一提的是,在默認情況下,Envoy 的熔斷器限制非常嚴格 ———— 特別要注意這一點!
  • 緩沖區(qū)。 Nginx 允許將磁盤用作請求的緩沖區(qū)。這在用戶采用的是無法理解 chunked 傳輸編碼的傳統(tǒng)的 HTTP/1.0 后端的情況下很有用。Nginx 可以將它們先緩沖到磁盤上然后轉換成帶有 Content-Length 的請求。Envoy 有一個 Buffer 過濾器,但是由于無法將數(shù)據(jù)保存到磁盤,能夠緩沖多少內(nèi)容受限于我們機器的內(nèi)存大小。

下一步

  • HTTP/3 的黃金時代即將來臨。幾個主流瀏覽器均已加入了對 HTTP 3.0 的支持(目前由一個參數(shù)或者命令行選項來控制是否開啟)。Envoy 也已經(jīng)在進行實驗性的支持。升級 Linux 內(nèi)核支持 UDP 加速后,我們會在我們的邊緣環(huán)境上進行試驗。
  • 內(nèi)部基于 xDS 的負載均衡以及異常值檢測。目前,我們正在考慮使用負載上報服務(LRS)和端點發(fā)現(xiàn)服務(EDS)的組合作為構建模塊,以便為 Envoy 和 gRPC 創(chuàng)建一套通用的后備型、負載感知式的負載均衡。
  • 基于 WASM 的 Envoy 擴展。等到 Golang 的 proxy-wasm SDK 可用時,我們便可以開始在 Go 里面編寫 Envoy 的擴展,這將使我們能夠訪問各種內(nèi)部的 Golang 庫。
  • 替換 Bandaid。將所有的 Dropbox 代理層統(tǒng)一成一個單個的數(shù)據(jù)平面聽上去很有吸引力。為此,我們需要將許多 Bandaid 功能(尤其是在負載均衡方面)遷移到 Envoy。這還有很長的路要走,但這是我們目前的計劃。
  • Envoy 移動端。最終,我們想看看能否將 Envoy 用于移動端的應用程序。站在接入層的角度,在所有的移動端平臺上支持一套具有統(tǒng)一的監(jiān)控和現(xiàn)代化功能(HTTP/3,gRPC,TLS1.3 等)的單個服務棧非常耀眼。 

 

責任編輯:未麗燕 來源: Dockone.io
相關推薦

2020-01-13 15:22:42

ERP云平臺遷移

2021-01-28 09:00:00

SQL數(shù)據(jù)庫NoSQL

2017-10-16 00:17:56

云計算信息管理遷移

2023-08-23 09:00:00

區(qū)塊鏈以太坊

2012-08-24 09:07:25

IBMdW

2012-10-29 09:27:16

2015-03-20 13:40:17

2010-07-20 09:48:33

2016-10-26 16:44:44

WatchfinderAWS云計算

2011-09-07 09:30:57

服務器虛擬機

2021-07-13 09:45:48

CentOSAlmaLinux命令

2011-06-24 10:10:35

SVN

2016-11-11 00:00:16

MySQLOracle數(shù)據(jù)

2020-01-06 12:50:50

Windows 7遷移Windows 10

2009-04-16 17:23:37

OracleBasicFileSecureFile

2011-08-09 14:27:16

WindowsServ服務器ADDS

2022-03-04 18:14:26

CentOSLinux

2022-08-29 14:14:22

云計算云遷移數(shù)據(jù)分析

2010-09-29 11:06:21

活動目錄OpenLDAP

2019-01-02 16:40:13

MongoDBPostgres數(shù)據(jù)庫
點贊
收藏

51CTO技術棧公眾號

青青草视频在线观看免费| 中文字幕久久av| 欧美日韩国产综合视频| 久久一区视频| 久久亚洲综合国产精品99麻豆精品福利 | 妺妺窝人体色www在线小说| 无码精品黑人一区二区三区| 免费在线观看精品| 欧美交受高潮1| 日韩免费成人av| 伊人久久亚洲| 欧美体内she精视频| 精品人妻大屁股白浆无码| 黄色免费在线播放| 国产精品一二一区| 国产精品女主播| 国产精品2020| 亚洲最大在线| 日韩欧美一区二区视频| 毛葺葺老太做受视频| 在线观看操人| 国产精品色婷婷| 久久99影院| 国产夫妻自拍av| 免费在线看成人av| 日本一区二区在线播放| 久久久久香蕉视频| 99re久久最新地址获取| 国产视频一区在线| 95视频在线观看| www一区二区三区| 在线视频综合导航| 内射国产内射夫妻免费频道| 污污的网站在线看| 亚洲三级在线看| 亚洲狠狠婷婷综合久久久| 日本一级在线观看| 国产aⅴ精品一区二区三区色成熟| 国产精品户外野外| 国产精品第5页| 国产精品久久久久毛片大屁完整版| 久久6免费高清热精品| 国产一级淫片久久久片a级| 国产不卡一区| 国产亚洲欧洲高清| 国产又粗又猛又爽视频| 校园春色另类视频| 日韩精品视频免费| 国产高清自拍视频| 欧美日韩一区二区三区四区不卡| 精品国产凹凸成av人导航| 日本少妇激三级做爰在线| 精品美女一区| 精品视频123区在线观看| 久久久久久久久久久免费视频| sm在线播放| 精品高清美女精品国产区| 欧洲精品一区二区三区久久| 91资源在线观看| 欧美日韩国产精品一区二区不卡中文| 黄色www网站| 亚洲欧洲美洲av| 日本道免费精品一区二区三区| 黄色片视频在线播放| 韩国主播福利视频一区二区三区| 狠狠综合久久av一区二区小说| 一本大道熟女人妻中文字幕在线| 欧美三级精品| 在线播放中文字幕一区| 国产伦精品一区二区三区妓女下载| 一区三区自拍| 精品视频—区二区三区免费| 右手影院亚洲欧美| 日本一区二区免费高清| 日韩视频在线免费| 激情综合网五月天| 国产欧美一区二区三区国产幕精品| 18一19gay欧美视频网站| 啪啪小视频网站| 国产一区二区三区蝌蚪| 国产一区精品视频| 成人性生交大片免费看午夜| 亚洲人成网站在线| 日本a视频在线观看| 日本精品在线中文字幕| 制服丝袜激情欧洲亚洲| 国产十八熟妇av成人一区| 免费观看不卡av| 久久亚洲私人国产精品va| 日韩污视频在线观看| 日本成人超碰在线观看| 51成人做爰www免费看网站| 婷婷五月综合久久中文字幕| 国产精品私人自拍| 国产二区视频在线| 成人亚洲视频| 亚洲高清色综合| 亚洲天堂最新地址| 精品96久久久久久中文字幕无| 日韩免费不卡av| 国产xxxx在线观看| 国产午夜精品一区二区三区视频| 国产香蕉一区二区三区| 欧洲一区二区三区精品| 日韩一区二区精品葵司在线| 丰满少妇在线观看资源站| 68国产成人综合久久精品| 欧美在线视频观看免费网站| 99热这里只有精品3| 久久婷婷国产综合精品青草| 黄色小视频大全| 欧洲精品一区二区三区| 精品国产一区二区三区久久久蜜月| 成人黄色免费网址| 亚洲国产99| 91亚洲午夜在线| 二人午夜免费观看在线视频| 亚洲成在人线免费| www.污污视频| 日产午夜精品一线二线三线| 欧美精品激情在线| 国产精品久久婷婷| 国产日韩亚洲欧美综合| 欧美视频在线免费播放| 久久久91麻豆精品国产一区| 中日韩午夜理伦电影免费| 国产精品美女久久久久av爽| 国产成人在线观看| 一区二区三区四区不卡| 亚洲精品粉嫩美女一区| 国产午夜精品久久久| 日本在线视频免费观看| 国产精品99久久久久久久女警| 亚洲精品人成| 成人国产一区| 国产香蕉一区二区三区在线视频| 日韩欧美激情视频| 成人免费视频app| 少妇久久久久久被弄到高潮| 免费观看亚洲天堂| 操91在线视频| 99国产精品一区二区三区| 国产精品美女一区二区三区| 国产成人手机视频| 精品理论电影在线| 国产精品99蜜臀久久不卡二区| 偷拍自拍在线| 欧美午夜片在线免费观看| 人妻换人妻a片爽麻豆| 欧美午夜视频| 国产精品白丝jk白祙| 日韩精品亚洲人成在线观看| 日韩视频在线永久播放| 免费在线观看国产精品| 国产成人精品综合在线观看| 国产乱人伦精品一区二区三区| 视频国产精品| 久久久噜噜噜久久久| 男人的天堂a在线| 精品欧美国产一区二区三区| 人妻丰满熟妇aⅴ无码| 久久久久久网| 亚洲国产精品www| 日日夜夜综合| 色综合久久精品亚洲国产| 亚洲av无码国产精品久久不卡 | 日本三级福利片| 欧美第一在线视频| 欧美黄色免费网站| 天堂中文在线观看视频| 色综合中文字幕| 欧美成人另类视频| 韩日av一区二区| 国产精品久久久久久久久电影网| 蜜桃一区av| 国产精品久久久久av| 九七电影韩国女主播在线观看| 欧美videos大乳护士334| 99视频在线看| 国产精品午夜久久| 日本女人性视频| 亚洲免费网站| 制服丝袜综合日韩欧美| 超碰成人福利| 国产精品免费一区二区三区都可以| 麻豆tv免费在线观看| 亚洲福利在线播放| 中文字幕在线播放日韩| 一区av在线播放| 国产免费无遮挡吸奶头视频| 国产一区二区三区四区五区美女 | 国产激情无码一区二区三区| 国产不卡在线视频| 国产三级日本三级在线播放| 欧美韩日精品| 日本欧洲国产一区二区| 久久久久毛片免费观看| 国产成人久久精品| 久色国产在线| 最近2019免费中文字幕视频三| 亚洲精品免费在线观看视频| 欧美性淫爽ww久久久久无| 国产一级片免费看| 国产精品久久久久久妇女6080 | 91av在线免费观看| 麻豆网在线观看| 亚洲电影在线看| 91成人国产综合久久精品| 欧美日韩国产精品一区二区不卡中文| 四虎永久免费在线| 国产人妖乱国产精品人妖| 午夜影院福利社| 韩国理伦片一区二区三区在线播放| 免费看的黄色大片| 欧美视频网站| 99re99热| 久久人人99| 欧美二区三区在线| 久久国产精品色av免费看| 亚洲xxxx在线| 国产精品一区免费在线 | 久久久久久久久亚洲| 亚洲麻豆精品| 一区三区二区视频| 你懂的在线观看| 亚洲国产精品推荐| 性猛交xxxx乱大交孕妇印度| 欧美丰满一区二区免费视频 | 亚洲综合网在线| 中文字幕欧美三区| 成人精品999| 久久婷婷久久一区二区三区| 国产精品久久久免费观看| 国产成人午夜电影网| 91在线第一页| 国产在线视频一区二区三区| 污污的视频免费| 免费成人你懂的| 久久婷婷综合色| 青青草97国产精品免费观看 | 国产欧美日韩另类视频免费观看| 国产高清自拍视频| 97国产一区二区| a视频免费观看| proumb性欧美在线观看| 亚洲中文字幕无码一区| www.在线欧美| 欧美双性人妖o0| 99久久亚洲一区二区三区青草 | 国偷自产av一区二区三区| av噜噜色噜噜久久| 午夜日韩影院| 国产精品v欧美精品v日韩| caoporn成人免费视频在线| 5566av亚洲| 91精品久久久久久综合五月天| 国产91aaa| 久久资源综合| 久久婷婷开心| 国产videos久久| 一级二级三级欧美| 希岛爱理一区二区三区| 女人床在线观看| 最新日韩在线| 北条麻妃在线一区| 青青草原综合久久大伊人精品优势| 天天干天天草天天| 国产一区二区三区黄视频| 欧美高清精品一区二区| 成人av资源站| 亚洲av熟女国产一区二区性色| 日本一区二区三区高清不卡| 99精品中文字幕| 一区二区三区欧美日韩| 国产精品500部| 在线免费视频一区二区| 国产精品玖玖玖| 精品日韩一区二区三区免费视频| 天天干天天色天天| 国产一区二区三区精品久久久| 日本在线观看| 国语对白做受69| 欧亚一区二区| 91沈先生播放一区二区| 综合亚洲色图| 一区二区不卡在线| 欧美另类综合| 男人女人黄一级| 国产一区二区精品在线观看| 成人免费毛片日本片视频| 国产人久久人人人人爽| 久久精品国产亚洲av无码娇色 | 91视频免费版污| 国产福利91精品一区二区三区| 国内精品久久99人妻无码| 国产精品国产三级国产有无不卡| 国产在线视频你懂的| 欧美色成人综合| 色呦呦中文字幕| 中文字幕在线视频日韩| 成人免费一区二区三区牛牛| 国产精品扒开腿爽爽爽视频| 中文字幕一区二区三区中文字幕| 蜜桃网站成人| 欧美日韩国产一区精品一区| caoporn超碰97| 成人小视频免费在线观看| 亚洲女人毛茸茸高潮| 欧美日在线观看| 性一交一乱一乱一视频| 在线播放日韩av| 欧亚在线中文字幕免费| 亚洲精品女av网站| av在线不卡顿| 久久精品免费一区二区| 国产精品亚洲人在线观看| 欧洲美一区二区三区亚洲| 亚洲成人高清在线| 国产三级三级在线观看| 一区二区中文字幕| 欧美极品影院| 国产偷久久久精品专区| 亚洲激情中文在线| 国产三级三级三级看三级| 91蜜桃网址入口| 五月天婷婷网站| 日韩一区二区免费视频| 国产三区在线观看| 国产精品自产拍在线观看中文| 午夜欧洲一区| 尤物av无码色av无码| 国产精品18久久久久久久网站| 国产一区在线观看免费| 欧美亚洲一区二区在线观看| 三级在线播放| 91a在线视频| 欧美一级全黄| 精品国产一二三四区| 成人18视频日本| 国产第一页第二页| 欧美va在线播放| 好看的中文字幕在线播放| av在线亚洲男人的天堂| 你懂的视频一区二区| 在线观看中文av| 亚洲综合一区在线| 午夜精品一二三区| 欧美片一区二区三区| 日韩高清二区| 国产aaa免费视频| 成人激情免费网站| 日本少妇激情舌吻| 日韩国产精品一区| 中文字幕21页在线看| 欧洲亚洲一区二区三区四区五区| 国产农村妇女精品一二区| 色呦呦一区二区| 在线免费观看不卡av| 成年人免费在线视频| 国产精品自产拍在线观| 性欧美欧美巨大69| 中文字幕第三区| 亚洲高清中文字幕| 视频三区在线观看| 国产精品久久久999| 久久综合国产| 老女人性生活视频| 午夜精品影院在线观看| 精品无人乱码| 国产在线拍偷自揄拍精品| 在线中文字幕亚洲| 国产麻豆剧传媒精品国产av| 色香蕉成人二区免费| 亚洲欧美视频一区二区| 国产精品入口免费| 久久国产福利| 婷婷国产成人精品视频| 精品国产一区二区三区不卡| av日韩电影| 正在播放亚洲| 91丝袜高跟美女视频| 亚洲精品无码久久久久| 欧美老肥婆性猛交视频| 全国精品免费看| 爱爱爱爱免费视频| 午夜天堂影视香蕉久久| 国产高清一级毛片在线不卡| 91网站在线免费观看| 中日韩视频在线观看| 国产馆在线观看| 亚洲第一免费网站| 99精品国自产在线| 国产真人做爰毛片视频直播| 国产欧美精品在线观看| 性一交一乱一乱一视频| 国产精品久久久av| 一本久久综合| 午夜剧场免费在线观看| 亚洲欧洲xxxx| 亚洲精品不卡在线观看|