微服務 API 網關 Kong 實踐

原文: https://nullwy.me/2020/05/kon...
若是以爲個人文章對你有用,請隨意讚揚

Kong 簡介

Kong 是雲原生、高效、可擴展、分佈式的微服務抽象層,被稱爲 API 網關,或者 API 中間件。Kong 在 2015 年 4 月由 Mashape 公司開源,基於 OpenResty 和 Apache Cassandra/PostgreSQL 構建,提供易於使用的 RESTful API 來操做和配置 API 系統12nginx

Mashape 是 API 集市,是爲應用開發者與 API 提供者服務的 API 交易市場,Mashape 讓發者可以方便地查找與購買 API,而 API 提供商則能輕鬆地銷售與管理 API3。隨着 Mashape 市場上的 API 愈來愈多,原先基於 Node.js 實現的 API 代理再也不適用,不能處理大流量尖峯,沒法快速擴容。因而,尋找處理大流量的方案,同時須要保證可靠性和安全性,成爲 Mashape 亟待解決的問題。2013 年,在 CloudFlare(當時 OpenResty 背後的公司)的工程師的建議下,Mashape 開始在 OpenResty 基礎上開發 Kong 項目4。Mashape 公司的名字,MashAPE,有人猿猩猩的含義,公司 logo 也是相應動物。相似的,Kong,對應的是,King Kong,就是電影裏的金剛4。在 Mashape 開啓 Kong 項目兩年後,2015 年 4 月,Mashape 公司開源了 Kong2git

2017 年 5月,Mashape 和 RapidAPI 合併,組成全球最大的 API 集市56。5 個月後,Mashape, Inc. 更名爲 Kong Inc.,新的公司以 Kong 項目爲聚焦,把所有工程師投入到 Kong 開發中,而且於此同時他們發佈了 Kong 企業版7github

Kong,做爲微服務的請求的網關,能經過插件提供負載均衡、日誌記錄、鑑權、限流、轉換以及其餘等功能。相對與舊的、沒有使用網關的方式,Kong 把這些通用功能中心化,讓微服務更加專一於業務自己。正則表達式

kong-old-way-vs-kong-way

Kong 的總體架構,以下圖所示1算法

  • 管理 API:經過 RESTful API 管理 Kong;能自動化集成;管理 API 能經過插件擴展
  • 插件:使用 Lua 腳本建立 Plugins;實現強力的定製化;與第三方服務集成
  • 集羣和數據存儲:數據存儲可選擇 PostgreSQL 或 Cassandra;能從單節點擴展爲集羣;使用內存緩存提升性能
  • OpenResty:攔截請求/響應生命週期;基於 NGINX 擴展;Lua 腳本化
  • NGINX:驗證過的高性能基礎組件;HTTP 和反向代理服務器;處理底層操做

kong-architecture

Kong 安裝

目前最新的 Kong 版本是 2.0.x,2.0 發佈時間是 2020 年 1 月,而 1.0 發佈時間是 2018 年 12 月89。筆者公司使用的 Kong 版本是 0.14.1,暫時未升級自最新版,因此下文闡述的 Kong 版本主要以 0.14.1 爲準,並同時會說起其餘版本的特性。sql

安裝 Kong 很簡單,參見官方文檔便可。在 Ubuntu 18.04 下安裝 Kong 0.14.1,能夠執行下面的命令:docker

# kong 安裝
$ sudo apt update
$ sudo apt install openssl libpcre3 procps perl
$ wget -O kong-community-edition-0.14.1.trusty.all.deb https://bintray.com/kong/kong-community-edition-deb/download_file?file_path=dists/kong-community-edition-0.14.1.trusty.all.deb
$ sudo dpkg -i kong-community-edition-0.14.1.trusty.all.deb
$ kong version
0.14.1

Kong 依賴數據庫,Postgres 或者 Cassandra,默認依賴 Postgres(kong 1.1 開始支持無數據庫聲明式配置10)。咱們預先安裝 Postgres:數據庫

# 安裝 postgresql
$ sudo apt install postgresql
$ sudo service postgresql start
$ psql --version
psql (PostgreSQL) 10.12 (Ubuntu 10.12-0ubuntu0.18.04.1)

