转自:http://blog.54im.com/2016/12/15/centos-install-kong-cassandra/#
对于一些传统的大型项目,传统的方式会有一些缺陷,比如说新人熟悉系统成本高(因为整个系统作为一个整体,彼此会有一定的牵连),项目重启时间长,重构困难(对于一个新技术的引入,可能需要对整个项目推到重来),不易于更换新的技术,并且整个项目会慢慢变成巨无霸。
近年来微服务受到越来越多国内企业的青睐。当企业现有系统的复杂度、维护和扩展成本成为业务发展瓶颈时,微服务可以帮助企业拆分、解耦现有的系统,从而提高系统的可维护和可扩展性。Airbnb、Dropbox、Twitter等互联网公司,在使用微服务架构之后,功能发布周期减少了75%,微服务的核心在于根据业务和具体粒度划分API,那么如何管理API就显得尤为重要。
拿日常的电商购物举例,一个服务实现一个不同的特性或者功能。每一个独立的微服务都是一个小型应用。一些微服务可能会暴露一些api 给其他的一些微服务或者是客户。
购物车,商品详情,支付等模块分别提供单独的rest api接口,给上层访问。所有的请求前面有统一的入口,api网关负责路由,认证,性能分析等功能
当然,微服务也有一定的缺陷,比如说每个服务(每个应用) 如果都有一个数据库的话,那么如何维持数据库事务。再比如说,服务之间的调用可能会由于网络的原因变得不可达,那么代码中要额外增加请求失败的代码。运维维护成本升高,开发时需注意模块间耦合。
上面这个架构其实很容易看出,api网关很容易成为性能瓶颈,而且也存在单点。api层面耦合太高。
引入Kong做api网关
微服务上层会有许多消费者(网站、移动端、甚至物联网设备),而这些消费者可能会依赖一些共同的基础服务,kong可以作为API网关提供一些插件来实现这些基础功能,比如登录,权限认证等。
Kong是Mashape开源的一款API网关,起初是用来管理 Mashape 公司15000个微服务的,后来在2015年开源,现在已经在很多创业公司、大型企业和政府机构中广泛使用。基于Lua和Cassandra或PostgreSQL,支持分布式操作,有很强的可移植性和可扩展性。可以在任何一种基础设施上运行,作为应用和API之间的中间层,加上众多功能强大的插件,可以实现认证授权、访问控制等功能。并且提供易于使用的RESTful API来操作和配置系统。
Kong可以说是API的得力助手。对需要从事API管理的广大开发员来说,它是最出色的工具之一。多年来,Kong一直在支持优秀的开发项目,比如Mashape(世界上规模最大的API市场)。最棒的是,Kong得到了强大的Nginx的支持。MaxCDN也将Mashape与其无限恶意软件扫描器(Infinite Malware Scanner)结合使用,用来检测其内容分发网络(CDN)上的恶意软件。
https://github.com/Mashape/kong start 7k+
Kong可灵活扩展:你可以通过增加更多Kong Server机器对Kong服务进行水平扩展,通过前置的负载均衡器向这些机器分发请求。这样就可以支持更多流量,同时确保网络延迟很短。根据文档描述,两个Cassandra节点就足以支撑绝大多数情况,但如果网络非常拥挤,可以考虑适当增加更多节点。
Kong可在任何地方运行:它可以部署在单个或多个数据中心环境的私有云或公有云上。它还支持大多数流行的操作系统,比如Linux、Mac和Windows。Kong包括许多实用技巧,以便针对大多数现代平台完成安装和配置工作。
Kong具有模块性:它可以与新的插件协同运行,扩展基本功能。可将你的API与许多不同的插件整合起来,这些插件在API请求响应循环的生命周期中被执行。插件使用Lua编写,而且Kong还有如下几个基础功能:HTTP基本认证、密钥认证、CORS( Cross-origin Resource Sharing,跨域资源共享)、TCP、UDP、文件日志、API请求限流、请求转发以及nginx监控。以增强安全、分析、验证、日志及/或监测机制。最好的例子之一就是Nginx Plus插件(https://getkong.org/plugins/nginx-plus-monitoring/),该插件提供了服务器实时监测机制,以获得关于负载和请求的度量指标和统计数字。
为了更好地理解系统,这是使用Kong的API的典型请求工作流:
Kong提供认证,限流,负载均衡等功能
Kong可与两种不同的组件协同工作:
Kong 当前最大支持 PostgreSQL 9.4+ 版本和 Cassandra 2.2.x 版本2个数据库
官方在AWS上进行性能测试, Kong运行在三个m3.medium EC2实例上; 一个用于部署Kong Server,一个用于Cassandra做数据存储,另一个用于上游API。 在将上游API的upstream_url添加到Kong中之后,我们测试从1到2000个并发连接的测试。
两分钟以上的117,185个请求,平均延迟为10ms,每秒976个请求,或者每天约84,373,200个请求通过Kong返回,只有一个超时。
两种方式对OpenResty或者nginx的使用方式都是基于动态增加upstream以及对upstream的DNS resolver来实现。
当前使用最新版本kong 0.9,我们部署在Centos 6.5 x64位系统上
$ wget https://bintray.com/mashape/kong-rpm-el6-0.9.x/rpm -O bintray-mashape-kong-rpm-el6-0.9.x.repo
$ mv bintray-mashape-kong-rpm-el6-0.9.x.repo /etc/yum.repos.d/
$ yum install kong
|
$ kong start -c |
检查kong是不是正常启动了,默认不修改配置文件的情况下会报连接不上PostgreSQL,这里先忽略,继续装cassandra
正常启动的话会输出:[OK] Started
kong的监听端口:
8000: API请求的代理层。
8001: restful的配置管理API。
8443: 代理HTTPS
7946: 用于和其他Kong节点通讯,支持TCP/UDP流量
7373: 用于本地集群代理通讯
|
$ kong stop
|
$ kong reload
|
这里就不演示安装了,yum也行,绿色版解压也行。java1.7 或者1.8都可以,注意cassandra3.0必须要jdk1.8版本才行。这里我们由于kong的支持版本是2.x,这里介绍2.x的安装。
$ vim /etc/yum.repos.d/datastax.repo
[datastax]
name = DataStax Repo for Apache Cassandra
baseurl = http://rpm.datastax.com/community
enabled = 1
gpgcheck = 0
$ yum search dsc
已加载插件:fastestmirror
Loading mirror speeds from cached hostfile
========================================================= N/S Matched: dsc ==========================================================
dsc1.1.noarch : Meta RPM for installation of the DataStax DSC platform
dsc12.noarch : Meta RPM for installation of the DataStax DSC platform
dsc20.noarch : Meta RPM for installation of the DataStax DSC platform
dsc21.noarch : Meta RPM for installation of the DataStax DSC platform
dsc22.noarch : Meta RPM for installation of the DataStax DSC platform
dsc30.noarch : Meta RPM for installation of the DataStax DSC platform
虽然有3.0,但是目前kong只支持2.2.x ,这里我就安装2.2版本
$ yum install dsc22
|
$ /etc/init.d/cassandra start
Starting Cassandra: OK
|
cqlsh是cassandra的客户端查询工具
cqlsh客户的工具需要python2.7支持,centos6.x默认是python2.6版本,这里我新安装下python2.7
首先安装 python 工具需要的额外软件包 SSL, bz2, zlib
yum install -y zlib-devel bzip2-devel openssl-devel xz-libs wget
|
源码安装Python 2.7.x,当然你也可以偷懒yum安装,添加相应的yum就可以
$ wget http://www.python.org/ftp/python/2.7.8/Python-2.7.8.tar.xz
$ xz -d Python-2.7.8.tar.xz
$ tar -xvf Python-2.7.8.tar
$ cd Python-2.7.8
$ ./configure --prefix=/usr/local
$ make
$ make altinstall
|
$ python2.7 -V
Python 2.7.8
$ mv /usr/bin/python /usr/bin/python2.6.6
$ ln -s /usr/local/bin/python2.7 /usr/bin/python
$ which yum
/usr/bin/yum
#修改 yum中的python
将第一行 #!/usr/bin/python 改为 #!/usr/bin/python2.6
|
$curl https://bootstrap.pypa.io/get-pip.py | python2.7 -
|
执行cqlsh客户端验证cassadnra安装
$ cqlsh
Traceback (most recent call last):
File "/usr/bin/cqlsh.py", line 160, in
from cqlshlib import cql3handling, cqlhandling, pylexotron, sslhandling
ImportError: No module named cqlshlib
$ pip install cqlshlib
$ cqlsh
Connected to Test Cluster at 127.0.0.1:9042.
[cqlsh 5.0.1 | Cassandra 2.2.8 | CQL spec 3.3.1 | Native protocol v4]
Use HELP for help.
cqlsh> create schema testschema
... with replication = {'class':'SimpleStrategy', 'replication_factor':1};
cqlsh> describe keyspaces;
system testschema system_traces
cqlsh> use testschema;
cqlsh:testschema> create table user (
... user_id varchar primary key,
... first varchar,
... last varchar,
... age int
... );
... user_id varchar primary key,
... first varchar,
... last varchar,
... age int
... );
cqlsh:testschema>
cqlsh:testschema> insert into user (user_id, first, last, age) values ('rfroncois', 'ronn', 'francois', 20);
cqlsh:testschema> select * from user;
user_id | age | first | last
-----------+-----+-------+----------
rfroncois | 20 | ronn | francois
cqlsh:testschema>
|
将数据库改为cassandra
$ cat /usr/local/kong/kong.conf
ssl_cert_key = /usr/local/kong/ssl/kong-default.key
ssl_cert_csr_default = /usr/local/kong/ssl/kong-default.csr
nginx_acc_logs = /usr/local/kong/logs/access.log
nginx_pid = /usr/local/kong/pids/nginx.pid
nginx_conf = /usr/local/kong/nginx.conf
anonymous_reports = true
lua_code_cache = on
serf_path = serf
nginx_kong_conf = /usr/local/kong/nginx-kong.conf
nginx_err_logs = /usr/local/kong/logs/error.log
log_level = notice
proxy_listen = 0.0.0.0:8000
ssl_cert = /usr/local/kong/ssl/kong-default.crt
cassandra_port = 9042
lua_ssl_verify_depth = 1
admin_listen = 0.0.0.0:8001
ssl_cert_key_default = /usr/local/kong/ssl/kong-default.key
ssl_cert_default = /usr/local/kong/ssl/kong-default.crt
serf_log = /usr/local/kong/logs/serf.log
dnsmasq_pid = /usr/local/kong/pids/dnsmasq.pid
proxy_listen_ssl = 0.0.0.0:8443
nginx_worker_processes = auto
cassandra_contact_points = 127.0.0.1
proxy_ip = 0.0.0.0
cluster_profile = wan
pg_ssl = false
proxy_ssl_port = 8443
cassandra_timeout = 5000
pg_database = kong
lua_package_path = ?/init.lua;./kong/?.lua
cassandra_consistency = ONE
proxy_port = 8000
serf_node_id = /usr/local/kong/serf/serf.id
proxy_ssl_ip = 0.0.0.0
admin_ip = 0.0.0.0
kong_conf = /usr/local/kong/kong.conf
admin_port = 8001
prefix = /usr/local/kong
cluster_listen = 0.0.0.0:7946
cassandra_username = kong
pg_host = 127.0.0.1
pg_port = 5432
cluster_ttl_on_failure = 3600
dnsmasq_port = 8053
nginx_daemon = on
serf_pid = /usr/local/kong/pids/serf.pid
cassandra_repl_strategy = SimpleStrategy
dnsmasq = true
nginx_optimizations = true
pg_user = kong
pg_ssl_verify = false
cassandra_data_centers = dc1:2,dc2:3
cassandra_ssl_verify = false
ssl = true
database = cassandra
cassandra_repl_factor = 1
serf_event = /usr/local/kong/serf/serf_event.sh
cassandra_keyspace = kong
mem_cache_size = 128m
cassandra_ssl = false
cluster_listen_rpc = 127.0.0.1:7373
|
$ kong restart
|
添加你的具体业务 API 服务到 Kong 中,Kong 通过 RESTful API 来管理具体的Kong 实例。具体请求如下,Kong 管理的 API 在8001 端口。请求配置如下:
$ curl -i -X POST \
--url http://localhost:8001/apis/ \
--data 'name=api-rest' \
--data 'upstream_url=http://api.yonghuivip.com/api/' \
--data 'request_host=api.yonghuivip.com'
|
当收到一个请求,Kong会检查它,并尝试将其路由到正确的API。 Kong支持2种路由机制
$ curl -i -X POST \
--url http://localhost:8001/apis/ \
-d 'name=mockbin' \
-d 'upstream_url=http://api.yonghuivip/' \
-d 'request_host=api.yonghuivip' \
-d 'request_path=/status'
```
这里首先看下DNS方式
|
$ curl -i -X GET \
–url http://localhost:8000/ \
–header ‘Host: api.yonghuivip’
当从浏览器执行请求时,您可能无法设置主机头。 因此,Kong还会检查一个名为X-Host-Override的头的请求,并像Host头一样设置它:
|
$ curl -i -X GET \
–url http://localhost:8000/ \
–header ‘X-Host-Override: api.yonghuivip.com’
URI方式
|
$ curl -i -X GET \
–url http://localhost:8000/status/200
> 通过kong代理请求的url 是 KONG_URL:PROXY_PORT/status/200 (http://localhost:8000/status/200)
> 我们配置的upstream_url 是 http://api.54im.com/
> 那我们最后的实际请求地址为 http://api.54im.com/status/200.
使用"strip_request_path"属性
> 通过在API上启用strip_request_path属性,请求将被代理,而request_path属性不包含在上游请求中。 让我们通过向管理API发出请求来启用此选项:
|
$ curl -i -X PATCH \
–url http://localhost:8001/apis/api \
-d ‘strip_request_path=true’ \
-d ‘request_path=/api’
可以看下使用strip_request_path后,请求路径的变化
request_path | strip_request_path | incoming request |upstream request
---|---|---|---
/api-rest| false | /some_path | not proxied
/api-rest | false | /api | /api
/api-rest | false | /api/some_path | /api/some_path
/api-rest | true | /some_path | not proxied
/api-rest | true | /api | /
/api-rest | true | /api/some_path | /some_path
我们这边就使用 Request URI方式测试
KONG 路由到业务的 API 使用: path URI 的方式请求 + 设置 strip_request_path 为 true
我现在拿这个api实验下
> http://api.54im.com/api/member/login?xxx=xxx&xxx=xxx
新增API
|
$ curl -i -X POST \
–url http://localhost:8001/apis/ \
-d ‘name=api’ \
-d ‘upstream_url=http://api.54im.com/‘ \
-d ‘request_host=api.54im.com’ \
-d ‘strip_request_path=true’ \
-d ‘request_path=/api’
返回
|
{
“upstream_url”: “http://api.54im.com/“,
“strip_request_path”: true,
“request_path”: “/api”,
“id”: “be9bb14d-f665-4d53-866c-1cd6e77bfc8b”,
“created_at”: 1476347080000,
“preserve_host”: false,
“name”: “api-rest”,
“request_host”: “api.54im.com”
}
我们来请求通过kong代理的业务的api
|
curl -i -X GET –url “http://localhost:8000/api/api/member/login?xxx=xxx&xxx=xxx“
> 这里说明下这个结构
>http://localhost:8000/api/api/member/login?
>http://localhost:8000/ 是kong提供的API代理层接口,提供给外界,当然你上层还是可以用负载均衡
>api 是上面新增接口中的 request_path
>api/member/login? 是我们业务具体的接口了
业务返回
```{
"code": 0,
"data": {
"xxx": "xxx",
"expires_in": "7200",
"refresh_token": "xxx",
"xxx": "xxx",
"uid": "21000"
},
"message": "OK",
"now": 1476348465224
}
|
业务nginx日志也可以看到该请求
10.10.1.2 - - [13/Oct/2016:16:33:10 +0800] "GET /api/member/login?v=2%2e0%2e1 HTTP/1.0" 500 184 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.21 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 127.0.0.1 0.005 -
|
官网目前有这几类插件
新增一个key-auth插件给api-rest这个api
[root@10-19-63-216:~]$ curl -i -X POST \
> --url http://localhost:8001/apis/api/plugins/ \
> --data 'name=key-auth'
HTTP/1.1 201 Created
Date: Thu, 13 Oct 2016 09:01:55 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/0.9.3
|
返回
{
"api_id": "be9bb14d-f665-4d53-866c-1cd6e77bfc8b",
"id": "9018a2c9-f532-4ea0-96aa-a7165181d8fe",
"created_at": 1476349315000,
"enabled": true,
"name": "key-auth",
"config": {
"key_names": [
"apikey"
],
"hide_credentials": false
}
}
|
我们同样请求之前那个接口看下
$ curl -i -X GET --url "http://localhost:8000/api/api/member/login?xxx=xxx&xxx=xxx"
HTTP/1.1 401 Unauthorized
Date: Thu, 13 Oct 2016 09:03:17 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
WWW-Authenticate: Key realm="kong"
Server: kong/0.9.3
{"message":"No API key found in headers or querystring"}
|
那现在要认证了,我该如何访问接口呢,别急,继续往下
现在已经配置了 key-auth 插件,还需要添加 consumer 到 API,才能继续通过 Kong 代理请求。Consumer 在调用API时,与单个用户请求关联,能被用来跟踪、访问管理等等。
建立一个用户名称为Ivon 的 consumer,下面请求命令成功之后,Kong 还会为Jason 用户建立一个唯一的 custom_id。
$ curl -i -X POST \
--url http://localhost:8001/consumers/ \
--data "username=Jason"
|
返回
{
"username": "Ivon",
"created_at": 1476349684000,
"id": "4d6b3281-d26e-416b-b8fc-8185422386d5"
}
|
为消费者Ivon提供密钥凭据
curl -i -X POST \
--url http://localhost:8001/consumers/Ivon/key-auth/ \
--data 'key=18b4ccb1a20076813c208d8e8a281a94'
|
如果不指定key参数,kong会为你生成一个,推荐不指定。
返回
{
"key": "18b4ccb1a20076813c208d8e8a281a94",
"consumer_id": "4d6b3281-d26e-416b-b8fc-8185422386d5",
"created_at": 1476350157000,
"id": "9bd5165b-a798-4ff3-91d9-398216bbcb55"
}
|
再试着请求下之前的业务接口
$ curl -i -X GET --url "http://localhost:8000/api/api/member/login?xxx=xxx&xxx=xxx"
HTTP/1.1 200 OK
Date: Thu, 13 Oct 2016 09:20:14 GMT
Content-Type: application/json;charset=UTF-8
Content-Length: 314
Connection: keep-alive
Server: Tengine
X-Kong-Upstream-Latency: 651
X-Kong-Proxy-Latency: 0
Via: kong/0.9.3
{"code":0,"data":{"access_token":"xxx","expires_in":"7200","refresh_token":"c57300c0-9301-4b2c-b7f8-32b9a8cba9c5","token":"xxx","uid":"21000"},"message":"OK","now":1476350414858}
|
apikey 必须写在 url 的第一个参数,否则会报“No API Key found in headers, body or querystring”的错误。
- 浏览器弹框需要输入用户名密码才能访问
这里新增一个api
curl -i -X POST \
--url http://localhost:8001/apis/ \
-d 'name=activity' \
-d 'upstream_url=http://activity.54im.com/' \
-d 'request_host=activity.54im.com' \
-d 'strip_request_path=true' \
-d 'request_path=/activity'
|
请求下
curl -i -X GET --url "http://localhost:8000/activity/"
|
返回拼团的html
给activity-hub这个API增加账户密码验证
$ curl -X POST http://localhost:8001/apis/activity/plugins \
--data "name=basic-auth" \
--data "config.hide_credentials=true"
|
返回
{"api_id":"5e40fba2-bc19-4a11-9cdb-568922e9d7d8","id":"27ac00a6-dbf0-4b6b-85bb-88db1f3ff2d6","created_at":1476352721000,"enabled":true,"name":"basic-auth","config":{"hide_credentials":true}}
|
给Ivon消费者增加认证插件的账户密码
$ curl -X POST http://localhost:8001/consumers/Ivon/basic-auth \
> --data "username=ivon" \
> --data "password=123456"
返回
{"password":"d1a3724559dc3d79c8bfdda72ebcd29728cfc7c5","consumer_id":"4d6b3281-d26e-416b-b8fc-8185422386d5","id":"e966af78-16e7-44f0-84e9-3d1b145a5cfc","username":"ivon","created_at":1476352923000}
|
再请求下业务接口
$ curl -i -X GET --url "http://localhost:8000/activity/"
HTTP/1.1 401 Unauthorized
Date: Thu, 13 Oct 2016 10:00:12 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
WWW-Authenticate: Basic realm="kong"
Server: kong/0.9.3
{"message":"Unauthorized"}
|
返回没有认证
加上账户密码后访问API
curl -u ivon:123456 -i -X GET --url "http://localhost:8000/activity/"
可以正常访问之前的html页面
|
IP 限制,黑名单、白名单。支持单IP、多IP、IP区间等配置。
$ curl -X POST http://localhost:8001/apis/activity/plugins \
--data "name=ip-restriction" \
--data 'config.blacklist=192.168.20.156'
|
以下是限制一分钟内只允许同一个IP 有5个请求,一小时内10000个请求
$ curl -X POST http://localhost:8001/apis/activity/plugins \
--data "name=rate-limiting" \
--data "config.minute=5" \
--data "config.hour=10000"
```
### Request Size Limiting 插件演示
> 防止 DOS 攻击,限制访问的请求body 大于多megabytes 的请求。
|
$ curl -X POST http://localhost:8001/apis/activity/plugins \
–data “name=request-size-limiting” \
–data “config.allowed_payload_size=128”
# Kong 管理api
#### 查看Kong 节点信息
|
curl -X GET http://localhost:8001/
返回
|
{
“hostname”: “”,
“lua_version”: “LuaJIT 2.1.0-alpha”,
“plugins”: {
“available_on_server”: [
…
],
“enabled_in_cluster”: [
…
]
},
“configuration” : {
…
},
“tagline”: “Welcome to Kong”,
“version”: “0.6.0”
}
#### 请求状态统计
请求状态统计,其实就是nginx的status模块
|
curl -X GET http://localhost:8001/status
返回
|
{
“server”: {
“total_requests”: 3203,
“connections_active”: 1,
“connections_accepted”: 1,
“connections_handled”: 1,
“connections_reading”: 0,
“connections_writing”: 1,
“connections_waiting”: 0
},
“database”: {
“apis”: 2,
“consumers”: 0,
“plugins”: 2,
“nodes”: 1,
…
}
}
#### Kong集群状态
|
curl -X GET http://localhost:8001/status
#### 检索API
|
curl -X GET http://localhost:8001/apis/{name or id}
#### 查看API列表
|
curl -X GET http://localhost:8001/apis
#### 更新API
|
curl -X PATCH http://localhost:8001/apis/{name or id}
#### 删除API
|
curl -X DELETE http://localhost:8001/apis/{name or id}
#### 创建消费者
|
curl -X GET http://localhost:8001/consumers/{username or id}
#### 增加插件
|
curl -X POST http://localhost:8001/{name or id}/plugins
```
Here is a list of third-party tools maintained by the community: