
服务说明
请求经过多个中间件的转发,最终pod服务接收到请求。在整个过程中slb和tengine是tcp长连接、tengine和nginx-ingress为http转发(可能存在http短连接)、nginx-ingress和k8s svc为http转发(可能存在http短连接)
tengine和nginx-ingress 需要配置http-alive优化频繁的短连接,优化资源和服务响应时间。
配置优化
nginx 配置优化
当使用nginx作为反向代理时,为了支持长连接,需要做到两点:
从client到nginx的连接是长连接
从nginx到server的连接是长连接
保持和client的长连接:
默认情况下,nginx已经自动开启了对client连接的keep alive支持(同时client发送的HTTP请求要求keep alive)。一般场景可以直接使用,但是对于一些比较特殊的场景,还是有必要调整个别参数(keepalive_timeout和keepalive_requests)。
http {
keepalive_timeout 120s 120s;
keepalive_requests 10000;
}
1)keepalive_timeout
语法:
keepalive_timeout timeout [header_timeout];
第一个参数:设置keep-alive客户端连接在服务器端保持开启的超时值(默认75s);值为0会禁用keep-alive客户端连接;
第二个参数:可选、在响应的header域中设置一个值“Keep-Alive: timeout=time”;通常可以不用设置;
注:keepalive_timeout默认75s,一般情况下也够用,对于一些请求比较大的内部服务器通讯的场景,适当加大为120s或者300s;
2)keepalive_requests:
keepalive_requests指令用于设置一个keep-alive连接上可以服务的请求的最大数量,当最大请求数量达到时,连接被关闭。默认是100。这个参数的真实含义,是指一个keep alive建立之后,nginx就会为这个连接设置一个计数器,记录这个keep alive的长连接上已经接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则nginx会强行关闭这个长连接,逼迫客户端不得不重新建立新的长连接。
大多数情况下当QPS(每秒请求数)不是很高时,默认值100凑合够用。但是,对于一些QPS比较高(比如超过10000QPS,甚至达到30000,50000甚至更高) 的场景,默认的100就显得太低。
简单计算一下,QPS=10000时,客户端每秒发送10000个请求(通常建立有多个长连接),每个连接只能最多跑100次请求,意味着平均每秒钟就会有100个长连接因此被nginx关闭。同样意味着为了保持QPS,客户端不得不每秒中重新新建100个连接。因此,就会发现有大量的TIME_WAIT的socket连接(即使此时keep alive已经在client和nginx之间生效)。因此对于QPS较高的场景,非常有必要加大这个参数,以避免出现大量连接被生成再抛弃的情况,减少TIME_WAIT。
保持和server的长连接:
为了让nginx和后端server(nginx称为upstream)之间保持长连接,典型设置如下:(默认nginx访问后端都是用的短连接(HTTP1.0),一个请求来了,Nginx 新开一个端口和后端建立连接,后端执行完毕后主动关闭该链接)
http {
upstream BACKEND {
server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s;
keepalive 300; // 这个很重要!
}
server {
listen 8080;
server_name "";
location / {
proxy_pass http://BACKEND;
proxy_set_header Host $Host;
proxy_set_header x-forwarded-for $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
add_header Cache-Control no-store;
add_header Pragma no-cache;
proxy_http_version 1.1; // 这两个最好也设置
proxy_set_header Connection "";
}
}
}
1)location中有两个参数需要设置:
http {
server {
location / {
proxy_http_version 1.1; // 这两个最好也设置
proxy_set_header Connection "";
}
}
}
HTTP协议中对长连接的支持是从1.1版本之后才有的,因此最好通过proxy_http_version指令设置为”1.1”;
而”Connection” header应该被清理。
2)upstream中的keepalive设置:
此处keepalive的含义不是开启、关闭长连接的开关;也不是用来设置超时的timeout;更不是设置长连接池最大连接数。官方解释:
The connections parameter sets the maximum number of idle keepalive connections to upstream servers connections(设置到upstream服务器的空闲keepalive连接的最大数量)
When this number is exceeded, the least recently used connections are closed. (当这个数量被突破时,最近使用最少的连接将被关闭)
It should be particularly noted that the keepalive directive does not limit the total number of connections to upstream servers that an nginx worker process can open.(特别提醒:keepalive指令不会限制一个nginx worker进程到upstream服务器连接的总数量)
总结
keepalive 这个参数一定要小心设置,尤其对于QPS比较高的场景,推荐先做一下估算,根据QPS和平均响应时间大体能计算出需要的长连接的数量。比如前面10000 QPS和100毫秒响应时间就可以推算出需要的长连接数量大概是1000. 然后将keepalive设置为这个长连接数量的10%到30%。比较懒的同学,可以直接设置为keepalive=1000之类的,一般都OK的了。
综上,出现大量TIME_WAIT的情况
1)导致 nginx端出现大量TIME_WAIT的情况有两种:
keepalive_requests设置比较小,高并发下超过此值后nginx会强制关闭和客户端保持的keepalive长连接;(主动关闭连接后导致nginx出现TIME_WAIT)
keepalive设置的比较小(空闲数太小),导致高并发下nginx会频繁出现连接数震荡(超过该值会关闭连接),不停的关闭、开启和后端server保持的keepalive长连接;
2)导致后端server端出现大量TIME_WAIT的情况:
nginx没有打开和后端的长连接,即:没有设置proxy_http_version 1.1;和proxy_set_header Connection “”;从而导致后端server每次关闭连接,高并发下就会出现server端出现大量TIME_WAIT
ingress-nginx 优化
nginx-ingress 到upstream的长连接通过在configmap中配置 upstream-keepalive-connections 参数设置,(ingress-nginx 0.20 之前的版本有 bug,即使配置了也不生效:ingress-nginx upstream 的 keep-alive 不生效。)
data:
allow-backend-server-header: 'true'
client-header-buffer-size: 32k
enable-underscores-in-headers: 'true'
generate-request-id: 'true'
ignore-invalid-headers: 'true'
large-client-header-buffers: 4 32k
log-format-upstream: >-
$remote_addr - [$remote_addr] - $remote_user [$time_local] "$request"
$status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length
$request_time [$proxy_upstream_name] $upstream_addr
$upstream_response_length $upstream_response_time $upstream_status $req_id
$host [$proxy_alternative_upstream_name]
max-worker-connections: '65536'
proxy-body-size: 20m
proxy-connect-timeout: '10'
reuse-port: 'true'
server-tokens: 'false'
ssl-redirect: 'false'
upstream-keepalive-connections: '200' # 增加这一行
upstream-keepalive-timeout: '900'
worker-cpu-affinity: auto
ingress-nginx 0.20 之前的版本有 bug,即使配置了也不生效。
0.20之前版本配置模版如下所示(地址:https://github.com/kubernetes/ingress-nginx/blob/nginx-0.19.0/rootfs/etc/nginx/template/nginx.tmpl):
{{/* Whenever nginx proxies a request without a "Connection" header, the "Connection" header is set to "close" */}}
{{/* when making the target request. This means that you cannot simply use */}}
{{/* "proxy_set_header Connection $http_connection" for WebSocket support because in this case, the */}}
{{/* "Connection" header would be set to "" whenever the original request did not have a "Connection" header, */}}
{{/* which would mean no "Connection" header would be in the target request. Since this would deviate from */}}
{{/* normal nginx behavior we have to use this approach. */}}
# Retain the default nginx handling of requests without a "Connection" header
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
0.20及以后版本配置模版如下所示:(地址:https://github.com/kubernetes/ingress-nginx/blob/nginx-0.20.0/rootfs/etc/nginx/template/nginx.tmpl)
# See https://www.nginx.com/blog/websocket-nginx
map $http_upgrade $connection_upgrade {
default upgrade;
{{ if (gt $cfg.UpstreamKeepaliveConnections 0) }}
# See http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive
'' '';
{{ else }}
'' close;
{{ end }}
}
最后要配置ingress upstream后端使用keep-alive需要在ingress annotations 下配置进行connection字段置空(兴爷验证过,妥妥的没问题),如下所示:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/connection-proxy-header: ''
参考文档:
nginx官方