微服務網關 Kong 科普

Kong 是由 Mashape 開發的並於2015年開源的一款API 網關,它是基於OpenResty(Nginx + Lua模塊)和 Apache Cassandra/PostgreSQL 構建的,能提供易於使用的RESTful API來操作和配置API管理系統。Kong 可以水平擴展多個 Kong Server,通過前置的負載均衡配置把請求均勻地分發到各個Server,來應對大批量的網絡請求。

 

Kong 的擴展是通過插件機制進行的,並且也提供了插件的定製示例方法。插件定義了一個請求從進入到最後反饋到客戶端的整個生命週期,所以可以滿足大部分的定製需求,本身 Kong 也已經集成了相當多的插件,包括密鑰認證、CORS、文件日誌、API 請求限流、請求轉發、健康檢查、熔斷等。官網地址: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 處理的各個階段的鉤子, 使用 Lua 擴展 Nginx

  • Kong = Openresty + Customized Framework,Kong作爲 OpenResty 的一個應用程序

在使用Kong之前,最好新瞭解一下 OpenResty和Nginx,可以參考微信公衆號「朱小廝博客」中的《Nginx架構原理科普》和《OpenResty概要及原理科普》這兩篇文章。

Kong 網關具有以下的特性:

  • 可擴展性: 通過簡單地添加更多的服務器,可以輕鬆地進行橫向擴展,這意味着您的平臺可以在一個較低負載的情況下處理任何請求。

  • 模塊化: 可以通過添加新的插件進行擴展,這些插件可以通過RESTful Admin API輕鬆配置。

  • 在任何基礎架構上運行: Kong 網關可以在任何地方都能運行。可以在雲或內部網絡環境中部署 Kong,包括單個或多個數據中心設置,以及 public,private 或 invite-only APIs。

Kong的整體架構如下所示:

  • Kong Restful 管理API提供了API、API消費者、插件、upstreams、證書等管理。

  • Kong 插件攔截請求/響應,相當於 Servlet中的攔截器,實現請求的AOP處理。

  • 數據中心用於存儲 Kong 集羣節點信息、API、消費者、插件等信息,目前提供了PostgreSQL和Cassandra支持,如果需要高可用建議使用Cassandra。

  • Kong 集羣中的節點通過 Gossip 協議自動發現其他節點,當通過一個 Kong 節點的管理 API 進行一些變更時也會通知其他節點。每個 Kong 節點的配置信息是會緩存的,如插件,那麼當在某一個 Kong 節點修改了插件配置時,需要通知其他節點配置的變更。

  • Kong 核心基於 OpenResty,實現了請求/響應的 Lua 處理化。

Kong 網關的API接口的典型請求工作流程如下圖所示:

當 Kong 運行時,每個對 API 的請求將先被 Kong 命中,然後這個請求將會被代理轉發到最終的 API 接口。在請求(Requests)和響應(Responses)之間,Kong 將會執行已經事先安裝和配置好的任何插件,授權 API 訪問操作。Kong 是每個API請求的入口點(Endpoint)。

Install

Kong 可運行在某些 Linux 發行版、Mac OS X 和 Docker 中,無論是本地機還是雲端服務器皆可運行。除了免費的開源版本,Mashape 還提供了付費的企業版[1],其中包括技術支持、使用培訓服務以及 API 分析插件。

爲了演示方便,下面就以Docker環境中部署Kong爲例來做相關講解,內容參考官網:https://docs.konghq.com/install/docker/。Kong 安裝有兩種方式,一種是沒有數據庫依賴的DB-less 模式,另一種是with a Database 模式。我們這裏使用第二種帶Database的模式,因爲這種模式功能更全。

1. 構建 Kong 的容器網絡

首先我們創建一個 docker 自定義網絡,以允許容器相互發現和通信。在下面的創建命令中 kong-net 是我們創建的Docker網絡名稱。

 $ docker network create kong-net

2. 搭建數據庫環境

Kong 目前使用 Cassandra 或者PostgreSQL,你可以執行以下命令中的一個來選擇你的Database。請注意定義網絡 --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. 初始化或者遷移數據庫

我們使用docker run --rm來初始化數據庫,該命令執行後會退出容器而保留內部的數據卷(volume)。這個命令我們還是要注意的,一定要跟你聲明的網絡,數據庫類型、host名稱一致。同時注意Kong的版本號,注:當前 Kong 最新版本爲 2.x,不過目前的kong-dashboard (Kong Admin UI) 尚未支持 2.x 版的Kong,爲了方便後面的演示,這裏以最新的 1.x 版的Kong作爲演示。(截止2020-04-24時,Kong 最新版爲1.5.1)

