学习haproxy基础配置,这一篇就够了

以下内容整理自《Web服务器群集》第12章,深入浅出的介绍了haproxy配置文件语法及一些基本指令。

阅读心得 LEARNINGS

本章主要阅读部分为12.1节、12.3节、12.4节,其中:

12.1节对集群调度器和负载均衡算法做了基本介绍;12.3节提供了一个安装案例以及对配置文件格式及指令进行了很多介绍;12.4节详细介绍了ACL规则。12.5节介绍了如何使用Keepalived实现Haproxy高可用。

重点摘录 NOTES

1、基本介绍(整理自12.1节)

目前场景的Web集群调度器分为软件和硬件,软件通常使用开源的LVS、Haproxy、Nginx,硬件使用比较多的是F5。

haproxy特点

相比LVS和Nginx,LVS性能最好,但是搭建相对复杂,Nginx的upstream模块支持集群功能,但是对集群节点的健康检查功能不强,性能不如Haproxy。

负载均衡调度算法

LVS、Haproxy、Nginx 最常用的调度算法有三种:

Round Robin:即轮询调度。根据轮询分配访问请求来实现负载均衡的效果。此算法还有一种加权轮询,即根据每个节点分配的权重轮询分配访问请求。

Least Connections:即最小连接数法。根据后端节点的连接数大小动态分配前端请求。每次将新的请求指派给连接数最小的客户端。

Source Hashing :基于来源访问调度算法。该算法用于服务端需要保存Session会话记录的场景。可以基于来源IP、Cookie等做集群调度。该算法的好处是实现会话保持,但某些IP访问量非常大时,可能会导致负载不均衡,不分节点访问量变大,影响业务使用。

2、配置文件和指令(整理自12.3节)

以下内容整理自12.3节Haproxy配置文件通常由全局配置(global)和代理配置组成。而代理配置可以由defaults、listen、frontend、backend四个区段组成。

注意:在1.4版本,只有global、defaults和listen三个组成部分。

指令特点:

指令都有作用域,如timeout connect只能在defaults、listen和backend中定义,而不能在frontend区段定义。配置的继承,如listen、frontend、backend区段可以继承defaults区段的指令。如果重新声明指令,则会覆盖该指令。

haproxy 基本配置

global配置

全局配置部分。一般设置进程级别的配置。例如:

global log 127.0.0.1 local0 notice //表示使用本地127.0.0.1上的rsyslog服务器中的local0设备记录,日志等级为notice。 pidfile /var/run/haproxy.pid maxconn 4000 user nobody group nobody #nbproc 1 daemon

区段指令

log:配置全局日志记录,local0为rsyslog日志设备,默认存放到系统日志,notice为输出的日志级别。语法:log [len <length>] [format <format>] [sample <ranges>:<sample_size>] <facility> [max level [min level]] 地址可以是ipv4/v6地址、Unix socket域、文件描述符(fd@number)、stdout、stderr、ring buffer之一。

pidfile:指定haproxy进程的pid文件,启动进程的用户必须能够访问该文件。

maxconn:每个haproxy进程可以接收的最大连接数

user/group:指定haproxy进程的用户和组,也可以使用uid和gid选项代替。

nbproc:设置haproxy的负载均衡并发进程数。(已废弃)由于受到许多限制,“nbproc”指令现在被弃用并计划在haproxy 2.5版本中移除。可以使用nbthread指令指定线程数量,运行在所有分配的处理器上。

//指定nbthread 4,top -H 查看线程运行状态 PIDPPID USER STAT VSZ %VSZ CPU %CPU COMMAND 8 1 nobody S11640 1% 0 0% haproxy -W -db -f /usr/local/etc/haproxy/haproxy.cfg 7 1 nobody S11640 1% 0 0% haproxy -W -db -f /usr/local/etc/haproxy/haproxy.cfg 10 1 nobody S11640 1% 1 0% haproxy -W -db -f /usr/local/etc/haproxy/haproxy.cfg 9 1 nobody S11640 1% 1 0% haproxy -W -db -f /usr/local/etc/haproxy/haproxy.cfg 1 0 root S10824 1% 1 0% haproxy -W -db -f /usr/local/etc/haproxy/haproxy.cfg

daemon:设置以守护进程运行。

defaults配置

定义默认配置,所有的frontend、backend和listen都从该区段继承默认配置。如果在这些区段中重新声明指令配置项,则会覆盖defaults中的配置。语法:defaults [<name>]示例:

defaults log global; mode http retries 3 timeout connect 5000 timeout client 50000 timeout server 50000 option httplog option forwardfor option dontlognull option httpclose

注意:如果没有特殊说明,作用域为defaults, listen, frontend, backend

区段指令

log:默认日志配置,定义日志为global配置中定义的日志记录方式。语法: log global | <addr> [len <length>][format <format>] [sample <ranges>:<sample_size>] <facility> [<level> [<minlevel>]]作用域:defaults,frontend,listen,backend

log global log stdout format short daemon # send log to systemd log stdout format raw daemon # send everything to stdout log stderr format raw daemon notice # send important events to stderr log 127.0.0.1:514 local0 notice # only send important events log 127.0.0.1:514 local0 notice notice # same but limit output level log "${LOCAL_SYSLOG}:514" local0 notice # send to local server

log_format:定义日志输出格式。该指令将覆盖前面定义的option tcplog、log-format、option httplog指令。语法:log-format <string>作用域:defaults frontend,listen

mode:设置Haproxy默认运行方式。如果没有指定默认为tcp模式。该指令有tcp、http和health三种模式。

tcp模式下客户端和服务器端会建立全双工的连接;http模式将客户端请求转发到后端服务器。health模式已经废弃。

语法:mode { tcp|http }

retries:设置服务器连接失败后,执行的重试次数,超过该值则认为节点不可用。作用域:defaults,listen,backend

timeout connect:设置连接后端服务器的最大等待时间。该参数特定于后端,但可以在“defaults”部分中为所有对象指定一次。 作用域:defaults,backend,listen

timeout client:客户端和haproxy之间非活动连接的最大时长,超过该值haproxy将断开和此客户端的连接,单位为ms。 作用域:defaults,frontend,listen

timeout server:haproxy和后端服务器之间非活动连接保持的最大时长,达到该值haproxy将断开和此服务器的连接,单位为ms。 作用域:defaults,backend,listen

option httplog:记录http请求日志,默认情况下,日志输出格式很差,只包含源地址、目的地址和实例名。通过指定“option httplog”,每个日志行都变成了更丰富的格式。作用域:defaults,frontend,listen option forwardfor:设置X-Forward-For请求头,传递给后端服务器。option dontlognull:启用该项,禁止记录空连接日志记录。 作用域:defaults,frontend,listen

option httpclose:客户端和服务请求完毕后主动关闭http连接。默认情况下,haproxy工作在keepalive模式。

listen配置

该区段定义了一个完整的代理,属于frontend和backend的结合体,在新版中该区段不是必须的。但在老版本中所有的配置都在这一部分完成,为保证配置的兼容性,这一部分被保留。语法:listen <name>示例:

#通过listen定义一个名为stats的Haproxy监控页面,监听端口为8080,可以访问::8080/stats查看web监控页面。 listen stats bind 0.0.0.0:8080 stats uri /stats stats refresh 30s stats realm Haproxy Manager stats auth admin:admin stats hide-version stats admin if TRUE

区段指令

stats uri 定义监控页面的uristats refresh:统计页面自动刷新时间stats realm:统计页面confirm密码框显示文本stats auth:设置登录名和密码stats hide-version:隐藏统计页面上Haproxy版本信息stats admin if TRUE:该选项可在监控页面手动启动或禁用后端真实服务器。仅在1.4.9+版本有效。

fontend配置

该区段描述接受客户端连接的监听sockets集。可根据ACL规则指定要使用的后端服务器组。语法:fontend <name>示例:

frontend main bind *:80 acl url_static path_beg -i /static /images /js /css acl url_static path_end -i .jpg .gif .png .css .js use_backend static if url_static default_backend app

区段指令

frontend main:通过frontend定义一个名为main的前端虚拟节点。

bind:用于定义监听端口语法:bind [<addr>:<port_range>] interface <interface>,*或0.0.0.0 表示接收所有地址的请求。

acl:用来定义ACL规则策略。

