ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

HAProxy配置参数与命令实战:从负载均衡到健康检查与平滑重载

HAProxy配置参数与命令实战:从负载均衡到健康检查与平滑重载 第一次完整读完一份HAProxy配置是在一个凌晨的故障现场后端某台服务已经半死不活负载均衡却还在把流量往里塞。那时候我对HAProxy的印象还停留在“负载均衡工具”这个层面连配置文件里check是干嘛的都没弄明白。后来把配置参数和命令参数从头到尾啃了一遍才发现HAProxy的坑基本都集中在参数理解上——参数写对了它能让你非常省心参数写错或者漏掉它会在关键时刻给你来一下狠的。这篇文章就把我实际用到过的HAProxy配置参数和命令参数完整梳理一遍适合正在学习HAProxy、或者想把手头配置优化一遍的人。1. 先搞懂配置骨架global、defaults、frontend、backend、listen各管哪摊事1.1 global和defaults进程级参数与默认参数的边界很多人第一次打开HAProxy配置文件会觉得它不像nginx那样一个http{}包所有而是分了好几层。其实它的分层逻辑很清晰第一层就是global段。global段管的是进程本身不是业务转发。比如maxconn 100000整个进程允许的最大连接数这是全局上限。nbthread 4进程开多少个线程。daemon以后台守护进程方式运行。user haproxy、group haproxy绑定运行用户降低提权风险。chroot /var/lib/haproxy把进程限制在某个目录内防止被攻破后乱写文件。pidfile /run/haproxy.pidPID文件路径后面讲命令参数时会反复用到它。stats socket /run/haproxy.sock mode 600 level adminunix socket管理入口运维脚本靠它做动态摘除和恢复节点。spread-checks 5让健康检查时间在5%范围内随机抖动避免所有后端在同一瞬间被打爆。defaults段则是默认参数池。它本身不接收流量、不定义后端而是为后面的frontend、backend、listen提供继承值。defaults mode http log global option httplog option dontlognull timeout connect 5s timeout client 30s timeout server 30s这样后面的配置段里如果没单独写timeout或mode就会自动用defaults里的值。这个设计的价值在于“少重复、好统一”但副作用是你如果不看defaults只看某个backend里几行参数容易漏掉它实际生效的完整配置。实际排障时第一件事就是把defaults的内容摊开看一遍。1.2 frontend/backend/listen三种配置段怎么选HAProxy配置里最容易让人犯迷糊的就是frontend、backend、listen三者的区别。我总结成一句话frontend只管“流量怎么进来”监听哪个端口、做什么七层判断、转给谁。backend只管“流量怎么出去”后端服务器列表、负载算法、健康检查。listen是前两者的合并体既监听端口又定义后端。配置段能否bind监听端口能否定义后端server典型用途frontend能不能HTTP场景按URL/Host分流backend不能能配合frontend使用管理后端节点listen能能TCP四层场景如MySQL、Redis、MongoDB为什么要把frontend和backend拆开因为一个frontend可以挂多个backend。比如同样的入口监听*:80根据请求路径/api/*走API后端其他走Web后端。如果写成listen每个业务都要单独开一个端口没法在同一个入口做七层路由。反过来四层TCP场景通常不需要按路径分流一个端口对应一组后端就够了直接用listen最简洁。2. 高频核心参数的“为什么”bind、timeout、balance、server2.1 bind和mode入口参数和转发模式bind定义监听地址和端口语法是bind [地址]:端口 [参数]。最基础的是bind *:80表示监听所有IPv4地址的80端口。真正的重点是bind后面可以挂SSL参数bind *:443 ssl crt /etc/haproxy/certs/example.pemcrt指定一个PEM文件里面同时包含证书和私钥。如果有多张证书可以写多个crt或者用一个目录。这个参数直接影响HTTPS支持配错最典型的报错是“证书文件不存在”或者“私钥不匹配”。mode决定HAProxy工作在四层还是七层mode tcp纯四层转发不解析HTTP内容性能和吞吐更高适合数据库、Redis、长连接协议。mode http七层转发能看URL、Host、请求头能改协议、做ACL路由。很多人在一个defaults里写死mode http然后四层业务段又忘了覆盖结果MySQL流量被当成HTTP解析后端连接被莫名重置。我的习惯是defaults里只写通用参数把mode显式写在每个frontend/listen里避免继承错乱。2.2 timeout家族连不上、响应慢、连接被重置都从这儿找原因HAProxy的timeout参数有好几个最常用的五个timeout connect建立与后端TCP连接的最大等待时间。后端IP不可达时这个值决定用户等多久会失败。timeout client从客户端读取数据的最大间隔时间。客户端连着但一直不发数据超过就会断开。timeout server从后端读取数据的最大间隔时间。后端响应太慢超过就被切断。timeout queue请求在队列里等待空闲后端的最大时间。timeout http-request完整HTTP请求头的接收超时。为什么这组参数必须显式配置因为HAProxy对超时的默认行为非常严格。如果你在defaults里不写任何timeout新版HAProxy启动时会直接报错拒绝启动。早期版本即使能起来也会用一些很小的默认值导致大文件下载、长轮询、WebSocket全被误杀。我踩过一个真实案例给一个后端接口配了timeout server 30s业务方说“文件下载到一半就断了”。查监控发现后端生成文件需要40多秒前30秒没向客户端写数据HAProxy判断为后端无响应直接断开。这种情况不是后端挂了而是超时参数跟不上业务耗时。大文件、慢接口、流式响应都必须显式放大对应超时。2.3 balance算法轮询、最少连接、IP哈希、URI哈希的选型逻辑balance写在backend或listen里决定请求分给哪个后端。我实际用过的算法可以按场景分四类roundrobin按权重轮流分配。适合HTTP短请求、各后端性能相近的场景。注意它不是严格轮流而是“平滑加权轮询”权重高的节点拿到的请求比例高。leastconn把请求发给当前活跃连接数最少的后端。适合长连接场景比如WebSocket、数据库连接池。如果用roundrobin跑长连接新连接永远优先落在权重靠前的节点很快就把那台打满。source对客户端IP做哈希同一个IP的请求固定落到同一台后端。适合后端要本地会话但又不方便改Cookie的场景。uri对请求URI做哈希同一个URI的请求固定落到同一台后端。比较适合缓存场景让同类请求集中到同一台机器提高缓存命中率。选型的核心逻辑是你到底想让“同一类流量”尽量落在同一台机器还是平均分散到所有机器。短连接要求吞吐用roundrobin长连接看真实负载用leastconn会话保持无Cookie方案用source。2.4 server行参数check、inter、fall、rise背后的门道server是backend里最重要的一行语法是server 名称 地址:端口 [参数]我用一段配置说明server web1 192.168.1.10:8080 check inter 3s fall 3 rise 2 weight 100 maxconn 300check开启健康检查。不加这个节点就算已经宕机流量照样打过去。inter 3s每3秒检查一次。检查频率越高故障发现越快但对后端的压力也越大。fall 3连续3次检查失败节点标记为宕机。rise 2连续2次检查成功节点标记为恢复。weight 100节点权重配合roundrobin使用。maxconn 300该节点最多同时承载300个连接超过的请求进入队列等待直到timeout queue超时。端口这里有个容易踩的坑server这行建议永远显式写出后端端口。如果省略端口在TCP模式下HAProxy会沿用客户端连过来时的目标端口这个行为非常隐蔽可能导致流量打到错误的端口上。3. 把流量管起来的进阶参数ACL、会话保持、健康检查3.1 ACL用一条规则把请求分流到不同后端ACL是HAProxy做七层路由的核心语法是acl 名称 匹配条件 [参数]我常用的匹配条件path_beg /api/v1请求路径以/api/v1开头。path_end .php请求路径以.php结尾。hdr(host) -i admin.example.com请求头Host等于指定域名忽略大小写。src 192.168.1.0/24客户端IP在某个网段内。ssl_fc客户端是否走HTTPS。定义完ACL后通过use_backend和if把它接到后端选择逻辑上acl is_api path_beg -i /api/ acl is_admin hdr(host) -i admin.example.com use_backend api_servers if is_api use_backend admin_servers if is_admin default_backend web_servers这里三个规则按顺序判断先检查是不是API再检查是不是管理后台都不满足就走默认web_servers。比较实用的技巧是ACL条件里可以用or、!、and做组合比如if is_api or is_admin。不过组合太复杂会让配置可读性变差我更推荐把规则拆小、命名清晰。3.2 cookie与stick table会话保持的完整姿势会话保持解决的是“同一用户的多次请求尽量落到同一台后端”。最常见的方案是Cookie保持配置方式分成两步。第一步在backend里开启Cookie模式cookie SERVERID insert indirect nocache第二步在每台server上指定对应的Cookie值server web1 192.168.1.10:8080 check cookie web1 server web2 192.168.1.11:8080 check cookie web2用户第一次请求时HAProxy选一台后端然后在响应里插入Set-Cookie: SERVERIDweb1。用户下次请求带着这个CookieHAProxy一看值等于web1就直接分给web1。insert indirect nocache三个参数的含义是由HAProxy主动插入Cookie客户端已有同名Cookie时优先用客户端的并且告诉浏览器不要让中间缓存缓存带Set-Cookie的响应。这套方案做下来后端应用本身不需要处理Cookie逻辑都在负载均衡层完成。如果客户端环境不支持Cookie可以用stick table方案按客户端IP或某个请求头做会话绑定。但IP会变化stick table也有内存占用我用得比较少。3.3 健康检查参数别让坏节点混进流量池健康检查是HAProxy能自动踢掉故障节点的关键。HTTP场景最常见的是option httpchk GET /health http-check expect status 200第一行让HAProxy以HTTP方式探活第二行要求后端返回200才认为健康。这里有个多vhost环境特别容易踩的坑后端Nginx如果按Host分发站点而健康检查请求没带Host会产生404。解决方法是把Host写进健康检查请求里option httpchk GET /health HTTP/1.1\r\nHost:\ service.example.comTCP四层场景里仅靠TCP connect能判断端口通不通但判断不了服务是否真的可用。Redis可以这样探活option tcp-check tcp-check connect tcp-check send PING\r\n tcp-check expect string PONG这一步把健康检查从“端口活着”提升到“协议能正常交互”实际运维中价值很大——很多故障是端口还在监听但进程已经卡死TCP connect照样成功协议层探活能测出这类假活。4. 命令参数才是日常从启动、检查到优雅重载4.1 启动、检查、调试命令配置文件弄清楚之后操作系统命令是另一道坎。先看最常用的启动三连haproxy -f /etc/haproxy/haproxy.cfg -c只检查配置文件语法不会真正启动。这是每次改配置后必须执行的一步。haproxy -f /etc/haproxy/haproxy.cfg -D -p /run/haproxy.pid以后台守护进程方式启动并把PID写入指定文件。haproxy -f /etc/haproxy/haproxy.cfg -db前台运行不打到后台。适合在终端里调试观察启动日志。这里-db和-d容易混淆。-db只是“禁用后台模式”进程还是正常调度-d是“debug模式”不仅前台运行还会输出更详细的调试信息。排查启动问题优先用-d确认配置无误后再换回-D启动。还有两个辅助参数非常有用-C /etc/haproxy在加载配置前先切换工作目录能解决相对路径找不到证书或文件的问题。-V显示详细启动日志。改完配置想确认某个backend有没有被加载用-V最直接。4.2 优雅重载与热升级-sf、-st、-x的取舍这是PAProxy运维里最需要谨慎的一段。改完配置后不能简单粗暴地杀掉旧进程再启动新进程否则存量连接会全部断开。传统做法是“让新进程接管旧进程自然退出”haproxy -f /etc/haproxy/haproxy.cfg -D -p /run/haproxy.pid -sf $(cat /run/haproxy.pid)这条命令做了三件事启动一个新的HAProxy进程把PID写到/run/haproxy.pid向旧进程发送-sf指定的优雅停止信号。-sf和-st的区别要记清楚-sfsoft stop向旧进程发送SIGUSR1让旧进程处理完正在进行的连接再退出适合日常发布、配置更新。-sthard stop直接发SIGTERM立即中断所有存量连接只适合明确要快速止损的场景。如果在发布时误用了-st线上所有长时间连接会被瞬间切断Redis连接池、WebSocket会报大量异常。更平滑的方式是配合-x参数haproxy -f new.cfg -x /run/haproxy.sock -sf $(cat /run/haproxy.pid)-x让新进程从旧进程的管理socket里继承监听socket新旧进程之间无缝交接监听端口丢掉的请求更少。不过这个方案需要旧进程的stats socket设置了level admin且运行期间一直存在。在新版本里更推荐用master-worker模式配合systemd的systemctl reload haproxy内部自动完成优雅重载。4.3 常用命令参数速查表我把日常最常用的命令参数整理成一个表方便快速查。参数作用实测提醒-f指定配置文件可多次使用也可以指向目录自动加载目录下所有配置-c只检查配置语法返回码非0说明有问题-C加载配置前切换工作目录解决相对路径问题-D后台守护进程运行生产环境标准启动方式-db禁止后台模式前台运行适合看启动日志-ddebug模式输出调试信息排查问题优先用-qquiet静默模式脚本调用时避免刷屏-V显示详细启动日志确认哪个配置段被加载-v/-vv显示版本和构建选项查编译时带了哪些特性-p写入PID文件重载脚本和systemd依赖它-L设置本地实例名多实例部署时用于日志区分-n限制全局最大连接数临时压测时覆盖maxconn-m限制内存用量MB防止进程吃满内存-sf启动后优雅停止旧进程日常平滑发布-st启动后强制停止旧进程慎用会断存量连接-x从旧进程socket继承监听热升级核心参数顺带说一句排查HTTPS证书和密码套件问题时经常要配合openssl命令验证HAProxy的证书是否正常openssl s_client -connect 127.0.0.1:443 -servername example.com可以看到证书链、协议版本、密码套件信息能快速判断是HAProxy证书配置问题还是后端服务问题。5. 可直接抄的完整配置示例HTTP分流 TCP四层 统计页5.1 完整HTTP前后端配置注释版下面这份配置覆盖了前面提到的绝大部分参数业务场景是一个域名的HTTPS入口按路径把/api/分流到API后端其他流量走Web后端并开放一个内网统计页面。global log /dev/log local0 info maxconn 100000 nbthread 4 user haproxy group haproxy chroot /var/lib/haproxy pidfile /run/haproxy.pid stats socket /run/haproxy.sock mode 600 level admin spread-checks 5 defaults log global option httplog option dontlognull option forwardfor option redispatch retries 3 timeout connect 5s timeout client 30s timeout server 30s timeout queue 30s timeout http-request 10s timeout http-keep-alive 10s frontend http-in bind *:80 bind *:443 ssl crt /etc/haproxy/certs/example.pem mode http http-request set-header X-Forwarded-Proto https if { ssl_fc } acl is_api path_beg -i /api/ acl is_admin hdr(host) -i admin.example.com use_backend api_servers if is_api use_backend admin_servers if is_admin default_backend web_servers backend web_servers mode http balance roundrobin cookie SERVERID insert indirect nocache option httpchk GET /health http-check expect status 200 server web1 192.168.1.10:8080 check inter 3s fall 3 rise 2 cookie web1 server web2 192.168.1.11:8080 check inter 3s fall 3 rise 2 cookie web2 backend api_servers mode http balance leastconn timeout server 60s option httpchk GET /api/health http-check expect status 200 server api1 192.168.1.20:9090 check inter 5s fall 3 rise 2 backend admin_servers mode http balance source option httpchk GET /admin/health server admin1 192.168.1.30:8080 check inter 3s fall 3 rise 2 listen stats bind 127.0.0.1:8404 mode http stats enable stats uri /monitor stats realm HAProxy stats auth admin:admin123 stats refresh 5s stats hide-version这段配置有几点可以注意option forwardfor让后端拿到真实客户端IPoption redispatch允许在后端故障时把请求重试到其他节点stats auth是统计页登录认证绝对不能省。统计页我只绑定了内网回环地址避免公网裸奔。5.2 四层TCP配置MySQL负载均衡示例四层场景用listen最省事。下面是一个只读MySQL负载均衡的示例listen mysql-read bind 192.168.1.100:3306 mode tcp balance leastconn option tcplog timeout connect 3s timeout client 30s timeout server 30s server mysql-r1 10.0.1.5:3306 check inter 5s fall 3 rise 2 server mysql-r2 10.0.1.6:3306 check inter 5s fall 3 rise 2为什么这里用leastconn而不是roundrobinMySQL连接是典型的“短连接少、长连接占资源”的场景连接数越少的节点通常负载越低。这里没有用协议层探活是因为MySQL的健康检查比较复杂端口能通加进程活着对只读业务已经够用。如果要做更严格的探活可以考虑在mysql上跑一个专门的探测账号用option tcp-check做真实登录验证。6. 我踩过的那些坑配置和命令层面的真实教训6.1 配置层面的四个坑第一个坑timeout server设置过短导致大文件下载中断。这个前面已经详细说了最典型的表现是客户端报“连接被重置”但后端日志里一切正常。排查思路是先看HAProxy日志里的srv timeout字段而不是先怀疑后端。第二个坑多vhost环境的健康检查误杀。后端Nginx配置了多个server块option httpchk GET /health发过去的请求没有Host被默认站点接管返回404健康检查失败整个后端被踢掉。解决办法就是显式带Host或者用http-check expect status 200配合其他条件。第三个坑chroot启用后证书路径用相对路径。配置里写了chroot /var/lib/haproxy但crt写的是/etc/haproxy/example.pem启动时进程在chroot环境里找不到这个文件。这个问题不一定会让你启动失败但会导致证书加载失败HTTPS入口不可用。我的习惯是chroot后所有文件路径统一放在chroot目录内。第四个坑maxconn设很大但系统文件描述符不够。maxconn 100000听着很猛但操作系统的ulimit -n默认可能只有1024。启动后连接数到几百就报错。解决方式是调大ulimit -nsystemd里对应LimitNOFILE。6.2 命令层面的四个坑第一个命令坑改完配置不执行-c直接reload。手动运维时不先做语法检查一个小括号错误就能导致新进程启动失败。传统重载脚本如果在启动新进程前先杀了旧进程整个服务就直接断掉。所以我现在任何环境都是一条铁律haproxy -c -f过不了关绝不往后走。第二个命令坑pidfile路径写错导致-sf没发给真正的旧进程。重载脚本里-sf $(cat /run/haproxy.pid)如果HAProxy实际上是用另一个配置文件里的pidfile路径启动的脚本就找不到正确PID新进程和旧进程同时抢监听端口出现地址冲突。第三个命令坑把-db当成-D用在终端里跑了个前台进程一关终端服务就没了。排查问题时用-db没问题生产启动一定要用-D或者干脆交给systemd托管。第四个命令坑误用-st完成发版。我见过有人为了图“快”在重载脚本里用-st代替-sf结果连接池里的所有长连接被瞬间清空业务大量报错。在HAProxy里优雅处理和强制处理是有明确界限的日常变更都应该是-sf。最后分享一个我自己的习惯每次改完配置先把原文件备份成带时间戳的副本再-c检查然后写一行changelog记录改了哪个参数。HAProxy这种配置即文档的工具后面接手的同事看的不是注释而是你这行配置在真实环境里踩过的坑。把每次调整都记下来比任何速查手册都管用。
返回列表