day05-haproxy + keepalived 集群高可用集群
一、Haproxy概述
一种高效、可靠、免费的高可用及负载均衡软件,非常适合于高负载站点的七层数据请求。客户端通过Haproxy代理服务器获得站点页面,而代理服务器收到客户请求后根据负载均衡的规则将请求数据转发给后端真实服务器,实现了一种事件驱动、单一进程模型,能支持非常大的并发连接数。
同一客户端访问服务器,Haproxy保持会话的三种方案:
- Haproxy将客户端ip进行Hash计算并保存,由此确保相同IP访问时被转发到同一真实服务器上。
- Haproxy依靠真实服务器发送给客户端的cookie信息进行回话保持。
- Haproxy保存真实服务器的session及服务器标识,实现会话保持功能。
二、Haproxy代理模式
四层Tcp代理:Haproxy仅在客户端和服务器之间双向转发流量,可用于邮件服务内部协议通信服务器、Mysql服务等;
七层应用代理:Haproxy会分析应用层协议,并且能通过运行、拒绝、交换、增加、修改或者删除请求(request)或者回应(reponse)里指定内容来控制协议。可用于HTTP代理或https代理。
四层负载均衡
最为简单的负载均衡方式,将网络流量引导至多台服务器以使用四层(即传输层)负载均衡。这种方式会根据IP范围与端口进行用户流量转发,web-backend中的全部服务器都应当拥有同样的内容, 否则用户可能会遭遇内容不一致问题。
七层负载均衡
网络流量使用7层负载均衡意味着均衡器能够根据用户的请求内容将请求转发至不同后端服务器。这种方式允许在同一域名及端口上运行多套Web应用服务器。它可以根据“IP+端口”的方式进行负载分流,还可以根据网站的URL、访问域名、浏览器类别、语言等决定负载均衡的策略。
三、Haproxy负载均衡算法
HaProxy的负载均衡算法现在具体有如下8种:
- roundrobin:轮询
- static-rr:权重轮询
- leastconn:最少连接者优先
- source:根据请求源IP,这个跟Nginx的ip_hash机制类似
- ri:根据请求的URI
- rl_param:表示根据请求的URI参数 ‘balance url_param’ requires an URL parameter name;
- hdr(name):根据HTTP请求头来锁定每一次HTTP请求
- rdp-cookie(name):根据cookie来锁定并哈希每一次TCP请求
四、Haproxy的配置
Haproxy的配置过程分为3个主要部分:
-
命令行参数,这是最优先的;
-
global(全局)段,设置进程级参数;
-
代理配置段,通常位于default,listen,backend这样的形式内。配置文件的语法是由关键字后跟可选的一个或者多个参数(参数之间有空格)组成。如果字符串中包含空格,必须用’\’进行转义。
Haproxy配置中分五大部分:
-
global:全局参数配置,进程级的,用来控制Haproxy启动前的一些进程及系统设置。
-
defaults:配置一些默认的参数,可以被frontend,backend,listen段集成使用。
-
frontend:用来匹配接收客户所请求的域名,uri等,并针对不同的匹配,做不同的请求处理。
-
backend:定义后端服务器集群,以及对后端服务器集群的一些权重、队列、连接数等选项的设置,类似于nginx中的upstream模块。
-
listen:可以理解为frontend和backend的组合体。
Haproxy配置文件的配置方法主要有两种,一种是由前端(frontend)和后端(backend)配置块组成,前端和后端都可以有多个。第二种方法是只有一个listen配置块来同时实现前端和后端。最常用也是推荐的方法为第一种,即frontend和backend的模式。
4.1 配置参数详解
global # 全局参数global模块的设置
log 127.0.0.1 local2 # log语法:log <address_1>[max_level_1]
# 全局的日志配置,使用log关键字,指定使用127.0.0.1上的syslog服务中的local0日志设备,记录日志等级为info的日志
chroot /var/lib/haproxy #工作目录
pidfile /var/run/haproxy.pid #进程pid文件
maxconn 4000 #最大连接数
user haproxy #所属用户
group haproxy #所属用户组
daemon #以守护进程方式运行haproxy
stats socket /var/lib/haproxy/stats #定义socket套接字,针对在线维护很有帮助
defaults # defaults模块的设置
mode http #默认的模式{ tcp|http|health},health只会返回OK
log global #应用全局的日志配置
option httplog #启用日志记录HTTP请求,默认不记录HTTP请求日志
option dontlognull # 启用该项,日志中将不会记录空连接。所谓空连接就是在上游的负载均衡器者监控系统为了探测该 服务是否存活可用时,需要定期的连接或者获取某一固定的组件或页面,或者探测扫描端口是否在监听或开放等动作被称为空连接;官方文档中标注,如果该服务上游没有其他的负载均衡器的话,建议不要使用该参数,因为互联网上的恶意扫描或其他动作就不会被记录下来
option http-server-close #每次请求完毕后主动关闭http通道
option forwardfor except 127.0.0.0/8 #如果服务器上的应用程序想记录发起请求的客户端的IP地址,需要在HAProxy上配置此选项, 这样 HAProxy会把客户端的IP信息发送给服务器,在HTTP请求中添加"X-Forwarded-For"字段。启用 X-Forwarded-For,在requests头部插入客户端IP发送给后端的server,使后端server获取到客户端的真实IP。
option redispatch # 当使用了cookie时,haproxy将会将其请求的后端服务器的serverID插入到cookie中,以保证会话的SESSION持久性;而此时,如果后端的服务器宕掉了, 但是客户端的cookie是不会刷新的,如果设置此参数,将会将客户的请求强制定向到另外一个后端server上,以保证服务的正常。
retries 3 # 定义连接后端服务器的失败重连次数,连接失败次数超过此值后将会将对应后端服务器标记为不可用
timeout http-request 10s #http请求超时时间
timeout queue 1m #一个请求在队列里的超时时间
timeout connect 10s #连接超时
timeout client 1m #客户端超时
timeout server 1m #服务器端超时
timeout http-keep-alive 10s #设置http-keep-alive的超时时间
timeout check 10s #检测超时
maxconn 3000 #每个进程可用的最大连接数
listen stats #定义一个listen模块,用于状态检测
mode http #模式采用http
bind 0.0.0.0:8888 #绑定本机的地址及端口
stats enable #启用状态检测功能
stats uri /haproxy-status #状态检测的URI
stats auth haproxy:123456 #访问检测界面的用户名和密码
frontend main *:80 #frontend模块的设置,定义了一个前端
acl url_static path_beg -i /static /images /javascript /stylesheets
acl url_static path_end -i .jpg .gif .png .css .js #这里定义了一个acl规则
use_backend static if url_static #如果匹配到了acl,则访问后端的static模块
default_backend my_webserver #如果没有匹配到acl,则将请求丢给默认的模块
backend static #定义第一个后端模块,static
balance roundrobin #负载均衡算法为轮询
server static 127.0.0.1:80 check #后端服务器地址
backend my_webserver #定第二个后端,my_wenserver
balance roundrobin #负载均衡算法
server web01 172.31.2.33:80 check inter 2000 fall 3 weight 30 #定义的多个后端
server web02 172.31.2.34:80 check inter 2000 fall 3 weight 30 #定义的多个后端
server web03 172.31.2.35:80 check inter 2000 fall 3 weight 30 #定义的多个后端
4.2 健康检查
Haproxy作为Loadblance,支持对backend的健康检查,以保证在后端backend不能服务时,把从frontend进来的request分配至其他可以服务的backend,从而保证整体服务的可用性。
httpchk <method><uri><version>
option httpchk HEAD / HTTP/1.0
check:启动健康检测
inter:健康检测时间间隔
rise:检测服务可用的连接次数
fall:检测服务不可用的连接次数
error-limit:往server写数据连续失败次数的上限,执行on-error的设定
observe<mode>:把正常服务过程作为健康检测请求,即实时检测
on-error<mode>:满足error-limit后执行的操作(fastinter、fail-check、sudden-death、mark-down)。其中:
fastinter表示立即按照fastinter的检测延时进行;
fail-check表示改次error作为一次检测;
sudden-death表示模仿一次fatal,如果紧接着一次fail则server为down;
mark-down表示直接把server设置为down状态。
4.3 检测方式
4.3.1 通过监听端口进行健康检测
这种检测方式,haproxy只会去检查server的端口,并不能保证服务真正可用。
listen http_proxy 0.0.0.0:80
mode http
cookie SERVERID
balance roundrobin
option httpchk
server web1 192.168.1.1:80 cookie server01 check
server web2 192.168.1.2:80 cookie serve02 check inter 500 rise 1 fall 2
4.3.2 通过URI进行健康检测
这种检测方式,是用去GET后端server的web页面,基本可以代表后端服务的可用性。
listen http_proxy 0.0.0.0:80
mode http
cookie SERVERID
balance roundrobin
option httpchk GET /index.html
server web1 192.168.1.1:80 cookie server01 check
server web2 192.168.1.2:80 cookie serve02 check inter 500 rise 1 fall 2
4.3.3 通过request获取的头部信息进行匹配进行健康检测
这种检测方式,是基于一些高级、精细的监测需求,通过对后端头部访问的头部信息进行匹配检测。
listen http_proxy 0.0.0.0:80
mode http
cookie SERVERID
balance roundrobin
option httpchk HEAD /index.jsp HTTP/1.1\r\n\Host:\www.xxx.com
server web1 192.168.1.1:80 cookie server01 check
server web2 192.168.1.2:80 cookie serve02 check inter 500 rise 1 fall 2
五、安装Keepalived
准备三个节点: lb1, lb2, lb3
5.1 lb1节点
yum install -y keepalived
vi /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
notification_email {
[email protected] #接收人邮箱地址
}
notification_email_from [email protected] #发送人邮箱
smtp_server 127.0.0.1 #邮箱服务器
smtp_connect_timeout 30
router_id lb1 #主机名,每个节点不同
vrrp_mcast_group4 224.0.100.100 #vrrp_strict 注释,不然严格遵守vvrp,访问不了vip
}
vrrp_instance VI_1 {
state MASTER #主服务器
interface ens160 #VIP 漂移到的网卡
virtual_router_id 51 #多个节点必须相同
priority 100 #优先级,备服务器比这个低
advert_int 1
authentication {
auth_type PASS
auth_pass csdc456csdc
}
virtual_ipaddress {
192.168.88.201/24 #vip
}
}
5.2 lb2节点
yum install -y keepalived
vi /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
notification_email {
[email protected] #接收人邮箱地址
}
notification_email_from [email protected] #发送人邮箱
smtp_server 127.0.0.1 #邮箱服务器
smtp_connect_timeout 30
router_id lb2 #主机名,每个节点不同
vrrp_mcast_group4 224.0.100.100 #vrrp_strict 注释,不然严格遵守vvrp,访问不了vip
}
vrrp_instance VI_1 {
state BACKUP #备服务器
interface ens160 #VIP 漂移到的网卡
virtual_router_id 51 #多个节点必须相同
priority 90 #优先级,备服务器比这个低
advert_int 1
authentication {
auth_type PASS
auth_pass csdc456csdc
}
virtual_ipaddress {
192.168.88.201/24 #vip
}
}
5.3 lb3节点
yum install -y keepalived
vi /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
notification_email {
[email protected] #接收人邮箱地址
}
notification_email_from [email protected] #发送人邮箱
smtp_server 127.0.0.1 #邮箱服务器
smtp_connect_timeout 30
router_id lb3 #主机名,每个节点不同
vrrp_mcast_group4 224.0.100.100 #vrrp_strict 注释,不然严格遵守vvrp,访问不了vip
}
vrrp_instance VI_1 {
state BACKUP #备服务器
interface ens160 #VIP 漂移到的网卡
virtual_router_id 51 #多个节点必须相同
priority 80 #优先级,备服务器比这个低
advert_int 1
authentication {
auth_type PASS
auth_pass csdc456csdc
}
virtual_ipaddress {
192.168.88.201/24 #vip
}
}
在lb0、lb1、lb2节点执行下列命令启动Keepalived
systemctl start keepalived
systemctl enable keepalived
六、安装HAProxy
准备三个节点: lb1, lb2, lb3
6.1 lb1 lb2 lb3节点
yum install haproxy -y
vi /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Example configuration for a possible web application. See the
# full configuration options online.
#
# http://haproxy.1wt.eu/download/1.4/doc/configuration.txt
#
#---------------------------------------------------------------------
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
# to have these messages end up in /var/log/haproxy.log you will
# need to:
#
# 1) configure syslog to accept network log events. This is done
# by adding the '-r' option to the SYSLOGD_OPTIONS in
# /etc/sysconfig/syslog
#
# 2) configure local2 events to go to the /var/log/haproxy.log
# file. A line like the following can be added to
# /etc/sysconfig/syslog
#
# local2.* /var/log/haproxy.log
#
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
# turn on stats unix socket
stats socket /var/lib/haproxy/stats
#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
mode tcp
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 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
#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend kubernetes
bind *:6443
mode tcp
default_backend kubernetes-master
#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend kubernetes-master
balance roundrobin
server master1 192.168.88.97:6443 check maxconn 2000
server master2 192.168.88.98:6443 check maxconn 2000
server master3 192.168.88.99:6443 check maxconn 2000
运行如下命令启动:
systemctl start haproxy
systemctl enable haproxy
七、tomcat session cluster的实现
7.1 如何保持session会话
在集群系统下实现session统一的有如下几种方案:
1、请求精确定位:sessionsticky,例如基于访问ip的hash策略,即当前用户的请求都集中定位到一台服务器中,这样单台服务器保存了用户的session登录信息,如果宕机,则等同于单点部署,会丢失,会话不复制。
2、session复制共享:sessionreplication,如tomcat自带session共享,主要是指集群环境下,多台应用服务器之间同步session,使session保持一致,对外透明。 如果其中一台服务器发生故障,根据负载均衡的原理,调度器会遍历寻找可用节点,分发请求,由于session已同步,故能保证用户的session信息不会丢失,会话复制,。
此方案的不足之处:必须在同一种中间件之间完成(如:tomcat-tomcat之间).
session复制带来的性能损失会快速增加.特别是当session中保存了较大的对象,而且对象变化较快时, 性能下降更加显著,会消耗系统性能。这种特性使得web应用的水平扩展受到了限制。
Session内容通过广播同步给成员,会造成网络流量瓶颈,即便是内网瓶颈。在大并发下表现并不好
3、基于cache DB缓存的session共享
基于memcache/redis缓存的 session 共享
即使用cacheDB存取session信息,应用服务器接受新请求将session信息保存在cache DB中,当应用服务器发生故障时,调度器会遍历寻找可用节点,分发请求,当应用服务器发现session不在本机内存时,则去cache DB中查找,如果找到则复制到本机,这样实现session共享和高可用。
7.2 nginx+tomcat+redis实现session共享
主机 | 操作系统 | IP地址 |
---|---|---|
Nginx | Centos 7.5 | 192.168.6.240 |
Tomcat-1 | Centos 7.5 | 192.168.6.241 |
Tomcat-1 | Centos 7.5 | 192.168.6.242 |
Redis | Centos 7.5 | 192.168.6.243 |
MySQL | Centos 7.5 | 192.168.6.244 |
架构拓扑简述:
Nginx做为反向代理,实现静动分离,将客户动态请求根据权重随机分配给两台Tomcat服务器,Redis做为两台Tomcat的共享session数据服务器,MySQL做为两台Tomcat的后端数据库。
7.3 nginx安装配置
使用Nginx作为Tomcat的负载平衡器,Tomcat的会话Session数据存储在Redis,能够实现零宕机的7x24效果。因为将会话存储在Redis中,因此Nginx就不必配置成stick粘贴某个Tomcat方式,这样才能真正实现后台多个Tomcat负载平衡。
yum install pcre pcre-devel -y
yum install openssl openssl-devel -y
mkdir -p /home/oldboy/tools
cd /home/oldboy/tools/
wget -q http://nginx.org/download/nginx-1.6.2.tar.gz
useradd nginx -s /sbin/nologin -M
mkdir /application/
tar xf nginx-1.6.2.tar.gz
cd nginx-1.6.2
./configure --user=nginx --group=nginx --prefix=/application/nginx1.6.2 --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module
echo $?
make && make install
echo $?
ln -s /application/nginx1.6.2/ /application/nginx
cd ../
配置nginx反向代理:反向代理+负载均衡+健康探测,nginx.conf文件内容:
[root@linux-node1 ~]# cat /application/nginx/conf/nginx.conf
worker_processes 4;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#blog lb by oldboy at 201303
upstream backend_tomcat {
#ip_hash;
server 192.168.6.241:8080 weight=1 max_fails=2 fail_timeout=10s;
server 192.168.6.242:8080 weight=1 max_fails=2 fail_timeout=10s;
#server 192.168.6.243:8080 weight=1 max_fails=2 fail_timeout=10s;
}
server {
listen 80;
server_name www.98yz.cn;
charset utf-8;
location / {
root html;
index index.jsp index.html index.htm;
}
location ~* \.(jsp|do)$ {
proxy_pass http://backend_tomcat;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
}
}
}
7.4 安装部署tomcat
在tomcat-1和tomcat-2节点上安装JDK
[root@linux-node2 ~]# yum install java -y
[root@linux-node2 ~]# java -version
openjdk version "1.8.0_201"
OpenJDK Runtime Environment (build 1.8.0_201-b09)
OpenJDK 64-Bit Server VM (build 25.201-b09, mixed mode)
[root@linux-node2 ~]# mkdir /soft/src -p
[root@linux-node2 ~]# cd /soft/src
[root@linux-node2 ~]# wget https://mirrors.aliyun.com/apache/tomcat/tomcat-9/v9.0.16/bin/apache-tomcat-9.0.16.tar.gz
[root@linux-node2 ~]# tar xf apache-tomcat-9.0.16.tar.gz -C /soft
[root@linux-node2 ~]# cd ..
[root@linux-node2 ~]# cp -r apache-tomcat-9.0.16/ tomcat-8080
[root@linux-node2 soft]# cd tomcat-8080/
修改配置文件
[root@linux-node2 tomcat-8080]# vim conf/server.xml
设置默认虚拟主机,并增加jvmRoute
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat-1">
修改默认虚拟主机,并将网站文件路径指向/web/webapp1,在host段增加context段
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context docBase="/web/webapp1" path="" reloadable="true"/>
</Host>
增加文档目录与测试文件
[root@linux-node2 ~]# mkdir -p /web/webapp1
[root@linux-node2 ~]# cd /web/webapp1
[root@linux-node2 webapp1]# cat index.jsp
<%@page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
<head>
<title>tomcat-1</title>
</head>
<body>
<h1><font color="red">Session serviced by tomcat</font></h1>
<table aligh="center" border="1">
<tr>
<td>Session ID</td>
<td><%=session.getId() %></td>
<% session.setAttribute("abc","abc");%>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
tomcat-1
</body>
<html>
Tomcat-2节点与tomcat-1节点配置基本类似,只是jvmRoute不同,另外为了区分由哪个节点提供访问,测试页标题也不同(生产环境两个tomcat服务器提供的网页内容是相同的)。其他的配置都相同。
用浏览器访问nginx主机,验证负载均衡
验证健康检查的方法可以关掉一台tomcat主机,用客户端浏览器测试访问。
从上面的结果能看出两次访问,nginx把访问请求分别分发给了后端的tomcat-1和tomcat-2,客户端的访问请求实现了负载均衡,但sessionid并一样。所以,到这里我们准备工作就全部完成了,下面我们来配置tomcat通过redis实现会话保持。
7.5 安装redis(略)
7.6 tomcat session redis同步
通过TomcatClusterRedisSessionManager,这种方式支持redis3.0的集群方式
下载TomcatRedisSessionManager-2.0.zip包,https://github.com/ran-jit/tomcat-cluster-redis-session-manager
,放到$TOMCAT_HOMA/lib下,并解压
[root@linux-node2 ]# cd /soft/tomcat-8080/lib/
[root@linux-node2 lib]# wget https://github.com/ran-jit/tomcat-cluster-redis-session-manager/releases/download/2.0.4/tomcat-cluster-redis-session-manager.zip
[root@linux-node2 lib]# unzip tomcat-cluster-redis-session-manager.zip
Archive: tomcat-cluster-redis-session-manager.zip
creating: tomcat-cluster-redis-session-manager/conf/
inflating: tomcat-cluster-redis-session-manager/conf/redis-data-cache.properties
creating: tomcat-cluster-redis-session-manager/lib/
inflating: tomcat-cluster-redis-session-manager/lib/commons-logging-1.2.jar
inflating: tomcat-cluster-redis-session-manager/lib/commons-pool2-2.4.2.jar
inflating: tomcat-cluster-redis-session-manager/lib/jedis-2.9.0.jar
inflating: tomcat-cluster-redis-session-manager/lib/tomcat-cluster-redis-session-manager-2.0.4.jar
inflating: tomcat-cluster-redis-session-manager/readMe.txt
[root@linux-node4 lib]# cp tomcat-cluster-redis-session-manager/lib/* ./
[root@linux-node4 lib]# cp tomcat-cluster-redis-session-manager/conf/redis-data-cache.properties ../conf/
[root@linux-node2 lib]# cat ../conf/redis-data-cache.properties
#-- Redis data-cache configuration
//远端redis数据库的地址和端口
#- redis hosts ex: 127.0.0.1:6379, 127.0.0.2:6379, 127.0.0.2:6380, ....
redis.hosts=192.168.6.244:6379
//远端redis数据库的连接密码
#- redis password (for stand-alone mode)
redis.password=pwd@123
//是否支持集群,默认的是关闭
#- set true to enable redis cluster mode
redis.cluster.enabled=false
//连接redis的那个库
#- redis database (default 0)
#redis.database=0
//连接超时时间
#- redis connection timeout (default 2000)
#redis.timeout=2000
//在这个<Context>标签里面配置
[root@linux-node4 lib]# vim ../conf/context.xml
<Valve className="tomcat.request.session.redis.SessionHandlerValve" />
<Manager className="tomcat.request.session.redis.SessionManager" />
配置会话到期时间在../conf/web.xml
<session-config>
<session-timeout>60</session-timeout>
</session-config>
启动tomcat服务
[root@linux-node2 lib]# ../bin/startup.sh
Tomcat-2节点与tomcat-1节点配置相同
测试,我们每次强刷他的sessionID都是一致的,所以我们认为他的session会话保持已经完成,你们也可以选择换个客户端的IP地址来测试(192.168.6.240/index.jsp)