use_backend 指定符合ACL策略的请求所访问后端服务器。语法:use_backend <backend> [{if | unless} <condition>]作用域:frontend,listen参数说明:

backend是一个有效的backend或listen定义的区段名。condition是由ACL规则组成的条件,如果省略这个规则将被无条件应用。

示例:

use_backend dynamicifurl_dyn use_backend static ifurl_css url_img extension_img default_backend dynamic

default_backend:定义默认服务器组,用于use_backend 应用的条件都不匹配时才应用。 语法:default_backend <backend>作用域:defaults,frontend,listen

errorloc:重定向错误到指定的url,类似nginx的error_page指令。语法:errorloc<url> errorloc302 <url>

acl denyfile path_end .html http-request deny if denyfile errorloc 403 http://www.example.com

backend配置

用于定义一组后端服务器,代理将连接到服务器转发收到的请求。

语法:backend <name>示例:

backend static balance roundrobin server static 127.0.0.1:80 check #静态文件部署在本机 backend app balance roundrobin option redispatch option abortonclose option httpchk GET /index.html option persist server server1 172.22.10.2:80 check inter 2000 fall 3 server server2 172.22.10.3:80 check inter 2000 fall 3

区段指令

注意:如没有特殊说明,除upstream指令外,其他指令的作用域是:defaults,listen,backend

balance:指定应用的负载均衡算法语法:balance <algorithm> [ <arguments> ]目前haproxy支持的调度算法

roundrobin:简单轮询调度static-rr:基于权重的调度算法leastconn:最小连接数量算法source:根据请求源ip地址url:根据请求的URLurl_param:根据请求的url参数进行调度hdr(name):根据HTTP请求头来锁定每一次HTTP请求rdp-cookie:根据cookie名来锁定并hash每一次TCP请求。

server指令:定义后端服务器的地址和参数,这些参数将影响nginx对后端服务器的选择。

语法:server <name>

[:port] [param*]

作用域:listen,backend

param*参数说明:

check:启用服务器健康检查,如果没有启用,则不进行健康检查,并且始终认为服务器可用。inter <delay>:执行健康检查间隔周期。单位为延迟ms,默认为2000ms。fall <count>:在count次健康检查失败后,服务器将被认为不可用,默认值为3。rise <count>:在count次连续成功检查后,服务器被认为可正常运行,默认值为2。maxconn <maxconn>:指定将发送到此服务器的最大并发连接数 如果传入的并发连接数超过这个值,它们将被排队并等待一个连接被释放。weight <weight>:用于调整服务器相对其他服务器的权重。权重越高,负载越高。缺省值为1,最大值为256。

option abortonclose:确定当客户端关闭和代理服务器间的连接时,是否关闭与后端服务器的连接。含义和nginx proxy_ignore_client_abort指令相同。

option redispatch是否在连接失败的时进行会话重新分配。当使用cookie时,haproxy会将其请求的后端服务器插入到cookie中,以保证会话的session持久性。一旦后端服务器出现故障,就会将客户端请求强制定向到另外一个健康的后端服务器上,以保证服务的正常运行。语法:option redispatch [<interval>]作用域:defaults,listen,backend

option httpchk启用HTTP服务器健康状态检查。语法:option httpchk [<method>] <uri> [<version>]作用域:defaults,listen,backend

扩展问题

Haproxy 解决集群session共享问题

用户在访问代理到后端服务器时,服务器可能会在session中保留用户的登录信息,但是当用户再次发送请求时,根据负载均衡策略可能会被代理到不同的服务器,导致用户需要重新登陆,所以需要在实施负载均衡时考虑session共享问题。 Haproxy可以使用用户IP识别或cookie识别两种方法保持客户端session一致。用户ip识别配置指令:balance source Haproxy将用户IP经过哈希函数计算后指定到固定的后端服务器上。

backend webcluster option httpchk GET /test.html balance source server server1 172.22.10.2:80 check inter 2000 fall 3 server server2 172.22.10.3:80 check inter 2000 fall 3

cookie识别配置指令:cookie SESSION_COOKIE insert indirect nocache Haproxy在web服务器端发送给客户端的cookie中,插入haproxy定义的后端服务器COOKIE ID

