刚刚(2018-11-20 17:11:36)才发现kong的网页上有一篇文档非常详细的介绍了kong转发请求的过程:Kong: Proxy Reference。
2019-05-06 16:28:56:kong 1.1.x有了一个重大变换,实现了db-less模式,可以不使用数据库了,见笔记二十六:查看全部笔记。
如果是刚开始学习kong,直接从1.x开始,0.x已经不再维护,0.15是0.x的最后一个版本。
前19篇笔记是刚开始接触kong时记录的,使用的版本是0.14.1,当时对kong一知半解,笔记比较杂乱。第二十篇开始是再次折腾时的笔记,使用的版本是1.0.3,笔记相对条理一些。
从0.x到1.x需要关注的变化有:
可以通过哈希设置,比如说按照consumer、ip、header、cookie中的一个进行hash,使得有相同的特征的请求被同一个后端服务处理。
在upstream中设置。
Route是kong的代理规则,定义了哪些请求代理给哪些service,以请求的方法、host、路径等作为转发依据,见Route的配置。
Route与Service是多对一的关系,多个Route可以对应一个Service,每个Route中定义一种类型的Path。
Route中host匹配的是请求中的host,可以使用通配符*
,但只能使用一个,可以是前匹配或者后匹配:
{
"hosts": ["*.example.com", "service.com"]
}
Route将请求代理给Service时,默认将请求头中的host修改为Route中配置的host,如果不希望这样,可以设置preserve_host
,使用原始请求头中的host。
Route的path可以是正则表达式,正则表达式的优先级低于非正则表达式描述的path。如果同一个url能匹配多个path,非正则表达式描述的path
选用最长匹配,并且优先级高于正则表达式,正则表达式描述的path
需要明确设置优先级regex_priority
,按照设置的优先级选择。
正则语法为PCRE,并且使用正则表达式描述的path支持捕获,捕获的数据位于ngx.ctx.route_matches
中,可以在代码中获取:
--/version/(?<version>\d+)/users/(?<user>\S+)
--/version/1/users/john
local route_matches = ngx.ctx.route_matches
-- route_matches.uri_captures is:
-- { "1", "john", version = "1", user = "john" }
从Route将请求转发给Service时,默认使用原始的path,不做更改,如果设置了strip_path
,会将path修改,去掉匹配的部分。
Route中的匹配条件有host
、path
、method
三个,条件越多的Route的优先级越高。可以创建一个优先级最低的Route作为最后的“兜底”Route:
{
"paths": ["/"],
"service": {
"id": "..."
}
}
每个Service对应一个upstream,service中配置了对应的upstream的host、port、path等,也就是upstream的地址,以及相关的访问参数,见Service的定义。
Service中的Host可以不是Upstream的名字,可以是一个外部的域名,这样请求直接被转发给外部。
Upstream就是隐藏在kong后面的服务,它的name
就是Service中的host
,upstream中配置的是负载均衡算法,以及健康检查等,这些配置针对的是upstream关联的Target。
代理请求的时候有一些参数设置,例如下面的默认超时时间:
upstream_connect_timeout 默认60000毫秒
upstream_send_timeout 默认60000毫秒
upstream_read_timeout 默认60000毫秒
这些参数都是在Service中配置的,除了超时时间,还有重试次数等。
将请求转发给Upstream的时候使用HTTP/1.1,并且会请求头中设置下面的字段:
Host: <your_upstream_host>
Connection: keep-alive
X-Forwarded-For: <address>
X-Forwarded-Proto: <protocol>
X-Forwarded-Host: <host>
X-Forwarded-Port: <port>
如果是websocket请求,kong会设置下面的请求头,升级为websocket连接:
Connection: Upgrade
Upgrade: websocket
将Upstream的响应返回的时候,会在响应头中设置字段:
Via: kong/x.x.x
X-Kong-Proxy-Latency: <latency>
X-Kong-Upstream-Latency: <latency>
对请求进行代理的时候,主要有两项处理,一是进行负载均衡,二是调用插件进行处理。
一个Upstream可以包含多个Target,负载均衡的过程,就是为当前的请求选择一个Target的过程。
Target是IP或者host加端口,是提供服务的最小单位,每个target可以设置不同的权重:
{
"target": "1.2.3.4:80",
"weight": 15,
"upstream_id": "ee3310c1-6789-40ac-9386-f79c0cb58432"
}
第一种负载均衡方式是通过DNS进行负载均衡,这是Service中直接配置的是外部服务的域名或者IP,而不是Upstream的name的时候,可以采用的方法。
这种方式其实是把负载均衡放在kong外部做的,kong只需要把请求转发给对应域名,具体的负载均衡方法在DNS中设置,涉及不到Upstream和Target,kong的文档中把这也算作一种kong的负载均衡方法。
第二种方式是Ring-balancer
,这种方式是kong管理的,kong相当于一个服务注册中心,负责动态增删后端服务(也就是Target),以及平衡负载,这种方式通过upstream
和target
设置。
Upstream是对多个target封装,多个target封装成一个虚拟的host,这个虚拟的host就是upstream的name,被用在Service的host中。
每个upstream中有一个预先设置好的slot数量,upstream中的多个target按照各自的权重分到slot中的一块,slot需要是预计的target数量的100倍。
Target可以通过admin api进行增加、删除,变更target的开销很小,upstream变更的开销比较大,因为涉及到slot的重新分配。
Target的权重设置为0,target将不被选用。
负载均衡算法默认是带有权重的轮询(weighted-round-robin ),除此之外还可使用hash的方式,hash的输入可以是:none(不使用hash的方式), consumer, ip, header, cookie。
使用hash方式的时候,要注意,第一,target地址要使用IP,不能是域名,域名解析会带来开销,而且有些域名服务器不会返回所有可用IP,第二,选择的hash输入要是足够变化多端的,使hash的输出要足够分散。
负载均衡的细节参考Loadbalancing reference。
蓝绿的切换只需要修改service关联的upstream即可,一个“蓝色”的upstream,一个“绿色”的upstream,蓝绿切换的时候,两个upstream使用不同的名字和不同的target。
蓝绿切换的时候直接修改service中的host即可。
金丝雀部署通过修改target的权重即可实现,target的权重设置为零,则不被使用,权重越高分担的流量越高。
Target的健康检查是一个有坑的地方,Health Checks and Circuit Breakers中有具体描述。
健康检查在Upstream中配置,有主动(active)和被动(passive)两种方式。
主动方式是按照配置的,定时访问Target,判断Target是否存货。Paasive的方式被动响应结果,如果发现失败的响应,则将对应的Target标记为失败。
第一个坑是,健康检查需要配置,如果不配置,默认就没有健康检查。
第二个坑是,如果只使用被动的方式,当一个Target被认为失败后,就会一直被认为失败,修复以后,必须手动发起一次访问,才能将其重新加载负载均衡列表中:
$ curl -i -X POST http://localhost:8001/upstreams/my_upstream/targets/10.1.2.3:1234/healthy
HTTP/1.1 204 No Content
一定要配置健康检查,并且最好使用主动的方式。
首先要创建certificates对象,certificate包含证书和私钥,以及snis,snis指定了证书绑定的host。
$ curl -i -X POST http://localhost:8001/certificates \
-F "cert=@/path/to/cert.pem" \
-F "key=@/path/to/cert.key" \
-F "snis=ssl-example.com,other-ssl-example.com"
HTTP/1.1 201 Created
...
使用了snis中的host的route,可以通过https协议访问。
可以在route中配置接受的协议类型,如果只配置了http,那么http和https协议都接受,如果只配置了https,只接受https协议:
{
"hosts": ["..."],
"paths": ["..."],
"methods": ["..."],
"protocols": ["http", "https"],
"service": {
"id": "..."
}
}