在 Postgres 下添加 Kong 須要的的數據庫實例和用戶。下面的示例,建立數據庫 kong,用戶名 kong,密碼爲 kong編程

$ sudo -u postgres psql
postgres=# CREATE USER kong; CREATE DATABASE kong OWNER kong;
postgres=# ALTER USER kong WITH PASSWORD 'kong';
postgres=# \q

執行完成後,便可使用用戶名爲 kong 的用戶鏈接 Postgres,psql -h localhost -U kong -d kongjson

Kong 安裝完成後,默認會建立配置文件 /etc/kong/kong.conf.default,這份配置文件在 GitHub 上也能找到,被註釋掉的配置項,就是默認設置。

在啓動 Kong 網關服務器前,咱們參考 kong.conf.default,建立本身的 kong.conf 配置文件。咱們把配置文件放在 /home/yulewei/kong 目錄下,同時也把這目錄看成爲 Kong 的 prefix 目錄。修改這配置 kong.conf,文件末尾添加:

prefix = /home/yulewei/kong/
pg_user=kong
pg_password=kong
pg_database = kong

使用 kong 命令,啓動 Kong 網關服務器:

# 初始化或遷移數據庫數據
$ kong migrations up -c /home/yulewei/kong/kong.conf
# 啓動 kong
$ kong start -c /home/yulewei/kong/kong.conf

Kong 默認綁定 4 個端口:

  • :8000 用來接收來自客戶端的 HTTP 流量的請求,並轉發到上游服務
  • :8443 用來接收來自客戶端的 HTTPS 流量的請求,並轉發到上游服務
  • :8001 用來接收訪問 Admin API 的 HTTP 流量的請求
  • :8444 用來接收訪問 Admin API 的 HTTPS 流量的請求

因此,能夠執行下面的命令,來確認 Kong 是否正常運行:

# 確認 Kong 是否正常運行
$ curl -i http://localhost:8000/
$ curl -i http://localhost:8001/

Kong 底層依賴 OpenResty,啓動 Kong 後,能夠看到 nginx 進程:

# 查看 nginx 進程
$ ps -ef | grep nginx
yulewei  19090     1  0 16:05 ?        00:00:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx -p /home/yulewei/kong -c nginx.conf
yulewei  19091 19090  0 16:05 ?        00:00:00 nginx: worker process
yulewei  19092 19090  0 16:05 ?        00:00:00 nginx: worker process

安裝 Kong 0.14.1,自動安裝的 OpenResty 版本是 1.13.6.2,OpenResty 捆綁的安裝了 LuaJIT。

$ /usr/local/openresty/nginx/sbin/nginx -v
nginx version: openresty/1.13.6.2

$ /usr/local/openresty/bin/resty -V
resty 0.21
nginx version: openresty/1.13.6.2
built by gcc 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4)
built with OpenSSL 1.0.2n  7 Dec 2017
TLS SNI support enabled
configure arguments: ... 省略 ...

$ /usr/local/openresty/luajit/bin/luajit -v
LuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2017 Mike Pall. http://luajit.org/

另外,同時也安裝了 LuaRocks,LuaRocks 關聯的是 OpenResty 捆綁的 LuaJIT。事實上,Kong 就是一個 LuaRocks 的 rock 包,在Kong 項目的 GitHub 上能夠看到 rockspec 文件。Kong 的安裝,底層實現上,是經過 luarocks 命令完成的,相似這樣的命令,luarocks install kong 0.14.1-01112。可使用 luarocks show 命令查看這個 Kong 的 rcok 包:

$ luarocks show kong
kong 0.14.1-0 - Kong is a scalable and customizable API Management Layer built on top of Nginx.

License:     MIT
Homepage:     http://getkong.org
Installed in:     /usr/local

Modules:
    kong (/usr/local/share/lua/5.1/kong/init.lua)
    kong.api (/usr/local/share/lua/5.1/kong/api/init.lua)
... 省略 ...

