通過實例理解API網(wǎng)關(guān)的主要功能特性
在當(dāng)今的技術(shù)領(lǐng)域中,“下云”的概念正逐漸抬頭,像David Heinemeier Hansson[1](37signals公司的聯(lián)合創(chuàng)始人, Ruby on Rails的Creator)就直接將公司所有的業(yè)務(wù)都從公有云搬遷到了自建的數(shù)據(jù)中心[2]中。雖說大多數(shù)企業(yè)不會這么“極端”,但隨著企業(yè)對云原生架構(gòu)采用的廣泛與深入,不可避免地面臨著對云服務(wù)的依賴。云服務(wù)在過去的幾年中被廣泛應(yīng)用于構(gòu)建靈活、可擴展的應(yīng)用程序和基礎(chǔ)設(shè)施,為企業(yè)提供了許多便利和創(chuàng)新機會。然而,隨著業(yè)務(wù)規(guī)模的增長和數(shù)據(jù)量的增加,云服務(wù)的成本也隨之上升。企業(yè)開始意識到,對云服務(wù)的依賴已經(jīng)成為一個值得重新評估的議題。云服務(wù)的開銷可能占據(jù)了企業(yè)可用的預(yù)算的相當(dāng)大部分。為了保持競爭力并更好地控制成本,企業(yè)需要尋找方法來減少對云服務(wù)的依賴,尋找更經(jīng)濟的解決方案,同時確保仍能獲得所需的性能、安全性和可擴展性。
在這樣的背景下,我們的關(guān)注點是選擇一款適宜的API網(wǎng)關(guān),從主流功能特性的角度來評估候選者的支持。API網(wǎng)關(guān)作為現(xiàn)代云原生應(yīng)用架構(gòu)中的關(guān)鍵組件,扮演著連接前端應(yīng)用和后端服務(wù)的中間層,負(fù)責(zé)管理、控制和保護API的訪問。它的功能特性對于確保API的安全性、可靠性和可擴展性至關(guān)重要。
盡管API網(wǎng)關(guān)并不是一個新鮮事物了,但對于那些長期依賴于云供應(yīng)商的服務(wù)的人來說,它似乎變得有些“陌生”。因此,本文旨在幫助我們重新理解API網(wǎng)關(guān)的主要特性,并獲得對API網(wǎng)關(guān)選型的能力,以便在停止使用云供應(yīng)商服務(wù)之前,找到一個合適的替代品^_^。
1. API網(wǎng)關(guān)回顧
API網(wǎng)關(guān)是現(xiàn)代應(yīng)用架構(gòu)中的關(guān)鍵組件之一,它的存在簡化了應(yīng)用程序的架構(gòu),并為客戶端提供一個單一的訪問入口,并進行相關(guān)的控制、優(yōu)化和管理。API網(wǎng)關(guān)可以幫助企業(yè)實現(xiàn)微服務(wù)架構(gòu)、提高系統(tǒng)的可擴展性和安全性,并提供更好的開發(fā)者體驗和用戶體驗。
1.1 API網(wǎng)關(guān)的演化
隨著互聯(lián)網(wǎng)的快速發(fā)展和企業(yè)對API的需求不斷增長,API網(wǎng)關(guān)作為一種關(guān)鍵的中間層技術(shù)逐漸嶄露頭角并經(jīng)歷了一系列的演進和發(fā)展。這里將API網(wǎng)關(guān)的演進歷史粗略分為以下幾個階段:
- API網(wǎng)關(guān)之前的早期階段
在互聯(lián)網(wǎng)發(fā)展的早期階段,大多數(shù)應(yīng)用程序都是以單體應(yīng)用的形式存在[3]。后來隨著應(yīng)用規(guī)模的擴大和業(yè)務(wù)復(fù)雜性的增加,單體應(yīng)用的架構(gòu)變得不夠靈活和可擴展,面向服務(wù)架構(gòu)(Service-Oriented Architecture,SOA)逐漸興起,企業(yè)開始將應(yīng)用程序拆分成一組獨立的服務(wù)。這個時期,每個服務(wù)都是獨立對外暴露API,客戶端也是通過這些API直接訪問服務(wù),但這會導(dǎo)致一些安全性、運維和擴展性的問題。之后,企業(yè)也開始意識到需要一種中間層來管理和控制這種客戶端到服務(wù)的通信行為,并確保服務(wù)的可靠性和安全性,于是開始有了API網(wǎng)關(guān)的概念。
- API網(wǎng)關(guān)的興起
早期的API網(wǎng)關(guān),其主要功能就是單純的路由和轉(zhuǎn)發(fā)。API網(wǎng)關(guān)將請求從客戶端轉(zhuǎn)發(fā)到后端服務(wù),并將后端服務(wù)的響應(yīng)返回給客戶端。在這個階段,API網(wǎng)關(guān)的功能非常簡單,主要用于解決客戶端和后端服務(wù)之間的通信問題。
- API網(wǎng)關(guān)的成熟
隨著微服務(wù)架構(gòu)的興起和API應(yīng)用的不斷發(fā)展,企業(yè)開始將應(yīng)用程序進一步拆分成更小的、獨立部署的微服務(wù)。每個對外暴露的微服務(wù)都有自己的API,并通過API網(wǎng)關(guān)進行統(tǒng)一管理和訪問。API網(wǎng)關(guān)在微服務(wù)架構(gòu)中的作用變得更加重要,它的功能也逐漸豐富起來了。
在這一階段,它不僅負(fù)責(zé)路由和轉(zhuǎn)發(fā)請求,API網(wǎng)關(guān)還增加了安全和治理的功能,可以滿足幾個不同領(lǐng)域的微服務(wù)需求。比如:API網(wǎng)關(guān)可以通過身份認(rèn)證、授權(quán)、訪問控制等功能來保護API的安全;通過基于重試、超時、熔斷的容錯機制等來對API的訪問進行治理;通過日志記錄、基于指標(biāo)收集以及Tracing等對API的訪問進行觀測與監(jiān)控;支持實時的服務(wù)發(fā)現(xiàn)等。
API網(wǎng)關(guān)(圖來自網(wǎng)絡(luò))
- API網(wǎng)關(guān)的云原生化
隨著云原生技術(shù)的發(fā)展,如容器化和服務(wù)網(wǎng)格(Service Mesh)等,API網(wǎng)關(guān)也在不斷演進和適應(yīng)新的環(huán)境。在云原生環(huán)境中,API網(wǎng)關(guān)實現(xiàn)了與容器編排系統(tǒng)(如Kubernetes)和服務(wù)網(wǎng)格集成,其自身也可以作為一個云原生服務(wù)來部署,以實現(xiàn)更高的可伸縮性、彈性和自動化。同時,新的技術(shù)和標(biāo)準(zhǔn)也不斷涌現(xiàn),如GraphQL和gRPC等,API網(wǎng)關(guān)也增加了對這些新技術(shù)的集成和支持。
1.2 API網(wǎng)關(guān)的主要功能特性
從上面的演化歷史我們看到:API網(wǎng)關(guān)的演進使其從最初簡單的請求轉(zhuǎn)發(fā)角色,逐漸成為整個API管理和微服務(wù)架構(gòu)中的關(guān)鍵組件。它不僅扮演著API管理層與后端服務(wù)層之間的適配器,也是云原生架構(gòu)中不可或缺的基礎(chǔ)設(shè)施,使微服務(wù)管理更加智能化和自動化。下面是現(xiàn)代API網(wǎng)關(guān)承擔(dān)的主要功能特性,我們后續(xù)也會基于這些特性進行示例說明:
- 請求轉(zhuǎn)發(fā)和路由
- 身份認(rèn)證和授權(quán)
- 流量控制和限速
- 高可用與容錯處理
- 監(jiān)控和可觀測性
2. 那些主流的API網(wǎng)關(guān)
下面是來自CNCF Landscape[4]中的主流API網(wǎng)關(guān)集合(截至2023.11月),圖中展示了關(guān)于各個網(wǎng)關(guān)的一些細(xì)節(jié),包括star數(shù)量和背后開發(fā)的公司或組織:
圖片
主流的API網(wǎng)關(guān)還有各大公有云提供商的實現(xiàn),比如:Amazon的API Gateway[5]、Google Cloud的API Gateway[6]以及上圖中的Azure API Management等,但它們不在我們選擇范圍之內(nèi);雖然被CNCF收錄,但多數(shù)API網(wǎng)關(guān)受到的關(guān)注并不高,超過1k star的不到30%,這些不是很受關(guān)注或dev不是那么active的項目也無法在生產(chǎn)環(huán)境擔(dān)當(dāng)關(guān)鍵角色;而像APISIX[7]、Kong[8]這兩個受關(guān)注很高的網(wǎng)關(guān),它們是建構(gòu)在Nginx之上實現(xiàn)的,技術(shù)棧與我們不契合;而像EMISSARY INGRESS[9]、Gloo等則是完全云原生化或者說是Kubernetes Native的,無法在無Kubernetes的基于VM或裸金屬的環(huán)境下部署和運行。
好吧,剩下的只有幾個Go實現(xiàn)的API Gateway了,在它們之中,我們選擇用Tyk API網(wǎng)關(guān)[10]來作為后續(xù)API功能演示的示例。
注:這并不代表Tyk API網(wǎng)關(guān)就要比其他Go實現(xiàn)的API Gateway優(yōu)秀[11],只是它的資料比較齊全,適合在本文中作演示罷了。
3. API網(wǎng)關(guān)主要功能特性示例(Tyk API網(wǎng)關(guān)版本)
3.1 Tyk API網(wǎng)關(guān)簡介
記得在至少5年前就知道Tyk API網(wǎng)關(guān)[12]的存在,印象中它是使用Go語言開發(fā)的早期的那批API網(wǎng)關(guān)之一。Tyk從最初的純開源項目,到如今由背后商業(yè)公司支持,以O(shè)pen Core模式開源[13]的網(wǎng)關(guān),一直保持了active dev的狀態(tài)。經(jīng)過多年的演進,它已經(jīng)一款功能強大的開源兼商業(yè)API管理和網(wǎng)關(guān)解決方案[14],提供了全面的功能和工具,幫助開發(fā)者有效地管理、保護和監(jiān)控API。同時,Tyk API網(wǎng)關(guān)支持多種安裝部署方式,即可以單一程序的方式放在物理機或VM上運行,也可以支持容器部署,通過docker-compose[15]拉起,亦可以通過Kubernetes Operator[16]將其部署在Kubernetes中,這也讓Tyk API網(wǎng)關(guān)具備了在各大公有云上平滑遷移的能力。
圖片
關(guān)于Tyk API網(wǎng)關(guān)開源版本的功能詳情[17],可以點擊左邊超鏈接到其官網(wǎng)查閱,這里不贅述。
3.2 安裝Tyk API網(wǎng)關(guān)
下面我們就來安裝一下Tyk API網(wǎng)關(guān),我們直接在VM上安裝,VM上的環(huán)境是CentOS 7.9。Tyk API提供了很多中安裝方法,這里使用CentOS的yum包管理工具安裝Tyk API網(wǎng)關(guān)[18],大體步驟如下(演示均以root權(quán)限操作)。
3.2.1 創(chuàng)建tyk gateway軟件源
默認(rèn)的yum repo中是不包含tyk gateway的,我們需要在/etc/yum.repos.d下面創(chuàng)建一個新的源,即新建一個tyk_tyk-gateway.repo文件,其內(nèi)容如下:
[tyk_tyk-gateway]
name=tyk_tyk-gateway
baseurl=https://packagecloud.io/tyk/tyk-gateway/el/7/$basearch
repo_gpgcheck=1
gpgcheck=0
enabled=1
gpgkey=https://packagecloud.io/tyk/tyk-gateway/gpgkey
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
metadata_expire=300
[tyk_tyk-gateway-source]
name=tyk_tyk-gateway-source
baseurl=https://packagecloud.io/tyk/tyk-gateway/el/7/SRPMS
repo_gpgcheck=1
gpgcheck=0
enabled=1
gpgkey=https://packagecloud.io/tyk/tyk-gateway/gpgkey
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
metadata_expire=300
接下來我們執(zhí)行下面命令來創(chuàng)建tyk_tyk-gateway這個repo的YUM緩存:
$yum -q makecache -y --disablerepo='*' --enablerepo='tyk_tyk-gateway'
導(dǎo)入 GPG key 0x5FB83118:
用戶ID : "https://packagecloud.io/tyk/tyk-gateway (https://packagecloud.io/docs#gpg_signing) <support@packagecloud.io>"
指紋 : 9179 6215 a875 8c40 ab57 5f03 87be 71bd 5fb8 3118
來自 : https://packagecloud.io/tyk/tyk-gateway/gpgkey
repo配置和緩存完畢后,我們就可以安裝Tyk API Gateway了:
$yum install -y tyk-gateway
安裝后的tky-gateway將以一個systemd daemon服務(wù)[19]的形式存在于主機上,程序意外退出或虛機重啟后,該服務(wù)也會被systemd自動拉起。通過systemctl status命令可以查看服務(wù)的運行狀態(tài):
# systemctl status tyk-gateway
● tyk-gateway.service - Tyk API Gateway
Loaded: loaded (/usr/lib/systemd/system/tyk-gateway.service; enabled; vendor preset: disabled)
Active: active (running) since 日 2023-11-19 20:22:44 CST; 12min ago
Main PID: 29306 (tyk)
Tasks: 13
Memory: 19.6M
CGroup: /system.slice/tyk-gateway.service
└─29306 /opt/tyk-gateway/tyk --conf /opt/tyk-gateway/tyk.conf
11月 19 20:34:54 iZ2ze18rmx2avqb5xgb4omZ tyk[29306]: time="Nov 19 20:34:54" level=error msg="Connection to Redis faile...b-sub
11月 19 20:35:04 iZ2ze18rmx2avqb5xgb4omZ tyk[29306]: time="Nov 19 20:35:04" level=error msg="cannot set key in pollerC...ured"
11月 19 20:35:04 iZ2ze18rmx2avqb5xgb4omZ tyk[29306]: time="Nov 19 20:35:04" level=error msg="Redis health check failed...=main
Hint: Some lines were ellipsized, use -l to show in full.
3.2.2 安裝redis
我們看到tyk-gateway已經(jīng)成功啟動,但從其服務(wù)日志來看,它在連接redis時報錯了!tyk gateway默認(rèn)將數(shù)據(jù)存儲在redis中,為了讓tyk gateway正常運行,我們還需要安裝redis!這里我們使用容器的方式安裝和運行一個redis服務(wù):
$docker pull redis:6.2.14-alpine3.18
$docker run -d --name my-redis -p 6379:6379 redis:6.2.14-alpine3.18
e5d1ec8d5f5c09023d1a4dd7d31d293b2d7147f1d9a01cff8eff077c93a9dab7
拉取并運行redis后,我們通過redis-cli驗證一下與redis server的連接:
# docker run -it --rm redis:6.2.14-alpine3.18 redis-cli -h 192.168.0.24
192.168.0.24:6379>
我們看到可以正常連接!但此時Tyk Gateway仍然無法與redis正常連接,我們還需要對Tyk Gateway做一些配置調(diào)整!
3.2.3 配置Tyk Gateway
yum默認(rèn)將Tyk Gateway安裝到/opt/tyk-gateway下面,這個路徑下的文件布局如下:
$tree -F -L 2 .
.
├── apps/
│ └── app_sample.json
├── coprocess/
│ ├── api.h
│ ├── bindings/
│ ├── coprocess_common.pb.go
│ ├── coprocess_mini_request_object.pb.go
│ ├── coprocess_object_grpc.pb.go
│ ├── coprocess_object.pb.go
│ ├── coprocess_response_object.pb.go
│ ├── coprocess_return_overrides.pb.go
│ ├── coprocess_session_state.pb.go
│ ├── coprocess_test.go
│ ├── dispatcher.go
│ ├── grpc/
│ ├── lua/
│ ├── proto/
│ ├── python/
│ └── README.md
├── event_handlers/
│ └── sample/
├── install/
│ ├── before_install.sh*
│ ├── data/
│ ├── init_local.sh
│ ├── inits/
│ ├── post_install.sh*
│ ├── post_remove.sh*
│ ├── post_trans.sh
│ └── setup.sh*
├── middleware/
│ ├── ottoAuthExample.js
│ ├── sampleMiddleware.js
│ ├── samplePostProcessMiddleware.js
│ ├── samplePreProcessMiddleware.js
│ ├── testPostVirtual.js
│ ├── testVirtual.js
│ └── waf.js
├── policies/
│ └── policies.json
├── templates/
│ ├── breaker_webhook.json
│ ├── default_webhook.json
│ ├── error.json
│ ├── monitor_template.json
│ └── playground/
├── tyk*
└── tyk.conf
其中tyk.conf就是tyk gateway的配置文件,我們先看看其默認(rèn)的內(nèi)容:
$cat /opt/tyk-gateway/tyk.conf
{
"listen_address": "",
"listen_port": 8080,
"secret": "xxxxxx",
"template_path": "/opt/tyk-gateway/templates",
"use_db_app_configs": false,
"app_path": "/opt/tyk-gateway/apps",
"middleware_path": "/opt/tyk-gateway/middleware",
"storage": {
"type": "redis",
"host": "redis",
"port": 6379,
"username": "",
"password": "",
"database": 0,
"optimisation_max_idle": 2000,
"optimisation_max_active": 4000
},
"enable_analytics": false,
"analytics_config": {
"type": "",
"ignored_ips": []
},
"dns_cache": {
"enabled": false,
"ttl": 3600,
"check_interval": 60
},
"allow_master_keys": false,
"policies": {
"policy_source": "file"
},
"hash_keys": true,
"hash_key_function": "murmur64",
"suppress_redis_signal_reload": false,
"force_global_session_lifetime": false,
"max_idle_connections_per_host": 500
}
我們看到:storage下面存儲了redis的配置信息,我們需要將redis的host配置修改為我們的VM地址:
"host": "192.168.0.24",
然后重啟Tyk Gateway服務(wù):
$systemctl daemon-reload
$systemctl restart tyk-gateway
之后,我們再查看tyk gateway的運行狀態(tài):
systemctl status tyk-gateway
● tyk-gateway.service - Tyk API Gateway
Loaded: loaded (/usr/lib/systemd/system/tyk-gateway.service; enabled; vendor preset: disabled)
Active: active (running) since 一 2023-11-20 06:54:07 CST; 41s ago
Main PID: 20827 (tyk)
Tasks: 15
Memory: 24.8M
CGroup: /system.slice/tyk-gateway.service
└─20827 /opt/tyk-gateway/tyk --conf /opt/tyk-gateway/tyk.conf
11月 20 06:54:07 iZ2ze18rmx2avqb5xgb4omZ tyk[20827]: time="Nov 20 06:54:07" level=info msg="Loading API configurations...=main
11月 20 06:54:07 iZ2ze18rmx2avqb5xgb4omZ tyk[20827]: time="Nov 20 06:54:07" level=info msg="Tracking hostname" api_nam...=main
11月 20 06:54:07 iZ2ze18rmx2avqb5xgb4omZ tyk[20827]: time="Nov 20 06:54:07" level=info msg="Initialising Tyk REST API ...=main
11月 20 06:54:07 iZ2ze18rmx2avqb5xgb4omZ tyk[20827]: time="Nov 20 06:54:07" level=info msg="API bind on custom port:0"...=main
11月 20 06:54:07 iZ2ze18rmx2avqb5xgb4omZ tyk[20827]: time="Nov 20 06:54:07" level=info msg="Checking security policy: ...fault
11月 20 06:54:07 iZ2ze18rmx2avqb5xgb4omZ tyk[20827]: time="Nov 20 06:54:07" level=info msg="API Loaded" api_id=1 api_n...ip=--
11月 20 06:54:07 iZ2ze18rmx2avqb5xgb4omZ tyk[20827]: time="Nov 20 06:54:07" level=info msg="Loading uptime tests..." p...k-mgr
11月 20 06:54:07 iZ2ze18rmx2avqb5xgb4omZ tyk[20827]: time="Nov 20 06:54:07" level=info msg="Initialised API Definition...=main
11月 20 06:54:07 iZ2ze18rmx2avqb5xgb4omZ tyk[20827]: time="Nov 20 06:54:07" level=warning msg="All APIs are protected ...=main
11月 20 06:54:07 iZ2ze18rmx2avqb5xgb4omZ tyk[20827]: time="Nov 20 06:54:07" level=info msg="API reload complete" prefix=main
Hint: Some lines were ellipsized, use -l to show in full.
從服務(wù)日志來看,現(xiàn)在Tyk Gateway可以正常連接redis并提供服務(wù)了!我們也可以通過下面的命令驗證網(wǎng)關(guān)的運行狀態(tài):
$curl localhost:8080/hello
{"status":"pass","version":"5.2.1","description":"Tyk GW","details":{"redis":{"status":"pass","componentType":"datastore","time":"2023-11-20T06:58:57+08:00"}}}
“/hello”是Tyk Gateway的內(nèi)置路由,由Tyk網(wǎng)關(guān)自己提供服務(wù)。
到這里Tyk Gateway的安裝和簡單配置就結(jié)束了,接下來,我們就來看看API Gateway的主要功能特性,并借助Tyk Gateway來展示一下這些功能特性。
注:查看Tyk Gateway的運行日志,可以使用journalctl -u tyk-gateway -f命令實時follow最新日志輸出。
3.3 功能特性:請求轉(zhuǎn)發(fā)與路由
請求轉(zhuǎn)發(fā)和路由是API Gateway的主要功能特性之一,API Gateway可以根據(jù)請求的路徑、方法、查詢參數(shù)等信息將請求轉(zhuǎn)發(fā)到相應(yīng)的后端服務(wù),其內(nèi)核與反向代理類似,不同之處在于API Gateway增加了“API”這層抽象,更加專注于構(gòu)建、管理和增強API。
下面我們來看看Tyk如何配置API路由,我們首先創(chuàng)建一個新API。
3.3.1 創(chuàng)建一個新API
Tyk開源版支持兩種創(chuàng)建API的方式,一種是通過調(diào)用Tyk的控制類API[20],一種則是通過傳統(tǒng)的配置文件,放入特定目錄下[21]。無論哪種方式添加完API,最終都要通過Tyk Gateway熱加載(hot reload)或重啟才能生效。
注:Tyk Gateway的商業(yè)版本提供Dashboard,可以以圖形化的方式管理API,并且商業(yè)版本的API定義會放在Postgres或MongoDB中,我們這里用開源版本,只能手工管理了,并且API定義只能放在文件中。
下面,我們就來在Tyk上創(chuàng)建一個新的API路由,該路由示例的示意圖如下:
圖片
在未添加新API之前,我們使用curl訪問一下該API路徑:
$curl localhost:8080/api/v1/no-authn
Not Found
Tyk Gateway由于找不到API路由,返回Not Found。接下來,我們采用調(diào)用tyk gateway API的方式來添加路由:
$curl -v -H "x-tyk-authorization: {tyk gateway secret}" \
-s \
-H "Content-Type: application/json" \
-X POST \
-d '{
"name": "no-authn-v1",
"slug": "no-authn-v1",
"api_id": "no-authn-v1",
"org_id": "1",
"use_keyless": true,
"auth": {
"auth_header_name": "Authorization"
},
"definition": {
"location": "header",
"key": "x-api-version"
},
"version_data": {
"not_versioned": true,
"versions": {
"Default": {
"name": "Default",
"use_extended_paths": true
}
}
},
"proxy": {
"listen_path": "/api/v1/no-authn",
"target_url": "http://localhost:18081/",
"strip_listen_path": true
},
"active": true
}' http://localhost:8080/tyk/apis | python -mjson.tool
* About to connect() to localhost port 8080 (#0)
* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> POST /tyk/apis HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8080
> Accept: */*
> x-tyk-authorization: {tyk gateway secret}
> Content-Type: application/json
> Content-Length: 797
>
} [data not shown]
* upload completely sent off: 797 out of 797 bytes
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Wed, 22 Nov 2023 05:38:40 GMT
< Content-Length: 53
<
{ [data not shown]
* Connection #0 to host localhost left intact
{
"action": "added",
"key": "no-authn-v1",
"status": "ok"
}
從curl返回結(jié)果我們看到:API已經(jīng)被成功添加。這時tyk gateway的安裝目錄/opt/tyk-gateway的子目錄apps下會新增一個名為no-authn-v1.json的配置文件,這個文件內(nèi)容較多,有300行,這里就不貼出來了,這個文件就是新增的no-authn API的定義文件[22]。
不過此刻,Tyk Gateway還需熱加載后才能為新的API提供服務(wù),調(diào)用下面API可以觸發(fā)Tyk Gateway的熱加載:
$curl -H "x-tyk-authorization: {tyk gateway secret}" -s http://localhost:8080/tyk/reload/group | python -mjson.tool
{
"message": "",
"status": "ok"
}
注:即便觸發(fā)熱加載成功,但如果body中的json格式錯,比如多了一個結(jié)尾逗號,Tyk Gateway是不會報錯的!
API路由創(chuàng)建完畢并生效后,我們再來訪問一下API:
$ curl localhost:8080/api/v1/no-authn
{
"error": "There was a problem proxying the request"
}
我們看到:Tyk Gateway返回的已經(jīng)不是“Not Found”了!現(xiàn)在我們創(chuàng)建一下no-authn這個API服務(wù),考慮到適配更多后續(xù)示例,這里建立這樣一個http server:
// api-gateway-examples/httpserver
func main() {
// 解析命令行參數(shù)
port := flag.Int("p", 8080, "Port number")
apiVersion := flag.String("v", "v1", "API version")
apiName := flag.String("n", "example", "API name")
flag.Parse()
// 注冊處理程序
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println(*r)
fmt.Fprintf(w, "Welcome api: localhost:%d/%s/%s\n", *port, *apiVersion, *apiName)
})
// 啟動HTTP服務(wù)器
addr := fmt.Sprintf(":%d", *port)
log.Printf("Server listening on port %d\n", *port)
log.Fatal(http.ListenAndServe(addr, nil))
}
我們啟動一個該http server的實例:
$go run main.go -p 18081 -v v1 -n no-authn
2023/11/22 22:02:42 Server listening on port 18081
現(xiàn)在我們再通過tyk gateway調(diào)用一下no-authn這個API:
$curl localhost:8080/api/v1/no-authn
Welcome api: localhost:18081/v1/no-authn
我們看到這次路由通了!no-authn API返回了期望的結(jié)果!
3.3.2 負(fù)載均衡
如果no-authn API存在多個服務(wù)實例,Tyk Gateway也可以將請求流量負(fù)載均衡到多個no-authn服務(wù)實例上去,下圖是Tyk Gateway進行請求流量負(fù)載均衡[23]的示意圖:
要實現(xiàn)負(fù)責(zé)均衡,我們需要調(diào)整no-authn API的定義,這次我們直接修改/opt/tyk-gateway/apps/no-authn-v1.json,變更的配置主要有三項:
// /opt/tyk-gateway/apps/no-authn-v1.json
"proxy": {
"preserve_host_header": false,
"listen_path": "/api/v1/no-authn",
"target_url": "", // (1) 改為""
"disable_strip_slash": false,
"strip_listen_path": true,
"enable_load_balancing": true, // (2) 改為true
"target_list": [ // (3) 填寫no-authn服務(wù)實例列表
"http://localhost:18081/",
"http://localhost:18082/",
"http://localhost:18083/"
],
修改完配置后,調(diào)用Tyk的控制類API使之生效,然后我們啟動三個no-authn的API實例:
$go run main.go -p 18081 -v v1 -n no-authn
$go run main.go -p 18082 -v v1 -n no-authn
$go run main.go -p 18083 -v v1 -n no-authn
接下來,我們多次調(diào)用curl訪問no-authn API:
$curl localhost:8080/api/v1/no-authn
Welcome api: localhost:18081/v1/no-authn
$curl localhost:8080/api/v1/no-authn
Welcome api: localhost:18082/v1/no-authn
$curl localhost:8080/api/v1/no-authn
Welcome api: localhost:18083/v1/no-authn
$curl localhost:8080/api/v1/no-authn
Welcome api: localhost:18081/v1/no-authn
$curl localhost:8080/api/v1/no-authn
Welcome api: localhost:18082/v1/no-authn
$curl localhost:8080/api/v1/no-authn
Welcome api: localhost:18083/v1/no-authn
我們看到:Tyk Gateway在no-authn API的各個實例之間做了等權(quán)重的輪詢。如果我們停掉實例3,再來訪問該API,我們將得到下面結(jié)果:
$curl localhost:8080/api/v1/no-authn
Welcome api: localhost:18081/v1/no-authn
$curl localhost:8080/api/v1/no-authn
Welcome api: localhost:18082/v1/no-authn
$curl localhost:8080/api/v1/no-authn
Bad Request
$curl localhost:8080/api/v1/no-authn
Welcome api: localhost:18081/v1/no-authn
$curl localhost:8080/api/v1/no-authn
Welcome api: localhost:18082/v1/no-authn
$curl localhost:8080/api/v1/no-authn
Bad Request
注:Tyk Gateway商業(yè)版通過Dashboard支持配置帶權(quán)重的RR負(fù)載均衡算法[24]。
我們看到:實例3已經(jīng)下線,但Tyk Gateway并不會跳過該已經(jīng)下線的實例,這在生產(chǎn)環(huán)境會給客戶端帶來不一致的響應(yīng)。
3.3.3 服務(wù)實例存活檢測(uptime test)
Tyk Gateway在開啟負(fù)載均衡的時候,也提供了對后端服務(wù)實例的存活檢測機制,當(dāng)某個服務(wù)實例down了后,負(fù)載均衡機制會繞過該實例將請求發(fā)到下一個處于存活狀態(tài)的實例;而當(dāng)down機實例恢復(fù)后,Tyk Gateway也能及時檢測到服務(wù)實例上線,并將其加入流量負(fù)載調(diào)度。
支持存活檢測(uptime test)的API定義配置如下:
// /opt/tyk-gateway/apps/no-authn-v1.json
"uptime_tests": {
"disable": false,
"poller_group":"",
"check_list": [
{
"url": "http://localhost:18081/"
},
{
"url": "http://localhost:18082/"
},
{
"url": "http://localhost:18083/"
}
],
"config": {
"enable_uptime_analytics": true,
"failure_trigger_sample_size": 3,
"time_wait": 300,
"checker_pool_size": 50,
"expire_utime_after": 0,
"service_discovery": {
"use_discovery_service": false,
"query_endpoint": "",
"use_nested_query": false,
"parent_data_path": "",
"data_path": "",
"port_data_path": "",
"target_path": "",
"use_target_list": false,
"cache_disabled": false,
"cache_timeout": 0,
"endpoint_returns_list": false
},
"recheck_wait": 0
}
}
"proxy": {
... ...
"enable_load_balancing": true,
"target_list": [
"http://localhost:18081/",
"http://localhost:18082/",
"http://localhost:18083/"
],
"check_host_against_uptime_tests": true,
... ...
}
我們新增了uptime_tests的配置,uptime_tests的check_list中的url的值要與proxy中target_list中的值完全一樣,這樣Tyk Gateway才能將二者對應(yīng)上。另外proxy的check_host_against_uptime_tests要設(shè)置為true。
這樣配置并生效后,等我們將服務(wù)實例3停掉后,后續(xù)到no-authn的請求就只會轉(zhuǎn)發(fā)到實例1和實例2了。而當(dāng)恢復(fù)實例3運行后,Tyk Gateway又會將流量分擔(dān)到實例3上。
3.3.4 動態(tài)負(fù)載均衡
上面負(fù)載均衡示例中target_list中的目標(biāo)實例的IP和端口的手工配置的,而在云原生時代,我們經(jīng)常會基于容器承載API服務(wù)實例,當(dāng)容器因故退出,并重新啟動一個新容器時,IP可能會發(fā)生變化,這樣上述的手工配置就無法滿足要求,這就對API Gateway提出了與服務(wù)發(fā)現(xiàn)組件集成的要求:通過服務(wù)發(fā)現(xiàn)組件動態(tài)獲取服務(wù)實例的訪問列表,進而實現(xiàn)動態(tài)負(fù)載均衡[25]。
Tyk Gateway內(nèi)置了主流服務(wù)發(fā)現(xiàn)組件(比如Etcd、Consul、ZooKeeper等)的對接能力,鑒于環(huán)境所限,這里就不舉例了,大家可以在Tyk Gateway的服務(wù)發(fā)現(xiàn)示例文檔頁面[26]找到與不同服務(wù)發(fā)現(xiàn)組件對接時的配置示例。
3.3.5 IP訪問限制
針對每個API,API網(wǎng)關(guān)還提供IP訪問限制的特性,比如Tyk Gateway就提供了IP白名單[27]和IP黑名單[28]功能,通常二選一開啟一種限制即可。
以白名單為例,即凡是在白名單中的IP才被允許訪問該API。下面是白名單配置樣例:
// /opt/tyk-gateway/apps/no-authn-v1.json
"enable_ip_whitelisting": true,
"allowed_ips": ["12.12.12.12", "12.12.12.13", "12.12.12.14"],
生效后,當(dāng)我們訪問no-authn API時,會得到下面錯誤:
$curl localhost:8080/api/v1/no-authn
{
"error": "access from this IP has been disallowed"
}
如果開啟的是黑名單,那么凡是在黑名單中的IP都被禁止訪問該API,下面是黑名單配置樣例:
// /opt/tyk-gateway/apps/no-authn-v1.json
"enable_ip_blacklisting": true,
"blacklisted_ips": ["12.12.12.12", "12.12.12.13", "12.12.12.14", "127.0.0.1"],
生效后,當(dāng)我們訪問no-authn API時,會得到如下結(jié)果:
$curl 127.0.0.1:8080/api/v1/no-authn
{
"error": "access from this IP has been disallowed"
}
到目前為止,我們的API網(wǎng)關(guān)和定義的API都處于“裸奔”狀態(tài),因為沒有對客戶端進行身份認(rèn)證,任何客戶端都可以訪問到我們的API,顯然這不是我們期望的,接下來,我們就來看看API網(wǎng)關(guān)的一個重要功能特性:身份認(rèn)證與授權(quán)。
3.4 功能特性:身份認(rèn)證和授權(quán)
在《通過實例理解Go Web身份認(rèn)證的幾種方式[29]》一文中,我們提到過:建立全局的安全通道是任何身份認(rèn)證方式的前提。
3.4.1 建立安全通道,卸載TLS證書
Tyk Gateway支持在Gateway層面統(tǒng)一配置TLS證書[30],同時也起到在Gateway卸載TLS證書的作用:
圖片
這次我們要在tyk.conf中進行配置,才能在Gateway層面生效。這里我們借用《通過實例理解Go Web身份認(rèn)證的幾種方式[31]》一文中生成的幾個證書(大家可以在https://github.com/bigwhite/experiments/tree/master/authn-examples/tls-authn/make_certs下載),并將它們放到/opt/tyk-gateway/certs/下面:
$ls /opt/tyk-gateway/certs/
server-cert.pem server-key.pem
然后,我們在/opt/tyk-gateway/tyk.conf文件中增加下面配置:
// /opt/tyk-gateway/tyk.conf
"http_server_options": {
"use_ssl": true,
"certificates": [
{
"domain_name": "server.com",
"cert_file": "./certs/server-cert.pem",
"key_file": "./certs/server-key.pem"
}
]
}
之后,重啟tyk gateway服務(wù),使得tyk.conf的配置修改生效。
注:在/etc/hosts中設(shè)置server.com為127.0.0.1。
現(xiàn)在我們用之前的http方式訪問一下no-authn的API:
$curl server.com:8080/api/v1/no-authn
Client sent an HTTP request to an HTTPS server.
由于全局啟用了HTTPS,采用http方式的請求將被拒絕。我們換成https方式訪問:
// 不驗證服務(wù)端證書
$curl -k https://server.com:8080/api/v1/no-authn
Welcome api: localhost:18081/v1/no-authn
// 驗證服務(wù)端的自簽證書
$curl --cacert ./inter-cert.pem https://server.com:8080/api/v1/no-authn
Welcome api: localhost:18081/v1/no-authn
3.4.2 Mutual TLS雙向認(rèn)證
在《通過實例理解Go Web身份認(rèn)證的幾種方式[32]》一文中,我們介紹的第一種身份認(rèn)證方式就是TLS雙向認(rèn)證,那么Tyk Gateway對MTLS的支持如何呢?Tyk官方文檔[33]提到它既支持client mTLS[34],也支持upstream mTLS[35]。
我們更關(guān)心的是client mTLS,即客戶端在與Gateway建連后,Gateway會使用Client CA驗證客戶端的證書!我最初認(rèn)為這個Client CA的配置是在tyk.conf中,但找了許久,也沒有發(fā)現(xiàn)配置Client CA的地方。
在no-authn API的定義文件(no-authn-v1.json)中,我們做如下配置改動:
"use_mutual_tls_auth": true,
"client_certificates": [
"/opt/tyk-gateway/certs/inter-cert.pem"
],
但使用下面命令訪問API時報錯:
$curl --key ./client-key.pem --cert ./client-cert.pem --cacert ./inter-cert.pem https://server.com:8080/api/v1/no-authn
{
"error": "Certificate with SHA256 bc8717c0f2ea5a0b81813abb3ec42ef8f9bf60da251b87243627d65fb0e3887b not allowed"
}
如果將"client_certificates"的配置中的inter-cert.pem改為client-cert.pem,則是可以的,但個人感覺這很奇怪,不符合邏輯,將tyk gateway的文檔、issue甚至代碼翻了又翻,也沒找到合理的配置client CA的位置。
Tyk Gateway支持多種身份認(rèn)證方式[36],下面我們來看一種使用較為廣泛的方式:JWT Auth。
主要JWT身份認(rèn)證方式的原理和詳情,可以參考我之前的文章《通過實例理解Go Web身份認(rèn)證的幾種方式[37]》。
3.4.3 JWT Token Auth
下面是我為這個示例做的一個示意圖:
圖片
這是我們?nèi)粘i_發(fā)中經(jīng)常遇到的場景,即通過portal用用戶名和密碼登錄后便可以拿到一個jwt token,然后后續(xù)的訪問功能API的請求僅攜帶該jwt token即可。API Gateway對于portal/login API不做任何身份認(rèn)證;而對后續(xù)的功能API請求,通過共享的secret(也稱為static secret)對請求中攜帶的jwt token進行簽名驗證。
portal/login API由于不進行authn,這樣其配置與前面的no-authn API幾乎一致,只是API名稱、路徑和target_list有不同:
// apps/portal-login-v1.json
{
"name": "portal-login-v1",
"slug": "portal-login-v1",
"listen_port": 0,
"protocol": "",
"enable_proxy_protocol": false,
"api_id": "portal-login-v1",
"org_id": "1",
"use_keyless": true,
... ...
"proxy": {
"preserve_host_header": false,
"listen_path": "/api/v1/portal/login",
"target_url": "",
"disable_strip_slash": false,
"strip_listen_path": true,
"enable_load_balancing": true,
"target_list": [
"http://localhost:28084"
],
"check_host_against_uptime_tests": true,
... ...
}
對應(yīng)的portal login API也不復(fù)雜:
// api-gateway-examples/portal-login/main.go
package main
import (
"log"
"net/http"
"time"
"github.com/golang-jwt/jwt/v5"
)
func main() {
// 創(chuàng)建一個基本的HTTP服務(wù)器
mux := http.NewServeMux()
username := "admin"
password := "123456"
key := "iamtonybai"
// for uptime test
mux.HandleFunc("/health", func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
})
// login handler
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
// 從請求頭中獲取Basic Auth認(rèn)證信息
user, pass, ok := req.BasicAuth()
if !ok {
// 認(rèn)證失敗
w.WriteHeader(http.StatusUnauthorized)
return
}
// 驗證用戶名密碼
if user == username && pass == password {
// 認(rèn)證成功,生成token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"username": username,
"iat": jwt.NewNumericDate(time.Now()),
})
signedToken, _ := token.SignedString([]byte(key))
w.Write([]byte(signedToken))
} else {
// 認(rèn)證失敗
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
}
})
// 監(jiān)聽28084端口
err := http.ListenAndServe(":28084", mux)
if err != nil {
log.Fatal(err)
}
}
運行該login API服務(wù)后,我們用curl命令獲取一下jwt token:
$curl -u 'admin:123456' -k https://server.com:8080/api/v1/portal/login
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDA3NTEyODEsInVzZXJuYW1lIjoiYWRtaW4ifQ.-wC8uPsLHDxSXcEMxIxJ8O2l3aWtWtWKvhtmuHmgIMA
現(xiàn)在我們再來建立protected API:
// apps/protected-v1.json
{
"name": "protected-v1",
"slug": "protected-v1",
"listen_port": 0,
"protocol": "",
"enable_proxy_protocol": false,
"api_id": "protected-v1",
"org_id": "1",
"use_keyless": false, // 設(shè)置為false, gateway才會進行jwt的驗證
... ...
"enable_jwt": true, // 開啟jwt
"use_standard_auth": false,
"use_go_plugin_auth": false,
"enable_coprocess_auth": false,
"custom_plugin_auth_enabled": false,
"jwt_signing_method": "hmac", // 設(shè)置alg為hs256
"jwt_source": "aWFtdG9ueWJhaQ==", // 設(shè)置共享secret: base64("iamtonybai")
"jwt_identity_base_field": "username", // 設(shè)置代表請求中的用戶身份的字段,這里我們用username
"jwt_client_base_field": "",
"jwt_policy_field_name": "",
"jwt_default_policies": [
"5e189590801287e42a6cf5ce" // 設(shè)置security policy,這個似乎是jwt auth必須的
],
"jwt_issued_at_validation_skew": 0,
"jwt_expires_at_validation_skew": 0,
"jwt_not_before_validation_skew": 0,
"jwt_skip_kid": false,
... ...
"version_data": {
"not_versioned": true,
"default_version": "",
"versions": {
"Default": {
"name": "Default",
"expires": "",
"paths": {
"ignored": null,
"white_list": null,
"black_list": null
},
"use_extended_paths": true,
"extended_paths": {
"persist_graphql": null
},
"global_headers": {
"username": "$tyk_context.jwt_claims_username" // 設(shè)置轉(zhuǎn)發(fā)到upstream的請求中的header字段username
},
"global_headers_remove": null,
"global_response_headers": null,
"global_response_headers_remove": null,
"ignore_endpoint_case": false,
"global_size_limit": 0,
"override_target": ""
}
}
},
... ...
"enable_context_vars": true, // 開啟上下文變量
"config_data": null,
"config_data_disabled": false,
"tag_headers": ["username"], // 設(shè)置header
... ...
}
這個配置就相對復(fù)雜許多,也是翻閱了很長時間資料才驗證通過的配置。JWT Auth必須有關(guān)聯(lián)的policy設(shè)置,我們在tyk gateway開源版中要想設(shè)置policy,需要現(xiàn)在tyk.conf中做如下設(shè)置:
// /opt/tyk-gateway/tyk.conf
"policies": {
"policy_source": "file",
"policy_record_name": "./policies/policies.json"
},
而policies/policies.json的內(nèi)容如下:
// /opt/tyk-gateway/policies/policies.json
{
"5e189590801287e42a6cf5ce": {
"rate": 1000,
"per": 1,
"quota_max": 100,
"quota_renewal_rate": 60,
"access_rights": {
"protected-v1": {
"api_name": "protected-v1",
"api_id": "protected-v1",
"versions": [
"Default"
]
}
},
"org_id": "1",
"hmac_enabled": false
}
}
上述設(shè)置完畢并重啟tyk gateway生效后,且protected api服務(wù)也已經(jīng)啟動時,我們訪問一下該API服務(wù):
$curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDA3NTEyODEsInVzZXJuYW1lIjoiYWRtaW4ifQ.-wC8uPsLHDxSXcEMxIxJ8O2l3aWtWtWKvhtmuHmgIMA" -k https://server.com:8080/api/v1/protected
invoke protected api ok
我們看到curl發(fā)出的請求成功通過了Gateway的驗證!并且通過protected API輸出的請求信息來看,Gateway成功解析出username,并將其作為Header中的字段傳遞給了protected API服務(wù)實例:
http.Request{Method:"GET", URL:(*url.URL)(0xc0002f6240), Proto:"HTTP/1.1", ProtoMajor:1, ProtoMinor:1, Header:http.Header{"Accept":[]string{"*/*"}, "Accept-Encoding":[]string{"gzip"}, "Authorization":[]string{"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDA3NTEyODEsInVzZXJuYW1lIjoiYWRtaW4ifQ.-wC8uPsLHDxSXcEMxIxJ8O2l3aWtWtWKvhtmuHmgIMA"}, "User-Agent":[]string{"curl/7.29.0"}, "Username":[]string{"admin"}, "X-Forwarded-For":[]string{"127.0.0.1"}}, Body:http.noBody{}, GetBody:(func() (io.ReadCloser, error))(nil), ContentLength:0, TransferEncoding:[]string(nil), Close:false, Host:"localhost:28085", Form:url.Values(nil), PostForm:url.Values(nil), MultipartForm:(*multipart.Form)(nil), Trailer:http.Header(nil), RemoteAddr:"[::1]:55583", RequestURI:"/", TLS:(*tls.ConnectionState)(nil), Cancel:(<-chan struct {})(nil), Response:(*http.Response)(nil), ctx:(*context.cancelCtx)(0xc0002e34f0)}
如果不攜帶Authorization頭字段或jwt的token是錯誤的,那么結(jié)果將如下所示:
$ curl -k https://server.com:8080/api/v1/protected
{
"error": "Authorization field missing"
}
$ curl -k -H "Authorization: Bearer xxx" https://server.com:8080/api/v1/protected
{
"error": "Key not authorized"
}
一旦通過API Gateway的身份認(rèn)證,上游的API服務(wù)就會拿到客戶端身份,有了唯一身份后,就可以進行授權(quán)操作[38]了,其實policy設(shè)置本身也是一種授權(quán)訪問控制。Tyk Gateway自身也支持RBAC等模型[39],也支持與OPA(open policy agent)等的集成,但更多是在商業(yè)版的tyk dashboard下完成的,這里也就不重點說明了。
下面的Gateway的幾個主要功能特性由于試驗環(huán)境受限以及文章篇幅考量,我不會像上述例子這么細(xì)致的說明了,只會簡單說明一下。
3.5 功能特性:流量控制與限速
Tyk Gateway內(nèi)置提供了強大的流量控制功能,可以通過全局級別和API級別的限速[40]來管理請求流量。此外,Tyk Gateway 還支持請求配額(request quota)[41]來限制每個用戶或應(yīng)用程序在一個時間周期內(nèi)的請求次數(shù)。
流量不僅和請求速度和數(shù)量有關(guān)系,與請求的大小也有關(guān)系,Tyk Gateway還支持在全局層面和API層面設(shè)置Request的size limit[42],以避免超大包對網(wǎng)關(guān)運行造成不良影響。
3.6 功能特性:高可用與容錯處理
在許多情況下,我們要為客戶確保服務(wù)水平(service level),比如:最大往返時間、最大響應(yīng)時延等。Tyk Gateway提供了一系列功能,可幫助我們確保網(wǎng)關(guān)的高可用運行和SLA服務(wù)水平。
Tyk支持健康檢查[43],這對于確定Tyk Gateway的狀態(tài)極為重要,沒有健康檢查,就很難知道網(wǎng)關(guān)的實際運行狀態(tài)如何。
Tyk Gateway還內(nèi)置了斷路器(circuit breaker)[44],這個斷路器是基于比例的,因此如果y個請求中的x請求都失敗了,斷路器就會跳閘,例如,如果x = 10,y = 100,則閾值百分比為10%。當(dāng)失敗比例到達10%時,斷路器就會切斷流量,同時跳閘還會觸發(fā)一個事件,我們可以記錄和處理該事件。
當(dāng)upstream的服務(wù)響應(yīng)遲遲不歸時,Tyk Gateway還可以設(shè)置強制超時[45],可以確保服務(wù)始終在給定時間內(nèi)響應(yīng)。這在高可用性系統(tǒng)中非常重要,因為在這種系統(tǒng)中,響應(yīng)性能至關(guān)重要,這樣才能干凈利落地處理錯誤。
3.7 功能特性:監(jiān)控與可觀測性
微服務(wù)時代,可觀測性對運維以及系統(tǒng)高可用的重要性不言而喻。Tyk Gateway在多年的演化過程中,也逐漸增加了對可觀測的支持,
可觀測主要分三大塊:
- log
Tyk Gateway支持設(shè)置輸出日志的級別(log level),默認(rèn)是info級別。Tyk輸出的是結(jié)構(gòu)化日志,這使得它可以很好的與其他日志收集查詢系統(tǒng)集成,Tyk支持與主流的日志收集工具對接[46],包括:logstash、sentry、Graylog、Syslog等。
- metrics
度量數(shù)據(jù)是反映網(wǎng)關(guān)系統(tǒng)健康狀況、錯誤計數(shù)和類型、IT基礎(chǔ)設(shè)施(服務(wù)器、虛擬機、容器、數(shù)據(jù)庫和其他后端組件)及其他流程的硬件資源數(shù)據(jù)的重要參考。運維團隊可以通過使用監(jiān)控工具來利用實時度量的數(shù)據(jù)[47],識別運行趨勢、在系統(tǒng)故障時設(shè)置警報、確定問題的根本原因并緩解問題。
Tyk Gateway內(nèi)置了對主流metrics采集方案Prometheus+Grafana的支持[48],可以在網(wǎng)關(guān)層面以及對API進行實時度量數(shù)據(jù)采集和展示。
- tracing
Tyk Gateway從5.2版本開始支持了與服務(wù)Tracing界的標(biāo)準(zhǔn):OpenTelemetry的集成[49],這樣你可以使用多種支持OpenTelemetry的Tracing后端,比如Jaeger、Datadog等。Tracing可在Gateway層面開啟,也可以延展到API層面。
4. 小結(jié)
本文對已經(jīng)相對成熟的API網(wǎng)關(guān)技術(shù)做了回顧,對API網(wǎng)關(guān)的演進階段、主流特性以及當(dāng)前市面上的主流API網(wǎng)關(guān)進行了簡要說明,并以Go實現(xiàn)的Tyk Gateway社區(qū)開源版為例,以示例方式對API網(wǎng)關(guān)的主要功能做了介紹。
總體而言,Tyk Gateway是一款功能強大,社區(qū)相對活躍并有商業(yè)公司支持的產(chǎn)品,文檔很豐富,但從實際使用層面,這些文檔對Tyk社區(qū)版本的使用者來說并不友好,指導(dǎo)性不足(更多用商業(yè)版的Dashboard說明,與配置文件難于對應(yīng)),就像本文例子中那樣,為了搞定JWT認(rèn)證,筆者著實花了不少時間查閱資料,甚至閱讀源碼。
Tyk Gateway的配置設(shè)計平坦,沒有層次和邏輯,感覺是隨著時間隨意“堆砌”上去的。并且配置文件更新時,如果出現(xiàn)格式問題,Tyk Gateway并不報錯,讓人難于確定配置是否真正生效了,只能用Tyk Gateway的控制API[50]去查詢結(jié)果來驗證,非常繁瑣低效。
本文涉及的源碼可以在這里[51]下載,文中涉及的一些tyk gateway api和security policy的配置也可以在其中查看。
5. 參考資料
- Leaving the Cloud[52] - https://37signals.com/podcast/leaving-the-cloud/
- The Past, Present, and Future of API Gateways[53] - https://www.infoq.com/articles/past-present-future-api-gateways/
- How moving from AWS to Bare-Metal saved us 230,000/yr[54] - https://blog.oneuptime.com/moving-from-aws-to-bare-metal/
- A Comprehensive Guide to API Gateways, Kubernetes Gateways, and Service Meshes[55] - https://navendu.me/posts/gateway-and-mesh/
- Use API gateways in microservices[56] - https://learn.microsoft.com/en-us/azure/architecture/microservices/design/gateway
- The Tyk API Gateway and Postman[57] - https://blog.postman.com/the-tyk-api-gateway-and-postman/
- Getting Started with Tyk API Gateway with Keycloak[58] - https://javascript.plainenglish.io/getting-started-to-tyk-api-gateway-with-keycloak-16307435584a
- Observing your API traffic with Tyk, Elasticsearch & Kibana[59] - https://medium.com/@asoorm/observing-your-api-metrics-with-tyk-elasticsearch-kibana-74e8fd946c39
- Set up JWT token in tyk gateway[60] - https://community.tyk.io/t/set-up-jwt-token-in-tyk-gateway/6572/9
參考資料
[1] David Heinemeier Hansson: https://dhh.dk/
[2] 將公司所有的業(yè)務(wù)都從公有云搬遷到了自建的數(shù)據(jù)中心: https://37signals.com/podcast/leaving-the-cloud/
[3] 以單體應(yīng)用的形式存在: https://tonybai.com/2023/10/09/service-weaver-coding-in-monolithic-deploy-in-microservices/
[4] CNCF Landscape: https://https://landscape.cncf.io
[5] Amazon的API Gateway: https://aws.amazon.com/cn/api-gateway/
[6] Google Cloud的API Gateway: https://cloud.google.com/api-gateway
[7] APISIX: https://apisix.apache.org/
[8] Kong: https://konghq.com/
[9] EMISSARY INGRESS: https://github.com/emissary-ingress/emissary
[10] Tyk API網(wǎng)關(guān): https://tyk.io/blog/res-api-management-vendor-comparisons/
[11] 不代表Tyk API網(wǎng)關(guān)就要比其他Go實現(xiàn)的API Gateway優(yōu)秀: https://tyk.io/blog/enter-the-leader-tyk-recognised-as-a-leader-in-gartners-2023-magic-quadrant-for-api-management/
[12] Tyk API網(wǎng)關(guān): https://github.com/TykTechnologies/tyk
[13] Open Core模式開源: https://opensource.com/article/21/11/open-core-vs-open-source
[14] 開源兼商業(yè)API管理和網(wǎng)關(guān)解決方案: https://tyk.io/docs/tyk-oss-gateway/
[15] docker-compose: https://tonybai.com/2021/11/26/build-all-in-one-runtime-environment-with-docker-compose
[16] Kubernetes Operator: https://tonybai.com/2022/08/15/developing-kubernetes-operators-in-go-part1
[17] Tyk API網(wǎng)關(guān)開源版本的功能詳情: https://tyk.io/docs/tyk-oss-gateway/
[18] 使用CentOS的yum包管理工具安裝Tyk API網(wǎng)關(guān): https://tyk.io/docs/tyk-oss/ce-redhat-rhel-centos/
[19] systemd daemon服務(wù): https://tonybai.com/2016/12/27/when-docker-meets-systemd/
[20] 調(diào)用Tyk的控制類API: https://tyk.io/docs/getting-started/create-api/#tutorial-create-an-api-with-the-tyk-gateway-api
[21] 通過傳統(tǒng)的配置文件,放入特定目錄下: https://tyk.io/docs/getting-started/create-api/#tutorial-create-an-api-in-file-based-mode
[22] API的定義文件: https://tyk.io/docs/tyk-gateway-api/api-definition-objects/
[23] 請求流量負(fù)載均衡: https://tyk.io/docs/planning-for-production/ensure-high-availability/load-balancing/
[24] 支持配置帶權(quán)重的RR負(fù)載均衡算法: https://tyk.io/docs/planning-for-production/ensure-high-availability/load-balancing/
[25] 動態(tài)負(fù)載均衡: https://tyk.io/docs/planning-for-production/ensure-high-availability/service-discovery/
[26] 服務(wù)發(fā)現(xiàn)示例文檔頁面: https://tyk.io/docs/planning-for-production/ensure-high-availability/service-discovery/examples/
[27] IP白名單: https://tyk.io/docs/tyk-apis/tyk-gateway-api/api-definition-objects/ip-whitelisting/
[28] IP黑名單: https://tyk.io/docs/tyk-apis/tyk-gateway-api/api-definition-objects/ip-blacklisting/
[29] 通過實例理解Go Web身份認(rèn)證的幾種方式: https://tonybai.com/2023/10/23/understand-go-web-authn-by-example
[30] 統(tǒng)一配置TLS證書: https://tyk.io/docs/basic-config-and-security/security/tls-and-ssl/
[31] 通過實例理解Go Web身份認(rèn)證的幾種方式: https://tonybai.com/2023/10/23/understand-go-web-authn-by-example/
[32] 通過實例理解Go Web身份認(rèn)證的幾種方式: https://tonybai.com/2023/10/23/understand-go-web-authn-by-example
[33] Tyk官方文檔: https://tyk.io/docs/basic-config-and-security/security/mutual-tls/
[34] client mTLS: https://tyk.io/docs/basic-config-and-security/security/mutual-tls/client-mtls
[35] upstream mTLS: https://tyk.io/docs/basic-config-and-security/security/mutual-tls/upstream-mtls
[36] Tyk Gateway支持多種身份認(rèn)證方式: https://tyk.io/docs/apim-best-practice/api-security-best-practice/authentication/
[37] 通過實例理解Go Web身份認(rèn)證的幾種方式: https://tonybai.com/2023/10/23/understand-go-web-authn-by-example/
[38] 授權(quán)操作: https://tonybai.com/2023/11/04/understand-go-web-authz-by-example/
[39] 支持RBAC等模型: https://tyk.io/docs/tyk-dashboard/rbac/#understanding-the-concept-of-users-and-permissions
[40] 全局級別和API級別的限速: https://tyk.io/docs/basic-config-and-security/control-limit-traffic/rate-limiting/
[41] 支持請求配額(request quota): https://tyk.io/docs/basic-config-and-security/control-limit-traffic/request-quotas/
[42] 設(shè)置Request的size limit: https://tyk.io/docs/basic-config-and-security/control-limit-traffic/request-size-limits/
[43] Tyk支持健康檢查: https://tyk.io/docs/planning-for-production/ensure-high-availability/health-check/
[44] 內(nèi)置了斷路器(circuit breaker): https://tyk.io/docs/planning-for-production/ensure-high-availability/circuit-breakers/
[45] 設(shè)置強制超時: https://tyk.io/docs/planning-for-production/ensure-high-availability/enforced-timeouts/
[46] Tyk支持與主流的日志收集工具對接: https://tyk.io/docs/log-data/#logging
[47] 使用監(jiān)控工具來利用實時度量的數(shù)據(jù): https://tyk.io/docs/planning-for-production/monitoring/
[48] 對主流metrics采集方案Prometheus+Grafana的支持: https://tyk.io/blog/service-level-objectives-for-your-apis-with-tyk-prometheus-and-grafana/
[49] 支持了與服務(wù)Tracing界的標(biāo)準(zhǔn):OpenTelemetry的集成: https://tyk.io/docs/product-stack/tyk-gateway/advanced-configurations/distributed-tracing/open-telemetry/open-telemetry-overview/
[50] Tyk Gateway的控制API: https://tyk.io/docs/tyk-gateway-api/
[51] 這里: https://github.com/bigwhite/experiments/tree/master/api-gateway-examples
[52] Leaving the Cloud: https://37signals.com/podcast/leaving-the-cloud/
[53] The Past, Present, and Future of API Gateways: https://www.infoq.com/articles/past-present-future-api-gateways/
[54] How moving from AWS to Bare-Metal saved us 230,000/yr: https://blog.oneuptime.com/moving-from-aws-to-bare-metal/
[55] A Comprehensive Guide to API Gateways, Kubernetes Gateways, and Service Meshes: https://navendu.me/posts/gateway-and-mesh/
[56] Use API gateways in microservices: https://learn.microsoft.com/en-us/azure/architecture/microservices/design/gateway
[57] The Tyk API Gateway and Postman: https://blog.postman.com/the-tyk-api-gateway-and-postman/
[58] Getting Started with Tyk API Gateway with Keycloak: https://javascript.plainenglish.io/getting-started-to-tyk-api-gateway-with-keycloak-16307435584a
[59] Observing your API traffic with Tyk, Elasticsearch & Kibana: https://medium.com/@asoorm/observing-your-api-metrics-with-tyk-elasticsearch-kibana-74e8fd946c39
[60] Set up JWT token in tyk gateway: https://community.tyk.io/t/set-up-jwt-token-in-tyk-gateway/6572/9
[61] “Gopher部落”知識星球: https://public.zsxq.com/groups/51284458844544
[62] 鏈接地址: https://m.do.co/c/bff6eed92687