# 来自骷髅岛的 Ingress Controller：Kong

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

传统的 API：

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745899456065/e978dd81-e56d-403a-a86c-349a5a888023.png align="center")

API Gateway：

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745899471264/2ee6391d-8980-4dbb-8474-11d256706825.png align="center")

[5 月 8 日，Kong 发布了 Ingress Controller](https://konghq.com/blog/kubernetes-ingress-controller-for-kong/)，对 Kubernetes 和对 Kong 自身来说都是个有意思的事情。

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

Kong 使用[插件](https://konghq.com/plugins/)的方式提供了一些常见功能，这些现在也可以用 CRD 方式进行使用，其中包括日志、限流、认证、鉴权几大类。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745899493192/a8bd7e7f-61dd-4c21-ad74-0e5678b67887.png align="center")

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

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

## 安装

官方提供了一个简易的 Kubernetes 环境中的[安装文件](https://raw.githubusercontent.com/Kong/kubernetes-ingress-controller/master/deploy/single/all-in-one-postgres.yaml)；另外在 Ingress Controller 出现之前，Kong 也有一个相对更丰富的 [Kubernetes 下的安装文档](https://getkong.org/install/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 -&gt; Ingress 的方式对外提供服务。
    

## 提供服务

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

官方提供了一个简单的[例子应用](https://raw.githubusercontent.com/Kong/kubernetes-ingress-controller/master/deploy/manifests/dummy-application.yaml)，我们当然也可以选择别的应用来试用。为他编写一个 Ingress 资源：

```yaml
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：
    

```yaml
tls:
- hosts:
    - dummy.example.com
    secretName: rocks
```

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

## 试用限流插件

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

```yaml
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](https://getkong.org/plugins/rate-limiting/)。在这一个 YAML 中我们会发现，其中并没有表明具体使用的插件以及对应的服务，通过对 controller 管理端口的 `/plugins` 进行查询，也会看到其中并没有定义活动的插件。

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

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

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

```json
{
    "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 ==&gt; GET / HTTP/1.1 200 0.22 secs: 729 bytes ==&gt; GET / HTTP/1.1 429 0.20 secs: 38 bytes ==&gt; GET / HTTP/1.1 429 0.21 secs: 38 bytes ==&gt; GET / HTTP/1.1 429 0.20 secs: 38 bytes ==&gt; GET / HTTP/1.1 200 0.20 secs: 729 bytes ==&gt; GET / HTTP/1.1 200 0.20 secs: 727 bytes ==&gt; GET / HTTP/1.1 429 0.20 secs: 38 bytes ==&gt; GET /

证明限流功能已经生效。

### 试用消费者

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

首先创建一个 KongConsumer 对象：

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

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

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

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

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

最后，在 Ingress 资源中启用**两个插件**，分别是 `key-auth` 和 `response-transformer`

```yaml
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`