backend webcluster option httpchk GET /test.html cookie SERVERID insert indirect nocache server server1 172.22.10.2:80 cookie srv1 check inter 2000 fall 3 server server2 172.22.10.3:80 cookie srv2 check inter 2000 fall 3

配置Web监控平台Haproxy不仅实现了服务的故障转移,还推出了一个基于Web的监控平台,通过这个监控平台可以查看当前Haproxy的基本设置,以及所有后端服务器的运行状态。

listen stats bind 0.0.0.0:8080 stats uri /stats stats refresh 30s stats realm Haproxy Manager stats auth admin:admin stats hide-version stats admin if TRUE

ACL规则(整理自12.4节)

概述

ACL(Access Control Lists) 访问控制列表,用于定义三层到七层的规则来匹配一些特殊的请求,通过提取请求报文首部、报文内容活着其他一些状态信息,从而根据需要进行不同策略的转发响应。与ACL规则一起使用的指令有use_backend和default_backend,use_backend 后跟实例名称,表示满足ACL规则后转发的后端服务器组。default_backend表示所有ACL规则都不满足的情况下,请求默认使用的后端服务器组。Haproxy中的ACL规则在frontend部分定义。语法:acl <aclname> method -i [匹配的路径或文件] 参数说明: <aclname>:定义acl的名称,名称区分大小写,可以重名,这样就可以把多个测试条件设定为一个共同的ACL。method:设定实现ACL的方法,Haproxy定义的ACL常用方法有:

hdr_beg(host):检测请求报文首部字段开头部分是否匹配指定模式hdr_end(host):检测请求报文首部字段结尾部分是否匹配指定的模式。hdr_reg(host):检测请求报文首部字段正则匹配的模式。url_sub:表示url中包含哪些字符。url_dir:表示请求url中包含哪些子路径。path_beg:检测请求的URL是否匹配路径开头。path_end:检测请求的URL是否匹配路径结尾。 也可以根据访问的地址和端口进行规则设置dst:目标地址dst_port:目标端口src:源地址src_port:源端口

-i选项:表示忽略大小写,后跟指定的模式,如匹配的路径、文件或正则表达式等。

示例:

acl www_policy hdr_reg(host) -i ^(www.example.com|example.com) acl url_policy url_sub -i buy_sid= acl url_static path_bug /static/ /images /img /css use_backend server_www if www_policy use_backend server_app if url_policy use_backend server_static if url_static default_backend server_default

参考案例

为方便快速部署,这里并没有采用书中的安装方式,而是使用docker-compose快速部署了一套集群,本案例共提供了Dockefile、haproxy.cfg nginx.conf和docker-compose.yml四个配置文件,你可以直接拷贝相关的文件到目录,用docker-compose方式直接运行。

Dockerfile该代码源自haproxy官方提供的dockerfile,可以直接构建下面dockerfile镜像,也可以下载官方镜像来运行。