有個小細節值得注意,經過 luarocks 看到,Kong 採用的協議是 MIT。但事實上,Kong 0.5.0 開始協議從 MIT 改爲了 Apache 2.0。此處是一個小 bug,Kong 的 rockspec 文件沒有及時更新,這個問題後來修復了,參見 #4125

GUI 管理工具

管理 Kong 能夠直接使用 Admin API,固然也有基於 Admin API 實現 GUI 管理工具。

Kong 官方的企業版提供了 GUI 管理工具,Kong Manager(Kong EE 0.34 以前稱爲 Admin GUI),Kong 社區版沒有提供 GUI 管理工具。

第三方的開源 GUI 工具,比較活躍的就是 Konga,值得推薦,以下圖。

konga

另外,還有其餘的 GUI 工具,好比 Kong Dashboard,也能夠了解下。

Kong 使用

Kong 核心概念:

  • Service:對應位於 Kong 後方的自身的 Upstream API 或微服務。
  • Route:Kong 的入口點,定義瞭如何把請求發送到特定 Service 的規則。一個 Service 能夠有多個Route
  • Plugin:插件提供了模塊化系統,用來修改或控制 Kong。插件提供了大量功能,好比訪問控制、緩存、限流、日誌記錄等。
  • Consumer:消費者,表示使用 API 的用戶,能用來對用戶進行訪問控制、跟蹤等。

Kong 網關的請求響應工做流,以下圖所示:

kong-overview

反向代理

Kong 的核心功能就是對現有的上游服務的 API 做反向代理。反向代理,官方的完整的文檔參見13。如今咱們來試驗下 Kong 的反向代理功能,執行下面的命令:

# 添加 service
$ curl -XPOST -H 'Content-Type: application/json' \
     -d '{"name":"example.service","url":"http://httpbin.org"}' \
     http://localhost:8001/services/

# 在 service 上添加 route
$ curl -XPOST -H 'Content-Type: application/json' \
     -d '{"paths":["/base64"],"strip_path":false}' \
     http://localhost:8001/services/example.service/routes

上面的第一條命令,經過調用 Kong 提供的 Admin API,讓 Kong 建立了名爲 example.serviceserviceservice 指向的上游服務是 http://httpbin.org。第二條命令,在 example.service 上添加 route 規則,規則是讓請求路徑前綴爲 /base64 的請求轉發到這個 service。來驗證下,剛剛的 Kong 的配置:

# 驗證 Kong 配置結果
$ curl http://httpbin.org/base64/aGVsbG8ga29uZw==
hello kong
$ curl http://localhost:8000/base64/aGVsbG8ga29uZw==
hello kong

上文的 Kong 配置,等價的 nginx.conf 配置文件的寫法是:

server {
  listen 8000;
  location /base64 {
    proxy_pass http://httpbin.org/base64;
  }
}

除了前綴外,route 規則的 paths 字段也支持 PCRE 正則表達式,來看下示例:

curl -XPOST -H 'Content-Type: application/json' \
     -d '{"paths":["/status/\\d+"],"strip_path":false}' \
     http://localhost:8001/services/example.service/routes

上面的命令,添加 route 規則,設置的 paths 字段值爲 /status/\d+,讓只有請求路徑的中包含數字才能匹配。

# 驗證 Kong 配置結果
$ curl -sI http://httpbin.org/status/418 | head -n1
HTTP/1.1 418 I'M A TEAPOT
$ curl -sI http://localhost:8000/status/418 | head -n1
HTTP/1.1 418 I'M A TEAPOT
$ curl -sI http://localhost:8000/status/200 | head -n1
HTTP/1.1 200 OK
$ curl http://localhost:8000/status/abc
{"message":"no route and no API found with those values"}

負載均衡

上文的反向代理指向的是單臺的上游服務器,若是要指向多臺上游服務器,實現負載均衡,要如何配置呢?負載均衡,Nginx 能夠經過 upstream 指令實現,而相似的,Kong 經過建立 upstream 對象實現。

假設在服務器 192.168.2.100:80192.168.2.101:80 上運行着本地版的 httpbin.org 的 REST API 服務(經過 docker run -p 80:80 kennethreitz/httpbin)。執行下面的命令:

