如何使用HAProxy在UbuntuVPS上设置HTTP负载平衡
关于 HAProxy
HAProxy(High Availability Proxy)是一个开源负载均衡器,可以对任何 TCP 服务进行负载均衡。 它特别适合 HTTP 负载平衡,因为它支持会话持久性和第 7 层处理。
借助 DigitalOcean Private Networking,可以将 HAProxy 配置为前端,通过专用网络连接对两个 VPS 进行负载平衡。
序幕
我们将在这里使用三个 VPS(液滴):
Droplet 1 - 负载均衡器主机名:haproxy 操作系统:Ubuntu 公共 IP:1.1.1.1 私有 IP:10.0.0.100
Droplet 2 - 节点 1 主机名:lamp1 操作系统:Ubuntu 上的 LAMP 私有 IP:10.0.0.1
Droplet 2 - 节点 2 主机名:lamp2 操作系统:Ubuntu 上的 LAMP 私有 IP:10.0.0.2
安装 HAProxy
使用 apt-get
命令安装 HAProxy。
apt-get install haproxy
我们需要启用 HAProxy 以通过 init 脚本启动。
nano /etc/default/haproxy
将 ENABLED
选项设置为 1
ENABLED=1
要检查此更改是否正确执行,请执行不带任何参数的 HAProxy 的初始化脚本。 您应该看到以下内容。
root@haproxy:~# service haproxy Usage: /etc/init.d/haproxy {start|stop|reload|restart|status}
配置 HAProxy
我们将移动默认配置文件并创建我们自己的配置文件。
mv /etc/haproxy/haproxy.cfg{,.original}
创建并编辑一个新的配置文件:
nano /etc/haproxy/haproxy.cfg
让我们首先将配置逐块添加到此文件中:
global log 127.0.0.1 local0 notice maxconn 2000 user haproxy group haproxy
log
指令提到将发送日志消息的系统日志服务器。 在 Ubuntu 上,rsyslog 已经安装并正在运行,但它不会监听任何 IP 地址。 稍后我们将修改 rsyslog 的配置文件。
maxconn
指令指定前端的并发连接数。 默认值为 2000
,应根据您的 VPS 配置进行调整。
user
和 group
指令将 HAProxy 进程更改为指定的用户/组。 这些不应该改变。
defaults log global mode http option httplog option dontlognull retries 3 option redispatch timeout connect 5000 timeout client 10000 timeout server 10000
我们在本节中指定默认值。 要修改的值是各种 timeout
指令。 connect
选项指定等待与 VPS 的连接尝试成功的最长时间。
client
和 server
超时适用于客户端或服务器应在 TCP 过程中确认或发送数据时。 HAProxy 建议将 client
和 server
超时设置为相同的值。
retries
指令设置连接失败后在 VPS 上执行的重试次数。
option redispatch
在连接失败的情况下启用会话重新分配。 因此,如果 VPS 出现故障,会话粘性将被覆盖。
listen appname 0.0.0.0:80 mode http stats enable stats uri /haproxy?stats stats realm Strictly\ Private stats auth A_Username:YourPassword stats auth Another_User:passwd balance roundrobin option httpclose option forwardfor server lamp1 10.0.0.1:80 check server lamp2 10.0.0.2:80 check
这包含前端和后端的配置。 我们将 HAProxy 配置为在端口 80 上侦听 appname
,这只是用于识别应用程序的名称。 stats
指令启用连接统计页面,并使用 stats auth
指令指定的凭据通过 HTTP Basic authentication 保护它。
这个页面可以用stats uri
中提到的URL查看,所以在这种情况下,它是http://1.1.1.1/haproxy?stats
; 此页面的演示可以在这里查看。
balance
指令指定要使用的负载平衡算法。 可用选项包括循环 (roundrobin
)、静态循环 (static-rr
)、最少连接 (leastconn
)、源 (source
)、URI ([ X135X])和 URL 参数(url_param
)。
各个算法的信息可以从【X58X】官方文档【X84X】中获取。
server
指令声明一个后端服务器,语法是:
server <name> <address>[:port] [param*]
我们在此处提到的名称将出现在日志和警报中。 该指令支持许多参数,我们将在本文中使用check
和cookie
参数。 check
选项启用 VPS 上的健康检查,否则,VPS 始终被视为可用。
完成配置后,启动 HAProxy 服务:
service haproxy start
测试负载平衡和故障转移
要测试此设置,请在所有 Web 服务器(后端服务器 - 此处为 LAMP1 和 LAMP2)上创建一个 PHP 脚本。
/var/www/file.php
<?php header('Content-Type: text/plain'); echo "Server IP: ".$_SERVER['SERVER_ADDR']; echo "\nClient IP: ".$_SERVER['REMOTE_ADDR']; echo "\nX-Forwarded-for: ".$_SERVER['HTTP_X_FORWARDED_FOR']; ?>
现在我们将使用 curl 并多次请求该文件。
> curl http://1.1.1.1/file.php Server IP: 10.0.0.1 Client IP: 10.0.0.100 X-Forwarded-for: 117.213.X.X > curl http://1.1.1.1/file.php Server IP: 10.0.0.2 Client IP: 10.0.0.100 X-Forwarded-for: 117.213.X.X > curl http://1.1.1.1/file.php Server IP: 10.0.0.1 Client IP: 10.0.0.100 X-Forwarded-for: 117.213.X.X
请注意,HAProxy 如何交替切换 LAMP1 和 LAMP2 之间的连接,这就是 Round Robin 的工作原理。 我们在这里看到的客户端 IP 是负载均衡器的私有 IP 地址,而 X-Forwarded-For
标头是您的 IP。
要查看故障转移的工作原理,请转到 Web 服务器并停止服务:
lamp1@haproxy:~#service apache2 stop
再次使用 curl
发送请求,看看事情是如何工作的。
会话粘性
如果您的 Web 应用程序根据用户的登录会话提供动态内容(哪个应用程序没有),由于 VPS 之间的不断切换,访问者会遇到奇怪的事情。 会话粘性确保访问者坚持服务于他们的第一个请求的 VPS。 这可以通过使用 cookie 标记每个后端服务器来实现。
我们将使用以下 PHP 代码来演示会话粘性如何工作。
/var/www/session.php
<?php header('Content-Type: text/plain'); session_start(); if(!isset($_SESSION['visit'])) { echo "This is the first time you're visiting this server"; $_SESSION['visit'] = 0; } else echo "Your number of visits: ".$_SESSION['visit']; $_SESSION['visit']++; echo "\nServer IP: ".$_SERVER['SERVER_ADDR']; echo "\nClient IP: ".$_SERVER['REMOTE_ADDR']; echo "\nX-Forwarded-for: ".$_SERVER['HTTP_X_FORWARDED_FOR']."\n"; print_r($_COOKIE); ?>
此代码创建一个 PHP 会话 并显示单个会话中的页面浏览量。
Cookie插入方法
在此方法中,从 HAProxy 到客户端的所有响应都将包含一个 Set-Cookie:
标头,其中后端服务器的名称作为其 cookie 值。 因此,客户端(Web 浏览器)将在其所有请求中包含此 cookie,HAProxy 将根据 cookie 值将请求转发到正确的后端服务器。
对于此方法,您需要添加 cookie
指令并修改 listen
下的 server
指令
cookie SRVNAME insert server lamp1 10.0.0.1:80 cookie S1 check server lamp2 10.0.0.2:80 cookie S2 check
这会导致 HAProxy 添加一个 Set-Cookie:
标头,其中包含一个名为 SRVNAME
的 cookie,其值为 S1
或 S2
,具体取决于哪个后端响应了请求。 添加后重新启动服务:
service haproxy restart
并使用 curl
检查它是如何工作的。
> curl -i http://1.1.1.1/session.php HTTP/1.1 200 OK Date: Tue, 24 Sep 2013 13:11:22 GMT Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.8 Set-Cookie: PHPSESSID=l9haakejnvnat7jtju64hmuab5; path=/ Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Vary: Accept-Encoding Content-Length: 143 Connection: close Content-Type: text/plain Set-Cookie: SRVNAME=S1; path=/ This is the first time you're visiting this server Server IP: 10.0.0.1 Client IP: 10.0.0.100 X-Forwarded-for: 117.213.X.X Array ( )
这是我们提出的第一个请求,从 Set-Cookie: SRVNAME=S1; path=/
中可以看出,LAMP1 已回答了它。 现在,为了模拟 Web 浏览器对下一个请求执行的操作,我们使用 curl 的 --cookie
参数 将这些 cookie 添加到请求中。
> curl -i http://1.1.1.1/session.php --cookie "PHPSESSID=l9haakejnvnat7jtju64hmuab5;SRVNAME=S1;" HTTP/1.1 200 OK Date: Tue, 24 Sep 2013 13:11:45 GMT Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.8 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Vary: Accept-Encoding Content-Length: 183 Connection: close Content-Type: text/plain Your number of visits: 1 Server IP: 10.0.0.1 Client IP: 10.0.0.100 X-Forwarded-for: 117.213.87.127 Array ( [PHPSESSID] => l9haakejnvnat7jtju64hmuab5 [SRVNAME] => S1 ) > curl -i http://1.1.1.1/session.php --cookie "PHPSESSID=l9haakejnvnat7jtju64hmuab5;SRVNAME=S1;" HTTP/1.1 200 OK Date: Tue, 24 Sep 2013 13:11:45 GMT Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.8 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Vary: Accept-Encoding Content-Length: 183 Connection: close Content-Type: text/plain Your number of visits: 2 Server IP: 10.0.0.1 Client IP: 10.0.0.100 X-Forwarded-for: 117.213.87.127 Array ( [PHPSESSID] => l9haakejnvnat7jtju64hmuab5 [SRVNAME] => S1 )
这两个请求都由 LAMP1 提供服务,并且会话得到了适当的维护。 如果您希望 Web 服务器上的所有文件具有粘性,则此方法很有用。
Cookie 前缀方法
另一方面,如果您只希望特定 cookie 具有粘性,或者不想为会话粘性设置单独的 cookie,则 prefix
选项适合您。
要使用此方法,请使用以下 cookie
指令:
cookie PHPSESSID prefix
PHPSESSID
可以替换为您自己的 cookie 名称。 server
指令与之前的配置相同。
现在让我们看看它是如何工作的。
> curl -i http://1.1.1.1/session.php HTTP/1.1 200 OK Date: Tue, 24 Sep 2013 13:36:27 GMT Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.8 Set-Cookie: PHPSESSID=S1~6l2pou1iqea4mnhenhkm787o56; path=/ Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Vary: Accept-Encoding Content-Length: 143 Content-Type: text/plain This is the first time you're visiting this server Server IP: 10.0.0.1 Client IP: 10.0.0.100 X-Forwarded-for: 117.213.X.X Array ( )
请注意 server
cookie S1
如何作为会话 cookie 的前缀。 现在,让我们用这个 cookie 再发送两个请求。
> curl -i http://1.1.1.1/session.php --cookie "PHPSESSID=S1~6l2pou1iqea4mnhenhkm787o56;" HTTP/1.1 200 OK Date: Tue, 24 Sep 2013 13:36:45 GMT Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.8 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Vary: Accept-Encoding Content-Length: 163 Content-Type: text/plain Your number of visits: 1 Server IP: 10.0.0.1 Client IP: 10.0.0.100 X-Forwarded-for: 117.213.X.X Array ( [PHPSESSID] => 6l2pou1iqea4mnhenhkm787o56 ) > curl -i http://1.1.1.1/session.php --cookie "PHPSESSID=S1~6l2pou1iqea4mnhenhkm787o56;" HTTP/1.1 200 OK Date: Tue, 24 Sep 2013 13:36:54 GMT Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.8 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Vary: Accept-Encoding Content-Length: 163 Content-Type: text/plain Your number of visits: 2 Server IP: 10.0.0.1 Client IP: 10.0.0.100 X-Forwarded-for: 117.213.X.X Array ( [PHPSESSID] => 6l2pou1iqea4mnhenhkm787o56 )
我们可以清楚地看到,这两个请求都由 LAMP1 处理,并且会话运行良好。
为 HAProxy 配置日志记录
当我们开始配置 HAProxy 时,我们添加了一行:log 127.0.0.1 local0 notice
,它将 syslog 消息发送到 localhost IP 地址。 但默认情况下,Ubuntu 上的 rsyslog 不会监听任何地址。 所以我们必须让它这样做。
编辑 rsyslog 的配置文件。
nano /etc/rsyslog.conf
添加/编辑/取消注释以下行:
$ModLoad imudp $UDPServerAddress 127.0.0.1 $UDPServerRun 514
现在 rsyslog 将在地址 127.0.0.1 上的 UDP 端口 514 上工作,但所有 HAProxy 消息都将发送到 /var/log/syslog
,因此我们必须将它们分开。
为 HAProxy 日志创建规则。
nano /etc/rsyslog.d/haproxy.conf
将以下行添加到它。
if ($programname == 'haproxy') then -/var/log/haproxy.log
现在重新启动 rsyslog 服务:
service rsyslog restart
这会将所有 HAProxy 消息和访问日志写入 /var/log/haproxy.log
。
HAProxy 中的 Keepalive
在 listen
指令下,我们使用了 option httpclose
添加了 Connection: close
标头。 这告诉客户端(Web 浏览器)在收到响应后关闭连接。
如果要在 HAProxy 上启用 keep-alives,请将 option httpclose
行替换为:
option http-server-close timeout http-keep-alive 3000
明智地设置保持活动超时,这样一些连接就不会耗尽负载均衡器的所有资源。
测试 Keepalive
可以使用 curl
通过同时发送多个请求来测试 Keepalive。 以下示例中将省略不必要的输出:
> curl -v http://1.1.1.1/index.html http://1.1.1.1/index.html * About to connect() to 1.1.1.1 port 80 (#0) * Trying 1.1.1.1... connected > GET /index.html HTTP/1.1 > User-Agent: curl/7.23.1 (x86_64-pc-win32) libcurl/7.23.1 OpenSSL/0.9.8r zlib/1.2.5 > Host: 1.1.1.1 > Accept: */* > ......[Output omitted]......... * Connection #0 to host 1.1.1.1 left intact * Re-using existing connection! (#0) with host 1.1.1.1 * Connected to 1.1.1.1 (1.1.1.1) port 80 (#0) > GET /index.html HTTP/1.1 > User-Agent: curl/7.23.1 (x86_64-pc-win32) libcurl/7.23.1 OpenSSL/0.9.8r zlib/1.2.5 > Host: 1.1.1.1 > Accept: */* > .......[Output Omitted]......... * Connection #0 to host 1.1.1.1 left intact * Closing connection #0
在此输出中,我们必须查找以下行:Re-using existing connection! (#0) with host 1.1.1.1
,这表明 curl 使用相同的连接来发出后续请求。