下面指定的數據庫是 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. 啓動 Kong 容器

完成初始化或者遷移數據庫後,我們就可以啓動一個連接到數據庫容器的 Kong 容器,請務必保證你的數據庫容器啓動狀態,同時檢查所有的環境參數 -e 是否是你定義的環境。

 $ 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 默認綁定4個端口:

  • 8000:用來接收客戶端的 HTTP 請求,並轉發到 upstream。

  • 8443:用來接收客戶端的 HTTPS 請求,並轉發到 upstream。

  • 8001:HTTP 監聽的 API 管理接口。

  • 8444:HTTPS 監聽的 API 管理接口。

到這裏,Kong 已經安裝完畢,我們可以使用 docker ps命令查看當前運行容器,正常情況下可以看到 Kong 和  PostgreSQL 的兩個容器:

$ 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

我們可以通過 curl -i http://localhost:8001/ 來查看 Kong 是否運行完好。

Kong UI

Kong 企業版提供了管理UI,開源版本是沒有的。但是有很多的開源的管理 UI ,其中比較 Fashion的有Kong Dashboard和 Konga。Kong Dashboard 當前最新版本(3.6.x)並不支持最新版本的 Kong,最後一次更新也要追溯到1年多以前了,選擇 Konga 會更好一點。這裏簡單介紹一下Kong Dashboard和 Konga。

Kong Dashboard

Kong Dashboard的Github地址爲:https://github.com/PGBI/kong-dashboard。docker 環境中安裝運行如下:

$ docker run --rm  \
--network=kong-net \
-p 8080:8080 \
pgbi/kong-dashboard start \
--kong-url http://kong:8001

啓動之後,可以在瀏覽器中輸入 http://localhost:8080來訪問 Kong Dashboard 管理界面。

Konga

