来自骷髅岛的 Ingress Controller:Kong

Kong,是一个在 Nginx 反向代理基础上发展而来的 API 网关产品。我之前一直在推动的 Service Mesh,主要关注的是集群(Mesh)内微服务之间的关系,而 API 网关所管理的则是微服务集群边缘,对外服务的管理。(据我观测,Istio 近期的文档已经出现了 Gateway 等说法,似乎也对这方面的问题颇有兴趣的样子)。

传统的 API:

Traditional api

API Gateway:

API Gateway

5 月 8 日,Kong 发布了 Ingress Controller,对 Kubernetes 和对 Kong 自身来说都是个有意思的事情。

首先,Ingress Controller 本来应该负责集群的对外通信,有些 Ingress Controller,例如 haproxy 和 Traefik 已经初步具备了这方面的能力。 其次,Kong 之前使用 API 调用的方式来进行管理,在 Ingress Controller 的上下文中,改用 CRD 方式进行管理,对于我等 YAML 程序员来说,无疑是个大大的利好消息。

Kong 使用插件的方式提供了一些常见功能,这些现在也可以用 CRD 方式进行使用,其中包括日志、限流、认证、鉴权几大类。

plugins

Kong 同时提供商业和社区两个版本,目前有部分插件也是商业版独占的。

今天这一篇,就会对 Kong Ingress Controller 从部署到应用的介绍。

安装

官方提供了一个简易的 Kubernetes 环境中的安装文件;另外在 Ingress Controller 出现之前,Kong 也有一个相对更丰富的 Kubernetes 下的安装文档

全部组件都运行在 kong 命名空间。

数据库

安装过程中会创建一个 Postgres 的 StatefulSet,前面提到,这一版本对 Kubernetes 集群的最低版本要求是 1.8,如果是 1.8 版本,需要将这一个 StatefulSet API 版本改为 apps/v1beta2。另外这一部分需要用 PVC 的形式给数据提供存储空间,所以集群中应该设置缺省 StorageClass。完整的 Kubernetes 安装文档中,还介绍了 Cassandra 的存储方式。

CRD

安装过程中创建了如下的自定义资源:

  • 凭据:用于身份认证。
  • 服务消费者:给不同的 API 用户提供不同的消费者身份,以便实施不同的治理方式。
  • 插件:将过去使用 HTTP API 管理的插件系统,以 CRD 的形式在 Kubernetes 环境下统一管理。
  • KongIngress:对反向代理行为本身进行定义。

服务

其中提供了两组服务:

  • kong-ingress-controller:暴露 8001 端口,用于对 Kong 进行管理。这里建议将服务类型改为 ClusterIP,而不是直接暴露于公网。
  • kong-proxy:Ingress 服务,对其承载的接口调用都从此经过,可以根据集群情况酌情使用 NodePort 或者 ClusterIP -> Ingress 的方式对外提供服务。

提供服务

安装结束后,就可以使用网关来对外提供服务了。

官方提供了一个简单的例子应用,我们当然也可以选择别的应用来试用。为他编写一个 Ingress 资源:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dummy
  annotations:
    kubernetes.io/ingress.class: nginx # 这里仍然是 nginx
spec:
  rules:
  - host: dummy.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: http-svc
          servicePort: 89

如果没有公网 Loadbalancer 条件,可以使用 /etc/hosts、dnsmasq、或者 curl host 几种方式来模拟。

ingress 资源创建成功之后,就可以使用域名来访问这一服务了。

TLS 加密

作为 Ingress Controller,添加证书提供 https 服务也是基本要求之一。这方面 Kong Ingress Controller 使用的是 tls secret 的方式:

  1. 首先获取证书,可以自行签名,或者使用已有证书文件。
  2. kubectl create secret tls rocks --key privkey.pem --cert fullchain.pem:创建一个名为 rocks 的 Secret,其中包含我们的证书和私钥。
  3. 在 Ingress 资源定义中加入下列内容,引用刚才创建的 Secret:
tls:
- hosts:
    - dummy.example.com
    secretName: rocks

这样就可以使用 https 进行访问了。

试用限流插件

前面提到,Kong Ingress Controller 使用 CRD 方式来实现插件的应用。下面我们创建一个限流插件的 CRD:

apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
  name: one-per-second-ten-per-hour
config:
  hour: 10
  limit_by: ip
  second: 1

