原文: https://nullwy.me/2020/05/kon...
若是以爲個人文章對你有用,請隨意讚揚
Kong 是雲原生、高效、可擴展、分佈式的微服務抽象層,被稱爲 API 網關,或者 API 中間件。Kong 在 2015 年 4 月由 Mashape 公司開源,基於 OpenResty 和 Apache Cassandra/PostgreSQL 構建,提供易於使用的 RESTful API 來操做和配置 API 系統12。nginx
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 公司開源了 Kong2。git
2017 年 5月,Mashape 和 RapidAPI 合併,組成全球最大的 API 集市56。5 個月後,Mashape, Inc. 更名爲 Kong Inc.,新的公司以 Kong 項目爲聚焦,把所有工程師投入到 Kong 開發中,而且於此同時他們發佈了 Kong 企業版7。github
Kong,做爲微服務的請求的網關,能經過插件提供負載均衡、日誌記錄、鑑權、限流、轉換以及其餘等功能。相對與舊的、沒有使用網關的方式,Kong 把這些通用功能中心化,讓微服務更加專一於業務自己。正則表達式
Kong 的總體架構,以下圖所示1:算法
目前最新的 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 kong
。json
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-0
1112。可使用 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。
管理 Kong 能夠直接使用 Admin API,固然也有基於 Admin API 實現 GUI 管理工具。
Kong 官方的企業版提供了 GUI 管理工具,Kong Manager
(Kong EE 0.34 以前稱爲 Admin GUI
),Kong 社區版沒有提供 GUI 管理工具。
第三方的開源 GUI 工具,比較活躍的就是 Konga
,值得推薦,以下圖。
另外,還有其餘的 GUI 工具,好比 Kong Dashboard
,也能夠了解下。
Kong 核心概念:
Service
:對應位於 Kong 後方的自身的 Upstream
API 或微服務。Route
:Kong 的入口點,定義瞭如何把請求發送到特定 Service
的規則。一個 Service
能夠有多個Route
。Plugin
:插件提供了模塊化系統,用來修改或控制 Kong。插件提供了大量功能,好比訪問控制、緩存、限流、日誌記錄等。Consumer
:消費者,表示使用 API 的用戶,能用來對用戶進行訪問控制、跟蹤等。Kong 網關的請求響應工做流,以下圖所示:
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.service
的 service
,service
指向的上游服務是 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:80
和 192.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
上添加 target
,192.168.2.100:80
和 192.168.2.101:80
。再而後把 service
對象的 host
字段值設置爲 example.upstream
。這樣所有發送到這個 service
的請求都會被轉發到 example.upstream
這個 upstream
,upstream
再執行負載均衡算法,把請求轉發到最終的上游服務器。和 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.service
的 service
上開啓 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
上外,也能夠綁定在 route
和 consumer
上。若是開啓插件時,service
、route
或 consumer
所有都不關聯,就是全局範圍開啓插件,插件會在所有請求上運行。全局範圍上開啓 basic-auth
插件,命令以下:
# 全局範圍上開啓 basic-auth 插件 $ curl -XPOST --data "name=basic-auth" \ http://localhost:8001/plugins
關於 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%,開啓多個插件的場景,性能提高更高。