FROM alpine:3.13 # roughly, ?h=3.12-stable RUN set -eux; \ addgroup --gid 99 --system haproxy; \ adduser \ --disabled-password \ --home /var/lib/haproxy \ --ingroup haproxy \ --no-create-home \ --system \ --uid 99 \ haproxy ENV HAPROXY_VERSION 2.3.6 ENV HAPROXY_URL ENV HAPROXY_SHA256 6d4620e5da1d93ed75fdb81d5097b68bf175e309f2fab2890bba036 # seefor some helpful navigation of the possible "make" arguments RUN set -x \ \ && apk add --no-cache --virtual .build-deps \ gcc \ libc-dev \ linux-headers \ lua5.3-dev \ make \ openssl \ openssl-dev \ pcre2-dev \ readline-dev \ tar \ zlib-dev \ \ && wget -O haproxy.tar.gz "$HAPROXY_URL" \ && echo "$HAPROXY_SHA256 *haproxy.tar.gz" | sha256sum -c \ && mkdir -p /usr/src/haproxy \ && tar -xzf haproxy.tar.gz -C /usr/src/haproxy --strip-components=1 \ && rm haproxy.tar.gz \ \ && makeOpts= \ TARGET=linux-musl \ USE_GETADDRINFO=1 \ USE_LUA=1 LUA_INC=/usr/include/lua5.3 LUA_LIB=/usr/lib/lua5.3 \ USE_OPENSSL=1 \ USE_PCRE2=1 USE_PCRE2_JIT=1 \ USE_ZLIB=1 \ \ EXTRA_OBJS=" \ # see #issuecomment- for more details about prometheus support contrib/prometheus-exporter/service-prometheus.o \ " \ \ && nproc="$(getconf _NPROCESSORS_ONLN)" \ && eval "make -C /usr/src/haproxy -j $nproc all $makeOpts" \ && eval "make -C /usr/src/haproxy install-bin $makeOpts" \ \ && mkdir -p /usr/local/etc/haproxy \ && cp -R /usr/src/haproxy/examples/errorfiles /usr/local/etc/haproxy/errors \ && rm -rf /usr/src/haproxy \ \ && runDeps="$( \ scanelf --needed --nobanner --format %n#p --recursive /usr/local \ | tr , \n \ | sort -u \ | awk system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 } \ )" \ && apk add --no-network --virtual .haproxy-rundeps $runDeps \ && apk del --no-network .build-deps # # "4. Stopping and restarting HAProxy" # "when the SIGTERM signal is sent to the haproxy process, it immediately quits and all established connections are closed" # "graceful stop is triggered when the SIGUSR1 signal is sent to the haproxy process" STOPSIGNAL SIGUSR1 COPY docker-entrypoint.sh /usr/local/bin/ RUN ln -s usr/local/bin/docker-entrypoint.sh / # backwards compat ENTRYPOINT ["docker-entrypoint.sh"] CMD ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg"]

nginx.conf

usernginx; worker_processesauto; error_log/var/log/nginx/error.log warn; pid/var/run/nginx.pid; events { worker_connections1024; } http { include /etc/nginx/mime.types; default_typetext/plain; log_formatmain$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"; access_log/var/log/nginx/access.logmain; sendfileon; #tcp_nopush on; keepalive_timeout65; #gzipon; server { listen 80; server_namelocalhost; location / { root /usr/share/nginx/html; indexindex.html index.htm; } #error_page404/404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504/50x.html; location = /50x.html { root /usr/share/nginx/html; } } }

haproxy.cfg

# Simple configuration for an HTTP proxy listening on port 80 on all # interfaces and forwarding requests to a single backend "servers" with a # single server "server1" listening on 127.0.0.1:8000 # global log stdout local0 debug pidfile /var/run/haproxy.pid maxconn 4000 user nobody group nobody nbthread 4 daemon defaults log global mode http retries 3 timeout connect 5000ms timeout client 50000ms timeout server 50000ms option httplog option forwardfor option redispatch listen stats #bind *:80 #stats admin if TRUE frontend http-in bind *:80 stats enable stats uri /stats #stats refresh 30s stats realm Haproxy\ Manager stats auth admin:admin stats admin if TRUE default_backend servers backend servers #balance roundrobin http-response add-header "header" "11s" cookie SERVERID insert indirect nocache server server1 172.22.10.2:80 cookie srv1 check inter 2000 fall 3 maxconn 2048 server server2 172.22.10.3:80 cookie srv2 check inter 2000 fall 3 maxconn 2048

docker-compose.yml

version: 2.2 ################## #Server1: nginx #Server2: php7 fpm #Server3: php7 swoole services: haproxy: container_name: "haproxy" image: haproxy:2.3.6-alpine restart: always ports: - 80:80 networks: default: ipv4_address: 172.22.10.1 volumes: - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg #volumes: #- ./nginx.conf:/etc/nginx/nginx.conf command: ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg"] server1: container_name: "server1" restart: always image: nginx:1.16.0-alpine ports: - 8081:80 networks: default: ipv4_address: 172.22.10.2 volumes: - ./nginx.conf:/etc/nginx/nginx.conf command: ["nginx", "-g", "daemon off;"] server2: container_name: "server2" restart: always image: nginx:1.16.0-alpine ports: - 8082:80 networks: default: ipv4_address: 172.22.10.3 volumes: - ./nginx.conf:/etc/nginx/nginx.conf command: ["nginx", "-g", "daemon off;"] networks: default: driver: bridge ipam: driver: default config: - subnet: 172.22.0.0/16

参考文献:

haproxy官方文档