# 添加 upstream
curl -XPOST -H 'Content-Type: application/json' \
     -d '{"name":"example.upstream"}' \
     http://localhost:8001/upstreams/

# 在 upstream 上添加 target
curl -XPOST -H 'Content-Type: application/json' \
     -d '{"target":"192.168.2.100:80"}' \
     http://localhost:8001/upstreams/example.upstream/targets

# 在 upstream 上添加 target
curl -XPOST -H 'Content-Type: application/json' \
     -d '{"target":"192.168.2.101:80"}' \
     http://localhost:8001/upstreams/example.upstream/targets

# 添加 service
curl -XPOST -H 'Content-Type: application/json' \
     -d '{"name":"example.service","host":"example.upstream"}' \
     http://localhost:8001/services/

# 在 service 上添加 route
curl -XPOST -H 'Content-Type: application/json' \
     -d '{"paths":["/base64"],"strip_path":false}' \
     http://localhost:8001/services/example.service/routes

上面的命令,先建立了 upstream 對象,虛擬主機名(virtual hostname)爲 example.upstream 。而後在這個 upstream 上添加 target192.168.2.100:80192.168.2.101:80。再而後把 service 對象的 host 字段值設置爲 example.upstream。這樣所有發送到這個 service 的請求都會被轉發到 example.upstream 這個 upstreamupstream 再執行負載均衡算法,把請求轉發到最終的上游服務器。和 Nginx 同樣,默認的負載均衡算法爲加權輪詢算法(weighted-round-robin)。

# 驗證 Kong 配置結果
$ curl http://localhost:8000/base64/aGVsbG8ga29uZw==
hello kong

上文的 Kong 配置,等價的 nginx.conf 配置文件的寫法是:

upstream example.upstream {
  server 192.168.2.100:80;
  server 192.168.2.101:80;
}
server {
  listen 8000;
  location /base64 {
    proxy_pass http://example.upstream/base64;
  }
}

關於 Kong 負載均衡的更多介紹,能夠閱讀官方文檔14,本文再也不展開。

開啓插件

Kong 提供了不少插件,官方整理維護的所有插件列表,能夠在官網上看到。所有插件分 8 大類:身份認證類插件(Authentication)、安全控制類插件(Security)、流量控制類插件(Traffic Control)、無服務器計算類插件(Serverless)、分析與監控類插件(Analytics & Monitoring)、協議轉換類插件(Transformations)、日誌記錄類插件(Logging)、部署類插件(Deployment)。Kong 0.14.1 社區版默認綁定的預約義插件,所有 31 個,調用下面的 Admin API 能夠查看:

# Kong 社區版所有默認綁定的插件,共 31 個
$ curl http://localhost:8001/plugins/enabled
{"enabled_plugins":["response-transformer","oauth2","acl","correlation-id","pre-function","jwt","cors","ip-restriction","basic-auth","key-auth","rate-limiting","request-transformer","http-log","file-log","hmac-auth","ldap-auth","datadog","tcp-log","zipkin","post-function","request-size-limiting","bot-detection","syslog","loggly","azure-functions","udp-log","response-ratelimiting","aws-lambda","statsd","prometheus","request-termination"]}

如今咱們來試下 Kong 的 basic-auth 插件,用來實現 HTTP Basic 認證(RFC 7617)。執行下面的命令,在上文的 example.serviceservice 上開啓 basic-auth 插件:

# 在 service 上開啓 basic-auth 插件
$ curl -XPOST --data "name=basic-auth" \
       http://localhost:8001/services/example.service/plugins

這樣所有到 example.service 的請求都須要進行 Basic 認證。再次請求以前的 /base64 接口,返回狀態碼 401 Unauthorized

# 接口 HTTP 狀態碼返回 401
$ curl -i http://localhost:8000/base64/aGVsbG8ga29uZw==
HTTP/1.1 401 Unauthorized
... 省略 ...

{"message":"Unauthorized"}

添加身份認證的憑證,添加 username/password:

# 添加 consumer
$ curl -XPOST --data "username=Jason" \
       http://localhost:8001/consumers/
