作者: 李佶澳 轉載請保留:原文地址 發布時間:2018-10-10 14:37:53 +0800
說明
這是API網關Kong的系列教程中的一篇,使用過程中遇到的問題和解決方法記錄在API網關Kong的使用過程中遇到的問題以及解決方法。
通過Nginx、OpenResty和Kong的基本概念與使用方法了解了Kong的工作原理,通過API網關Kong與Kubernetes的集成方法了解了與Kubernetes的集成方法。這里學習下Kong的插件,並盡可能壓測一下感興趣的插件。
因為計划將Kong與Kubernetes集成,因此下面使用的是部署在Kubernetes中的Kong,配置是通過Kubernetes的cRD設置的,參考API網關Kong與Kubernetes的集成方法。
Kong-Ingress-Controller的版本是0.2.0,Kong的版本是0.14.1,是用下面的方式部署的:
./kubectl.sh create -f https://raw.githubusercontent.com/introclass/kubernetes-yamls/master/all-in-one/kong-all-in-one.yaml
Kong的Admin API
先了解下Kong的Admin API,后面的操作過程中,可以通過Kong的API查看數據變化。
GET /routers/ #列出所有路由
GET /services/ #列出所有服務
GET /consumers/ #列出所有用戶
GET /services/{service name or id}/routes #列出服務關聯的路由
GET /plugins/ #列出所有的插件配置
GET /plugins/enabled #列出所有可以使用的插件
GET /plugins/schema/{plugin name} #獲得插件的配置模版
GET /certificates/ #列出所有的證書
GET /snis/ #列出所有域名與證書的對應
GET /upstreams/ #列出所有的upstream
GET /upstreams/{name or id}/health/ #查看upstream的健康狀態
GET /upstreams/{name or id}/targets/all #列出upstream中所有的target
例如:List Services
$ curl 192.168.33.12:32685/services 2>/dev/null |python -m json.tool
{
"data": [
{
"connect_timeout": 60000,
"created_at": 1539153249,
"host": "demo-webshell.webshell.80",
"id": "0df71804-3f99-4e00-af5c-0234eb155228",
"name": "demo-webshell.webshell.80",
"path": "/",
"port": 80,
"protocol": "http",
"read_timeout": 60000,
"retries": 5,
"updated_at": 1539153249,
"write_timeout": 60000
},
...
],
"next": null
}
列出所有可以使用的插件:
$ curl 192.168.33.12:32685/plugins/enabled 2>/dev/null |python -m json.tool
{
"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定義的資源之間的關聯關系
Route
是請求的轉發規則,按照Hostname和PATH,將請求轉發給Service
,Kubernetes的Ingress中每個path
對應一個Route。
Services
是多個Upstream
的集合,是Route
的轉發目標。
Consumer
是API的用戶,里面記錄用戶的一些信息。
Plugin
是插件,plugin可以是全局的,綁定到Service,綁定到Router,綁定到Consumer。
Certificate
是https證書。
Sni
是域名與Certificate的綁定,指定了一個域名對應的https證書。
Upstream
是負載均衡策略。
Target
是最終處理請求的Backend服務。
使用過程了解
先通過部署一個webshell應用和為它設置key-auth插件的過程,了解整個使用過程。
先了解下插件的作用范圍和設置方法
Kong Add Plugin通過consumer_id、route_id、service_id限定插件的作用范圍:
作用於所有的Service、Router、Consumer: 創建時不指定consumer_id、service_id、route_id
作用於所有的Service、Router和指定的Consumer: 創建時只指定consumer_id
作用於所有的Consumer和指定的Service: 創建時只指定service_id,有些插件還需要指定route_id
作用於所有的Consumer和指定的Router: 創建時只指定route_id,有些插件還需要指定service_id
作用於特定的Service、Router、Consumer: 創建時不指定consumer_id、service_id、route_id
沒有綁定任何service、route、consumer的插件,稱為global
插件:
All plugins can be configured using the http://kong:8001/plugins/ endpoint.
A plugin which is not associated to any Service, Route or Consumer (or API, if you are using an older version of Kong) is considered "global",
and will be run on every request. Read the Plugin Reference and the Plugin Precedence sections for more information
在Kubernetes中部署目標應用和對應的Ingress
Kubernetes中部署的應用和Ingress是webshell-all-in-one.yaml:
./kubectl.sh create -f https://raw.githubusercontent.com/introclass/kubernetes-yamls/master/all-in-one/webshell-all-in-one.yaml
Kong的數據平面用NodePort的方式暴露,端口是30939,下面隨意選用的Node是192.168.33.12,所以請求地址都是192.168.33.12:30939
。
先驗證下沒有做任何配置時候的訪問:
$ curl -H "Host: webshell.com" 192.168.33.12:30939
<html> <head> <meta content="text/html; charset=utf-8"> <title>WebShell</title> </head> <body> <form method="post" accept-charset="utf-8"> Command: <input type="text" name="command" width="40%" value="hostname"> Params : <input type="text" name="params" width="80%" value=""> <input type="submit" value="submit"> </form> <pre> webshell-cc785f4f8-p4bds </pre> </body> </html>
可以訪問。
創建KongConsumer,並設置該用戶key-auth插件的key
創建名為websehll-user1
的KongConsumer:
apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
name: webshell-user1
namespace: demo-webshell
username: user1
custom_id: demo-webshell-user1
從kubernetes中查看:
$ kubectl -n demo-webshell get KongConsumer -o wide
NAME AGE
webshell-user1 1m
從Kong中查看Consumer:
curl 192.168.33.12:32685/consumers
配置webshell-user1的key-auth的key,創建一個KongCredential
,配置它關聯到上面創建的KongConsumer,(consumerRef: webshell-user1):
apiVersion: configuration.konghq.com/v1
kind: KongCredential
metadata:
namespace: demo-webshell
name: credential-webshell-user1
consumerRef: webshell-user1
type: key-auth
config:
key: 62eb165c070a41d5c1b58d9d3d725ca1
從kubernetes查看:
$ kubectl -n demo-webshell get KongCredential -o wide
NAME AGE
credential-webshell-user1 2m
從Kong中查詢Consumer的key-auth信息,ID是Kubernetes中KongConsumer的uid:
curl 192.168.33.12:32685/consumers/5433234c-d158-11e8-9da4-525400c042d5/key-auth/
這時候可以在kong-dashboard中看到名為user1的consumer,key為62eb165c070a41d5c1b58d9d3d725ca1
。
配置全局的key-auth插件
在kubernetes中創建下面的global插件:
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: global-plugin-key-auth
namespace: kong
labels:
global: "true" # optional, please note the quotes around true
disabled: false # optional
config:
plugin: key-auth
全局的插件不能重名。kong-ingress-controller(0.2.0)版本不關心全局插件所在的namespace,在任何一個namespace中都可以創建global plugin,實踐中需要注意進行限制。
這時候直接訪問Service,會提示缺少API key:
curl -H "Host: webshell.com" 192.168.33.12:30939
{"message":"No API key found in request"}
需要用下面的方式訪問:
curl -H "Host: webshell.com" -H "apikey: 62eb165c070a41d5c1b58d9d3d725ca1" 192.168.33.12:30939
配置關聯到Route的key-auth插件
Kong的Route對應Kubernetes的Ingress中的一個PATH。在Ingress中通過Kong Ingress Controller annotations綁定插件配置:
plugins.konghq.com: high-rate-limit, docs-site-cors
在demo-webshell空間中創建一個KongPlugin
:
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: plugin-key-auth-user1
namespace: demo-webshell
#consumerRef: webshell-user1, 0.14.1存在一個Bug,這里設置Consumer后,會導致kong-ingress-controller更新失敗
disabled: false # optional
config:
key_names: key
plugin: key-auth
Config中是key-auth插件的配置參數,前面的global plugin中沒有設置config,使用的是默認配置。
key_names
設置用來認證的key的名稱,默認是apikey,這里修改成了key,后面訪問的時候需要在header中添加的是key
字段。
在同一個namespace的Ingress上添加annotations,指定使用剛創建的名為plugin-key-auth-user1的KongPlugin:
metadata:
annotations:
plugins.konghq.com: plugin-key-auth-user1
這時候在kong-dashboard中,可以看到新建了一個綁定到Router的key-auth插件。
直接訪問,提示缺少key:
$ curl -H "Host: webshell.com" 192.168.33.12:30939
{"message":"No API key found in request"}%
用global插件的apikey,也提示缺少key:
$ curl -H "Host: webshell.com" -H "apikey: 62eb165c070a41d5c1b58d9d3d725ca1" 192.168.33.12:30939
{"message":"No API key found in request"}%
使用綁定的插件的中設置的key
才可以:
curl -H "Host: webshell.com" -H "key: 62eb165c070a41d5c1b58d9d3d725ca1" 192.168.33.12:30939
由此可見綁定到Route的插件優先級高於global插件。
在kong中查看綁定到Route的plugin:
$ curl 192.168.33.12:32685/routes/8c81fdb6-4bff-4807-9e38-ab9c22c24a88/plugins
"total":1,"data":[{"created_at":1539711481000,"config":{"key_names":["key"],"key_in_body":false,"anonymous":"","run_on_preflight":true,"hide_credentials":false},"id":"6d8c9e88-1211-4cfd-8410-c7d3a727f3e4","name":"key-auth","enabled":true,"route_id":"8c81fdb6-4bff-4807-9e38-ab9c22c24a88"}]}
配置關聯到Service的key-auth插件
Service也通過Kong Ingress Controller annotations綁定插件,在名為webshell
的Service中設置annotation:
kind: Service
metadata:
annotations:
plugins.konghq.com: plugin-key-auth-user1
這時候,在kong-dashboard中可以看到一個綁定到service的plugin。
嘗試綁定到另一個key-auth插件,試驗一下優先級。創建一個新的KongPlugin,key_names是key2
:
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: plugin-key-auth-user2
namespace: demo-webshell
consumerRef: webshell-user1
disabled: false # optional
config:
key_names: key2
plugin: key-auth
然后修改service的annotations,綁定新建的KongPlugin,plugin-key-auth-user2:
kind: Service
metadata:
annotations:
plugins.konghq.com: plugin-key-auth-user2
使用Route的key能夠通過驗證:
curl -H "Host: webshell.com" -H "key: 62eb165c070a41d5c1b58d9d3d725ca1" 192.168.33.12:30939
使用Service的key2不行:
$ curl -H "Host: webshell.com" -H "key2: 62eb165c070a41d5c1b58d9d3d725ca1" 192.168.33.12:30939
{"message":"No API key found in request"}
將Route的key-auth停止后,用key2就可以訪問了。
通過結果可以判斷Route綁定的插件是優先於Service綁定的插件的,而Service綁定的插件又優於Global插件。
通過KongIngress增強配置
Ingress默認關聯同一個namespace中同名的KongIngress。如果不想使用默認的關聯,可以在annotation中用configuration.konghq.com
指定同一個namespace中
的另一個KongIngress。
下面創建一個與ingress同名的KongIngress
apiVersion: configuration.konghq.com/v1
kind: KongIngress
metadata:
name: webshell-kong-ingress
namespace: demo-webshell
upstream:
hash_on: none
hash_fallback: none
healthchecks:
active:
concurrency: 10
healthy:
http_statuses:
- 200
- 302
interval: 0
successes: 0
http_path: "/"
timeout: 1
unhealthy:
http_failures: 0
http_statuses:
- 429
interval: 0
tcp_failures: 0
timeouts: 0
passive:
healthy:
http_statuses:
- 200
successes: 0
unhealthy:
http_failures: 0
http_statuses:
- 429
- 503
tcp_failures: 0
timeouts: 0
slots: 10
proxy:
protocol: http
path: /
connect_timeout: 10000
retries: 10
read_timeout: 10000
write_timeout: 10000
route:
methods:
- POST
- GET
regex_priority: 0
strip_path: false
preserve_host: true
protocols:
- http
- https
可以在kong-dashboard中看到,KongIngress中的設置被應用到route、upstream、proxy中。