聊一聊微服務(wù)網(wǎng)關(guān) Kong
Kong 是由 Mashape 開(kāi)發(fā)的并于2015年開(kāi)源的一款A(yù)PI 網(wǎng)關(guān),它是基于OpenResty(Nginx + Lua模塊)和 Apache Cassandra/PostgreSQL 構(gòu)建的,能提供易于使用的RESTful API來(lái)操作和配置API管理系統(tǒng)。Kong 可以水平擴(kuò)展多個(gè) Kong Server,通過(guò)前置的負(fù)載均衡配置把請(qǐng)求均勻地分發(fā)到各個(gè)Server,來(lái)應(yīng)對(duì)大批量的網(wǎng)絡(luò)請(qǐng)求。
Kong 的擴(kuò)展是通過(guò)插件機(jī)制進(jìn)行的,并且也提供了插件的定制示例方法。插件定義了一個(gè)請(qǐng)求從進(jìn)入到最后反饋到客戶(hù)端的整個(gè)生命周期,所以可以滿(mǎn)足大部分的定制需求,本身 Kong 也已經(jīng)集成了相當(dāng)多的插件,包括密鑰認(rèn)證、CORS、文件日志、API 請(qǐng)求限流、請(qǐng)求轉(zhuǎn)發(fā)、健康檢查、熔斷等。官網(wǎng)地址:https://konghq.com/,代碼托管地址:https://github.com/Kong/kong。
Nginx、Openresty和Kong三者緊密相連:
- Nginx = Http Server + Reversed Proxy + Load Balancer
- Openresty = Nginx + Lua-nginx-module,Openresty是寄生在 Nginx 上,暴露 Nginx 處理的各個(gè)階段的鉤子, 使用 Lua 擴(kuò)展 Nginx
- Kong = Openresty + Customized Framework,Kong作為 OpenResty 的一個(gè)應(yīng)用程序
在使用Kong之前,最好新了解一下 OpenResty和Nginx
Kong 網(wǎng)關(guān)具有以下的特性:
- 可擴(kuò)展性: 通過(guò)簡(jiǎn)單地添加更多的服務(wù)器,可以輕松地進(jìn)行橫向擴(kuò)展,這意味著您的平臺(tái)可以在一個(gè)較低負(fù)載的情況下處理任何請(qǐng)求。
- 模塊化: 可以通過(guò)添加新的插件進(jìn)行擴(kuò)展,這些插件可以通過(guò)RESTful Admin API輕松配置。
- 在任何基礎(chǔ)架構(gòu)上運(yùn)行: Kong 網(wǎng)關(guān)可以在任何地方都能運(yùn)行。可以在云或內(nèi)部網(wǎng)絡(luò)環(huán)境中部署 Kong,包括單個(gè)或多個(gè)數(shù)據(jù)中心設(shè)置,以及 public,private 或 invite-only APIs。
Kong的整體架構(gòu)如下所示:
- Kong Restful 管理API提供了API、API消費(fèi)者、插件、upstreams、證書(shū)等管理。
- Kong 插件攔截請(qǐng)求/響應(yīng),相當(dāng)于 Servlet中的攔截器,實(shí)現(xiàn)請(qǐng)求的AOP處理。
- 數(shù)據(jù)中心用于存儲(chǔ) Kong 集群節(jié)點(diǎn)信息、API、消費(fèi)者、插件等信息,目前提供了PostgreSQL和Cassandra支持,如果需要高可用建議使用Cassandra。
- Kong 集群中的節(jié)點(diǎn)通過(guò) Gossip 協(xié)議自動(dòng)發(fā)現(xiàn)其他節(jié)點(diǎn),當(dāng)通過(guò)一個(gè) Kong 節(jié)點(diǎn)的管理 API 進(jìn)行一些變更時(shí)也會(huì)通知其他節(jié)點(diǎn)。每個(gè) Kong 節(jié)點(diǎn)的配置信息是會(huì)緩存的,如插件,那么當(dāng)在某一個(gè) Kong 節(jié)點(diǎn)修改了插件配置時(shí),需要通知其他節(jié)點(diǎn)配置的變更。
- Kong 核心基于 OpenResty,實(shí)現(xiàn)了請(qǐng)求/響應(yīng)的 Lua 處理化。
Kong 網(wǎng)關(guān)的API接口的典型請(qǐng)求工作流程如下圖所示:
當(dāng) Kong 運(yùn)行時(shí),每個(gè)對(duì) API 的請(qǐng)求將先被 Kong 命中,然后這個(gè)請(qǐng)求將會(huì)被代理轉(zhuǎn)發(fā)到最終的 API 接口。在請(qǐng)求(Requests)和響應(yīng)(Responses)之間,Kong 將會(huì)執(zhí)行已經(jīng)事先安裝和配置好的任何插件,授權(quán) API 訪問(wèn)操作。Kong 是每個(gè)API請(qǐng)求的入口點(diǎn)(Endpoint)。
InstallKong
可運(yùn)行在某些 Linux 發(fā)行版、Mac OS X 和 Docker 中,無(wú)論是本地機(jī)還是云端服務(wù)器皆可運(yùn)行。除了免費(fèi)的開(kāi)源版本,Mashape 還提供了付費(fèi)的企業(yè)版[1],其中包括技術(shù)支持、使用培訓(xùn)服務(wù)以及 API 分析插件。
為了演示方便,下面就以Docker環(huán)境中部署Kong為例來(lái)做相關(guān)講解,內(nèi)容參考官網(wǎng):https://docs.konghq.com/install/docker/。Kong 安裝有兩種方式,一種是沒(méi)有數(shù)據(jù)庫(kù)依賴(lài)的DB-less 模式,另一種是with a Database 模式。我們這里使用第二種帶Database的模式,因?yàn)檫@種模式功能更全。
1. 構(gòu)建 Kong 的容器網(wǎng)絡(luò)
首先我們創(chuàng)建一個(gè) docker 自定義網(wǎng)絡(luò),以允許容器相互發(fā)現(xiàn)和通信。在下面的創(chuàng)建命令中 kong-net 是我們創(chuàng)建的Docker網(wǎng)絡(luò)名稱(chēng)。
- $ docker network create kong-net
2. 搭建數(shù)據(jù)庫(kù)環(huán)境
Kong 目前使用 Cassandra 或者PostgreSQL,你可以執(zhí)行以下命令中的一個(gè)來(lái)選擇你的Database。請(qǐng)注意定義網(wǎng)絡(luò) --network=kong-net 。
使用Cassandra:
- docker run -d --name kong-database \
- --network=kong-net \
- -p 9042:9042 \
- cassandra:3
使用 PostgreSQL:
- $ docker run -d --name kong-database \
- --network=kong-net \
- -p 5432:5432 \
- -e "POSTGRES_USER=kong" \
- -e "POSTGRES_DB=kong" \
- -e "POSTGRES_PASSWORD=kong" \
- postgres:9.6
3. 初始化或者遷移數(shù)據(jù)庫(kù)
我們使用docker run --rm來(lái)初始化數(shù)據(jù)庫(kù),該命令執(zhí)行后會(huì)退出容器而保留內(nèi)部的數(shù)據(jù)卷(volume)。這個(gè)命令我們還是要注意的,一定要跟你聲明的網(wǎng)絡(luò),數(shù)據(jù)庫(kù)類(lèi)型、host名稱(chēng)一致。同時(shí)注意Kong的版本號(hào),注:當(dāng)前 Kong 最新版本為 2.x,不過(guò)目前的kong-dashboard (Kong Admin UI) 尚未支持 2.x 版的Kong,為了方便后面的演示,這里以最新的 1.x 版的Kong作為演示。(截止2020-04-24時(shí),Kong 最新版為1.5.1)
下面指定的數(shù)據(jù)庫(kù)是 PostgreSQL,如果連接的是 Cassandra,可以將下面的 KONG_DATABASE 配置為 cassandra。
- $ docker run --rm \
- --network=kong-net \
- -e "KONG_DATABASE=postgres" \
- -e "KONG_PG_HOST=kong-database" \
- -e "KONG_PG_PASSWORD=kong" \
- -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
- kong:1.5.1 kong migrations bootstrap
4. 啟動(dòng) Kong 容器
完成初始化或者遷移數(shù)據(jù)庫(kù)后,我們就可以啟動(dòng)一個(gè)連接到數(shù)據(jù)庫(kù)容器的 Kong 容器,請(qǐng)務(wù)必保證你的數(shù)據(jù)庫(kù)容器啟動(dòng)狀態(tài),同時(shí)檢查所有的環(huán)境參數(shù) -e 是否是你定義的環(huán)境。
- $ docker run -d --name kong \
- --network=kong-net \
- -e "KONG_DATABASE=postgres" \
- -e "KONG_PG_HOST=kong-database" \
- -e "KONG_PG_PASSWORD=kong" \
- -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
- -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
- -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
- -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
- -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
- -e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
- -p 8000:8000 \
- -p 8443:8443 \
- -p 8001:8001 \
- -p 8444:8444 \
- kong:1.5.1
Kong 默認(rèn)綁定4個(gè)端口:
- 8000:用來(lái)接收客戶(hù)端的 HTTP 請(qǐng)求,并轉(zhuǎn)發(fā)到 upstream。
- 8443:用來(lái)接收客戶(hù)端的 HTTPS 請(qǐng)求,并轉(zhuǎn)發(fā)到 upstream。
- 8001:HTTP 監(jiān)聽(tīng)的 API 管理接口。
- 8444:HTTPS 監(jiān)聽(tīng)的 API 管理接口。
到這里,Kong 已經(jīng)安裝完畢,我們可以使用 docker ps命令查看當(dāng)前運(yùn)行容器,正常情況下可以看到 Kong 和 PostgreSQL 的兩個(gè)容器:
- $ docker ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- a28160da4a9d kong:latest "/docker-entrypoint.…" 10 seconds ago Up 9 seconds 0.0.0.0:8000-8001->8000-8001/tcp, 0.0.0.0:8443-8444->8443-8444/tcp kong
- 6c85a2e5491f postgres:9.6 "docker-entrypoint.s…" 31 minutes ago Up 31 minutes 0.0.0.0:5432->5432/tcp kong-database
我們可以通過(guò) curl -i http://localhost:8001/ 來(lái)查看 Kong 是否運(yùn)行完好。
Kong UI
Kong 企業(yè)版提供了管理UI,開(kāi)源版本是沒(méi)有的。但是有很多的開(kāi)源的管理 UI ,其中比較 Fashion的有Kong Dashboard和 Konga。Kong Dashboard 當(dāng)前最新版本(3.6.x)并不支持最新版本的 Kong,最后一次更新也要追溯到1年多以前了,選擇 Konga 會(huì)更好一點(diǎn)。這里簡(jiǎn)單介紹一下Kong Dashboard和 Konga。
Kong Dashboard
Kong Dashboard的Github地址為:https://github.com/PGBI/kong-dashboard。docker 環(huán)境中安裝運(yùn)行如下:
- $ docker run --rm \
- --network=kong-net \
- -p 8080:8080 \
- pgbi/kong-dashboard start \
- --kong-url http://kong:8001
啟動(dòng)之后,可以在瀏覽器中輸入 http://localhost:8080來(lái)訪問(wèn) Kong Dashboard 管理界面。
Konga
Konga (官網(wǎng)地址:https://pantsel.github.io/konga/,Github地址:https://github.com/pantsel/konga)可以很好地通過(guò)UI觀察到現(xiàn)在 Kong 的所有的配置,并且可以對(duì)于管理 Kong 節(jié)點(diǎn)情況進(jìn)行查看、監(jiān)控和預(yù)警。Konga 主要是用 AngularJS 寫(xiě)的,運(yùn)行于nodejs服務(wù)端。具有以下特性:
- 管理所有Kong Admin API對(duì)象。
- 支持從遠(yuǎn)程源(數(shù)據(jù)庫(kù),文件,API等)導(dǎo)入使用者。
- 管理多個(gè)Kong節(jié)點(diǎn)。使用快照備份,還原和遷移Kong節(jié)點(diǎn)。
- 使用運(yùn)行狀況檢查監(jiān)視節(jié)點(diǎn)和API狀態(tài)。
- 支持電子郵件和閑置通知。
- 支持多用戶(hù)。
- 易于數(shù)據(jù)庫(kù)集成(MySQL,PostgresSQL,MongoDB,SQL Server)。
下面使用的 PostgresSQL 是和上面在docker環(huán)境中安裝 Kong時(shí)的是一致的,注意用戶(hù)名、密碼、數(shù)據(jù)庫(kù)名稱(chēng)等配置,docker環(huán)境安裝啟動(dòng) Konga:
- $ docker run -d -p 1337:1337 \
- --network kong-net \
- --name konga \
- -e "DB_ADAPTER=postgres" \
- -e "DB_URI=postgresql://kong:kong@kong-database/kong" \
- pantsel/konga
如果Konga容器啟動(dòng)成功,可以通過(guò) http://localhost:1337/訪問(wèn)管理界面。通過(guò)注冊(cè)后進(jìn)入,然后在 CONNECTIONS 中添加 Kong 服務(wù)的管理路徑http://xxx.xxx.xxx.xxx:8001。Konga管理界面示例如下:
Kong Admin API
部署好 Kong 之后,則需要將我們自己的接口加入到 Kong 的中管理,Kong 提供了比較全面的RESTful API,每個(gè)版本會(huì)有所不同,詳細(xì)可以參考官網(wǎng):https://docs.konghq.com/2.0.x/admin-api/。Kong 管理API的端口是8001(8044),服務(wù)、路由、配置都是通過(guò)這個(gè)端口進(jìn)行管理,所以部署好之后頁(yè)面可以直接訪問(wèn) http://localhost:8001。
這里我們先來(lái)了解一下如何使用 RESTful 管理接口來(lái)管理 Service (服務(wù))、Route(路由)。
1. 添加一個(gè)Service
- $ curl -i -X POST http://localhost:8001/services \
- --data name=hello-service \
- --data url='http://xxx.xxx.xxx.xxx:8081/hello'
這里的 'http://xxx.xxx.xxx.xxx:8081/hello' 是在《網(wǎng)關(guān) Zuul 科普》中提及的一個(gè)簡(jiǎn)單的基礎(chǔ)服務(wù)接口,調(diào)用這個(gè)接口會(huì)返回 Hello!。
客戶(hù)端調(diào)用 Service 名稱(chēng) hello-service 訪問(wèn) 'http://xxx.xxx.xxx.xxx:8081/hello'。添加成功后,系統(tǒng)將返回:
- {
- "host": "xxx.xxx.xxx.xxx",
- "created_at": 1587959433,
- "connect_timeout": 60000,
- "id": "d96f418a-8158-4b1d-844d-ed994fdbcc2c",
- "protocol": "http",
- "name": "hello-service",
- "read_timeout": 60000,
- "port": 8081,
- "path": "\/hello",
- "updated_at": 1587959433,
- "retries": 5,
- "write_timeout": 60000,
- "tags": null,
- "client_certificate": null
- }
2. 為 Service 添加一個(gè) Route
- $ curl -i -X POST \
- --url http://localhost:8001/services/hello-service/routes \
- --data 'paths[]=/hello' \
- --data name=hello-route
添加成功后,系統(tǒng)將返回:
- {
- "id": "667bafde-7ca4-4fc4-b4f1-15c3cbec0b09",
- "path_handling": "v1",
- "paths": [
- "\/hello"
- ],
- "destinations": null,
- "headers": null,
- "protocols": [
- "http",
- "https"
- ],
- "methods": null,
- "snis": null,
- "service": {
- "id": "d96f418a-8158-4b1d-844d-ed994fdbcc2c"
- },
- "name": hello-route,
- "strip_path": true,
- "preserve_host": false,
- "regex_priority": 0,
- "updated_at": 1587959468,
- "sources": null,
- "hosts": null,
- "https_redirect_status_code": 426,
- "tags": null,
- "created_at": 1587959468
- }
3. 驗(yàn)證
我們可以通過(guò)訪問(wèn) http://localhost:8000/hello 來(lái)驗(yàn)證一下配置是否正確。
前面的操作就等效于配置 nginx.conf:
- server {
- listen 8000;
- location /hello {
- proxy_pass http://xxx.xxx.xxx.xxx8081/hello;
- }
- }
不過(guò),前面的配置操作都是動(dòng)態(tài)的,無(wú)需像 Nginx一樣需要重啟。
Service是抽象層面的服務(wù),它可以直接映射到一個(gè)物理服務(wù),也可以指向一個(gè)Upstream(同Nginx中的Upstream,是對(duì)上游服務(wù)器的抽象)。Route是路由的抽象,它負(fù)責(zé)將實(shí)際的請(qǐng)求映射到 Service。除了Serivce、Route之外,還有 Tag、Consumer、Plugin、Certificate、SNI、Upstream、Target等,讀者可以從官網(wǎng)的介紹文檔[2]中了解全貌。
下面在演示一個(gè)例子,修改 Service,將其映射到一個(gè) Upstream:
- # 添加 name為 hello-upstream 的 Upstream
- $ curl -i -X POST http://localhost:8001/upstreams \
- --data name=hello-upstream
- # 為 mock-upstream 添加 Target,Target 代表了一個(gè)物理服務(wù)(IP地址/hostname + port的抽象),一個(gè)Upstream可以包含多個(gè)Targets
- $ curl -i -X POST http://localhost:8001/upstreams/hello-upstream/targets \
- --data target="xxx.xxx.xxx.xxx:8081"
- # 修改 hello-service,為其配置
- $ curl -i -X PATCH http://localhost:8001/services/hello-service \
- --data url='http://hello-upstream/hello'
上面的配置等同于 Nginx 中的nginx.conf配置 :
- upstream hello-upstream{
- server xxx.xxx.xxx.xxx:8081;
- }
- server {
- listen 8000;
- location /hello {
- proxy_pass http://hello-upstream/hello;
- }
- }
當(dāng)然,這里的配置我們也可以通過(guò)管理界面來(lái)操作。上面操作完之后,在Konga中也有相關(guān)信息展示出來(lái):
Kong Plugins
Kong通過(guò)插件Plugins實(shí)現(xiàn)日志記錄、安全檢測(cè)、性能監(jiān)控和負(fù)載均衡等功能。下面我將演示一個(gè)例子,通過(guò)啟動(dòng) apikey 實(shí)現(xiàn)簡(jiǎn)單網(wǎng)關(guān)安全檢驗(yàn)。
1. 配置 key-auth 插件
- $ curl -i -X POST http://localhost:8001/routes/hello-route/plugins \
- --data name=key-auth
這個(gè)插件接收config.key_names定義參數(shù),默認(rèn)參數(shù)名稱(chēng) ['apikey']。在HTTP請(qǐng)求中 header和params參數(shù)中包含apikey參數(shù),參數(shù)值必須apikey密鑰,Kong網(wǎng)關(guān)將堅(jiān)持密鑰,驗(yàn)證通過(guò)才可以訪問(wèn)后續(xù)服務(wù)。
此時(shí)我們使用 curl -i http://localhost:8000/hello 來(lái)驗(yàn)證一下是否生效,如果如下所示,訪問(wèn)失敗(HTTP/1.1 401 Unauthorized,"No API key found in request" ),說(shuō)明 Kong 安全機(jī)制生效了。
- HTTP/1.1 401 Unauthorized
- Date: Mon, 27 Apr 2020 06:44:58 GMT
- Content-Type: application/json; charset=utf-8
- Connection: keep-alive
- WWW-Authenticate: Key realm="kong"
- Content-Length: 41
- X-Kong-Response-Latency: 2
- Server: kong/1.5.1
- {"message":"No API key found in request"}
在Konga中我們也可以看到相關(guān)記錄:
2. 為Service添加服務(wù)消費(fèi)者(Consumer),定義消費(fèi)者訪問(wèn) API Key, 讓他擁有訪問(wèn)hello-service的權(quán)限。
創(chuàng)建消費(fèi)者 Hidden:
- $ curl -i -X POST http://localhost:8001/consumers/ \
- --data username=Hidden
創(chuàng)建成功之后,返回:
- {
- "custom_id": null,
- "created_at": 1587970751,
- "id": "95546c8f-248c-45c7-bce5-d972d3d9291a",
- "tags": null,
- "username": "Hidden"
- }
- 之后為消
之后為消費(fèi)者 Hidden 創(chuàng)建一個(gè) api key,輸入如下命令:
- $ curl -i -X POST http://localhost:8001/consumers/Hidden/key-auth/ \
- --data key=ENTER_KEY_HERE
現(xiàn)在我們?cè)賮?lái)驗(yàn)證一下http://localhost:8000/hello:
- $ curl -i -X GET http://localhost:8000/hello \
- --header "apikey:ENTER_KEY_HERE"
返回:
- HTTP/1.1 200
- Content-Type: text/plain;charset=UTF-8
- Content-Length: 7
- Connection: keep-alive
- Date: Mon, 27 Apr 2020 07:08:38 GMT
- X-Kong-Upstream-Latency: 116
- X-Kong-Proxy-Latency: 71
- Via: kong/1.5.1
- Hello!
Well done.
Kong 官網(wǎng)(https://docs.konghq.com/hub/)列出了已有的所有插件,如下圖所示:
Kong 網(wǎng)關(guān)插件概括為如下:
- 身份認(rèn)證插件:Kong提供了Basic Authentication、Key authentication、OAuth2.0 authentication、HMAC authentication、JWT、LDAP authentication認(rèn)證實(shí)現(xiàn)。
- 安全控制插件:ACL(訪問(wèn)控制)、CORS(跨域資源共享)、動(dòng)態(tài)SSL、IP限制、爬蟲(chóng)檢測(cè)實(shí)現(xiàn)。
- 流量控制插件:請(qǐng)求限流(基于請(qǐng)求計(jì)數(shù)限流)、上游響應(yīng)限流(根據(jù)upstream響應(yīng)計(jì)數(shù)限流)、請(qǐng)求大小限制。限流支持本地、Redis和集群限流模式。
- 分析監(jiān)控插件:Galileo(記錄請(qǐng)求和響應(yīng)數(shù)據(jù),實(shí)現(xiàn)API分析)、Datadog(記錄API Metric如請(qǐng)求次數(shù)、請(qǐng)求大小、響應(yīng)狀態(tài)和延遲,可視化API Metric)、Runscope(記錄請(qǐng)求和響應(yīng)數(shù)據(jù),實(shí)現(xiàn)API性能測(cè)試和監(jiān)控)。
- 協(xié)議轉(zhuǎn)換插件:請(qǐng)求轉(zhuǎn)換(在轉(zhuǎn)發(fā)到upstream之前修改請(qǐng)求)、響應(yīng)轉(zhuǎn)換(在upstream響應(yīng)返回給客戶(hù)端之前修改響應(yīng))。
- 日志應(yīng)用插件:TCP、UDP、HTTP、File、Syslog、StatsD、Loggly等。
總結(jié)
Kong 作為API網(wǎng)關(guān)提供了API管理功能及圍繞API管理實(shí)現(xiàn)了一些默認(rèn)的插件,另外還具備集群水平擴(kuò)展能力,從而提升整體吞吐量。Kong 本身是基于 OpenResty,可以在現(xiàn)有 Kong 的基礎(chǔ)上進(jìn)行一些擴(kuò)展,從而實(shí)現(xiàn)更復(fù)雜的特性。雖然有一些特性 Kong 默認(rèn)是缺失的,如API級(jí)別的超時(shí)、重試、fallback策略、緩存、API聚合、AB測(cè)試等,這些功能插件需要企業(yè)開(kāi)發(fā)人員通過(guò) Lua 語(yǔ)言進(jìn)行定制和擴(kuò)展。綜上所述,Kong API 網(wǎng)關(guān)默認(rèn)提供的插件比較豐富, 適應(yīng)針對(duì)企業(yè)級(jí)的API網(wǎng)關(guān)定位。
References
- https://github.com/Kong/kong
- https://www.jianshu.com/p/a2e0bc8f4bfb
- https://docs.konghq.com/install/docker
- https://www.cnblogs.com/duanxz/p/9770645.html
- https://docs.konghq.com/2.0.x/admin-api/
- https://docs.konghq.com/hub/
參考資料
[1]企業(yè)版: http://getkong.org/enterprise/
[2]官網(wǎng)的介紹文檔: https://docs.konghq.com/2.0.x/admin-api/