摘要:作為一組獨立的微服務之一被實現并作為一個多帶帶的進行發布。該配置將暴露所有由網關發布的,入口位于,用協議加密保護。由網關發布的所有的所有后端服務均在中被定義。與瀏覽器不同,網關并不能向客戶端發送帶有新的命名的重定向。
歡迎大家前往騰訊云+社區,獲取更多騰訊海量技術實踐干貨哦~
本文來自云+社區翻譯社,作者ArrayZoneYour
Nginx往往是構建微服務中必不可缺的一部分,從本文中你可以習得如何使用Nginx作為API網關。
HTTP API是現代應用架構的核心。HTTP協議使開發者可以更快地構建應用并使應用的維護變得更加容易。HTTP API提供了一套通用的接口,這使得在任意的應用規模下,我們都可以借助HTTP API從一個基本的微服務開始構建出一個具有完備功能的整體。借助HTTP,普通的web應用程序也可以在規模巨大的互聯網上提供高性能、高可用的API。
如果你還不理解API網關對微服務應用的重要性,可以參閱Building Microservices: Using an API Gateway
作為領先的高性能、輕量級反向代理和負載均衡器解決方案,NGINX Plus具有處理API流量所需的高級HTTP處理能力。這使得NGINX Plus成為構建API網關的理想平臺。在本文中,我們將使用一些常見的API網關為例展示如何配置NGINX Plus來以高效、可擴展、易維護的方式處理它們。最后我們會得到一套可作為生產環境部署基礎的完整配置。
注:除特殊注明外,本文中所有的配置同時適用于NGINX和NGINX Plus。
樣例API簡介(以倉儲背景為例)API網關的主要功能是為不同的API分別提供多帶帶,一致的入口點,它的實現與后端的實現與部署方式無關。實際場景中,往往不是所有的API都是以微服務的方式實現的。我們的API網關需要同時管理現有的API、巨無霸式的API(monoliths, 對與微服務相對的龐然大物的戲稱)以及開始局部切換為微服務的應用等等。
在本文中,我們假想一個庫存管理的API(WareHouse API)為例進行說明。我們使用實例的配置代碼來說明不同的用例。我們假設的API是一個RESTful API,它接受JSON請求并生成JSON數據響應請求。雖然我們本文中是以RESTful API為例進行講解,但是NGINX Plus作為API網關部署時并不要求或者限制JSON的使用;NGINX Plus本身并不知道API使用的架構或者數據格式。
WareHouse API 作為一組獨立的微服務之一被實現并作為一個多帶帶的API進行發布。其下的inventory 和 pricing 資源分別作為多帶帶的服務集成并部署在不同的后端上。由此可以畫出如下的API路徑結構:
api └── warehouse ├── inventory └── pricing
舉例來說,如果我們想獲得倉庫的庫存信息,則需要通過客戶端發送一個 HTTP GET請求到/api/warehouse/inventory.
API結構示意圖
組織NGINX的配置文件我們使用NGINX Plus作為API網關的好處是它可以同時扮演反向代理、負載均衡器以及現有HTTP流量所需的web服務器這三個角色。如果NGINX Plus已經是你的應用交付棧的一部分,那么你不需要再用它部署一個多帶帶的API網關。不過,API網關預期的默認行為與基于瀏覽器的流量所期望的默認行為不同,因此我們需要將API網關配置與現存(未來)的基于瀏覽器所需的流量對應的配置文件分來。
為了實現上述需求,我們為配置文件創建了以下目錄結構來支持多用途的NGINX Plus實例,這也為通過CI / CD 管道自動配置并部署提供了便利。
etc/ └── nginx/ ├── api_conf.d/ ....................................... API配置的子目錄 │ └── warehouse_api.conf ...... Warehouse API 的定義及配置 ├── api_backends.conf ..................... 后端服務配置 (upstreams) ├── api_gateway.conf ........................ API網關服務器的頂級配置 ├── api_json_errors.conf ............ JSON格式的HTTP錯誤響應配置 ├── conf.d/ │ ├── ... │ └── existing_apps.conf └── nginx.conf
API網關配置的目錄和文件名都加了api_前綴。上面的每個目錄和文件都對應著API網關的不同功能和特性,我們在下面會逐個詳細解釋。
定義API網關的頂級配置NGINX讀取配置將從主配置文件nginx.conf開始。為了讀取API網關配置,我們需要在nginx.conf中http塊中添加一條指令來引用包含網關配置的文件api_gateway.conf (大概在28行附近)。從文件內容中我們可以看到nginx.conf中默認從conf.d子目錄中讀取基于瀏覽器的HTTP配置。本文中將廣泛使用include命令來提高可讀性并實現部分配置的自動化。
include /etc/nginx/api_gateway.conf; # 所有的API網關配置 include /etc/nginx/conf.d/*.conf; # 正常的web流量配置
api_gateway.conf文件定義了將NGINX Plus作為API網關暴露給客戶端的虛擬服務器的配置。該配置將暴露所有由API網關發布的API,入口位于https://api.example.com/,用TLS協議加密保護。注意這里使用的配置文件是針對HTTPS的——并沒有使用明文傳輸的HTTP。這代表著我們默認并要求API客戶端知道正確的入口點并使用HTTPS連接。
log_format api_main "$remote_addr - $remote_user [$time_local] "$request"" "$status $body_bytes_sent "$http_referer" "$http_user_agent"" ""$http_x_forwarded_for" "$api_name""; include api_backends.conf; include api_keys.conf; server { set $api_name -; # Start with an undefined API name, each API will update this value access_log /var/log/nginx/api_access.log api_main; # Each API may also log to a separate file listen 443 ssl; server_name api.example.com; # TLS 配置 ssl_certificate /etc/ssl/certs/api.example.com.crt; ssl_certificate_key /etc/ssl/private/api.example.com.key; ssl_session_cache shared:SSL:10m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_protocols TLSv1.1 TLSv1.2; # API 定義, 每個文件對應一個 include api_conf.d/*.conf; # 錯誤響應 error_page 404 = @400; # 處理非法URI路徑的請求 proxy_intercept_errors on; # 不將后端的錯誤消息發送給客戶端 include api_json_errors.conf; # 定義返回給客戶端的JSON響應數據 default_type application/json; # 如果不指定 content-type 則默認為 JSON }
以上配置是靜態的,表現在每個獨立API的細節以及響應的后端服務是通過include命令引用相應的文件實現的。上面文件的最后四行負責處理默認的日志輸出以及錯誤處理。我們將在后面的 錯誤響應 一節中多帶帶討論。
單服務 vs. 微服務 API后端一些API可以通過單個后端實現,但是出于彈性或者負載均衡等原因,我們通常期望有不止一個后端。通過微服務的API,我們可以為每個服務定義多帶帶的后端,將他們組合在一起就形成了完整的API。在本文中,我們的倉儲API被部署為兩個獨立的服務,每一個都有多個后端。
upstream warehouse_inventory { zone inventory_service 64k; server 10.0.0.1:80; server 10.0.0.2:80; server 10.0.0.3:80; } upstream warehouse_pricing { zone pricing_service 64k; server 10.0.0.7:80; server 10.0.0.8:80; server 10.0.0.9:80; }
由API網關發布的所有API的所有后端API服務均在api_backends.conf中被定義。這里我們在每個塊中使用了多個IP地址-端口對來指示API代碼的部署位置,我們也可以使用主機名來替換IP地址。NGINX Plus 的訂閱用戶還可以使用動態的DNS負載均衡功能自動地將新的后端添加至在線運行配置。
定義Warehouse API這部分配置首先定義了Warehouse API的有效URI,然后定義了處理Warehouse API請求所用的通用策略。
# API 定義 # location /api/warehouse/inventory { set $upstream warehouse_inventory; rewrite ^ /_warehouse last; } location /api/warehouse/pricing { set $upstream warehouse_pricing; rewrite ^ /_warehouse last; } # 策略 # location = /_warehouse { internal; set $api_name "Warehouse"; # 在這里配置相應的策略 (認證, 限速, 日志記錄, ...) proxy_pass http://$upstream$request_uri; }
Warehouse API 通過一系列配置塊來定義。NGINX Plus具有靈活和高效的系統,這使得它可以將請求的URI與相應的配置塊匹配。一般來說請求會通過具體的路徑前綴進行匹配,location指令的順序并不重要。在上面的配置中我們在第三行和第八行定義了兩個路徑前綴。在每個配置中,$upstream變量被設定為分別代表 inventory 和 pricing 的后端API服務。
此處這樣配置的目的是將API的定義與API的交付邏輯分離。為了實現這一目標,我們盡量減少了API定義部分的配置內容。當我們為每個 location 確定了合適的 upstream 組之后,可以使用指令來查找相應的API策略。
使用rewrite指令將處理權移交給策略塊
rewrite指令的結果是NGINX Plus搜索開頭為/_warehouse的URI對應的 location 塊。上面的配置中使用了 = 修飾符來進行精確匹配,這提升了處理的速度。
在這個階段,我們的策略塊內容非常簡單。在配置中的 iternal 意味著客戶端不能直接向它發出請求。$api_name變量被重新定義為匹配API的名稱,以便它可以在日志文件中正常顯示。最后請求會通過使用 $request_uri 變量(包含未修改的原始請求URI)代理至API定義部分中指定的 upstreame 組。
API的 寬松定義 vs. 精確定義API的定義有兩種方法——寬松的或者精確的。每個API最適合的方法取決于API的安全要求以及后端服務是否需要處理無效的URI。
在warehouse_api.simple.conf文件中,我們使用了寬松的方式來定義Warehouse API。這意味著任何前綴滿足要求的URI都會被代理到相應的后端服務,即以下URI的API請求都會被作為有效URI進行處理:
/api/warehouse/inventory
/api/warehouse/inventory/
/api/warehouse/inventory/foo
/api/warehouse/inventoryfoo
/api/warehouse/inventoryfoo/bar/
如果我們只需要考慮將每個請求代理到正確的后端服務,那么寬松的定義可以提供最快的處理速度和最緊湊的配置。相對地,使用精確的定義方法可以通過明確定義每個可用API資源的URI路徑來了解API的完整URI空間。Warehouse API 的下列配置結合使用完全匹配 ( = ) 和正則表達式 ( ~ ) 實現了對每個URI的精確匹配。
location = /api/warehouse/inventory { # Complete inventory set $upstream inventory_service; rewrite ^ /_warehouse last; } location ~ ^/api/warehouse/inventory/shelf/[^/]*$ { # Shelf inventory set $upstream inventory_service; rewrite ^ /_warehouse last; } location ~ ^/api/warehouse/inventory/shelf/[^/]*/box/[^/]*$ { # Box on shelf set $upstream inventory_service; rewrite ^ /_warehouse last; } location ~ ^/api/warehouse/pricing/[^/]*$ { # Price for specific item set $upstream pricing_service; rewrite ^ /_warehouse last; }
上面的配置雖然啰嗦一點,但是更準確地描述了后端服務實現的資源。這可以使后端服務免受惡意用戶請求的影響,但是會增加額外的開銷來處理正則表達式的匹配。在這種配置下,NGINX Plus會接受部分URI,其余的會被視為無效而被拒絕:
匹配示例
使用精確的API定義可以利用現有的API文檔格式驅動API網關的配置,使OpenAPI規范(過去稱為Swagger)下的NGINX Plus API定義自動化。本文配套提供了相應的示例腳本。
重寫客戶端請求隨著API的發展,有時出現的突發情況或變化要求更新客戶端的請求。一個典型的例子就是原有的API資源被重命名或者移除。與web瀏覽器不同,API網關并不能向客戶端發送帶有API新的命名的重定向。不過幸運的是,我們可以通過重寫客戶端請求來解決這個問題。
在下面的代碼中,我們可以看到在第三行的位置,pricing服務之前是作為inventory服務的一部分實現的。所以現在我們使用rewrite指令來將舊的pricing資源請求切換至了對新的pricing資源的請求。
# 重寫規則 # rewrite ^/api/warehouse/inventory/item/price/(.*) /api/warehouse/pricing/$1; # API 定義 # location /api/warehouse/inventory { set $upstream inventory_service; rewrite ^(.*)$ /_warehouse$1 last; } location /api/warehouse/pricing { set $upstream pricing_service; rewrite ^(.*) /_warehouse$1 last; } # 處理策略 # location /_warehouse { internal; set $api_name "Warehouse"; # 在這里配置相應的策略 (認證, 限速, 日志記錄, ...) rewrite ^/_warehouse/(.*)$ /$1 break; # 移除 /_warehouse 前綴 proxy_pass http://$upstream; # 代理重寫后的URI }
不過使用重寫URI也意味著在上面代碼的倒數第二行我們處理代理請求的時候不能再使用$request_uri變量(像warehouse_api_simple.conf的第21行的做法一樣)。所以我們需要在上述代碼的第9行和第14行的位置使用不同的rewrite指令之后將URI移交給策略部分的代碼塊進行處理。
location /api/warehouse/inventory
錯誤響應基于HTTP API和瀏覽器的流量之間的一個關鍵區別是錯誤傳遞給客戶端的方式。當我們配置NGINX Plus作為API網關時,我們將其配置其以最適合API客戶端的方式返回錯誤信息。
# 錯誤響應 error_page 404 = @400; # 處理非法URI路徑的請求 proxy_intercept_errors on; # 不將后端的錯誤消息發送給客戶端 include api_json_errors.conf; # 定義返回給客戶端的JSON響應數據 default_type application/json; # 如果不指定 content-type 則默認為 JSON
上面的代碼展示了我們在頂層的API網關中關于錯誤響應的配置。
由于上面第二行的配置,當請求不能夠匹配到任何的API定義時,我們將返回該行定義的錯誤而不是NGINX Plus默認的錯誤響應給客戶端。這個可選的行為要求客戶端按照滿足API文檔規范的方式進行請求,這避免了未經授權的用戶通過API網關發現API的URI結構。
proxy_interceprt_errors指的是后端服務生成的錯誤信息。原始的錯誤信息可能包含著錯誤的堆棧信息或者其他以及一些其他我們不希望客戶端看到的敏感信息。打開這一配置之后,我們將錯誤信息標準化之后再發送給客戶端,從而進一步提升信息的安全級別。
再下一行,我們通過include指令引入了錯誤響應的完整列表,下面展示了其中的前幾行。如果你想采用JSON以外的其他錯誤格式,那么你可以修改最后一行default_type指定的內容。你還可以在每個API的策略塊中使用include指令來導入列表覆蓋默認的錯誤響應。
error_page 400 = @400; location @400 { return 400 "{"status":400,"message":"Bad request"} "; } error_page 401 = @401; location @401 { return 401 "{"status":401,"message":"Unauthorized"} "; } error_page 403 = @403; location @403 { return 403 "{"status":403,"message":"Forbidden"} "; } error_page 404 = @404; location @404 { return 404 "{"status":404,"message":"Resource not found"} "; }
在配置完成之后,此時客戶端發送無效的URI請求時會得到如下響應:
$ curl -i https://api.example.com/foo HTTP/1.1 400 Bad Request Server: nginx/1.13.10 Content-Type: application/json Content-Length: 39 Connection: keep-alive {"status":400,"message":"Bad request"}身份認證集成
在發布API時,我們通常都會通過身份認證來保護它們。NGINX Plus提供了幾種方法來保護API以及驗證API客戶端。相關的具體信息可以參閱NGINX官方文檔中的IP address?based access control lists,digital certificate authentication以及HTTP Basic authentication部分。在本文中,我們將專注于適用于API的認證方法。
API秘鑰認證API秘鑰是客戶端和API網關同時掌握其內容的共享秘鑰。其本質就是一個長度很長的復雜密碼,它通常作為一個長期憑證提供給API客戶端。創建API秘鑰的操作十分簡單,你只需要像下面一樣編碼一個隨機數即可。
$ openssl rand -base64 18 7B5zIqmRGXmrJTFmKa99vcit
現在回到頂層的API網關配置文件api_gateway.conf,可以看到第6行我們include了一個名為api_key.conf的文件,它包含著每個API客戶端的API秘鑰信息以及相匹配的客戶端名稱或相關描述。
map $http_apikey $api_client_name { default ""; "7B5zIqmRGXmrJTFmKa99vcit" "client_one"; "QzVV6y1EmQFbbxOfRCwyJs35" "client_two"; "mGcjH8Fv6U9y3BVF9H3Ypb9T" "client_six"; }
可以看到API秘鑰被定義在上面展示的代碼塊當中。其中的map指令接受了兩個參數。第一個參數定義了尋找API秘鑰的位置,這里我們通過獲取客戶端HTTP請求頭中的apikey作為變量$http_api_key接收。第二個參數創建了一個新變量$api_client_name并且將其與第一個參數即同行的API秘鑰相匹配。
此時,如果客戶端提供了API秘鑰7B5zIqmRGXmrJTFmKa99vcit是,變量$api_client_name會被設置為client_one。這個變量可以用于檢驗通過身份驗證的客戶端以及對日志的進一步審計。
可以看到map塊的格式非常簡單,這使得我們可以很容易地將api_keys.conf的生成集成到自動化的工作流當中。之后可以在API的策略塊中完成API秘鑰的校驗邏輯。
# 策略塊 # location = /_warehouse { internal; set $api_name "Warehouse"; if ($http_apikey = "") { return 401; # Unauthorized (please authenticate) } if ($api_client_name = "") { return 403; # Forbidden (invalid API key) } proxy_pass http://$upstream$request_uri; }
我們希望發送請求的客戶端都在它們的HTTP頭部中指定apikey內容為客戶端持有的API秘鑰。如果沒有HTTP頭信息或者其中沒有apikey,我們將返回給客戶端401狀態碼要求其完成認證。如果客戶端發送的API秘鑰不存在于api_keys.conf當中,$api_client_name會被設置為默認值即空字符串——此時我們將返回403狀態碼來告訴客戶端其認證無效。
完成以上配置之后,Warehouse API現在已經可以支持API秘鑰校驗了。
$ curl https://api.example.com/api/warehouse/pricing/item001 {"status":401,"message":"Unauthorized"} $ curl -H "apikey: thisIsInvalid" https://api.example.com/api/warehouse/pricing/item001 {"status":403,"message":"Forbidden"} $ curl -H "apikey: 7B5zIqmRGXmrJTFmKa99vcit" https://api.example.com/api/warehouse/pricing/item001 {"sku":"item001","price":179.99}JWT認證
現在,JSON Web Token ( JWT )已經越來越廣泛地被應用于API認證。不過要注意的是原生JWT支持是NGINX Plus才有的特性。關于如何啟用JWT支持可以參閱Authenticating API Clients with JWT and NGINX Plus。
總結本文是部署NIGNX Plus作為API網關系列文章中的第一篇。本文中使用到的所有文件可以在我們的GitHub Gist repo上下載或查看。在本系列的下一篇文章中我們將探討更高級的用例以保護后端服務免受惡意或者非法操作的用戶的侵害。
問答
如何用nginx編寫url重寫?
相關閱讀
如何用Nginx快速搭建一個安全的微服務架構?
Nginx 原理解析和配置摘要
使用API網關構建微服務
此文已由作者授權騰訊云+社區發布,原文鏈接:https://cloud.tencent.com/dev...
歡迎大家前往騰訊云+社區或關注云加社區微信公眾號(QcloudCommunity),第一時間獲取更多海量技術實踐干貨哦~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/39989.html
摘要:上篇文章緩存機制介紹了的緩存機制,相信大家對有了進一步的了解,本文將詳細介紹網關如何實現服務下線的實時感知。目前網關實現的是對網關下游服務的實時感知,而且需滿足以下條件生產者需部署在容器管理平臺生產者做正常的下線升級或者縮容操作。 上篇文章《Eureka 緩存機制》介紹了Eureka的緩存機制,相信大家對Eureka 有了進一步的了解,本文將詳細介紹API網關如何實現服務下線的實時感知...
摘要:無論是將其用作的服務器反向代理服務器負載均衡器,還是同時使用以上三種功能,和都能帶來很大好處。再就是下篇文章會介紹如何把和當作反向代理服務器和多個應用程序服務器的負載均衡器。而使用將會有助于解決這一問題。 【編者按】本文主要介紹 nginx 的主要功能以及如何通過 NGINX 優化 Python 應用性能。本文系國內 ITOM 管理平臺 OneAPM 編譯呈現。 Python 的著名之...
摘要:無論是將其用作的服務器反向代理服務器負載均衡器,還是同時使用以上三種功能,和都能帶來很大好處。再就是下篇文章會介紹如何把和當作反向代理服務器和多個應用程序服務器的負載均衡器。而使用將會有助于解決這一問題。 【編者按】本文主要介紹 nginx 的主要功能以及如何通過 NGINX 優化 Python 應用性能。本文系國內 ITOM 管理平臺 OneAPM 編譯呈現。 Python 的著名之...
摘要:為萬開發者提供每月數十億的請求支持。在請求和響應之間,將執行任何安裝的插件,擴展的功能集。其有效的成為每個的請求入口。主要組件介紹基于服務器,用來接受請求的。總結就是一個針對管理系統,并提供了很多關于網關功能的擴展插件介紹插件使用腳本編寫。 1、簡介 Kong 是一個企業級服務網關,底層是使用lua語言,整合Nginx 實現了強大的服務轉發,路由,驗證功能, 1.2 官方描述 Kong...
閱讀 2302·2021-09-30 09:47
閱讀 2219·2021-09-26 09:55
閱讀 2948·2021-09-24 10:27
閱讀 1540·2019-08-27 10:54
閱讀 967·2019-08-26 13:40
閱讀 2495·2019-08-26 13:24
閱讀 2419·2019-08-26 13:22
閱讀 1728·2019-08-23 18:38