HAProxy(高可用性代理)是一个开源负载均衡器,可以对任何 TCP 服务进行负载均衡。它特别适合 HTTP 负载平衡,因为它支持会话持久性和第 7 层处理。
与数字海洋专用网络,HAProxy 可以配置为前端,通过专用网络连接对两个 VPS 进行负载平衡。
我们将在这里使用三个 VPS(Droplet):
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
Use the apt-get
命令安装 HAProxy。
apt-get install haproxy
我们需要启用 HAProxy 由 init 脚本启动。
nano /etc/default/haproxy
Set the ENABLED
选项1
ENABLED=1
要检查此更改是否正确完成,请执行不带任何参数的 HAProxy 初始化脚本。您应该看到以下内容。
root@haproxy:~# service haproxy
Usage: /etc/init.d/haproxy {start|stop|reload|restart|status}
我们将移动默认配置文件并创建我们自己的配置文件。
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
The log
指令提到了日志消息将发送到的系统日志服务器。在 Ubuntu 上,rsyslog 已经安装并运行,但它不侦听任何 IP 地址。稍后我们将修改rsyslog的配置文件。
The maxconn
指令指定前端并发连接数。默认值为2000
并且应该根据您的 VPS 配置进行调整。
The user
and 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 连接尝试成功的最长时间。
The client
and server
当客户端或服务器期望在 TCP 过程中确认或发送数据时,将应用超时。 HAProxy 建议设置client
and server
超时为相同的值。
The retries
指令设置连接失败后在 VPS 上执行的重试次数。
The 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 以侦听端口 80appname
这只是一个用于识别应用程序的名称。这stats
指令启用连接统计页面并保护它HTTP 基本身份验证使用指定的凭据stats auth
指示。
可以通过以下提到的 URL 查看此页面stats uri
所以在这种情况下,它是http://1.1.1.1/haproxy?stats
;该页面的演示可以是在这里查看.
The balance
指令指定要使用的负载平衡算法。可用选项有循环赛(roundrobin
), 静态循环 (static-rr
), 最少连接 (leastconn
), 来源 (source
), URI (uri
) 和 URL 参数 (url_param
).
有关每个算法的信息可以从官方文档.
The server
指令声明一个后端服务器,语法为:
server <name> <address>[:port] [param*]
我们在这里提到的名称将出现在日志和警报中。有许多参数该指令支持,我们将使用check
and 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 之间的连接,这就是循环法的工作原理。我们在这里看到的客户端 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会话并显示单个会话中的页面浏览量。
在此方法中,从 HAProxy 到客户端的所有响应都将包含Set-Cookie:
标头以后端服务器的名称作为其 cookie 值。因此,客户端(Web 浏览器)将在其所有请求中包含此 cookie,HAProxy 将根据 cookie 值将请求转发到正确的后端服务器。
对于此方法,您需要添加cookie
指令并修改server
根据指令listen
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:
带有名为 cookie 的标头SRVNAME
其价值为S1
or S2
基于哪个后端响应了请求。添加后重新启动服务:
service haproxy restart
and use 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
(
)
这是我们提出的第一个请求,LAMP1 回答了这个请求,我们可以看到Set-Cookie: SRVNAME=S1; path=/
。现在,为了模拟 Web 浏览器对下一个请求执行的操作,我们使用以下方法将这些 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 来实现会话粘性,则prefix
选项适合您。
要使用此方法,请使用以下命令cookie
指示:
cookie PHPSESSID prefix
The 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 时,我们添加了一行:log 127.0.0.1 local0 notice
它将系统日志消息发送到本地主机 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
.
在下面listen
指令,我们使用option httpclose
这增加了一个Connection: close
标头。这告诉客户端(Web 浏览器)在收到响应后关闭连接。
如果要在 HAProxy 上启用 keep-alives,请替换option httpclose
符合:
option http-server-close
timeout http-keep-alive 3000
明智地设置保持活动超时,以便少数连接不会耗尽负载均衡器的所有资源。
Keepalives 可以使用测试curl
通过同时发送多个请求。以下示例将省略不必要的输出:
> 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使用相同的连接来发出后续请求。