Konga (官網地址:https://pantsel.github.io/konga/,Github地址:https://github.com/pantsel/konga)可以很好地通過UI觀察到現在 Kong 的所有的配置,並且可以對於管理 Kong 節點情況進行查看、監控和預警。Konga 主要是用 AngularJS 寫的,運行於nodejs服務端。具有以下特性:

  • 管理所有Kong Admin API對象。

  • 支持從遠程源(數據庫,文件,API等)導入使用者。

  • 管理多個Kong節點。使用快照備份,還原和遷移Kong節點。

  • 使用運行狀況檢查監視節點和API狀態。

  • 支持電子郵件和閒置通知。

  • 支持多用戶。

  • 易於數據庫集成(MySQL,PostgresSQL,MongoDB,SQL Server)。

下面使用的 PostgresSQL 是和上面在docker環境中安裝 Kong時的是一致的,注意用戶名、密碼、數據庫名稱等配置,docker環境安裝啓動 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容器啓動成功,可以通過 http://localhost:1337/訪問管理界面。通過註冊後進入,然後在 CONNECTIONS 中添加 Kong 服務的管理路徑http://xxx.xxx.xxx.xxx:8001。Konga管理界面示例如下:

Kong Admin API

部署好 Kong 之後,則需要將我們自己的接口加入到 Kong 的中管理,Kong 提供了比較全面的RESTful API,每個版本會有所不同,詳細可以參考官網:https://docs.konghq.com/2.0.x/admin-api/。Kong 管理API的端口是8001(8044),服務、路由、配置都是通過這個端口進行管理,所以部署好之後頁面可以直接訪問 http://localhost:8001

這裏我們先來了解一下如何使用 RESTful 管理接口來管理 Service (服務)、Route(路由)。

1. 添加一個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' 是在《網關 Zuul 科普》中提及的一個簡單的基礎服務接口,調用這個接口會返回 Hello!

客戶端調用 Service 名稱 hello-service 訪問 'http://xxx.xxx.xxx.xxx:8081/hello'。添加成功後,系統將返回:

{
  "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 添加一個 Route

$ curl -i -X POST \
--url http://localhost:8001/services/hello-service/routes \
--data 'paths[]=/hello' \
--data name=hello-route

添加成功後,系統將返回:

{
  "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. 驗證

我們可以通過訪問 http://localhost:8000/hello 來驗證一下配置是否正確。

前面的操作就等效於配置 nginx.conf:

server {
  listen 8000;
  location /hello {
    proxy_pass http://xxx.xxx.xxx.xxx8081/hello;
  }
}

不過,前面的配置操作都是動態的,無需像 Nginx一樣需要重啓。

Service是抽象層面的服務,它可以直接映射到一個物理服務,也可以指向一個Upstream(同Nginx中的Upstream,是對上游服務器的抽象)。Route是路由的抽象,它負責將實際的請求映射到 Service。除了Serivce、Route之外,還有 Tag、Consumer、Plugin、Certificate、SNI、Upstream、Target等,讀者可以從官網的介紹文檔[2]中瞭解全貌。

下面在演示一個例子,修改 Service,將其映射到一個 Upstream:

# 添加 name爲 hello-upstream 的 Upstream
$ curl -i -X POST http://localhost:8001/upstreams \
--data name=hello-upstream

# 爲 mock-upstream 添加 Target,Target 代表了一個物理服務(IP地址/hostname + port的抽象),一個Upstream可以包含多個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;
  }
}

當然,這裏的配置我們也可以通過管理界面來操作。上面操作完之後,在Konga中也有相關信息展示出來:

Kong Plugins

Kong通過插件Plugins實現日誌記錄、安全檢測、性能監控和負載均衡等功能。下面我將演示一個例子,通過啓動 apikey 實現簡單網關安全檢驗。

1. 配置 key-auth 插件

$ curl -i -X POST http://localhost:8001/routes/hello-route/plugins \
--data name=key-auth

這個插件接收config.key_names定義參數,默認參數名稱 ['apikey']。在HTTP請求中 header和params參數中包含apikey參數,參數值必須apikey密鑰,Kong網關將堅持密鑰,驗證通過纔可以訪問後續服務。

此時我們使用 curl -i http://localhost:8000/hello 來驗證一下是否生效,如果如下所示,訪問失敗(HTTP/1.1 401 Unauthorized,"No API key found in request" ),說明 Kong 安全機制生效了。

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中我們也可以看到相關記錄:

2. 爲Service添加服務消費者(Consumer),定義消費者訪問 API Key, 讓他擁有訪問hello-service的權限。

創建消費者 Hidden:

$ curl -i -X POST http://localhost:8001/consumers/ \
--data username=Hidden

創建成功之後,返回:

{
  "custom_id": null,
  "created_at": 1587970751,
  "id": "95546c8f-248c-45c7-bce5-d972d3d9291a",
  "tags": null,
  "username": "Hidden"
}

之後爲消費者 Hidden 創建一個 api key,輸入如下命令:

$ curl -i -X POST http://localhost:8001/consumers/Hidden/key-auth/ \
--data key=ENTER_KEY_HERE

現在我們再來驗證一下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 官網(https://docs.konghq.com/hub/)列出了已有的所有插件,如下圖所示:

Kong 網關插件概括爲如下:

  • 身份認證插件:Kong提供了Basic Authentication、Key authentication、OAuth2.0 authentication、HMAC authentication、JWT、LDAP authentication認證實現。

  • 安全控制插件:ACL(訪問控制)、CORS(跨域資源共享)、動態SSL、IP限制、爬蟲檢測實現。

  • 流量控制插件:請求限流(基於請求計數限流)、上游響應限流(根據upstream響應計數限流)、請求大小限制。限流支持本地、Redis和集羣限流模式。

  • 分析監控插件:Galileo(記錄請求和響應數據,實現API分析)、Datadog(記錄API Metric如請求次數、請求大小、響應狀態和延遲,可視化API Metric)、Runscope(記錄請求和響應數據,實現API性能測試和監控)。

  • 協議轉換插件:請求轉換(在轉發到upstream之前修改請求)、響應轉換(在upstream響應返回給客戶端之前修改響應)。

  • 日誌應用插件:TCP、UDP、HTTP、File、Syslog、StatsD、Loggly等。

總結

Kong 作爲API網關提供了API管理功能及圍繞API管理實現了一些默認的插件,另外還具備集羣水平擴展能力,從而提升整體吞吐量。Kong 本身是基於 OpenResty,可以在現有 Kong 的基礎上進行一些擴展,從而實現更復雜的特性。雖然有一些特性 Kong 默認是缺失的,如API級別的超時、重試、fallback策略、緩存、API聚合、AB測試等,這些功能插件需要企業開發人員通過 Lua 語言進行定製和擴展。綜上所述,Kong API 網關默認提供的插件比較豐富, 適應針對企業級的API網關定位。

References

  1. https://github.com/Kong/kong

  2. https://www.jianshu.com/p/a2e0bc8f4bfb

  3. https://docs.konghq.com/install/docker

  4. https://www.cnblogs.com/duanxz/p/9770645.html

  5. https://docs.konghq.com/2.0.x/admin-api/

  6. https://docs.konghq.com/hub/

參考資料

[1] 企業版: http://getkong.org/enterprise/

[2] 官網的介紹文檔: https://docs.konghq.com/2.0.x/admin-api/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章