Haproxy(二)详细记录
http://blog.51niux.com/?id=155 #已经记录了haproxy的简单搭建部署和参数,这里记录集中场景
一、各种负载均衡
1.1 tcp反向代理
tcp 22端口反向代理:
# vim /etc/haproxy/haproxy.cfg #在最后添加一段配置,反向代理一个22端口再反向代理一个80端口
listen test_ssh bind 0.0.0.0:1000 mode tcp option tcplog server test01 172.17.1.12:22 weight 1 check inter 2s rise 2 fall 3 maxconn 50000 server test02 172.17.1.13:22 weight 1 check inter 2s rise 2 fall 3 maxconn 50000 listen test_nginx bind 0.0.0.0:80 mode http option httplog log global server nginx01 172.17.1.12:80 check port 80 inter 5000 fall 5 server nginx02 172.17.1.13:80 check port 80 inter 5000 fall 5
简单测试22端口:
#从上图可以看到测试机器是直接ping不通172.17.1.12的。
#ssh 192.168.1.36 -p 1000 #指定haproxy的IP加haproxy端口
#通过haproxy的代理端口1000就可以访问172.17.1.12这台机器了的22端口了。
查看日志:
haproxy端的日志信息:
# tail -f /var/log/haproxy.log
Sep 27 14:20:55 localhost haproxy[1763]: 192.168.1.10:51244 [27/Sep/2017:14:19:52.435] test_ssh test_ssh/test01 1/0/62575 3663 cD 0/0/0/0/0 0/0
后端服务器172.16.1.12的ssh信息:
# tail -f /var/log/secure
Sep 27 14:19:52 localhost sshd[1581]: Accepted password for root from 172.17.1.10 port 54794 ssh2 Sep 27 14:19:53 localhost sshd[1581]: pam_unix(sshd:session): session opened for user root by (uid=0) Sep 27 14:20:53 localhost sshd[1581]: pam_unix(sshd:session): session closed for user root
#从上面的测试图和日志信息可以看出,其实这就类似于iptables的SNAT和DNAT的端口映射的意思,自己作为代理去向后端去请求,然后再将请求返回给客户端。
查看status页面:
1.2 7层代理:
只有访问图片后缀或者静态目录才会发送跳转:
# vim /etc/haproxy/haproxy.cfg
global log 127.0.0.1 local0 info chroot /usr/local/haproxy/data/haproxy user haproxy group haproxy daemon nbproc 1 pidfile /var/run/haproxy.pid maxconn 4000 stats socket /var/lib/haproxy/stats #unix socket文件 defaults mode http #默认模式是http log global #采用全局定义的日志 option httplog #日志类别为http日志格式 option dontlognull #不记录健康检查的日志信息 option http-server-close #每次请求完毕后主动关闭http通道 option forwardfor except 127.0.0.0/8 #不记录本机转发的日志 option redispatch #对应的服务器挂掉后,强制定向到其他健康的服务器 retries 3 #3次连接失败就认为服务不可用,也可以通过后面设置 timeout http-request 10s #请求超时 timeout queue 1m #队列超时 timeout connect 10s #连接超时 timeout client 1m #客户端连接超时 timeout server 1m #服务器连接超时 timeout http-keep-alive 10s #长连接超时 timeout check 10s #检查超时 maxconn 3000 #最大连接数 frontend http-img #定义监听的套接字,用于接收客户端请求并与之连接。名称为http-img bind *:80 #监听在80端口 acl url_static path_beg -i /static /images /javascript /stylesheets #path_beg用于测试请求的URI是否以<string>指定的模式开头。 acl url_static path_end -i .jpg .gif .png .css .js #path_end用于测试请求的URL是否以<string>指定的模式结尾 use_backend img_backup if url_static #如果条件匹配也就是通过acl,则由img_backup后端服务器提供服务 #default_backend default_web #如果不满足则让default_web来响应,我这里先注释掉看看效果 backend img_backup balance roundrobin server imgweb1 172.17.1.12:80 check server imgweb2 172.17.1.13:80 check #backend default_web # server dweb1 192.168.1.36:80 check
#从上图可以看出由于没有匹配到acl,所以直接拒绝了。
#如果是.jpg的请求就可以了。
#如果是/static开头的url也是可以通过的。
博文来自:www.51niux.com
1.3 日志调整一下
Sep 28 10:47:48 localhost haproxy[1585]: 192.168.8.127:63723 [28/Sep/2017:10:47:48.832] http-img http-img/<NOSRV> -1/-1/-1/-1/0 503 212 - - SC-- 2/2/0/0/0 0/0 "GET /favicon.ico HTTP/1.1"
#上面是默认的httplog的格式,可能你会觉得看着很不习惯,想改成nginx的形式,或者记录的更详细一点。
修改下配置文件:
frontend http-img bind *:80 acl url_static path_beg -i /static /images /javascript /stylesheets acl url_static path_end -i .jpg .gif .png .css .js use_backend img_backup if url_static capture request header User-Agent len 128 capture request header X-Forwarded-For len 100 capture request header Referer len 200 capture response header Server len 40 capture response header Server-ID len 40 log-format "%ci:%cp \"[%tr]\" %ST %B \"%r\" \"%b\" \"%f\" \"%hrl\" \"%bi\" %si:%sp"
#从上面的配置文件可以看出获取了一些头信息,然后将日志格式重新format了,然后除最外面的""外,里面的"都要用\转义一下。然后看下修改后日志格式的输出:
Sep 28 11:37:06 localhost haproxy[2044]: 192.168.8.150:65250 "[28/Sep/2017:11:37:06.722]" 200 32894 "GET /1.jpg HTTP/1.1" "img_backup" "http-img" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0 - -" "172.17.1.10" 172.17.1.13:80
二、 日志格式
2.1 了解日志格式:
了解下tcplog格式:
在前端指定“选项tcplog”时使用TCP格式,是纯TCP代理的推荐格式。
Sep 27 14:19:51 localhost haproxy[1763]: 192.168.1.10:51242 [27/Sep/2017:14:19:50.354] test_ssh test_ssh/test02 1/0/1473 2079 -- 0/0/0/0/0 0/0 #这是此日志下面对应的参数字段 #localhost haproxy[1763]: #process_name '[' pid ']:' #192.168.1.10:51242 #client_ip ':' client_port #[27/Sep/2017:14:19:50.354] #'[' accept_date ']' #test_ssh #frontend_name #test_ssh/test02 #backend_name '/' server_name #1/0/1473 #Tw '/' Tc '/' Tt* #2079 #bytes_read* #-- #termination_state #0/0/0/0/0 #actconn '/' feconn '/' beconn '/' srv_conn '/' retries* #0/0 #srv_queue '/' backend_queue
下面是详细的字段说明:
client_ip #是启动到haproxy的TCP连接的客户端的IP地址。 如果在UNIX套接字上接受连接,则IP地址将替换为单词“unix”。 请注意,当配置了“accept-proxy”并且PROXY协议被正确使用,或者使用“accept-netscaler-cip”并且NetScaler Client IP insetion协议被正确使用时,连接被接受,那么日志将 反映转发的连接信息。 client_port #是发起连接的客户端的TCP端口。如果在UNIX套接字上接受连接,则端口将替换为接受套接字的ID,这也在stats界面中报告。 accept_date #是通过haproxy接收到连接的确切日期(如果在系统的积压中有某些排队,则可能与网络上观察到的日期略有不同)。这通常与任何上游防火墙日志中可能出现的日期相同。 frontend_name #是接收和处理连接的前端(或监听器)的名称。 backend_name #是被选择来管理与服务器的连接的后端(或监听器)的名称。如果没有应用切换规则,这将与前端相同,这对于TCP应用是常见的。 server_name #是发送连接的最后一台服务器的名称,如果发生连接错误并发生重新分配,则可能与第一个服务器的名称不同。 请注意,此服务器属于处理请求的后端。 如果连接在到达服务器之前中断,则指示“<NOSRV>”而不是服务器名称。 Tw #是在各种队列中等待的总时间(毫秒)。如果连接在到达队列之前被中止,它可以是“-1”。 Tc #是等待连接建立到最终服务器的总时间(以毫秒为单位),包括重试次数。 如果在建立连接之前连接被中止,它可以是“-1”。 Tt #是在接受和最后一次关闭之间经过的总时间(以毫秒为单位)。 它涵盖了所有可能的处理。 有一个例外,如果指定了“选项logasap”,那么在发出日志的时刻计时停止。 在这种情况下,在该值之前加上'+'号,表示最后一个会更大。 bytes_read #是发送日志时从服务器发送到客户端的总字节数。 如果指定了“option logasap”,则该值将以“+”符号前缀,表示最后一个可能较大。 请注意,该值是64位计数器,因此日志分析工具必须能够处理它而不会溢出。 termination_state #是会话结束时会话的条件。 这表示会话状态,哪一方导致会话结束发生,以及什么原因,正常标志应为“ - ”,表示会话已由两端关闭,缓冲区中没有剩余数据。 actconn #是会话记录时进程上的并发连接总数。 检测何时达到某些每进程系统限制是有用的。 feconn #是会话记录时前端上的并发连接总数。 估计维持高负载所需的资源量以及检测何时达到前端的“maxconn”是有用的。 最常见的是,当这个值大幅跳跃时,这是因为后端服务器拥塞,但有时候可能是由于拒绝服务攻击造成的。 beconn #是会话记录时由后端处理的并发连接的总数。 它包括在服务器上活动的并发连接的总数以及队列中挂起的连接数。 估计给定应用程序支持高负载所需的附加服务器数量是有用的。 最常见的是,当这个值大幅跳跃时,这是因为后端服务器拥塞,但有时候可能是由于拒绝服务攻击造成的。 srv_conn #是在会话记录时服务器上仍然处于活动状态的并发连接总数。 它不能超过服务器配置的“maxconn”参数。 如果这个值经常接近或等于服务器的“maxconn”,这意味着流量调节涉及很多,这意味着服务器的maxconn值太低,或者没有足够的服务器来处理负载 最佳响应时间。 当服务器的“srv_conn”只有一个很高时,通常意味着这个服务器有一些麻烦,导致连接处理时间比其他服务器要长。 retries #是尝试连接到服务器时此会话遇到的连接重试次数。 除非服务器在尝试连接的同一时间停止,否则通常必须为零。 频繁的重试通常表示haproxy和服务器之间的网络问题,或服务器上配置错误的系统积压,阻止新的连接排队。 该字段可以可选地带有'+'符号的前缀,表示会话经历了一个在初始服务器上达到最大重试计数后重新分配。 srv_queue #是在服务器队列中之前处理的请求的总数。 当请求没有经过服务器队列时,它为零。 它可以通过将队列中花费的时间除以队列中的请求数来估计大致服务器的响应时间。 值得注意的是,如果一个会话经历了重新分配并通过两个服务器队列,它们的位置将被累积。 除非发生重新分配,否则请求不应通过服务器队列和后端队列。 backend_queue #是在后端的全局队列中之前处理的请求的总数。 当请求没有经过全局队列时,它为零。 它可以估计平均队列长度,当除以服务器的“maxconn”参数时,可以轻松地将其转换为多个缺少的服务器。 值得注意的是,如果会话经历重新分配,则可能会在后台的队列中通过两次,然后两个位置将被累积。 除非发生重新分配,否则请求不应通过服务器队列和后端队列。
了解下HTTPlog格式:
HTTP格式是最完整的,最适合HTTP代理。 在前端指定“option httplog”时启用该功能。 它提供与TCP格式相同级别的信息,其中还包括特定于HTTP协议的功能。 就像TCP格式一样,通常会在会话结束时发出日志,除非指定了“option logasap”,这通常只对下载站点有意义。 与“监视器”规则匹配的会话将不会记录。 也可以通过在前端指定“option dontlognull”来记录客户端没有发送数据的会话。 如果在前端指定了“option dontlog-normal”,则不会记录成功的连接。
Sep 27 15:29:58 localhost haproxy[2033]: 192.168.2.127:55645 [27/Sep/2017:15:29:58.239] test_nginx test_nginx/nginx02 0/0/0/0/0 200 248 - - ---- 4/4/0/1/0 0/0 "GET /1.txt HTTP/1.1" #haproxy[2033]: #process_name '[' pid ']:' #192.168.2.127:55645 #client_ip ':' client_port #[27/Sep/2017:15:29:58.239] #'[' request_date ']' #test_nginx #frontend_name #test_nginx/nginx02 #backend_name '/' server_name #0/0/0/0/0 #TR '/' Tw '/' Tc '/' Tr '/' Ta* #200 #status_code #248 #bytes_read* #- #captured_request_cookie #- #captured_response_cookie #---- #termination_state #4/4/0/1/0 #actconn '/' feconn '/' beconn '/' srv_conn '/' retries* #0/0 #srv_queue '/' backend_queue #"GET /1.txt HTTP/1.1" #'"' http_request '"'
下面是详细字段描述:
client_ip #是启动TCP的客户端的IP地址连接到haproxy。 client_port #发起连接的客户端的TCP端口。 request_date #是通过haproxy(log field%tr)接收到HTTP请求的第一个字节的确切日期。 frontend_name #是收到的前端的名字并处理了连接。 backend_name #是被选择来管理与服务器的连接的后端(或监听器)的名称。 如果没有应用切换规则,这将与前端相同。 server_name #是发送连接的最后一台服务器的名称,如果发生连接错误并发生重新分配,则可能与第一个服务器的名称不同。 TR #是在接收到第一个字节后等待来自客户端(不包括正文)的完整HTTP请求花费的总时间(毫秒)。 如果在接收到完整的请求或接收到错误的请求之前连接被中止,它可以是“-1”。 它应该总是非常小,因为一个请求通常适合一个数据包。 这里大部分时间通常表示客户端和haproxy之间的网络问题或手动输入的请求。 Tw #是在各种队列中等待的总时间(毫秒)。如果连接在到达队列之前中止,则可以为“-1”。 Tc #是等待连接建立到最终服务器的总时间(以毫秒为单位),包括重试次数。 如果请求在建立连接之前被中止,它可以是“-1”。 Tr #是等待服务器发送完整HTTP响应的总时间(毫秒),不计算数据。 如果在接收到完整的响应之前请求被中止,它可以是“-1”。 它通常与服务器的请求处理时间相匹配,尽管它可能会被客户端发送到服务器的数据量所改变。 “GET”请求的大部分时间通常表示重载的服务器。 Ta #是请求在haproxy中保持活动的时间,这是在接收到的请求的第一个字节和发送的最后一个字节之间经过的总时间(以毫秒为单位)。 它涵盖所有可能的处理,除了握手(见Th)和空闲时间(参见Ti)。 status_code #是返回给客户端的HTTP状态代码。 这种状态通常由服务器设置,但是当服务器无法到达或其响应被haproxy阻止时也可以通过haproxy设置。 bytes_read #是发送日志时发送到客户端的总字节数。 这包括HTTP头。 如果指定了“option logasap”,则该值将以“+”符号前缀,表示最后一个可能较大。 请注意,该值是64位计数器,因此日志分析工具必须能够处理它而不会溢出。 captured_request_cookie #是一个可选的“name = value”条目,指示客户端在请求中具有此cookie。 cookie名称及其最大长度由前端配置中的“capture cookie”语句定义。 当该选项未设置时,该字段是单个破折号(' - ')。 可能仅捕获一个cookie,通常用于跟踪客户端和服务器之间的会话ID交换,以检测由于应用程序错误而导致的客户端之间的会话交叉。 captured_response_cookie #是一个可选的“name = value”条目,表示服务器已经返回了一个具有响应的cookie。 cookie名称及其最大长度由前端配置中的“capture cookie”语句定义。 termination_state #是会话结束时会话的条件。 这表示会话状态,哪一方导致会话结束发生,由于什么原因(超时,错误,...) actconn #是会话记录时进程上的并发连接总数。 检测何时达到某些每进程系统限制是有用的。 feconn #是会话记录时前端上的并发连接总数。 beconn #是会话记录时由后端处理的并发连接的总数。 srv_conn #是在会话记录时服务器上仍然处于活动状态的并发连接总数。 retries #是尝试连接到服务器时此会话遇到的连接重试次数。 srv_queue #是在服务器队列中之前处理的请求的总数。 backend_queue #是在服务器队列中之前处理的请求的总数。 captured_request_headers #是由于在前端存在“捕获请求头”语句,在请求中捕获的标题列表。 captured_response_headers #是由于在前端存在“捕获响应头”语句,在响应中捕获的标题列表。 http_request #是完整的HTTP请求行,包括方法,请求和HTTP版本字符串。
博文来自:www.51niux.com
自定义日志格式:
指令日志格式允许您以http模式和tcp自定义日志模式。它需要一个字符串作为参数.HAproxy了解一些日志格式变量。 %在日志格式变量之前。变量可以使用大括号('{}')来引用参数,并且多个参数在大括号中用逗号分隔。可以通过使用“+”或“ - ”号对它们进行前缀来添加或移除标志。可以使用特殊变量“%o”来将其标志传播到所有其他标志变量在同一格式的字符串。引用特别方便(“Q”)和转义(“E”)字符串格式。
如果变量在方括号('['..']'之间命名),则将其用作样本表达式规则(参见第7.3节)。 这对于添加一些不太常见的信息(如客户端的SSL证书的DN)或记录用于将条目存储到条形表中的密钥很有用。
注意:空格必须被转义。 空格字符被认为是一个分隔符。为了发送一个逐字符'%',它必须在另一个'%'前面导致'%%'。 HAProxy将自动合并连续的分隔符。
注意:当使用RFC5424系统日志消息格式时,PARAM-VALUE内的字符'','\'和'''应以\'作为前缀进行转义。参考链接:https://tools.ietf.org/html/rfc5424#section-6.3.3
标志是:
* Q #引用一个字符串 * X #十六进制表示(IP,端口,%Ts,%rt,%pid) * E #以'\'为前缀的字符串中的转义字符''','\'和']'(预期目的用于RFC5424结构化数据日志格式) 例子: log-format %T\ %t\ Some\ Text log-format %{+Q}o\ %t\ %s\ %{-Q}r log-format-sd %{+Q,+E}o\ [exampleSDID@1234\ header=%[capture.req.hdr(0)]]
目前,默认的HTTP格式是这样定义的:
log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC \ %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"
默认的CLF格式是这样定义的:
log-format "%{+Q}o %{-Q}ci - - [%trg] %r %ST %B \"\" \"\" %cp \ %ms %ft %b %s %TR %Tw %Tc %Tr %Ta %tsc %ac %fc \ %bc %sc %rc %sq %bq %CC %CS %hrl %hsl"
并且默认TCP格式是这样定义的:
log-format "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts \ %ac/%fc/%bc/%sc/%rc %sq/%bq"
有关当前定义的变量,请参考下表:
var | 字段名称 | 描述 | 类型 |
%B | bytes_read | 从服务器到客户端 | 数字 |
%CC | captured_request_cookie(只有模式http) | 字符串 | |
%CS | captured_response_cookie(只有模式http) | 字符串 | |
%H | hostname | 字符串 | |
%HM | HTTP method(只有模式http) | 比如:POST | 字符串 |
%HP | HTTP request URI without query string(同上) | 比如:path | 字符串 |
%HQ | HTTP request URI query string(同上) | 比如:?bar=baz | 字符串 |
%HU | HTTP request URI(同上) | 比如:/foo?bar=baz | 字符串 |
%HV | HTTP version(同上) | 比如:HTTP/1.0 | 字符串 |
%ID | unique-id | 字符串 | |
%ST | status_code | 数字 | |
%T | gmt_date_time | date | |
%Ta | Active time of the request | 从TR到end | 数字 |
%Tc | Tc | 数字 | |
%Td | Td = Tt - (Tq + Tw + Tc + Tr) | 数字 | |
%Tl | local_date_time | date | |
%Th | connection handshake time | SSL, PROXY proto | 数字 |
%Ti | idle time before the HTTP request(同上) | 数字 | |
%Tq | Th + Ti + TR(同上) | 数字 | |
%TR | time to receive the full request from 1st byte(同上) | 数字 | |
%Tr | Tr(同上) | 响应时间 | 数字 |
%Ts | timestamp | 数字 | |
%Tt | Tt | 数字 | |
%Tw | Tw | 数字 | |
%U | bytes_uploaded | 从客户端到服务器 | 数字 |
%ac | actconn | 数字 | |
%b | backend_name | 字符串 | |
%bc | beconn | 后端并发连接 | 数字 |
%bi | backend_source_ip | 连接地址 | IP |
%bp | backend_source_port | 连接地址的端口 | 数字 |
%bq | backend_queue | 数字 | |
%ci | client_ip | 接收地址 | IP |
%cp | client_port | 接收端口 | 数字 |
%f | frontend_name | 字符串 | |
%fc | feconn | 前端并发连接 | 数字 |
%fi | frontend_ip | 前端通过地址 | IP |
%fp | frontend_port | 前端通过端口 | 数字 |
%ft | frontend_name_transport | “〜”后缀为SSL | 字符串 |
%lc | frontend_log_counter | 数字 | |
%hr | captured_request_headers default style | 字符串 | |
%hrl | captured_request_headers CLF style | 字符串列表 | |
%hs | captured_response_headers default style | 字符串 | |
%hsl | captured_response_headers CLF style | 字符串列表 | |
%ms | accept date milliseconds | 用0填充 | 数字 |
%pid | PID | 数字 | |
%r | http_request(同上) | 字符串 | |
%rc | retries | 数字 | |
%rt | request_counter | HTTP req或TCP会话 | 数字 |
%s | server_name | 字符串 | |
%sc | srv_conn | 服务器并发连接 | 数字 |
%si | server_IP | 目标地址 | IP |
%sp | server_port | 目标地址端口 | 数字 |
%sq | srv_queue | 数字 | |
%sslc | ssl_ciphers(仅限SSL) | 比如:AES-SHA | 字符串 |
%sslv | ssl_version(仅限SSL) | 比如:TLSv1 | 字符串 |
%t | date_time | 毫秒分辨率 | date |
%tr | date_time of HTTP request(只限HTTP模式) | date | |
%trg | gmt_date_time of start of HTTP request(同上) | date | |
%trl | locla_date_time of start of HTTP request(同上) | date | |
%ts | termination_state | 字符串 | |
%tsc | termination_state with cookie status(同上) | 字符串 |
2.2 计数器与会话状态:
计数器:
计时器在解决网络问题方面提供了很大的帮助。 所有值以毫秒(ms)报告。 这些定时器应与会话终止标志一起使用。 在前台设置的“选项tcplog”的TCP模式下,以“Tw / Tc / Tt”的形式报告3个控制点,在HTTP模式下,以“TR / Tw / Tc / Tr/ TA”。 另外还有“Th”,“Ti”,“Tq”等三项措施。
Th #总共接受tcp连接的时间,并执行低级协议的握手。 目前,这些协议是代理协议和SSL。 这可能只在整个连接的一生中发生一次。 这里很大一段时间可能表明客户端只是预先建立了连接,而不会说话,它正在经历网络问题,阻止它在合理的时间内完成握手(例如:MTU问题),或者SSL握手非常昂贵 计算。 Ti #是HTTP请求之前的空闲时间(仅HTTP模式)。 该定时器在握手结束和HTTP请求的第一个字节之间计数。 当处于保活模式的第二个请求时,它在传输结束之后开始计数以前的响应。 一些浏览器预先建立与服务器的连接,以减少未来请求的延迟,并保持待机,直到需要它。 这个延迟将被报告为空闲时间。 值-1表示连接没有收到任何内容。 TR #获取客户端请求的总时间(仅限HTTP模式)。 这是接收到的第一个字节和代理接收到标记HTTP头的结尾的空行的时间。 值“-1”表示头文件的末尾从未见过。 当客户端过早关闭或超时时,会发生这种情况。 这个时间通常很短,因为大多数请求都适合单个数据包。 大量的时间可能表示在测试期间手工输入的请求。 Tq #从接受日期或自从以前响应的最后一个字节(仅HTTP模式)发出客户端请求的总时间。 除非它们都是-1,否则它与Th + Ti + TR完全相同,在这种情况下它也返回-1。 这个计时器在HTTP保持活动和浏览器的预连接功能到来之前非常有用。 建议现在放弃对TR的支持,因为空闲时间会增加报告的噪音。 Tw #在等待连接插槽的队列中花费的总时间。 它占用后端队列以及服务器队列,并且取决于队列大小以及服务器完成先前请求所需的时间。 值“-1”表示请求在到达队列之前被杀死,这通常是无效或被拒绝的请求发生的情况。 Tc #总共建立到服务器的TCP连接时间。 这是代理发送连接请求之间的时间,以及服务器确认的那一刻,或TCP SYN数据包和匹配的SYN / ACK数据包之间的时间。 值“-1”表示连接从未建立。 Tr #服务器响应时间(仅HTTP模式)。 这是TCP连接建立到服务器的时刻和服务器发送完整响应头之间的时间。 它纯粹显示其请求处理时间,而不会因数据传输而导致网络开销。 Ta #在HTTP请求的总活动时间之间,代理接收到请求头的第一个字节和响应体的最后一个字节的发射之间。从这个领域,我们可以推断出“Td”的数据传输时间,通过减去其他计时器时有效: Td = Ta - (TR + Tw + Tc + Tr) Tt #会话持续时间,代理接受它的时刻与两端关闭的时刻。 例外是指定了“logasap”选项。 在这种情况下,它仅等于(Th + Ti + TR + Tw + Tc + Tr),并以“+”号前缀。 从这个领域,我们可以推导出“Td”的数据传输时间,减去其他定时器的有效期:Td = Tt - (Th + Ti + TR + Tw + Tc + Tr)
#这些计时器为故障原因提供了宝贵的迹象。 由于TCP协议定义了3,6,12秒的重传延迟,所以我们知道由于网络问题(电线,协商,拥塞),接近3s的倍数的定时器几乎总是与丢失的数据包相关。 此外,如果“Ta”或“Tt”接近于配置中指定的超时值,则通常意味着会话在超时时已被中止。
最常见的情况:
如果“Th”或“Ti”接近3000,则客户端和代理之间的数据包可能已经丢失。这在本地网络上是非常罕见的,但是当客户端在远端网络上并发送大量请求时可能会发生这种情况。这可能发生在这里比平常更大的值在这里没有任何网络原因。有时候,在攻击期间或在资源不足已经结束之后,haproxy可能会在几毫秒内接受数千个连接。接受这些连接的时间将不可避免地稍微延迟其他连接的处理,并且可能会发生在几千个新连接被立即被接受之后,要测量几十毫秒数量级的请求时间。使用其中一个保持活动模式可能会显示更大的空闲时间,因为“Ti”衡量等待其他请求花费的时间。
如果“Tc”接近3000,则在服务器连接阶段,服务器和代理之间的数据包可能已经丢失。该值应始终非常低,例如本地网络上的1ms,远程网络上的数十毫秒。
如果“Tr”几乎总是低于3000,除了一些似乎是3000的平均值的罕见值,代理服务器和服务器之间可能会丢失一些数据包。
如果即使对于小字节计数,“Ta”也是很大的,那通常是因为客户端和服务器都不会在haproxy在隧道模式下运行时决定关闭连接,而且两者都同意了保持连接模式。 为了解决这个问题,需要指定一个HTTP选项来操纵前端或后端上的保持活动或关闭选项。 当连接调节与服务器上的“maxconn”选项一起使用时,具有最小可能的“Ta”或“Tt”是重要的,因为在另一个释放之前不会将新的连接发送到服务器。
其他引人注目的HTTP日志('xx'表示任何忽略的值):
TR/Tw/Tc/Tr/+Ta #“option logasap”存在于前端,日志在数据阶段之前发出。 所有的计时器都是有效的,除了比现在更短的“Ta”。 -1/xx/xx/xx/Ta #客户端无法及时发送完整的请求,否则中止过早。 检查会话终止标志,然后“超时HTTP请求”和“超时客户端”设置。 TR/-1/xx/xx/Ta #无法处理请求,也许是因为服务器出现故障,因为请求无效或被ACL规则禁止。 检查会话终止标志。 TR/Tw/-1/xx/Ta #连接无法在服务器上建立。 或者它主动拒绝它,或者在Ta(TR + Tw)ms之后超时。 检查会话终止标志,然后检查“timeout connect”设置。 请注意,tarpit操作可能返回类似的模式,“Tw”等于客户端连接被维持打开的时间。 TR/Tw/Tc/-1/Ta #服务器已经接受了连接,但没有及时返回完整的响应,或者在Ta-(TR + Tw + Tc)ms之后意外关闭了它的连接。 检查会话终止标志,然后检查“超时服务器”设置。
断开会话状态:
TCP和HTTP日志在“termination_state”字段中提供会话终止指示符,就在活动连接数之前。 TCP模式为2个字符长,HTTP模式扩展为4个字符,每个字符具有特殊含义:
在第一个字符上,报告导致会话终止的第一个事件的代码:
C #TCP会话被客户端意外中止。 S #TCP会话意外地被服务器中止,或者服务器明确拒绝它。 P #由于连接限制执行,因为DENY过滤器匹配,因为检测到并且阻止了可能导致信息泄漏的服务器响应中的危险错误(例如:可缓存的cookie)的安全检查,会话被过早地中止。 L #会话由haproxy本地处理,并未传递到服务器。 这是统计和重定向发生的情况。 R #代理服务器上的一个资源已经耗尽(内存,套接字,源端口,...)。 通常,这会在连接阶段出现,系统日志应包含精确错误的副本。 如果发生这种情况,必须认为是一个非常严重的异常现象,应尽快以任何方式予以确定。 I #在自我检查期间,代理人识别出内部错误。 这应该永远不会发生,并且鼓励您报告包含此内容的任何日志,因为这几乎肯定会是一个错误。 在这样的事件之后,预防性重新启动进程也是明智的,以防由内存损坏引起。 D #会话被haproxy杀死,因为服务器被检测为down,并被配置为在下载时杀死所有连接。 U #会话被这个备份服务器上的haproxy杀死,因为检测到活动服务器已被启动,并且配置为在上次时终止所有备份连接。 K #该会议被一个由haproxy操作的管理员主动杀死。 c #客户端超时在等待客户端发送或接收数据时过期。 s #在等待服务器发送或接收数据时,服务器端超时已过期。 - #正常会话完成,客户端和服务器关闭,缓冲区中没有任何内容。
在第二个字符上,关闭TCP或HTTP会话状态:
R #代理正在等待来自客户端的完整,有效的REQUEST(仅限HTTP模式)。 没有发送到任何服务器。 Q #代理服务器在QUEUE中等待连接插槽。 这只能在服务器设置“maxconn”参数时发生。 在重新分配连续失败的尝试连接到垂死的服务器后,它也可能发生在全局队列中。 如果没有报告重新分配,则不会尝试对任何服务器进行任何连接尝试。 C #代理正在等待CONNECTION在服务器上建立。 服务器最多可能已经注意到连接尝试。 H #代理正在等待来自服务器的完整,有效的响应HEADERS(仅限HTTP)。 D #session处于DATA阶段。 L #当服务器已经完成时,代理仍然将LAST数据传输给客户端。 这是非常罕见的,因为它只能在客户端在接收到最后一个数据包时死机。 T #请求被保密。 在整个“超时时间”期间,客户端被保持开放,直到客户端关闭,两者都将在“Tw”计时器中报告。 - #数据传输结束后的正常会话完成。
第三个字符告诉客户端是否提供持久性cookie(仅在HTTP模式下):
N #客户端提供无Cookie。 通常新访客的情况是这样的,所以在日志中计数这个标志的次数通常表示站点频繁的有效趋势。 I #客户端提供了一个匹配没有已知服务器的INVALID cookie。 这可能是由于最近的配置更改,HTTP / HTTPS站点之间的混合Cookie,有条件地忽略的持久性或攻击。 D #客户端提供了一个指定服务器为“DOWN”的cookie,因此使用“选项持续”,并将客户端发送到此服务器,或者未设置客户端,并将客户端重新分配到其他服务器。 V #客户端提供了一个VALID cookie,并发送到相关联的服务器。 E #客户端提供了一个有效的cookie,但最后一个日期比“maxidle”cookie参数允许的更旧,所以cookie被认为是EXPIRED,被忽略。 该请求将被重新分派,就好像没有cookie一样。 O #客户端提供了一个有效的cookie,但是第一个日期比“maxlife”cookie参数允许的早一些,所以cookie也被认为是OLD,被忽略。 该请求将被重新分派,就好像没有cookie一样。 U #存在cookie,但是由于使用了其他一些服务器选择机制(通常是“使用服务器”规则),因此不用于选择服务器。 - #不适用(配置中没有设置cookie)。
最后一个字符报告对服务器返回的持久性cookie执行的操作(仅在HTTP模式下):
N #没有Cookie由服务器提供,也没有插入。 I #服务器没有提供cookie,代理INSERTED一个。 请注意,在“cookie插入”模式下,如果服务器提供了一个cookie,那么它仍将被覆盖并在此被报告为“I”。 U #代理更新由客户端呈现的cookie中的最后一个日期。 这只能在“maxidle”的插入模式下发生。 它发生在与cookie中指示的日期不同的日期的每一次活动。 如果发生任何其他更改,例如重新分配,则cookie将被标记为插入。 P #一个cookie由服务器提供并按原样传输。 R #服务器提供的cookie是代理服务器的REWRITTEN,它发生在“cookie重写”或“cookie前缀”模式中。 D #服务器提供的cookie由代理DELETED。 - #不适用(配置中没有设置cookie)。
两个第一个标志的组合给出了很多关于会话终止时发生的情况的信息,以及为什么它终止。 检测服务器饱和度,网络故障,本地系统资源不足,攻击等可能有帮助。最常见的终端标志组合如下所示。 它们按字母顺序排序,小写字母集合在大写字母之后,便于查找和理解。
标志原因:
-- #正常终止 CC #客户端可以在连接建立到服务器之前中止。 当haproxy尝试连接到最近死亡(或未检查)的服务器时,可能会发生这种情况,并且客户端在haproxy等待服务器响应或“超时连接”到期时中止。 CD #在数据传输期间,客户端意外中止。 这可能是由于浏览器崩溃,客户端和haproxy之间的中间设备决定主动断开连接,客户端和haproxy之间的网络路由问题,或服务器和客户端之间的保持活动会话终止 首先由客户。 cD #只要“超时客户端”延迟,客户端就没有发送或者确认任何数据。 这通常是由客户端的网络故障引起的,或客户端简单地离开网络不洁净。 CH #客户端在等待服务器开始响应时中止。 服务器可能需要太长的时间来响应,客户端点击“停止”按钮太快。 cH #在POST请求期间等待客户端数据时,“超时客户端”会触发。 这有时是由于无法传输全尺寸数据包的PPPoE网络的TCP MSS值过大引起的。 当客户端超时小于服务器超时并且服务器响应时间过长时,也可能发生这种情况。 CQ #客户端在其会话排队时中止,等待具有足够空插槽的服务器接受它。 这可能是所有的服务器都饱和了,或者分配的服务器花费的时间太长。 CR #在发送完整的HTTP请求之前,客户端被中止。 最可能的请求是使用telnet客户端手工输入,并且过早中止。 HTTP状态码在这里可能是400。 有时这可能是因为IDS杀死了haproxy和客户端之间的连接。 可以使用“选项http-ignore-probes”来忽略没有任何数据传输的连接。 cR #在客户端发送完整HTTP请求之前,“超时http-request”冲突。这有时是由于客户端对于无法传输全尺寸数据包的PPPoE网络的太多TCP MSS值造成的,或客户端手动发送请求而不能快速键入,或者忘记在最后一行输入空行请求。 HTTP状态码在这里可能是408。 CT #客户端在会话被冻结时中止。 检查这是否发生在有效请求上,以确保没有写出错误的tarpit规则是很重要的。 如果它们发生了很多事情,将“超时时间”值降低到更接近平均报告的“Tw”计时器可能是有意义的,以免对少数攻击者消耗资源。 LR #该请求被截取,并由haproxy本地处理。 一般来说,这意味着这是重定向或统计请求。 SC #服务器或其与haproxy之间的设备明确拒绝TCP连接(代理接收到TCP RST或ICMP消息)。 在某些情况下,也可以是网络堆栈告诉代理服务器不可达(例如:没有路由,或本地网络上没有ARP响应)。 当这种情况发生在HTTP模式下时,状态码在这里可能是502或503。 sC #连接到服务器之前的“超时连接”可以完成。 当在HTTP模式下发生这种情况时,状态码在这里可能是503或504。 SD #在数据传输过程中,与服务器的连接死机。 这通常意味着在与服务器交换数据时,haproxy已经从服务器接收到RST或来自中间设备的ICMP消息。 这可能是由于服务器崩溃或中间设备上的网络问题引起的。 sD #只要在数据阶段设置“超时服务器”,服务器就不发送也不发送任何数据。 这通常是由服务器(防火墙,负载均衡器,...)之前的L4设备的超时时间过短引起的,以及在客户端和服务器之间保持活着的会话,首先在haproxy上过期。 SH #在发送完整的HTTP响应头之前,服务器中断,或在处理请求时崩溃。 由于此时服务器中止是非常罕见的,因此检查其日志以控制是否崩溃以及为什么是明智的。 所记录的请求可能指示一小组错误的请求,从而显示应用程序中的错误。 有时这可能是因为IDS杀死了haproxy和服务器之间的连接。 sH #服务器可以返回其响应头之前的“超时服务器”冲突。 这是最常见的异常,表示太长的事务,可能是由服务器或数据库饱和引起的。 立即的解决方法是增加“超时服务器”设置,但请务必记住,用户体验将遭受这些长时间的响应。 唯一的长期解决方案是修复应用程序。 PC #代理拒绝建立与服务器的连接,因为尝试连接时已达到进程的套接字限制。 全局“maxconn”参数可能在配置中增加,因此不会再发生。 这个状态是非常罕见的,并且当全局“ulimit-n”参数被手动强制时可能会发生。 PD #在服务器发出头文件之后,代理程序阻止了请求或响应中错误格式化的分块编码消息。 在大多数情况下,这将指示从服务器到客户端的无效消息。 Haproxy支持高达2GB - 1(2147483647字节)的块大小。 任何较大的尺寸将被视为错误。 PH #该代理阻止了服务器的响应,因为它是无效的,不完整的,危险的(缓存控制)或匹配的安全过滤器。 在任何情况下,向客户端发送HTTP 502错误。 导致此错误的一个可能原因是包含未授权字符的HTTP标头名称中的无效语法。 在服务器响应之前,代理还会阻止来自客户端的分块编码请求,因为语法无效。 在这种情况下,HTTP 400错误将发送到客户端并在日志中报告。 PR #代理阻止了客户端的HTTP请求,因为HTTP语法无效,在这种情况下,它向客户端返回了HTTP 400错误,或者由于拒绝过滤器匹配,在这种情况下它返回了HTTP 403错误. PT #该代理阻止了客户端的请求,并在返回500服务器错误之前解除了连接。 没有发送到服务器。 连接保持打开,只要“Tw”计时器字段报告。 RC #已经耗尽了本地资源(内存,套接字,源端口),阻止与服务器建立连接。 错误日志将准确地告诉您什么是丢失的。 这是非常罕见的,只能通过适当的系统调整来解决.
两个最后一个标志的组合给出了很多关于客户端,服务器和haproxy如何处理持久性的信息。 当用户抱怨他们必须重新认证时,这对解决断开连接非常重要。 常见的标志是:
-- #持久性Cookie未启用。 NN #客户端没有提供cookie,响应中没有插入任何cookie。例如,这可以在GET请求中设置为“postonly”的插入模式。 II #指定无效服务器的cookie由客户端提供,有效的插入到响应中。 这通常在从配置中删除“服务器”条目时发生,因为当没有其他服务器知道它的cookie值可以由客户端显示。 NI #客户端没有提供cookie,一个被插入到响应中。 这通常发生在来自每个用户的“插入”模式的第一个请求,这使得它是一个简单的方法来计数真实用户。 VN #客户端提供了一个cookie,没有任何插入到响应中。 对于客户端已经获得Cookie的大多数响应,都会发生这种情况。 VU #客户端提供了一个cookie,最后一个访问日期不是最新的,所以提供了更新的cookie。 如果没有日期,或者如果有日期但是没有设置“maxidle”参数,那么这也可能发生,以便cookie可以切换到无限制的时间。 EI #客户端提供了一个cookie,最后一个访问日期对于“maxidle”参数来说太旧了,所以cookie被忽略,并在响应中插入了一个新的cookie。 OI #客户端提供了一个cookie,第一个访问日期对于“maxlife”参数来说太旧了,所以cookie被忽略,并在响应中插入了一个新的cookie。 DI #由cookie指定的服务器已关闭,已选择新的服务器,并在响应中发出新的Cookie。 VI #由cookie指定的服务器未被标记为死亡,但无法访问。 发生了一次重新分配,并选择了另一个,然后在响应中通告。
2.3 捕获HTTP头:
#此实例链接到外发代理: listen proxy-out mode http option httplog option logasap log global server cache1 192.168.1.1:3128 #记录虚拟服务器的名称 capture request header Host len 20 #记录POST期间上传的数据量 capture request header Content-Length len 10 #记录来源的开头 capture request header Referer len 20 #服务器名称(仅适用于传出代理) capture response header Server len 20 # 记录内容长度对“option logasap”很有用 capture response header Content-Length len 10 #记录响应中的预期缓存行为 capture response header Cache-Control len 8 #Via头将报告下一个代理的名称 capture response header Via len 20 #在重定向期间记录URL位置 capture response header Location len 20
#上面知识举了几个例子,其他的捕获header的方式一样。