前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >API 网关 kong 实战

API 网关 kong 实战

作者头像
王录华
修改2019-08-08 17:45:55
3.5K1
修改2019-08-08 17:45:55
举报

作者:李勇

1. 什么是Kong?

目前互联网后台架构一般是采用微服务,或者类似微服务的形式,应用的请求通常需要访问多个后台系统。如果让每一个后台系统都实现鉴权、限流、负载均衡、审计等基础功能是不合适的,通用的做法是把这些功能抽离出来放到网关层。Kong是目前最流行的网关平台。

Kong - The World’s Most Popular Open Source Microservice API Gateway and Platform.

  • Nginx = Http Server + Reversed Proxy + Load Balancer
  • Openresty = Nginx + Lua-nginx-module
  • Kong = Openresty + Customized Framework

2. 概念介绍

kong的API使用Restful风格,每个对象都是一个Object,其中最重要的两个对象是:

  • Service 代表一个后台服务
  • Route 是一条规则,告诉kong怎么把网关收到的请求发送到某个特定的后台服务,一个Service可以有多个Routes。

例如:主流http server都能根据不同的主机名,端口号,路径等信息把请求转发给不同的后台服务

kong 默认绑定4个端口:

  • :8000 用来接受用户的HTTP请求,并转发到后台系统
  • :8443 用来接受用户的HTTPS请求,并转发到后台系统
  • :8001 通过HTTP协议提供管理功能的API (Admin API)
  • :8444 通过HTTPS协议提供管理功能的API

这些端口可以在**/etc/kong/kong.conf**中修改,:8000 和 :8443 默认绑定0.0.0.0;:8001 和 :8444 默认绑定 127.0.0.1