目前并没有很完善的插件 CRD 规范的文档,因此其中的具体字段需要去该插件的文档页面去查找,例如这里引用的 Ratelimit。在这一个 YAML 中我们会发现,其中并没有表明具体使用的插件以及对应的服务,通过对 controller 管理端口的 /plugins 进行查询,也会看到其中并没有定义活动的插件。

要把它应用到具体服务上,还需要修改我们要控制的 Ingress 资源,在其中加入注解,来引用这一 CRD:

kubernetes.io/ingress.class: nginx
rate-limiting.plugin.konghq.com: one-per-second-ten-per-hour

提交新的 Ingress 之后,再次访问管理端口的 plugins 路径,会得到以下响应:

{
    "total": 1,
    "data": [
        {
            "created_at": 1525966801000,
            "config": {
                "redis_database": 0,
                "policy": "cluster",
                "redis_timeout": 2000,
                "hide_client_headers": false,
                "hour": 20,
                "limit_by": "ip",
                "redis_port": 6379,
                "second": 10,
                "fault_tolerant": true
            },
            "id": "8539eb6f-5467-11e8-a92e-000d3a07d45d",
            "name": "rate-limiting",
            "enabled": true,
            "route_id": "f2961715-11fd-410a-a934-dbd6822e5fac"
        }
    ]
}

可以使用 siege 或者 curl/wrk 等其他工具来访问 API,会发现超过限度之后,服务器返回 429 的状态码:

HTTP/1.1 200     0.20 secs:     727 bytes ==> GET  /
HTTP/1.1 200     0.22 secs:     729 bytes ==> GET  /
HTTP/1.1 429     0.20 secs:      38 bytes ==> GET  /
HTTP/1.1 429     0.21 secs:      38 bytes ==> GET  /
HTTP/1.1 429     0.20 secs:      38 bytes ==> GET  /
HTTP/1.1 200     0.20 secs:     729 bytes ==> GET  /
HTTP/1.1 200     0.20 secs:     727 bytes ==> GET  /
HTTP/1.1 429     0.20 secs:      38 bytes ==> GET  /

证明限流功能已经生效。

试用消费者

前面提到,可以使用消费者这一概念,对微服务的用户身份加以甄别,从而提供不同的管控方式。在前面的基础上,我们希望为部分用户修改一下响应内容。

首先创建一个 KongConsumer 对象:

apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
  name: rich
username: boss

接下来,为这个用户创建凭据,凭据是需要认证的,所以还要启用一个插件:key-auth,官方文档中并没有提及这一点:

apiVersion: configuration.konghq.com/v1
kind: KongCredential
metadata:
  name: rich-login
consumerRef: rich # 如果删除这一字段,就代表面向所有消费者。
type: key-auth
config:
  key: 62eb165c070a41d5c1b58d9d3d725ca1

然后,为这个用户创建一个插件配置

apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
  name: rich-response
consumerRef: rich
config:
  hour: 100
  limit_by: ip
  second: 10

最后,在 Ingress 资源中启用两个插件,分别是 key-authresponse-transformer

response-transformer.plugin.konghq.com: boss
key-auth.plugin.konghq.com: auth

重新配置 Ingress 之后,可以使用 curl 进行校验:

curl --header "apikey: aasome_key_data" -s -i https://dummy.example.com
HTTP/1.1 403 Forbidden
Date: Fri, 11 May 2018 04:17:57 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: kong/0.13.1

{"message":"Invalid authentication credentials"}

可以看到上面的认证没能通过。

curl --header "apikey: some_key_data" -s -i https://dummy.example.com | grep boss
boss:  true
    x-consumer-username=boss2

Key Auth 认证插件根据 APIKey 取得了用户名,并且激活了 Response Transformer 插件,在 Header 中加入了我们配置的内容。

Kong 原有的 API 在这里还是可以使用的,例如: 1. curl http://[api-url]/plugins 查询生效插件 2. curl http://[api-url] 返回 JSON 中的 /plugins/available_on_server 列出所有可用插件。 3. curl ttp://[api-url]/consumers 列出所有消费者。

相关链接:

  1. https://konghq.com/
  2. https://raw.githubusercontent.com/Kong/kubernetes-ingress-controller/master/deploy/single/all-in-one-postgres.yaml
  3. https://raw.githubusercontent.com/Kong/kubernetes-ingress-controller/master/deploy/manifests/dummy-application.yaml

相关

comments powered by Disqus