# 在 consumer 上添加 basic-auth 插件的憑證 username/password
$ curl -XPOST --data "username=test&password=123456" \
       http://localhost:8001/consumers/Jason/basic-auth

如今請求頭上帶上憑證,從新請求 /base64 接口,響應正常:

$ curl -u 'test:123456' http://localhost:8000/base64/aGVsbG8ga29uZw==
hello kong

Kong 插件,除了綁定到 service 上外,也能夠綁定在 routeconsumer 上。若是開啓插件時,servicerouteconsumer 所有都不關聯,就是全局範圍開啓插件,插件會在所有請求上運行。全局範圍上開啓 basic-auth 插件,命令以下:

# 全局範圍上開啓 basic-auth 插件
$ curl -XPOST --data "name=basic-auth" \
       http://localhost:8001/plugins

關於 Kong 插件的更多介紹,能夠閱讀官方文檔,本文再也不展開。

Kong 插件開發

Kong 基於 OpenResty,OpenResty 經過 ngx_http_lua_module 模塊實現了在 Nginx 中內嵌 Lua 腳本的能力。Kong 插件,使用 Lua 腳本實現,所有默認加載的預約義插件對應的 Lua 源碼(包括上文提到的 basic-auth 插件),能夠在 Kong 項目倉庫的 kong/plugins 目錄下看到。

除了能使用 Kong 預約義插件,咱們能夠根據 Kong 插件開發文檔15,開發自定義插件。

如何開發插件,文本不展開。Kong 官方提供了自定義插件的模板代碼,源碼參見項目 kong-plugin16。另外,有興趣也能夠參考筆者提供的 Kong 自定義插件示例,源碼參見 kong-plugin-demo17

值得注意的是,Kong 使用 Lua 的 rxi/classic 模塊來模擬 Lua 中的類,自定義 Kong 的插件時,實現 handler 須要繼承 BasePlugin class,目前最新的文檔仍是採用這種寫法。不過,Kong 1.2 開始,Kong 內部的預約義實現的插件,廢棄了繼承 BasePlugin class 的寫法,參見 Pull Request #4590,「plugins handlers do not have to inherit from BasePlugin anymore #4590」。去掉對 BasePlugin class 的繼承後,在開啓單個插件(key-auth)的場景下,壓測 Kong 性能提高 6%,開啓多個插件的場景,性能提高更高。

參考資料


  1. What is Kong? https://konghq.com/about-kong...
  2. 2015-04 Mashape 開源 API 網關——Kong https://www.infoq.cn/article/...
  3. 2012-07 打造大集市:API交易網站Mashape正式推出 https://www.csdn.net/article/...
  4. 2015-10 How Mashape Manages Over 15,000 APIs & Microservices https://stackshare.io/kong/ho...
  5. 2017-05 Mashape 和 RapidAPI 合併,組成全球最大的應用編程接口(API)集市! https://www.sohu.com/a/144114...
  6. 2017-05 The API Marketplace Joins RapidAPI https://konghq.com/blog/the-a...
  7. 2017-10 Welcome Kong Inc. A New Name, a New Product, a New Era. https://konghq.com/blog/intro...
  8. 2018-12 Kong 1.0 GA https://konghq.com/blog/kong-...
  9. 2020-01 Kong Gateway 2.0 GA https://konghq.com/blog/kong-...
  10. Documentation for Kong: DB-less and Declarative Configuration https://docs.konghq.com/1.1.x...
  11. Kong Installation: Compile Source https://docs.konghq.com/insta...
  12. Build tools to package and release Kong https://github.com/Kong/kong-...
  13. Documentation for Kong: Proxy Reference https://docs.konghq.com/0.14....
  14. Documentation for Kong: Load Balancing Reference https://docs.konghq.com/0.14....
  15. Documentation for Kong: Plugin Development https://docs.konghq.com/0.14....
  16. Kong 官方自定義插件的模板代碼 https://github.com/Kong/kong-...
  17. Kong 自定義插件示例 https://github.com/yulewei/ko...
相關文章
相關標籤/搜索