当然我们可以把Admin API作为一个服务通过kong的网关暴露出去,请参考[Kong API Loopback] (https://docs.konghq.com/1.1.x/secure-admin-api/#kong-api-loopback)不过要注意安全。

3. Cheat Sheet

3.1 文件路径

  • 配置文件:/etc/kong/kong.conf
  • 日志文件:/usr/local/kong/logs

3.2 基本启动停止

代码语言:javascript
复制
kong start [ -c /etc/kong/kong.conf ]
kong reload
kong stop

3.3 命令行参考

前面说过,Kong采用了Restful风格的API,所有对象都通过HTTP的不同方法去操作。比如 curl -X GET http://localhost:8111/{object}

其中/{object}是某个对象的在Admin API中的路径,下面以services为例:

方法

作用

url 路径

POST

添加一个service

/services

GET

查询service列表

/services

GET

查询某个特定service

/services/{name or id}

PATCH

更新service配置

/services/{name or id}

PUT

添加或更新serivce配置

/services/{name or id}

DELETE

删除service

/services/{name or id}

4. 实战

下面的内容以一个nodejs的server [node-demo] (https://github.com/4179e1/node-demo)为例,介绍kong的使用。

下文的配置中禁用了HTTPS,:8081 用来接受HTTP请求,:8111提供Admin API

4.1 Node Demo

这个服务默认绑定8080端口,提供若干API

代码语言:javascript
复制
$ curl http://127.0.0.1:8080/api?pretty
{
  "api": [
    {
      "method": "GET, POST",
      "desc": "GET demo",
      "url": "http://127.0.0.1:8080/api/demo/headers"
    },
    {
      "method": "GET",
      "desc": "GET demo",
      "url": "http://127.0.0.1:8080/api/demo/demo"
    },
    {
      "method": "PUT",
      "desc": "PUT demo",
      "url": "http://127.0.0.1:8080/api/demo/demo"
    },
    {
      "method": "DELETE",
      "desc": "DELETE demo",
      "url": "http://127.0.0.1:8080/api/demo/demo"
    },
    {
      "method": "POST",
      "desc": "POST demo",
      "url": "http://127.0.0.1:8080/api/demo/demo"
    }
  ]
}

其中/api/api/demo/headers会详细的打印请求参数

代码语言:javascript
复制
$ curl http://127.0.0.1:8080/api/demo/headers?pretty
{
  "result": true,
  "hostname": "localhost",
  "headers": {
    "host": "127.0.0.1:8080",
    "user-agent": "curl/7.58.0",
    "accept": "*/*"
  },
  "query": {
    "pretty": ""
  },
  "body": {}
}

在下面的例子中,我们尝试把网关的/api转发到 nodedemo 的 /api,也就是访问网关地址http://127.0.0.1/api的时候,把对应的请求转发到nodedemo 的http://127.0.0.1:8080/api

4.2 添加service 和 route

下面这条命令添加了nodedemo.service这个service,凡是访问这个服务的请求都会被反向代理到http://127.0.0.1:8080这个地址

代码语言:javascript
复制
curl -i -X POST -H 'Content-Type: application/json' --url http://localhost:8111/services/ --data '{"name": "nodedemo.service", "url" : "http://127.0.0.1:8080"}'

  • 我习惯给object的名字带上类型(.service),这样交叉引用的时候不容易混淆
  • kong的Admin API支持urlencode参数,但我更习惯使用json

定义service之后,我们还得声明路由,把哪些特定的请求发送到这个service,这里我们配置的规则是“所有以/api开始的路径都转发给nodedemo.servce”:

代码语言:javascript
复制
curl -X POST -H 'Content-Type: application/json' http://localhost:8111/services/nodedemo.service/routes -d '{"name": "nodedemo.route", "paths" : ["/api"], "strip_path": false }'

其中strip_path: false表示请求后端服务时,同时还会把/api 这个前缀带上,更多细节见后文[Tips]部分。

4.3 测试

可以看到这个请求已经转发给nodedemo,并且网关自动添加了几个headres

代码语言:javascript
复制
# curl http://127.0.0.1/api/demo/headers?pretty
{
  "result": true,
  "hostname": "localhost",
  "headers": {
    "host": "127.0.0.1:8080",
    "connection": "keep-alive",
    "x-forwarded-for": "127.0.0.1",
    "x-forwarded-proto": "http",
    "x-forwarded-host": "127.0.0.1",
    "x-forwarded-port": "80",
    "x-real-ip": "127.0.0.1",
    "user-agent": "curl/7.58.0",
    "accept": "*/*",
    "proxy-connection": "Keep-Alive"
  },
  "query": {
    "pretty": ""
  },
  "body": {}
}

4.4 负载均衡

我们知道,nginx是能通过upstream支持多个后端server的负载均衡的,kong中要怎么做呢?

4.4.1 创建一个upstream对象

下面创建一个叫nodedemo.upstream的对象

代码语言:javascript
复制
curl -X POST -H 'Content-Type: application/json' http://localhost:8111/upstreams -d '{"name":"nodedemo.upstream"}'
4.4.2 为upstream添加target

刚才创建的upstream对象是空的,还需要给他添加实际的后端server地址,kong中称为target,实际就是host:port的组合,其中weight用于round robin的加权平均。

为了看到负载均衡的效果,我们可以再起一个nodedmo运行在8088端口:PORT=8088 node server.js

下面把两个nodedemo添加到upstream中

代码语言:javascript
复制
curl -X POST -H 'Content-Type: application/json' http://localhost:8111/upstreams/nodedemo.upstream/targets -d '{"target": "127.0.0.1:8080", "weight": 100}'
curl -X POST -H 'Content-Type: application/json' http://localhost:8111/upstreams/nodedemo.upstream/targets -d '{"target": "127.0.0.1:8088", "weight": 100}'
4.4.3 把service的url指向upstream

我们创建nodedemo.service的时候,url是直接指向http://127.0.0.1:8080的后端地址,此时把它改为upstream即可

代码语言:javascript
复制
curl -X PATCH -H 'Content-Type: application/json' http://localhost:8111/services/nodedemo.service --data '{ "url": "http://nodedemo.upstream" }'

访问http://localhost/api/demo/headers 数次可以发现轮询了不同的后端。

4.5 插件

kong 自带了很多插件,我们让我们给nodedemo启用一个试试,request-transformer可以修改请求内容,这里我们给请求添加一个header Hello: world

代码语言:javascript
复制
curl -X POST -H 'Content-Type: application/json' http://localhost:8111/services/nodedemo.service/plugins -d '{"name":"request-transformer","config":{"add":{"headers":["Hello: world"]}}}'

再次请求api,可以发现这个header已经有了

代码语言:javascript
复制
# curl http://127.0.0.1/api/demo/headers?pretty
{
  "result": true,
  "hostname": "localhost",
  "headers": {
    "host": "127.0.0.1:8080",
    "connection": "keep-alive",
    "x-forwarded-for": "127.0.0.1",
    "x-forwarded-proto": "http",
    "x-forwarded-host": "127.0.0.1",
    "x-forwarded-port": "80",
    "x-real-ip": "127.0.0.1",
    "user-agent": "curl/7.58.0",
    "accept": "*/*",
    "hello": "world"     # <==== here
  },
  "query": {
    "pretty": ""
  },
  "body": {}
}

plugins也可以是全局的,Admin API调用的时候地址改为 /plugins 即可,如果同一个插件在全局和某个单独的service都配置了,以service的为准。

5. Tips

5.1 静态文件要放哪里?

Q:呃……我想用kong来搭建一个前后端分离的网站,我的静态文件要放哪?

A: 尽管kong是基于nginx的,但是作为一个API 网关,它对静态文件的支持不是特别友好,有两种方式:

  1. 修改kong的配置模版,详情请参考[Serving both a website and your APIs from Kong] (https://docs.konghq.com/1.2.x/configuration/)
  2. 后端再起一个http server,把静态文件都放到这个http server中,并配置对应的路由。

5.2 好多对象都有跟path相关的参数,它们都是干什么的?

我用到的path有几个

  1. route中的paths参数,表示符合这些请求路径要发到route对应的service中
  2. route中的strip_path 参数,决定kong转发给后端的时候是否保留源请求用于路由匹配的路径
  3. service中的path参数,上面的例子没有配置,默认为null,kong转发请求时会把这个作为前缀加上

假设网关以/api为路由把请求转发给nodedemo(即route.paths = ['/api']),它们的组合关系如下:

strip_path

service.path

请求地址

网关实际访问后端地址

true

null 或者 /

http://127.0.0.1/api/demo

http://127.0.0.1:8080/demo

true

/test

http://127.0.0.1/api/demo

http://127.0.0.1:8080/test/demo

false

null 或者 /

http://127.0.0.1/api/demo

http://127.0.0.1:8080/api/demo

false

/test

http://127.0.0.1/api/demo

http://127.0.0.1:8080/test/api/demo

以最后一行为例,相当于访问 http://127.0.0.1/api/demo 时,实际访问的是/test/api/demo,也就是把 service.path (/test)跟实际请求的路径(/api/demo)拼接起来发给后端。

5.3 wildcard域名匹配

kong同样可以基于域名把请求转发到不同的服务,比如a.example.com转发到服务A,b.example.com转发到服务B;同时kong还支持通配域名,比如*.example.com转发到服务C。

我遇到一个坑是这样的:在已经配置a.example.comb.example.com这两个路由转发的前提下,我遇到的一个坑是这样的:我还有一个服务C,需要让*.example.com/c/ (包括a.example.com/cb.example.com/c)都转发到服务C。略一思索,我给服务C的Hosts添加了一个通配地址*.example.com,然而这并不work,a.example.com/c/b.example.com/c/并不能匹配到这个路由,但是c.example.com/c/等没有配置过路由的域名可以匹配。

最终解决方案是,需要同时添加a.example.comb.example.com*.example.com三个域名,类似这样:

代码语言:javascript
复制
{
  "hosts": [
    "a.example.com",
    "b.example.com",
    "*.example.com"
  ],
}

我估计这不是kong的问题,大概nginx本来就是这样的,未验证

5.4 Log rotate

网关访问量太大,日志要把硬盘写满了怎么办?上logroate,添加一个配置文件/etc/logroate.d/kong,内容如下:

代码语言:javascript
复制
/usr/local/kong/logs/*.log {
        daily
        missingok
        rotate 3
        compress
        delaycompress
        notifempty
        create 640 root root
        sharedscripts
        postrotate
                [ -f /usr/local/kong/pids/nginx.pid ] && kill -USR1 `cat /usr/local/kong/pids/nginx.pid`
        endscript
}

这个文件是从CentOS自带的/etc/logrotate.d/nginx 修改而来的,保留最近3天的日志(rotate 3)

5.5 kong有GUI吗?

kong 社区版没有GUI组件,但是有一个第三方的[konga] (https://github.com/pantsel/konga)。

6. Reference

  • [微服务与API网关(上): 为什么需要API网关?] (http://blog.didispace.com/hzf-ms-apigateway-1/)
  • [ 微服务与API 网关(下)- Kong能为我们做什么?] (http://blog.didispace.com/hzf-ms-apigateway-2/)
  • [Documentation for Kong] (https://docs.konghq.com/)

关于作者

不怎么务正业的程序员,从事过开发、运维、SRE、技术支持等多个岗位。原Oracle系统架构和性能服务团队成员,目前在腾讯从事运营系统开发。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云服务与SRE架构师社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 什么是Kong?
  • 2. 概念介绍
  • 3. Cheat Sheet
    • 3.1 文件路径
      • 3.2 基本启动停止
        • 3.3 命令行参考
        • 4. 实战
          • 4.1 Node Demo
            • 4.2 添加service 和 route
              • 4.3 测试
                • 4.4 负载均衡
                  • 4.4.1 创建一个upstream对象
                  • 4.4.2 为upstream添加target
                  • 4.4.3 把service的url指向upstream
                • 4.5 插件
                • 5. Tips
                  • 5.1 静态文件要放哪里?
                    • 5.2 好多对象都有跟path相关的参数,它们都是干什么的?
                      • 5.3 wildcard域名匹配
                        • 5.4 Log rotate
                          • 5.5 kong有GUI吗?
                          • 6. Reference
                          • 关于作者
                          相关产品与服务
                          API 网关
                          腾讯云 API 网关(API Gateway)是腾讯云推出的一种 API 托管服务,能提供 API 的完整生命周期管理,包括创建、维护、发布、运行、下线等。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档