Files
Cloud-book/Linux服务/负载均衡.md
2025-08-27 17:10:05 +08:00

2869 lines
101 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Nginx负载均衡基本概述
## 为什么要使用负载均衡
- 当我们的Web服务器直接面向用户往往要承载大量并发请求单台服务器难以负荷我使用多台Web服务器组成集群前端使用Nginx负载均衡将请求分散的打到我们的后端服务器集群中实现负载的分发。那么会大大提升系统的吞吐率、请求性能、高容灾
![image-20210711155724840](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210711155724840.png)
- 往往我们接触的最多的是`SLB(Server Load Balance)`负载均衡,实现最多的也是`SLB`、那么`SLB`它的调度节点和服务节点通常是在一个地域里面。那么它在这个小的逻辑地域里面决定了他对部分服务的实时性、响应性是非常好的。
- 所以说当海量用户请求过来以后,它同样是请求调度节点,调度节点将用户的请求转发给后端对应的服务节点,服务节点处理完请求后在转发给调度节点,调度节点最后响应给用户节点。这样也能实现一个均衡的作用,那么**Nginx**则是一个典型的`SLB`
- 负载均衡的叫法有很多
- 负载均衡
- Load Balance
- LB
- 公有云中叫法
- SLB 阿里云负载均衡
- QLB 青云负载均衡
- CLB 腾讯云负载均衡
- ULB ucloud负载均衡
- 常见的负载均衡的软件
- Nginx
- Haproxy
- LVS
## 四层负载均衡
所谓四层负载均衡指的是`OSI`七层模型中的传输层,那么传输层**Nginx**已经能支持**TCP/IP**的控制,所以只需要对客户端的请求进行**TCP/IP**协议的包转发就可以实现负载均衡,那么它的好处是性能非常快、只需要底层进行应用处理,而不需要进行一些复杂的逻辑。
![image-20210711160121942](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210711160121942.png)
## 七层负载均衡
七层负载均衡它是在应用层,那么它可以完成很多应用方面的协议请求,比如我们说的**http**应用的负载均衡,它可以实现**http**信息的改写、头信息的改写、安全应用规则控制、**URL**匹配规则控制、以及转发、**rewrite**等等的规则,所以在应用层的服务里面,我们可以做的内容就更多,那么**Nginx**则是一个典型的七层负载均衡`SLB`
![image-20210711160208468](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210711160208468.png)
## 四层负载均衡与七层负载均衡区别
四层负载均衡数据包在底层就进行了分发,而七层负载均衡数据包则是在最顶层进行分发、由此可以看出,七层负载均衡效率没有四负载均衡高。
但七层负载均衡更贴近于服务,如:**http**协议就是七层协议,我们可以用**Nginx**可以作会话保持,**URL**路径规则匹配、**head**头改写等等,这些是四层负载均衡无法实现的。
**注意:四层负载均衡不识别域名,七层负载均衡识别域名**
# Nginx负载均衡配置场景
Nginx要实现负载均衡需要用到`proxy_pass`代理模块配置.
Nginx负载均衡与**Nginx**代理不同地方在于,**Nginx**的一个`location`仅能代理一台服务器,而**Nginx**负载均衡则是将客户端请求代理转发至一组**upstream**虚拟服务池.
![image-20210711160627756](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210711160627756.png)
## Nginx upstream虚拟配置语法
```bash
Syntax: upstream name { ... }
Default: -
Context: http
#upstream例
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com:8080;
server unix:/tmp/backend3;
server backup1.example.com:8080 backup;
}
server {
location / {
proxy_pass http://backend;
}
}
```
## 环境准备
| 角色 | 地址 | 主机名 |
| :---- | :---------------------- | :----- |
| LB01 | ens33:**192.168.88.10** | lb01 |
| web01 | ens33:**192.168.88.20** | web01 |
| web02 | ens33:**192.168.88.30** | web02 |
## Web01服务器上配置nginx
```bash
[root@web01 ~]# cd /etc/nginx/conf.d/
[root@web01 conf.d]# vim node.conf
server {
listen 80;
server_name node.test.com;
location / {
root /code/node;
index index.html;
}
}
[root@web01 conf.d]# mkdir -p /code/node
[root@web01 conf.d]# echo "web01 ..." > /code/node/index.html
[root@web01 conf.d]# systemctl restart nginx
# 关闭防火墙和SElinux
[root@web02 conf.d]# setenforce 0
[root@web02 conf.d]# systemctl stop firewalld
```
## Web02服务器上配置nginx
```bash
[root@web02 ~]# cd /etc/nginx/conf.d/
[root@web02 conf.d]# vim node.conf
server {
listen 80;
server_name node.test.com;
location / {
root /code/node;
index index.html;
}
}
[root@web02 conf.d]# mkdir -p /code/node
[root@web02 conf.d]# echo "web02 ..." > /code/node/index.html
[root@web02 conf.d]# systemctl restart nginx
# 关闭防火墙和SElinux
[root@web02 conf.d]# setenforce 0
[root@web02 conf.d]# systemctl stop firewalld
```
## 配置Nginx负载均衡
```bash
[root@lb01 ~]# cd /etc/nginx/conf.d/
[root@lb01 conf.d]# vim /etc/nginx/proxy_params
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;
proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 4 128k;
[root@lb01 conf.d]# vim node_proxy.conf
upstream node {
server 192.168.88.20:80;
server 192.168.88.30:80;
}
server {
listen 80;
server_name node.test.com;
location / {
proxy_pass http://node;
include proxy_params;
}
}
[root@lb01 conf.d]# nginx -t
[root@lb01 conf.d]# systemctl restart nginx
# 配置hosts解析
[root@lb01 conf.d]# vim /etc/hosts
192.168.88.10 node.test.com
```
windows访问更改windows下的hosts文件
- 打开浏览器访问http://node.test.com
![image-20210711201349945](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210711201349945.png)
![image-20210711201400731](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210711201400731.png)
## 负载均衡常见典型故障
如果后台服务连接超时Nginx是本身是有机制的如果出现一个节点down掉的时候Nginx会更据你具体负载均衡的设置将请求转移到其他的节点上但是如果后台服务连接没有down掉但是返回错误异常码了如:504、502、500,这个时候你需要加一个负载均衡的设置如下proxy_next_upstream http_500 | http_502 | http_503 | http_504 |http_404;意思是当其中一台返回错误码404,500...等错误时,可以分配到下一台服务器程序继续处理,提高平台访问成功率。
```bash
server {
listen 80;
server_name node.test.com;
location / {
proxy_pass http://node;
proxy_next_upstream error timeout http_500 http_502 http_503 http_504 http_404;
}
}
```
# Nginx负载均衡调度算法
| **调度算法** | **概述** |
| :----------- | :----------------------------------------------------------- |
| 轮询 | 按时间顺序逐一分配到不同的后端服务器(默认) |
| weight | 加权轮询,weight值越大,分配到的访问几率越高 |
| ip_hash | 每个请求按访问IP的hash结果分配,这样来自同一IP的固定访问一个后端服务器 |
| url_hash | 按照访问URL的hash结果来分配请求,是每个URL定向到同一个后端服务器 |
| least_conn | 最少链接数,那个机器链接数少就分发 |
## Nginx负载均衡[rr]轮询具体配置
```bash
upstream load_pass {
server 192.168.88.10:80;
server 192.168.88.20:80;
}
```
## Nginx负载均衡[wrr]权重轮询具体配置
```bash
upstream load_pass {
server 192.168.88.10:80 weight=5;
server 192.168.88.20:80;
}
```
## Nginx负载均衡ip_hash
- **ip_hash**: 根据客户端IP地址进行哈希,确保来自同一IP的客户端总是被转发到同一台后端服务器
- 具体配置不能和weight一起使用。
```bash
#如果客户端都走相同代理, 会导致某一台服务器连接过多
upstream load_pass {
ip_hash;
server 192.168.88.10:80;
server 192.168.88.20:80;
}
```
# Nginx负载均衡后端状态
- 后端Web服务器在前端Nginx负载均衡调度中的状态
| **状态** | **概述** |
| :----------- | :-------------------------------- |
| down | 当前的server暂时不参与负载均衡 |
| backup | 预留的备份服务器 |
| max_fails | 允许请求失败的次数 |
| fail_timeout | 经过max_fails失败后, 服务暂停时间 |
| max_conns | 限制最大的接收连接数 |
## 测试down状态测试该Server不参与负载均衡的调度
```bash
upstream load_pass {
#不参与任何调度, 一般用于停机维护
server 192.168.88.10:80 down;
}
```
## backup以及down状态
```bash
upstream load_pass {
server 192.168.88.10:80 backup;
server 192.168.88.20:80 max_fails=1 fail_timeout=10s;
}
location / {
proxy_pass http://load_pass;
include proxy_params;
}
```
## max_fails失败次数和fail_timeout多少时间内失败多少次则标记down
```bash
upstream load_pass {
server 192.168.88.10:80;
server 192.168.88.20:80 max_fails=2 fail_timeout=10s;
}
```
## max_conns最大TCP连接数
```bash
upstream load_pass {
server 192.168.88.10:80;
server 192.168.88.20:80 max_conns=1;
}
```
# Nginx负载均衡健康检查
- 在Nginx官方模块提供的模块中没有对负载均衡后端节点的健康检查模块但可以使用第三方模块。
`nginx_upstream_check_module`来检测后端服务的健康状态。
- 第三方模块项目地址https://github.com/yaoweibin/nginx_upstream_check_module
- 安装依赖包
```bash
[root@lb01 ~]# yum install -y gcc glibc gcc-c++ pcre-devel openssl-devel patch
```
- 下载nginx源码包以及nginx_upstream_check模块第三方模块
```bash
[root@lb01 ~]# wget http://nginx.org/download/nginx-1.20.1.tar.gz
[root@lb01 ~]# wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/master.zip
```
- 解压nginx源码包以及第三方模块
```bash
[root@lb01 ~]# tar xf nginx-1.20.1.tar.gz
[root@lb01 ~]# unzip master.zip
```
- 进入nginx目录打补丁(nginx的版本是1.14补丁就选择1.14的,p1代表在nginx目录p0是不在nginx目录)
```bash
[root@lb01 ~]# cd nginx-1.20.1
[root@lb01 nginx-1.14.2]# patch -p1 <../nginx_upstream_check_module-master/check_1.20.1+.patch
[root@lb01 nginx-1.14.2]# ./configure --prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--user=nginx --group=nginx --with-compat \
--with-file-aio --with-threads --with-http_addition_module \
--with-http_auth_request_module --with-http_dav_module \
--with-http_flv_module --with-http_gunzip_module \
--with-http_gzip_static_module --with-http_mp4_module \
--with-http_random_index_module --with-http_realip_module \
--with-http_secure_link_module --with-http_slice_module \
--with-http_ssl_module --with-http_stub_status_module \
--with-http_sub_module --with-http_v2_module --with-mail \
--with-mail_ssl_module --with-stream --with-stream_realip_module \
--with-stream_ssl_module --with-stream_ssl_preread_module \
--add-module=/root/nginx_upstream_check_module-master \
--with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' \
--with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'
[root@lb01 nginx-1.14.2]# make -j 2 && make install
[root@lb01 nginx-1.14.2]# nginx -v
nginx version: nginx/1.14.2
[root@lb01 nginx-1.14.2]# mkdir -p /var/cache/nginx/client_temp
```
- 在已有的负载均衡上增加健康检查的功能
```bash
[root@lb01 conf.d]# vim node_proxy.conf
upstream node {
server 192.168.88.10:80 max_fails=2 fail_timeout=10s;
server 192.168.88.20:80 max_fails=2 fail_timeout=10s;
check interval=3000 rise=2 fall=3 timeout=1000 type=tcp;
#interval 检测间隔时间,单位为毫秒
#rise 表示请求2次正常标记此后端的状态为up
#fall 表示请求3次失败标记此后端的状态为down
#type 类型为tcp
#timeout 超时时间,单位为毫秒
}
server {
listen 80;
server_name node.test.com;
location / {
proxy_pass http://node;
include proxy_params;
}
location /upstream_check {
check_status;
}
}
```
# Nginx负载均衡会话保持
- 在使用负载均衡的时候会遇到会话保持的问题,可通过如下方式进行解决。
- 使用nginx的ip_hash根据客户端的IP将请求分配到对应的IP上
- 基于服务端的session会话共享NFSMySQLmemcacheredisfile
- 在解决负载均衡会话问题我们需要了解session和cookie的区别。
- 浏览器端存的是cookie每次浏览器发请求到服务端时报文头是会自动添加cookie信息的。
- 服务端会查询用户的cookie作为key去存储里找对应的value(session)
- 同一域名下的网站的cookie都是一样的所以无论几台服务器无论请求分配到哪一台服务器上同一用户的cookie是不变的。也就是说cookie对应的session也是唯一的。所以这里只要保证多台业务服务器访问同一个共享存储服务器NFSMySQLmemcacheredisfile就行了。
## 会话保持配置
测试网站选择我们之前搭建过的phpmyadmin所以我们先再web01和web02上部署phpmyadmin
- 配置php环境(web01和web02都要)
```bash
# 导入英格提供的php源这个比较快
vim /etc/yum.repos.d/eagle.repo
[eagle]
name=Eagle's lab
baseurl=http://file.eagleslab.com:8889/%E8%AF%BE%E7%A8%8B%E7%9B%B8%E5%85%B3%E8%BD%AF%E4%BB%B6/%E4%BA%91%E8%AE%A1%E7%AE%97%E8%AF%BE%E7%A8%8B/Centos7%E6%BA%90/
gpgcheck=0
enabled=1
# 安装php环境php所需的组件比较多我们可以一次性安装全面了
yum -y install php71w php71w-cli php71w-common php71w-devel php71w-embedded php71w-gd php71w-mcrypt php71w-mbstring php71w-pdo php71w-xml php71w-fpm php71w-mysqlnd php71w-opcache php71w-pecl-memcached php71w-pecl-redis php71w-pecl-mongodb
# 配置php-fpm用于与nginx的运行用户保持一致
sed -i '/^user/c user = nginx' /etc/php-fpm.d/www.conf
sed -i '/^group/c group = nginx' /etc/php-fpm.d/www.conf
# 启动php-fpm
systemctl start php-fpm
```
- 配置Nginx(web01和web02都要)
```bash
[root@web01 ~]# vim /etc/nginx/conf.d/php.conf
server {
listen 80;
server_name php.test.com;
root /code/phpMyAdmin-4.8.4-all-languages;
location / {
index index.php index.html;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
[root@web01 ~]# nginx -t
[root@web01 ~]# systemctl restart nginx
```
- 安装phpmyadmin (web01和web02都要)
```bash
[root@web01 ~]# cd /code
[root@web01 code]# wget https://files.phpmyadmin.net/phpMyAdmin/4.8.4/phpMyAdmin-4.8.4-all-languages.zip
[root@web01 code]# unzip phpMyAdmin-4.8.4-all-languages.zip
```
- 配置phpmyadmin连接远程的数据库这个数据库可以用代理服务器安装后做测试(web01和web02都要)
```bash
[root@web01 code]# cd phpMyAdmin-4.8.4-all-languages/
[root@web01 phpMyAdmin-4.8.4-all-languages]# cp config.sample.inc.php config.inc.php
[root@web01 phpMyAdmin-4.8.4-all-languages]# vim config.inc.php
$cfg['Servers'][$i]['host'] = '192.168.88.10';
```
- 配置权限(web01和web02都要)
```bash
[root@web01 ~]# chown -R nginx.nginx /var/lib/php/
```
- 在lb01上部署mariadb数据库提供phpmyadmin访问
```bash
# 安装mariadb数据库软件
yum install mariadb-server mariadb -y
# 启动数据库并且设置开机自启动
systemctl start mariadb
systemctl enable mariadb
# 设置mariadb的密码
mysqladmin password '123456'
# 验证数据库是否工作正常
mysql -uroot -p123456 -e "show databases;"
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
+--------------------+
# 创建访问用户
grant all privileges on *.* to root@'192.168.88.%' identified by '123456' with grant option;
flush privileges;
```
## 使用浏览器访问页面获取cookie信息
![image-20210711212248164](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210711212248164.png)
![image-20210711212943898](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210711212943898.png)
- 查看服务器上的session
```bash
[root@web01 ~]# ll /var/lib/php/session/
总用量 4
-rw------- 1 www www 2625 7月 11 20:08 sess_aa3bdfab93197f49bbfddb15cb41a595
```
- 将web01上配置好的phpmyadmin以及nginx的配置文件推送到web02主机上
```bash
[root@web01 code]# scp -rp phpMyAdmin-4.8.4-all-languages root@192.168.88.30:/code/
[root@web01 code]# scp /etc/nginx/conf.d/php.conf root@192.168.88.30:/etc/nginx/conf.d/
```
- 在web02上重启Nginx服务
```bash
[root@web02 ~]# systemctl restart nginx
```
- 在web02上配置权限
```bash
[root@web02 ~]# chown -R nginx. /var/lib/php/
```
- 接入负载均衡
```bash
[root@lb01 ~]# vim /etc/nginx/conf.d/proxy_php.com.conf
upstream php {
server 192.168.88.20:80;
server 192.168.88.30:80;
}
server {
listen 80;
server_name php.test.com;
location / {
proxy_pass http://php;
include proxy_params;
}
}
[root@lb01 ~]# nginx -t
[root@lb01 ~]# systemctl restart nginx
```
- 使用负载均衡的轮询功能之后会发现如果将session保存在本地文件的话永远都登录不上去。
![image-20210711222302235](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210711222302235.png)
## 使用redis解决会话登录问题
- 安装redis内存数据库
```bash
[root@lb01 ~]# yum -y install redis
```
- 配置redis监听在0.0.0.0网段上
```bash
[root@lb01 ~]# sed -i '/^bind/c bind 127.0.0.1 0.0.0.0' /etc/redis.conf
```
- 启动redis
```bash
[root@lb01 ~]# systemctl start redis
[root@lb01 ~]# systemctl enable redis
```
- php配置session连接redis
```bash
[root@web01 ~]# vim /etc/php.ini
session.save_handler = redis
session.save_path = "tcp://192.168.88.10:6379"
;session.save_path = "tcp://192.168.88.10:6379?auth=centos" #如果redis存在密码则使用该方式
session.auto_start = 1
[root@web01 ~]# vim /etc/php-fpm.d/www.conf
#注释php-fpm.d/www.conf里面的两条内容否则session内容会一直写入/var/lib/php/session目录中
;php_value[session.save_handler] = files
;php_value[session.save_path] = /var/lib/php/session
```
- 重启php-fpm
```bash
[root@web01 ~]# systemctl restart php-fpm
```
- 将web01上配置好的文件推送到web02
```bash
[root@web01 ~]# scp /etc/php.ini root@192.168.88.30:/etc/php.ini
[root@web01 ~]# scp /etc/php-fpm.d/www.conf root@192.168.88.30:/etc/php-fpm.d/www.conf
```
- 在web02上重启php-fpm
```bash
[root@web02 ~]# systemctl restart php-fpm
```
- redis查看数据
```bash
[root@lb01 ~]# redis-cli
127.0.0.1:6379> keys *
1) "PHPREDIS_SESSION:7e772fe37b1488069e3c54e419df7eda"
```
![image-20210711223529884](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210711223529884.png)
# Nginx四层负载均衡概述
四层负载均衡是基于传输层协议包来封装的TCP/IP那我们前面使用到的七层是指的应用层他的组装在四层的基础之上无论四层还是七层都是指的OSI网络模型。
四层+七层来做负载均衡四层可以保证七层的负载均衡的高可用性nginx就无法保证自己的服务高可用需要依赖LVS或者keepalive。
tcp协议的负载均衡有些请求是TCP协议的mysql、ssh或者说这些请求只需要使用四层进行端口的转发就可以了所以使用四层负载均衡。
## 四层+七层构建大规模集群架构使用场
![image-20210711225314039](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210711225314039.png)
## 四层负载均衡总结
- 四层负载均衡仅能转发TCP/IP协议、UDP协议、通常用来转发端口tcp/22、udp/53
- 四层负载均衡可以用来解决七层负载均衡端口限制问题七层负载均衡最大使用65535个端口号
- 四层负载均衡可以解决七层负载均衡高可用问题;(多台后端七层负载均衡能同事的使用)
- 四层的转发效率比七层的高得多但仅支持tcp/ip协议不支持http和https协议
- 通常大并发场景通常会选择使用在七层负载前面增加四层负载均衡。
## Nginx四层负载均衡场景实践
Nginx如何配置四层负载均衡
1、通过访问负载均衡的5555端口实际是后端的web01的22端口在提供服务
2、通过访问负载均衡的6666端口实际是后端的mysql的3306端口在提供服务。
- 创建存放四层负载均衡配置文件的目录
```bash
[root@lb01 ~]# yum install nginx-mod-stream
[root@lb01 ~]# vim /etc/nginx/nginx.conf
include /etc/nginx/conf.c/*.conf;
#include /etc/nginx/conf.d/*.conf;
[root@lb01 ~]# mkdir /etc/nginx/conf.c
```
- 配置四层负载均衡
```bash
[root@lb01 ~]# vim /etc/nginx/conf.c/lb_domain.conf
stream {
upstream lb {
server 192.168.88.20:80 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.88.30:80 weight=5 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
proxy_connect_timeout 3s;
proxy_timeout 3s;
proxy_pass lb;
}
}
```
- 四层负载均衡开启日志
- 四层负载均衡是没有access的日志的因为在nginx.conf的配置中access的日志格式是配置在http下的而四层负载均衡配置实在http以外的
- 如果需要日志则需要配置在stream下面
```bash
[root@lb01 ~]# cd /etc/nginx/conf.c/
[root@lb01 conf.c]# vim lb_domain.conf
stream {
log_format proxy '$remote_addr $remote_port - [$time_local] $status $protocol '
'"$upstream_addr" "$upstream_bytes_sent" "$upstream_connect_time"' ;
access_log /var/log/nginx/proxy.log proxy;
upstream lb {
server 192.168.88.20:80 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.88.30:80 weight=5 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
proxy_connect_timeout 3s;
proxy_timeout 3s;
proxy_pass lb;
}
}
[root@lb01 conf.c]# nginx -t
[root@lb01 conf.c]# systemctl restart nginx
```
- 浏览器访问域名或者IP查看日志
```bash
[root@lb01 conf.c]# tail -f /var/log/nginx/proxy.log
192.168.88.1 55295 - [11/Jul/2021:23:27:09 +0800] 200 TCP "192.168.88.20:80" "0" "0.001"
192.168.88.1 64066 - [11/Jul/2021:23:27:13 +0800] 200 TCP "192.168.88.30:80" "0" "0.000"
192.168.88.1 58528 - [11/Jul/2021:23:27:16 +0800] 200 TCP "192.168.88.20:80" "0" "0.001"
192.168.88.1 65099 - [11/Jul/2021:23:27:20 +0800] 200 TCP "192.168.88.30:80" "0" "0.000"
192.168.88.1 55919 - [11/Jul/2021:23:27:20 +0800] 200 TCP "192.168.88.20:80" "6460" "0.001"
```
## Nginx四层负载均衡端口转发
- 使用nginx四层负载均衡实现tcp的转发
```bash
请求负载均衡 5555 ---> 192.168.88.20:22;
请求负载均衡 6666 ---> 192.168.88.30:3306;
```
- 配置nginx四层负载均衡实现tcp的转发
```bash
[root@lb01 ~]# vim /etc/nginx/conf.c/lb_domain.conf
stream {
log_format proxy '$remote_addr $remote_port - [$time_local] $status $protocol '
'"$upstream_addr" "$upstream_bytes_sent" "$upstream_connect_time"' ;
access_log /var/log/nginx/proxy.log proxy;
#定义转发ssh的22端口
upstream ssh {
server 192.168.88.20:22;
}
#定义转发mysql的3306端口
upstream mysql {
server 192.168.88.30:3306;
}
server {
listen 5555;
proxy_connect_timeout 3s;
proxy_timeout 300s;
proxy_pass ssh;
}
server {
listen 6666;
proxy_connect_timeout 3s;
proxy_timeout 3s;
proxy_pass mysql;
}
}
[root@lb01 ~]# nginx -t
[root@lb01 ~]# setenforce 0
[root@lb01 ~]# systemctl restart nginx
```
- 测试
-
# Keepalived 高可用基本概述
一般是指2台机器启动着完全相同的业务系统当有一台机器down机了另外一台服务器就能快速的接管对于访问的用户是无感知的。
硬件通常使用 F5
软件通常使用 keepalived
keepalived软件是基于VRRP协议实现的VRRP虚拟路由冗余协议主要用于解决单点故障问题
## VRRP原理
- VRRP协议是一种容错的主备模式的协议保证当主机的下一跳路由出现故障时由另一台路由器来代替出现故障的路由器进行工作通过VRRP可以在网络发生故障时透明的进行设备切换而不影响主机之间的数据通信。
- 虚拟路由器VRRP组中所有的路由器拥有虚拟的IP+MAC(00-00-5e-00-01-VRID)地址
- 主路由器:虚拟路由器内部通常只有一台物理路由器对外提供服务,主路由器是由选举算法产生,对外提供各种网络功能。
- 备份路由器VRRP组中除主路由器之外的所有路由器不对外提供任何服务只接受主路由的通告当主路由器挂掉之后重新进行选举算法接替master路由器。
- 选举机制
- 优先级
- 抢占模式下一旦有优先级高的路由器加入即成为Master
- 非抢占模式下只要Master不挂掉优先级高的路由器只能等待
- 三种状态
- Initialize状态系统启动后进入initialize状态
- Master状态
- Backup状态
# Keepalived高可用安装配置
## 安装keepalived
```bash
[root@lb01 ~]# yum install -y keepalived
[root@lb02 ~]# yum install -y keepalived
```
## 配置master
- 找到配置文件
```bash
[root@lb02 ~]# rpm -qc keepalived
/etc/keepalived/keepalived.conf
/etc/sysconfig/keepalived
```
- 修改配置
```bash
[root@lb01 ~]# vim /etc/keepalived/keepalived.conf
global_defs { #全局配置
router_id lb01 #标识身份->名称
}
vrrp_instance VI_1 {
state MASTER #标识角色状态
interface ens33 #网卡绑定接口
virtual_router_id 50 #虚拟路由id
priority 150 #优先级
advert_int 1 #监测间隔时间
#use_vmac #使用虚拟mac地址因为路由问题可能导致原本的IP不可用
authentication { #认证
auth_type PASS #认证方式
auth_pass 1111 #认证密码
}
virtual_ipaddress {
192.168.88.100 #虚拟的VIP地址
}
}
```
## 配置backup
```bash
[root@lb02 ~]# vim /etc/keepalived/keepalived.conf
global_defs {
router_id lb02
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 50
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.88.100
}
}
```
## 对比master与Backup的keepalived配置区别
| Keepalived配置区别 | Master节点配置 | Backup节点配置 |
| :------------------- | :------------- | :------------- |
| route_id唯一标识 | router_id lb01 | router_id lb02 |
| state角色状态 | state MASTER | state BACKUP |
| priority(竞选优先级) | priority 150 | priority 100 |
## 启动Master和Backup节点的keepalived
```bash
[root@lb01 ~]# systemctl start keepalived
[root@lb01 ~]# systemctl enable keepalived
[root@lb02 ~]# systemctl start keepalived
[root@lb02 ~]# systemctl enable keepalived
```
# 高可用keepalived抢占式与非抢占式
- 由于节点1的优先级高于节点2所以VIP在节点1上面
```bash
[root@lb01 ~]# ip addr |grep 192.168.88.100
inet 192.168.88.100/32 scope global ens33
```
- 关闭节点1的keepalived
```bash
[root@lb01 ~]# systemctl stop keepalived
```
- 节点2联系不上节点1主动接管VIP
```bash
[root@lb02 ~]# ip addr |grep 192.168.88.100
inet 192.168.88.100/32 scope global ens33
```
- 此时重新启动Master上的keepalived会发现VIP被强行抢占
```bash
[root@lb01 ~]# systemctl start keepalived
[root@lb01 ~]# ip addr |grep 192.168.88.100
inet 192.168.88.100/32 scope global ens33
```
- 配置非抢占式
- 两个节点的state都必须配置为BACKUP
- 两个节点都必须加上配置 nopreempt
- 其中一个节点的优先级必须要高于另外一个节点的优先级。
- 两台服务器都角色状态启用nopreempt后必须修改角色状态统一为BACKUP唯一的区分就是优先级。
```bash
Master配置
vrrp_instance VI_1 {
state BACKUP
priority 150
nopreempt
}
Backup配置
vrrp_instance VI_1 {
state BACKUP
priority 100
nopreempt
}
```
- 通过windows的arp去验证是否会切换MAC地址
```bash
# 当前master在lb01上
[root@lb01 ~]# ip addr |grep 192.168.88.100
inet 192.168.88.100/32 scope global ens33
# windows查看mac地址
C:\Users\Aaron>arp -a |findstr 192.168.88.100
192.168.88.100 00-0c-29-bb-9a-bb 动态
```
- 关闭lb01的keepalive
```bash
[root@lb01 ~]# systemctl stop keepalived
```
- lb02接管vIP
```bash
[root@lb02 ~]# ip addr |grep 192.168.88.100
inet 192.168.88.100/32 scope global ens33
```
- 再次查看windows的mac地址
```bash
C:\Users\Aaron>arp -a |findstr 192.168.88.100
192.168.88.100 00-0c-29-bb-9a-bb 动态
```
# 高可用keepalived故障脑裂
由于某些原因导致两台keepalived高可用服务器在指定时间内无法检测到对方的心跳各自取得资源及服务的所有权而此时的两台高可用服务器又都还活着。
## 脑裂故障原因
- 服务器网线松动等网络故障
- 服务器硬件故障发生损坏现象而崩溃
- 主备都开启firewalld防火墙
## 脑裂故障现象
- 正常情况下backup以监听为主所以抓包会看到只有master在发送vrrp的数据包
![image-20210712035352209](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712035352209.png)
- 打开设备的防火墙查看抓包情况可以看到两台设备认为自己是master
![image-20210712035539886](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712035539886.png)
```bash
[root@lb01 ~]# ip a |grep 192.168.88.100
inet 192.168.88.100/24 scope global secondary ens33
[root@lb02 ~]# ip a |grep 192.168.88.100
inet 192.168.88.100/24 scope global secondary ens33
```
## 解决脑裂故障方案
- 如果发生脑裂则随机kill掉一台即可
- 在backup上编写检测脚本, 测试如果能ping通master并且backup节点还有vIP的话则认为产生了脑裂
```bash
[root@lb02 ~]# vim check_split_brain.sh
#!/bin/sh
vip=192.168.88.100
lb01_ip=192.168.88.10
while true;do
ping -c 2 $lb01_ip &>/dev/null
if [ $? -eq 0 -a `ip add|grep "$vip"|wc -l` -eq 1 ];then
echo "ha is split brain.warning."
else
echo "ha is ok"
fi
sleep 5
done
```
# 高可用keepalived与nginx
Nginx默认监听在所有的IP地址上VIP会飘到一台节点上相当于那台nginx多了VIP这么一个网卡所以可以访问到nginx所在机器
但是.....如果nginx宕机会导致用户请求失败但是keepalived没有挂掉不会进行切换所以需要编写一个脚本检测Nginx的存活状态如果不存活则kill掉keepalived
```bash
[root@lb01 ~]# vim check_web.sh
#!/bin/sh
nginxpid=$(ps -C nginx --no-header|wc -l)
#1.判断Nginx是否存活,如果不存活则尝试启动Nginx
if [ $nginxpid -eq 0 ];then
systemctl start nginx
sleep 3
#2.等待3秒后再次获取一次Nginx状态
nginxpid=$(ps -C nginx --no-header|wc -l)
#3.再次进行判断, 如Nginx还不存活则停止Keepalived,让地址进行漂移,并退出脚本
if [ $nginxpid -eq 0 ];then
systemctl stop keepalived
fi
fi
```
## 在lb01主机的keepalived配置文件中调用此脚本
```bash
[root@lb01 ~]# vim /etc/keepalived/keepalived.conf
global_defs {
router_id lb01
}
#每5秒执行一次脚本脚本执行内容不能超过5秒否则会中断再次重新执行脚本
vrrp_script check_web {
script "/root/check_web.sh"
interval 5
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 50
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.88.100
}
}
#调用并运行脚本
track_script {
check_web
}
```
# 集群和分布式
系统性能扩展方式:
- Scale UP垂直扩展向上扩展,增强,性能更强的计算机运行同样的服务,成本高。
- Scale Out水平扩展向外扩展,增加设备并行地运行多个服务调度分配问题Cluster
垂直扩展不再提及:
- 随着计算机性能的增长,其价格会成倍增长
- 单台计算机的性能是有上限的,不可能无限制地垂直扩展
- 多核CPU意味着即使是单台计算机也可以并行的。
## 集群 Cluster
Cluster集群,为解决某个特定问题将多台计算机组合起来形成的单个系统
Cluster分为三种类型
- LBLoad Balancing负载均衡。调度负载按照算法调度。
- HAHigh Availiablity高可用避免SPOFsingle Point Of failure单点失败
- MTBF:Mean Time Between Failure 平均无故障时间
- MTTR:Mean Time To Restoration repair平均恢复前时间
- A=MTBF/MTBF+MTTR (0,1)99%,99.5%,99.9%,99.99%,99.999%
- HPCHigh-performance computing高性能 www.top500.org
## 分布式系统
分布式存储: CephGlusterFSFastDFSMogileFS
分布式计算hadoopSpark
分布式常见应用
分布式应用-服务按照功能拆分,使用微服务
分布式静态资源–静态资源放在不同的存储集群上
分布式数据和存储使用key-value缓存系统
分布式计算对特殊业务使用分布式计算比如Hadoop集群
## 集群和分布式
集群:同一个业务系统,部署在多台服务器上。集群中,每一台服务器实现的功能没有差别,数据和代
码都是一样的
分布式:一个业务被拆成多个子业务,或者本身就是不同的业务,部署在多台服务器上。分布式中,每
一台服务器实现的功能是有差别的,数据和代码也是不一样的,分布式每台服务器功能加起来,才是完整的业务。
分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率。
对于大型网站,访问用户很多,实现一个群集,在前面部署一个负载均衡服务器,后面几台服务器完成
同一业务。如果有用户进行相应业务访问时,负载均衡器根据后端哪台服务器的负载情况,决定由给哪一台去完成响应,并且一台服务器垮了,其它的服务器可以顶上来。分布式的每一个节点,都完成不同的业务,如果一个节点垮了,那这个业务可能就会失败。
## 集群设计原则
可扩展性—集群的横向扩展能力。小型机横向扩展小,面临淘汰
可用性—无故障时间(SLA)
性能—访问响应时间
容量—单位时间内的最大并发吞吐量(C10K 并发问题) 。LVS内核级并发好。
## 集群设计实现
### 基础设施层面
提升硬件资源性能—从入口防火墙到后端web server均使用更高性能的硬件资源
多域名—DNS 轮询A记录解析。指向不同IP 访问入口增多
多入口—将A记录解析到多个公网IP入口
多机房—同城+异地容灾
CDN(Content Delivery Network)—基于GSLB(Global Server Load Balance)实现全局负载均衡DNS
就近分配地址,提高效率
### 业务层面
分层:安全层、负载层、静态层、动态层、(缓存层、存储层)持久化与非持久化
分割:基于功能分割大业务为小服务
分布式:对于特殊场景的业务,使用分布式计算
## LB Cluster 负载均衡集群
### 按实现方式划分
- 硬件(大公司)
- F5 Big-IP
- Citrix Netscaler
- A10
- 软件(小公司)
- lvsLinux Virtual Server阿里四层SLB (Server Load Balance)使用下四层功能:物理层 数据链路层
- nginx支持七层调度阿里七层SLB使用Tengine
- haproxy支持七层调度
- atsApache Traffic Serveryahoo捐助给apache
- perlbalPerl 编写
- pound
### 基于工作的协议层次划分
- 传输层通用DNAT和DPORT
- LVSLinux Virtual Server
- nginxstream
- haproxymode tcp
- 应用层(专用):针对特定协议,常称为 proxy server
- httpnginx, httpd, haproxy(mode http), …
- fastcginginx, httpd, …
- mysqlmysql-proxy, …
### 负载均衡的会话保持
- session sticky同一用户调度固定服务器
- Source IPLVS sh算法对某一特定服务而言
- Cookie
- session replication每台服务器拥有全部session
- session multicast cluster (内存消耗大)
- session server专门的session服务器
- MemcachedRedis 只放session共享也存在单点失败即也要做集群哨兵机制
## HA 高可用集群实现
keepalivedvrrp协议
Ais应用接口规范
heartbeat
cman+rgmanager(RHCS)
coresync_pacemaker
# Linux Virtual Server简介
## LVS介绍
- LVSLinux Virtual Server负载调度器内核集成章文嵩花名 正明), 阿里的四层SLB(Server Load Balance)是基于LVS+keepalived实现。
- LVS 官网http://www.linuxvirtualserver.org/
- LVS 相关术语
- VS: Virtual Server负责调度
- RS: Real Server负责真正提供服务
## LVS工作原理
VS根据请求报文的目标IP和目标协议及端口将其调度转发至某RS根据调度算法来挑选RS。LVS是内核级功能工作在INPUT链的位置将发往INPUT的流量进行“处理”
查看内核支持LVS
```bash
[root@localhost ~]# grep -i -C 10 ipvs /boot/config-3.10.0-957.el7.x86_64
```
## LVS集群体系架构
![image-20210712073700965](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712073700965.png)
## LVS 功能及组织架构
下面摘自于阿里云https://help.aliyun.com/document_detail/27543.html?spm=5176.21213303.J_6028563670.7.505a3edaQq9WLJ&scm=20140722.S_help%40%40%E6%96%87%E6%A1%A3%40%4027543.S_0.ID_27543-OR_s%2Bhelpproduct-V_1-P0_0
负载均衡的应用场景为高访问量的业务,提高应用程序的可用性和可靠性。
## 应用于高访问量的业务
如果您的应用访问量很高您可以通过配置监听规则将流量分发到不同的云服务器ECSElastic Compute Service实例上。此外您可以使用会话保持功能将同一客户端的请求转发到同一台后端ECS提高访问效率。
## 扩展应用程序
您可以根据业务发展的需要随时添加和移除ECS实例来扩展应用系统的服务能力适用于各种Web服务器和App服务器。
## 消除单点故障
您可以在CLB实例下添加多台ECS实例。当其中一部分ECS实例发生故障后CLB会自动屏蔽故障的ECS实例将请求分发给正常运行的ECS实例保证应用系统仍能正常工作。
## 同城容灾 (多可用区容灾)
为了提供更加稳定可靠的CLB服务CLB已在各地域部署了多可用区以实现同地域容灾。当主可用区出现机房故障或不可用时CLB仍然有能力在非常短的时间内大约30s中断切换到另外一个备可用区恢复服务能力当主可用区恢复时CLB同样会自动切换到主可用区提供服务。
使用CLB时您可以将CLB实例部署在支持多可用区的地域以实现同城容灾。此外建议您结合自身的应用需要综合考虑后端服务器的部署。如果您的每个可用区均至少添加了一台ECS实例那么此种部署模式下的CLB服务的效率是最高的。
如下图所示在CLB实例下绑定不同可用区的ECS实例。正常情况下用户访问流量将同时转发至主、备可用区内的ECS实例当可用区A发生故障时用户访问流量将只转发至备可用区内的ECS实例。此种部署既可以避免因为单个可用区的故障而导致对外服务的不可用也可以通过不同产品间可用区的选择来降低延迟。
![img](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/p947-17228280245661.png)
如果您采取如下图所示的部署方案即在CLB实例的主可用区下绑定多台ECS实例而在备可用区没有任何ECS实例。正常情况下用户访问流量将只转发至主可用区内的ECS实例比较于上图流量传输延时低当可用区A发生故障时会造成业务中断因为备可用区没有ECS实例来接收请求。这样的部署方式很明显是以牺牲高可用性为代价来获取低延时。
![img](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/p948-17228280245663.png)
## 跨地域容灾
您可以在不同地域下部署CLB实例并分别挂载相应地域内不同可用区的ECS。上层利用云解析做智能DNS将域名解析到不同地域的CLB实例服务地址下可实现全局CLB。当某个地域出现不可用时暂停对应解析即可实现所有用户访问不受影响。
![img](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/p949-17228280245665.png)
## LVS应用场景
### 音视频/游戏等大并发流量场景
音视频/游戏等行业经常面临突发访问,海量流量,后端服务压力大。如短视频/长视频/直播/在校教育/游戏等业务中,由于服务端与用户端之间需要实时大量的互动,因此,用户流量非常大,而音视频业务的波峰波谷效应明显,这对整个系统的性能、弹性、稳定性和可用性带来了巨大的挑战,需要使用负载均衡进行流量分发
![img](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/O1CN01LSQXdk23trU4Dhqsn_!!6000000007314-2-tps-1530-1140.png)
### 零售/金融/企业等弹性高可靠场景
新零售新金融业务变化快,新业务上线、老业务调整时常发生,大促大型活动常态化;因此,对即开即用网络,快速交付能力,弹性伸缩能力,安全可靠、灵活计费等需求显著,需要使用负载均衡搭建高可靠架构。
![img](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/O1CN01Lnlvbb1ixt2Ma0OEg_!!6000000004480-2-tps-1530-1140.png)
### 云原生网络应用场景
随着云原生逐步成熟,互联网/金融/企业等诸多行业新建业务时选择云原生部署或对现有业务进行云原生化改造。无论是使用阿里云ACK/ASK/SAE还是开源K8S云原生网络均可用到负载均衡服务来实现流量调度。
![img](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/O1CN01gbRLLp208HUej1u2p_!!6000000006804-2-tps-1530-1140.png)
### 跨地域网络应用场景
跨地域跨可用区的容灾方案。互联网/金融/企业等业务逐步遍及全球需要将不同地域用户智能调度访问到相应的业务系统为了降本增效线下IDC业务需要与云上业务互通需要使用负载均衡构建跨地域或混合云容灾架构。
<img src="%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/O1CN01Uom3sx1DY0ZKQmtk4_!!6000000000227-2-tps-1530-1140.png" alt="img" style="zoom:80%;" />
## LVS集群类型中的术语
VSVirtual ServerDirector Server(DS), Dispatcher(调度器)Load Balancer
RSReal Server(lvs), upstream server(nginx)上游服务器, backend server(haproxy)
CIPClient IP
VIPVirtual serve IP VS外网的IP
DIPDirector IP VS内网的IP
RIPReal server IP
访问流程CIP <> VIP == DIP <> RIP
# LVS 工作模式和相关命令
## LVS集群的工作模式
lvs-nat修改请求报文的目标IP,多目标IP的DNAT
lvs-dr操纵封装新的MAC地址。MAC头的修改
lvs-tun在原请求IP报文之外新加一个IP首部。
lvs-fullnat修改请求报文的源和目标IP
### LVS的NAT模式
<img src="%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712080133506.png" alt="image-20210712080133506" style="zoom:80%;" />
lvs-nat本质是多目标IP的DNAT通过将请求报文中的目标地址和目标端口修改为某挑出的RS的RIP和PORT实现转发
- RIP和DIP应在同一个IP网络且应使用私网地址RS的网关要指向DIP
- 请求报文和响应报文都必须经由Director转发Director易于成为系统瓶颈
- 支持端口映射可修改请求报文的目标PORT
- VS必须是Linux系统RS可以是任意OS系统
<img src="%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712080410013.png" alt="image-20210712080410013" style="zoom:80%;" />
<img src="%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712080545578.png" alt="image-20210712080545578" style="zoom:80%;" />
### LVS的DR模式
<img src="%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712080949453.png" alt="image-20210712080949453" style="zoom:80%;" />
LVS-DRDirect Routing直接路由LVS默认模式,应用最广泛,通过为请求报文重新封装一个MAC首部
进行转发源MAC是DIP所在的接口的MAC目标MAC是某挑选出的RS的RIP所在接口的MAC地址
IP/PORT以及目标IP/PORT均保持不变
![image-20210712081307431](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712081307431.png)
DR模式的特点
- Director和各RS都配置有VIP
- 确保前端路由器将目标IP为VIP的请求报文发往Director三种方法
在前端网关做静态绑定VIP和Director的MAC地址
此方法一般不使用,过于固定
在RS上使用arptables工具
```bash
arptables -A IN -d $VIP -j DROP
arptables -A OUT -s $VIP -j mangle --mangle-ip-s $RIP
```
在RS上修改内核参数以限制arp通告及应答级别
```bash
/proc/sys/net/ipv4/conf/all/arp_ignore
/proc/sys/net/ipv4/conf/all/arp_announce
```
- RS的RIP可以使用私网地址也可以是公网地址RIP与DIP在同一IP网络RIP的网关不能指向DIP以确保响应报文不会经由Director
- RS和Director要在同一个物理网络
- 请求报文要经由Director但响应报文不经由Director而由RS直接发往Client
- 不支持端口映射(端口不能修改)
- RS可使用大多数OS系统
### LVS的TUN模式
![image-20210712082509947](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712082509947.png)
转发方式不修改请求报文的IP首部源IP为CIP目标IP为VIP而在原IP报文之外再封装一个IP首部源IP是DIP目标IP是RIP将报文发往挑选出的目标RSRS直接响应给客户端源IP是VIP目标IP是CIP
![image-20210712084005949](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712084005949.png)
TUN模式特点
- DIP, VIP, RIP可以是公网地址
- RS的网关一般不能指向DIP
- 请求报文要经由Director但响应不经由Director
- 不支持端口映射
- RS的OS须支持隧道功能
### LVS的FULLNAT模式
![image-20210712084209645](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712084209645.png)
- 通过同时修改请求报文的源IP地址和目标IP地址进行转发
- CIP --> DIP
- VIP --> RIP
- fullnat模式特点
- VIP是公网地址RIP和DIP是私网地址且通常不在同一IP网络因此RIP的网关一般不会指向DIP
- RS收到的请求报文源地址是DIP因此只需响应给DIP但Director还要将其发往Client
- 请求和响应报文都经由Director
- 支持端口映射
- 注意此类型kernel默认不支持
### LVS工作模式总结和比较
| | NAT | TUN | DR |
| ------------------- | ------------- | ---------- | -------------- |
| Real server | any | Tunneling | Non-arp device |
| Real server network | private | LAN/WAN | LAN |
| Real Server number | low(10-20) | High(100) | High(100) |
| Real server gateway | load balancer | own router | own router |
| 优点 | 端口转换 | WAN | 性能最好 |
| 缺点 | 性能瓶颈 | 支持隧道 | 不支持跨网段 |
- lvs-nat与lvs-fullnat
- 请求和响应报文都经由Director
- lvs-natRIP的网关要指向DIP
- lvs-fullnatRIP和DIP未必在同一IP网络但要能通信
- lvs-dr与lvs-tun
- 请求报文要经由Director但响应报文由RS直接发往Client
- lvs-dr通过封装新的MAC首部实现通过MAC网络转发
- lvs-tun通过在原IP报文外封装新IP头实现转发支持远距离通信
## LVS调试算法
ipvs scheduler根据其调度时是否考虑各RS当前的负载状态
分为两种:静态方法和动态方法
### 静态方法
- 仅根据算法本身进行调度
- RRroundrobin轮询 不考虑机器的性能好坏,轮询调度
- WRRWeighted RR加权轮询 权重越高,优先级高,调度更多资源给它 优先级高
- SHSource Hashing实现session sticky源IP地址hash将来自于同一个IP地址的请求始终发往第一次挑中的RS从而实现会话绑定
- DHDestination Hashing目标地址哈希第一次轮询调度至RS后续将发往同一个目标地址的请求始终转发至第一次挑中的RS典型使用场景是正向代理缓存场景中的负载均衡宽带运营商
### 动态方法
主要根据每RS当前的负载状态及调度算法进行调度Overhead=value 较小的RS将被调度
- LCleast connections 适用于长连接应用(最少连接
- `Overhead=activeconns *256+inactiveconns`
- WLCWeighted LC默认调度方法加权最少连接
- `Overhead=(activeconns * 256+inactiveconns)/weight`
- SEDShortest Expection Delay,初始连接高权重优先(最少期望延迟)
- `Overhead=(activeconns+1)*256/weight`
- NQNever Queue第一轮均匀分配后续SED从不排队调度方法
- LBLCLocality-Based LC动态的DH算法使用场景根据负载状态实现正向代理
- LBLCRLBLC with Replication带复制功能的LBLC解决LBLC负载不均衡问题从负载重的复制到负载轻的RS
### 内核版本 4.15版本后新增调度算法
- FOWeighted Fail Over调度算法,在此FO算法中遍历虚拟服务所关联的真实服务器链表找到还未过载未设置IP_VS_DEST_F_OVERLOAD标志的且权重最高的真实服务器进行调度。方便调式上下线。
- OVFOverflow-connection调度算法基于真实服务器的活动连接数量和权重值实现。将新连接调度到权重值最高的真实服务器直到其活动连接数量超过权重值位置之后调度到下一个权重值最高的真实服务器,在此OVF算法中遍历虚拟服务相关联的真实服务器链表找到权重值最高的可用真实服务器。一个可用的真实服务器需要同时满足以下条件
- 未过载未设置IP_VS_DEST_F_OVERLOAD标志
- 真实服务器当前的活动连接数量小于其权重值
- 其权重值不为零
## LVS 相关软件
### 程序包
- Unit File: ipvsadm.service
- 主程序:/usr/sbin/ipvsadm
- 规则保存工具:/usr/sbin/ipvsadm-save
- 规则重载工具:/usr/sbin/ipvsadm-restore
- 配置文件:/etc/sysconfig/ipvsadm-config
```bash
[root@localhost ~]# yum -y install ipvsadm
[root@localhost ~]# cat /usr/lib/systemd/system/ipvsadm.service
[Unit]
Description=Initialise the Linux Virtual Server
After=syslog.target network.target
[Service]
Type=oneshot
ExecStart=/bin/bash -c "exec /sbin/ipvsadm-restore < /etc/sysconfig/ipvsadm"
ExecStop=/bin/bash -c "exec /sbin/ipvsadm-save -n > /etc/sysconfig/ipvsadm"
ExecStop=/sbin/ipvsadm -C
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
```
### ipvsadm 命令
- ipvsadm核心功能
- 集群服务管理:增、删、改
- 集群服务的RS管理增、删、改
- 查看
```bash
#管理集群服务
ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] [-M netmask]
[--pe persistence_engine] [-b sched-flags]
ipvsadm -A #创建集群
ipvsadm -E #修改集群
ipvsadm -D -t|u|f service-address #删除
ipvsadm C #清空
ipvsadm R #重载
ipvsadm -S [-n] #保存
ipvsadm -L #查看
#管理集群中的RS
ipvsadm -a|e -t|u|f service-address -r server-address [options]
ipvsadm -d -t|u|f service-address -r server-address
ipvsadm -L|l [options]
ipvsadm -Z [-t|u|f service-address] #清空计数器
#保存规则
S
# ipvsadm S > /path/to/somefile
载入此前的规则:
R
# ipvsadm R < /path/form/somefile
```
- 说明
```bash
service-address
-t|u|f
-t: TCP协议的端口VIP:TCP_PORT 如:-t 192.168.88.20:80
-u: UDP协议的端口VIP:UDP_PORT 如:-u 192.168.88.20:80
-ffirewall MARK标记一个数字
[-s scheduler]指定集群的调度算法默认为wlc
server-address
rip[:port] 如省略port不作端口映射
选项:
lvs类型
-g: gateway, dr类型默认
-i: ipip, tun类型
-m: masquerade, nat类型
-w weight权重
```
- 举例
```bash
ipvsadm -A -t 192.168.88.100:80 -s wrr # 创建集群
ipvsadm -D -t 192.168.88.100:80 # 删除集群
ipvsadm -a -t 192.168.88.100:80 -r 192.168.88.20:8080 -m -w 3
# 往集群中添加RS并且设置nat配置权重为3
ipvsadm -d -t 192.168.88.100:80 -r 192.168.88.20:8080
# 删除RS
```
- 查看
```bash
ipvsadm -L|l [options]
numeric, -n以数字形式输出地址和端口号
exact扩展信息精确值
connection-c当前IPVS连接输出
stats统计信息
rate :输出速率信息
```
# LVS实战案例
## LVS-NAT模式案例
<img src="%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712100318910.png" alt="image-20210712100318910" style="zoom:80%;" />
实验环境说明:
LVS两张网卡一张nat网卡做为vIP与客户端建立连接一张仅主机网卡用于对内部服务器的连接
RS1和RS2两个RS的网卡都使用仅主机的网卡并且网关指向LVS的仅主机网卡
- 开启LVS流量转发功能并且配置NAT
```bash
[root@lvs ~]# vim /etc/sysctl.conf
net.ipv4.ip_forward = 1
[root@lvs ~]# sysctl -p
net.ipv4.ip_forward = 1
[root@lvs ~]# iptables -t nat -A POSTROUTING -s 192.168.153.0/24 -j MASQUERADE
[root@lvs ~]# iptables -t nat -L -n
```
- 先搭建web服务器
```bash
[root@rs1 ~]# yum -y install httpd
[root@rs1 ~]# systemctl start httpd
[root@rs1 ~]# systemctl enable httpd
[root@rs1 ~]# echo "RS1 ..." > /var/www/html/index.html
[root@rs1 ~]# firewall-cmd --add-service=http
[root@rs1 ~]# firewall-cmd --add-service=http --permanent
[root@rs2 ~]# yum -y install httpd
[root@rs2 ~]# systemctl start httpd
[root@rs2 ~]# systemctl enable httpd
[root@rs2 ~]# echo "RS2 ..." > /var/www/html/index.html
[root@rs2 ~]# firewall-cmd --add-service=http
[root@rs2 ~]# firewall-cmd --add-service=http --permanent
```
- 配置LVS
```bash
[root@lvs ~]# ipvsadm -A -t 192.168.88.10:80 -s rr
[root@lvs ~]# ipvsadm -a -t 192.168.88.10:80 -r 192.168.153.20:80 -m
[root@lvs ~]# ipvsadm -a -t 192.168.88.10:80 -r 192.168.153.30:80 -m
[root@lvs ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.88.100:80 rr
-> 192.168.153.20:80 Masq 1 0 0
-> 192.168.153.30:80 Masq 1 0 0
```
- 验证结果
![image-20210712102932211](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712102932211.png)
![image-20210712102939649](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712102939649.png)
- 抓包可以观察到客户端都是与LVS进行通信LVS对流量进行NAT
![image-20210712103059127](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712103059127.png)
## LVS-DR模式案例
![image-20210712111708684](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712111708684.png)
- 做这个实验之前需要清空LVS之前配置的NAT规则
```bash
# 由于我们后面做实验要用到ifconfig命令该命令需要安装net-tools工具。所以我们趁现在有网可以先安装一下三台机器都需要安装
[root@lvs ~]# yum install -y net-tools
[root@lvs ~]# iptables -t nat -F
```
- 修改RS1和RS2的网关地址
```bash
[root@rs1 ~]# ip route
default via 192.168.153.2 dev ens33 proto static metric 100
[root@rs2 ~]# ip route
default via 192.168.153.2 dev ens33 proto static metric 100
```
- 在RS上修改内核参数以限制arp通告及应答级别
```bash
[root@rs1 ~]# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
[root@rs1 ~]# echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
[root@rs2 ~]# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
[root@rs2 ~]# echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
```
- 给RS1和RS2设置VIP 192.168.153.100
```bash
[root@rs1 ~]# vim rs.sh
#!/bin/bash
vip=192.168.153.100/32
ifconfig lo:1 $vip
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
[root@rs1 ~]# . rs.sh
# 在RS2上也需要执行
```
- 配置LVS服务器
```bash
[root@lvs ~]# ifconfig lo:1 192.168.153.100/32
[root@lvs ~]# ipvsadm -A -t 192.168.153.100:80 -s rr
[root@lvs ~]# ipvsadm -a -t 192.168.153.100:80 -r 192.168.153.20
[root@lvs ~]# ipvsadm -a -t 192.168.153.100:80 -r 192.168.153.30
[root@lvs ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.153.100:80 rr
-> 192.168.153.20:80 Route 1 0 0
-> 192.168.153.30:80 Route 1 0 0
```
- 验证当多次访问192.168.153.100的时候可以得到不同的主机回复
![image-20210712111555645](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712111555645.png)
![image-20210712111611821](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210712111611821.png)
# HAProxy介绍
HAProxy是法国开发者威利塔罗(Willy Tarreau)在2000年使用C语言开发的一个开源软件是一款具备高并发(一万以上)、高性能的TCP和HTTP负载均衡器支持基于cookie的持久性自动故障切换支持正则表达式及web状态统计目前最新TLS版本为2.0
历史版本:
- 历史版本更新功能1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2-dev
- 1.8多线程HTTP/2缓存……
- 1.7:服务器动态配置,多类型证书……
- 1.6DNS解析支持HTTP连接多路复用……
- 1.5开始支持SSLIPV6会话保持……
从2013年HAProxy 分为社区版和企业版企业版将提供更多的特性和功能以及全天24小时的技术支持等服务。
## 企业版
企业版网站https://www.haproxy.com/
![image-20210713094311973](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210713094311973.png)
## 社区版
社区版网站http://www.haproxy.org/
githubhttps://github.com/haproxy
![image-20210713094402494](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210713094402494.png)
## 版本对比
| 功能 | 社区版 | 企业版 |
| ----------------------------------------- | ------ | ------ |
| 高级HTTP / TCP负载平衡和持久性 | 支持 | 支持 |
| 高级健康检查 | 支持 | 支持 |
| 应用程序加速 | 支持 | 支持 |
| 高级安全特性 | 支持 | 支持 |
| 高级管理 | 支持 | 支持 |
| HAProxy Dev Branch新功能 | | 支持 |
| 24*7 支持服务 | | 支持 |
| 实时仪表盘 | | 支持 |
| VRRP和Route Health Injection HA工具 | | 支持 |
| ACL映射和TLS票证密钥同步 | | 支持 |
| 基于应用程序的高级DDoS和Bot保护(自动保护) | | 支持 |
| Bot(机器人)监测 | | 支持 |
| Web应用防火墙 | | 支持 |
| HTTP协议验证 | | 支持 |
| 实时集群追踪 | | 支持 |
## HAProxy功能
<img src="%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210713100058631.png" alt="image-20210713100058631" style="zoom:100%;" />
## 支持功能
- TCP 和 HTTP反向代理
- SSL/TSL服务器
- 可以针对HTTP请求添加cookie进行路由后端服务器
- 可平衡负载至后端服务器,并支持持久连接
- 支持所有主服务器故障切换至备用服务器
- 支持专用端口实现监控服务
- 支持停止接受新连接请求,而不影响现有连接
- 可以在双向添加修改或删除HTTP报文首部
- 响应报文压缩
- 支持基于pattern实现连接请求的访问控制
- 通过特定的URI为授权用户提供详细的状态信息
- 支持http反向代理
- 支持动态程序的反向代理
- 支持基于数据库的反向代理
## 不具备的功能
- 正向代理--squidnginx
- 缓存代理--varnish
- web服务--nginx、tengine、apache、php、tomcat
- UDP--目前不支持UDP协议
- 单机性能--相比LVS性能较差
# 编译安装HAProxy
- 编译安装HAProxy 2.0 LTS版本更多源码包下载地址http://www.haproxy.org/download/
- centos7自带的源中yum安装的版本是较老的1.5的版本
## 解决lua环境
HAProxy 支持基于lua实现功能扩展lua是一种小巧的脚本语言于1993年由巴西里约热内卢天主教大学Pontifical Catholic University of Rio de Janeiro里的一个研究小组开发其设计目的是为了嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能。
Lua 官网www.lua.org
- Lua 应用场景
- 游戏开发
- 独立应用脚本
- Web 应用脚本
- 扩展和数据库插件如MySQL Proxy
- 安全系统,如入侵检测系统
### Centos 基础环境
参考链接http://www.lua.org/start.html
![image-20210713101859303](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210713101859303.png)
由于CentOS7 之前版本自带的lua版本比较低并不符合HAProxy要求的lua最低版本(5.3)的要求因此需要编译安装较新版本的lua环境然后才能编译安装HAProxy过程如下
- 当前系统版本
```bash
[root@localhost ~]# lua -v
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
```
- 安装基础命令及编译依赖环境
```bash
[root@localhost ~]# yum -y install gcc readline-devel
[root@localhost ~]# wget http://www.lua.org/ftp/lua-5.3.5.tar.gz
[root@localhost ~]# tar xvf lua-5.3.5.tar.gz -C /usr/local/src
[root@localhost ~]# cd /usr/local/src/lua-5.3.5
[root@localhost lua-5.3.5]# make linux test
```
- 查看编译安装的版本
```bash
[root@localhost lua-5.3.5]# src/lua -v
Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio
```
## 编译安装HAProxy
- HAProxy 2.0以上版本编译参数
```bash
[root@localhost ~]# yum -y install gcc openssl-devel pcre-devel systemd-devel
[root@localhost ~]# wget http://www.haproxy.org/download/2.1/src/haproxy-2.1.3.tar.gz
[root@localhost ~]# tar xvf haproxy-2.1.3.tar.gz -C /usr/local/src
[root@localhost ~]# cd /usr/local/src/haproxy-2.1.3/
[root@localhost haproxy-2.1.3]# cat README
```
- 参考INSTALL文件进行编译安装
```bash
[root@localhost haproxy-2.1.3]# make -j 2 ARCH=x86_64 TARGET=linux-glibc \
USE_PCRE=1 \
USE_OPENSSL=1 \
USE_ZLIB=1 \
USE_SYSTEMD=1 \
USE_LUA=1 \
LUA_INC=/usr/local/src/lua-5.3.5/src/ \
LUA_LIB=/usr/local/src/lua-5.3.5/src/
[root@localhost haproxy-2.1.3]# make install PREFIX=/apps/haproxy
[root@localhost haproxy-2.1.3]# ln -s /apps/haproxy/sbin/haproxy /usr/sbin/
```
- 查看生成的文件
```bash
[root@localhost ~]# tree -C /apps/haproxy/
/apps/haproxy/
├── doc
│   └── haproxy
│   ├── 51Degrees-device-detection.txt
│   ├── architecture.txt
│   ├── close-options.txt
│   ├── configuration.txt
│   ├── cookie-options.txt
│   ├── DeviceAtlas-device-detection.txt
│   ├── intro.txt
│   ├── linux-syn-cookies.txt
│   ├── lua.txt
│   ├── management.txt
│   ├── netscaler-client-ip-insertion-protocol.txt
│   ├── network-namespaces.txt
│   ├── peers.txt
│   ├── peers-v2.0.txt
│   ├── proxy-protocol.txt
│   ├── regression-testing.txt
│   ├── seamless_reload.txt
│   ├── SOCKS4.protocol.txt
│   ├── SPOE.txt
│   └── WURFL-device-detection.txt
├── sbin
│   └── haproxy
└── share
└── man
└── man1
└── haproxy.1
6 directories, 22 files
```
## 验证HAProxy版本
```bash
[root@localhost ~]# which haproxy
/usr/sbin/haproxy
[root@localhost ~]# haproxy -v
HA-Proxy version 2.1.3 2020/02/12 - https://haproxy.org/
Status: stable branch - will stop receiving fixes around Q1 2021.
Known bugs: http://www.haproxy.org/bugs/bugs-2.1.3.html
[root@localhost ~]# haproxy -vv
HA-Proxy version 2.1.3 2020/02/12 - https://haproxy.org/
Status: stable branch - will stop receiving fixes around Q1 2021.
Known bugs: http://www.haproxy.org/bugs/bugs-2.1.3.html
Build options :
TARGET = linux-glibc
CPU = generic
CC = gcc
CFLAGS = -m64 -march=x86-64 -O2 -g -fno-strict-aliasing -Wdeclaration-after-statement -fwrapv -Wno-unused-label -Wno-sign-compare -Wno-unused-parameter -Wno-old-style-declaration -Wno-ignored-qualifiers -Wno-clobbered -Wno-missing-field-initializers -Wtype-limits
OPTIONS = USE_PCRE=1 USE_OPENSSL=1 USE_LUA=1 USE_ZLIB=1 USE_SYSTEMD=1
Feature list : +EPOLL -KQUEUE -MY_EPOLL -MY_SPLICE +NETFILTER +PCRE -PCRE_JIT -PCRE2 -PCRE2_JIT +POLL -PRIVATE_CACHE +THREAD -PTHREAD_PSHARED -REGPARM -STATIC_PCRE -STATIC_PCRE2 +TPROXY +LINUX_TPROXY +LINUX_SPLICE +LIBCRYPT +CRYPT_H -VSYSCALL +GETADDRINFO +OPENSSL +LUA +FUTEX +ACCEPT4 -MY_ACCEPT4 +ZLIB -SLZ +CPU_AFFINITY +TFO +NS +DL +RT -DEVICEATLAS -51DEGREES -WURFL +SYSTEMD -OBSOLETE_LINKER +PRCTL +THREAD_DUMP -EVPORTS
Default settings :
bufsize = 16384, maxrewrite = 1024, maxpollevents = 200
Built with multi-threading support (MAX_THREADS=64, default=4).
Built with OpenSSL version : OpenSSL 1.0.2k-fips 26 Jan 2017
Running on OpenSSL version : OpenSSL 1.0.2k-fips 26 Jan 2017
OpenSSL library supports TLS extensions : yes
OpenSSL library supports SNI : yes
OpenSSL library supports : SSLv3 TLSv1.0 TLSv1.1 TLSv1.2
Built with Lua version : Lua 5.3.5
Built with network namespace support.
Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT IP_FREEBIND
Built with PCRE version : 8.32 2012-11-30
Running on PCRE version : 8.32 2012-11-30
PCRE library supports JIT : no (USE_PCRE_JIT not set)
Encrypted password support via crypt(3): yes
Built with zlib version : 1.2.7
Running on zlib version : 1.2.7
Compression algorithms supported : identity("identity"), deflate("deflate"), raw-deflate("deflate"), gzip("gzip")
Available polling systems :
epoll : pref=300, test result OK
poll : pref=200, test result OK
select : pref=150, test result OK
Total: 3 (3 usable), will use epoll.
Available multiplexer protocols :
(protocols marked as <default> cannot be specified using 'proto' keyword)
h2 : mode=HTTP side=FE|BE mux=H2
fcgi : mode=HTTP side=BE mux=FCGI
<default> : mode=HTTP side=FE|BE mux=H1
<default> : mode=TCP side=FE|BE mux=PASS
Available services : none
Available filters :
[SPOE] spoe
[CACHE] cache
[FCGI] fcgi-app
[TRACE] trace
[COMP] compression
```
## HAProxy启动脚本
```bash
[root@localhost ~]# vim /usr/lib/systemd/system/haproxy.service
[Unit]
Description=HAProxy Load Balancer
After=syslog.target network.target
[Service]
ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -q
ExecStart=/usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/haproxy.pid
ExecReload=/bin/kill -USR2 $MAINPID
[Install]
WantedBy=multi-user.target
```
- 默认缺少配置文件,无法启动
```bash
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl start haproxy
Job for haproxy.service failed because the control process exited with error code. See "systemctl status haproxy.service" and "journalctl -xe" for details.
[root@localhost ~]# tail /var/log/messages
Jul 13 10:39:33 localhost systemd: Starting HAProxy Load Balancer...
Jul 13 10:39:33 localhost haproxy: [ALERT] 193/103933 (8156) : Cannot open configuration file/directory /etc/haproxy/haproxy.cfg : No such file or directory
Jul 13 10:39:33 localhost systemd: haproxy.service: control process exited, code=exited status=1
Jul 13 10:39:33 localhost systemd: Failed to start HAProxy Load Balancer.
Jul 13 10:39:33 localhost systemd: Unit haproxy.service entered failed state.
Jul 13 10:39:33 localhost systemd: haproxy.service failed.
```
## 配置文件
- 查看配置文件范例
```bash
[root@localhost ~]# tree -C /usr/local/src/haproxy-2.1.3/examples/
/usr/local/src/haproxy-2.1.3/examples/
├── acl-content-sw.cfg
├── content-sw-sample.cfg
├── errorfiles
│   ├── 400.http
│   ├── 403.http
│   ├── 408.http
│   ├── 500.http
│   ├── 502.http
│   ├── 503.http
│   ├── 504.http
│   └── README
├── haproxy.init
├── option-http_proxy.cfg
├── socks4.cfg
├── transparent_proxy.cfg
└── wurfl-example.cfg
1 directory, 15 files
```
- 创建自定义的配置文件
```bash
[root@localhost ~]# mkdir /etc/haproxy
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
global
maxconn 100000
chroot /apps/haproxy
stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin
#uid 99
#gid 99
user haproxy
group haproxy
daemon
#nbproc 4
#cpu-map 1 0
#cpu-map 2 1
#cpu-map 3 2
#cpu-map 4 3
pidfile /var/lib/haproxy/haproxy.pid
log 127.0.0.1 local2 info
defaults
option http-keep-alive
option forwardfor
maxconn 100000
mode http
timeout connect 300000ms
timeout client 300000ms
timeout server 300000ms
listen stats
mode http
bind 0.0.0.0:9999
stats enable
log global
stats uri /haproxy-status
stats auth admin:123456
listen web_port
bind 192.168.88.10:80
mode http
log global
server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5
```
## 启动haproxy
```bash
[root@localhost ~]# mkdir /var/lib/haproxy
[root@localhost ~]# useradd -r -s /sbin/nologin -d /var/lib/haproxy haproxy
[root@localhost ~]# systemctl enable --now haproxy
```
## 验证haproxy状态
aproxy.cfg文件中定义了chroot、pidfile、user、group等参数如果系统没有相应的资源会导致haproxy无法启动具体参考日志文件 /var/log/messages
```bash
[root@localhost ~]# systemctl status haproxy
● haproxy.service - HAProxy Load Balancer
Loaded: loaded (/usr/lib/systemd/system/haproxy.service; enabled; vendor preset: disabled)
Active: active (running) since 二 2021-07-13 10:45:39 CST; 38s ago
Process: 8360 ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -q (code=exited, status=0/SUCCESS)
Main PID: 8363 (haproxy)
CGroup: /system.slice/haproxy.service
├─8363 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/hap...
└─8365 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/hap...
7月 13 10:45:39 localhost.localdomain systemd[1]: Starting HAProxy Load Balancer...
7月 13 10:45:39 localhost.localdomain systemd[1]: Started HAProxy Load Balancer.
7月 13 10:45:39 localhost.localdomain haproxy[8363]: [NOTICE] 193/104539 (8363) : New wor...d
7月 13 10:45:39 localhost.localdomain haproxy[8363]: [WARNING] 193/104539 (8365) : Server....
7月 13 10:45:39 localhost.localdomain haproxy[8363]: [ALERT] 193/104539 (8365) : proxy 'w...!
Hint: Some lines were ellipsized, use -l to show in full.
[root@localhost ~]# pstree -p |grep haproxy
|-haproxy(8363)---haproxy(8365)-+-{haproxy}(8366)
| |-{haproxy}(8367)
| `-{haproxy}(8368)
```
## 查看haproxy的状态页面
- 浏览器访问
![image-20210713104814134](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210713104814134.png)
![image-20210713104830656](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210713104830656.png)
# HAProxy基础配置详解
官方文档http://cbonte.github.io/haproxy-dconv/2.1/configuration.html
HAProxy 的配置文件haproxy.cfg由两大部分组成分别是global和proxies部分
- global全局配置段
- 进程及安全配置相关的参数
- 性能调整相关参数
- Debug参数
- proxies代理配置段
- defaults为frontend, backend, listen提供默认配置
- frontend前端相当于nginx中的server {}
- backend后端相当于nginx中的upstream {}
- listen同时拥有前端和后端配置
## global配置
#### global 配置参数说明
官方文档http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#3
```bash
chroot #锁定运行目录
deamon #以守护进程运行
stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin process 1 #socket文件
user, group, uid, gid #运行haproxy的用户身份
nbproc n #开启的haproxy work 进程数,默认进程数是一个
#nbthread 1 #指定每个haproxy进程开启的线程数默认为每个进程一个线程,和nbproc互斥版本有关
#如果同时启用nbproc和nbthread 会出现以下日志的错误,无法启动服务
Apr 7 14:46:23 haproxy haproxy: [ALERT] 097/144623 (1454) : config : cannot enable multiple processes if multiple threads are configured. Please use either nbproc or nbthread but not both.
cpu-map 1 0 #绑定haproxy 进程至指定CPU将第一个work进程绑定至0号CPU
maxconn n #每个haproxy进程的最大并发连接数
maxsslconn n #每个haproxy进程ssl最大连接数,用于haproxy配置了证书的场景下
maxconnrate n #每个进程每秒创建的最大连接数量
spread-checks n #后端server状态check随机提前或延迟百分比时间建议2-5(20%-50%)之间默认值0
pidfile #指定pid文件路径
log 127.0.0.1 local2 info #定义全局的syslog服务器日志服务器需要开启UDP协议最多可以定义两个
```
#### 多进程和线程
范例多进程和socket文件
```bash
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
global
maxconn 100000
chroot /apps/haproxy
stats socket /var/lib/haproxy/haproxy.sock1 mode 600 level admin process 1
stats socket /var/lib/haproxy/haproxy.sock2 mode 600 level admin process 2
user haproxy
group haproxy
daemon
nbproc 2
[root@localhost ~]# systemctl restart haproxy
[root@localhost ~]# pstree -p |grep haproxy
|-haproxy(8395)-+-haproxy(8399)
| `-haproxy(8400)
[root@localhost ~]# ll /var/lib/haproxy/
总用量 4
-rw-r--r--. 1 root root 5 7月 13 10:56 haproxy.pid
srw-------. 1 root root 0 7月 13 10:56 haproxy.sock1
srw-------. 1 root root 0 7月 13 10:56 haproxy.sock2
```
## 日志配置
### HAProxy配置
```bash
# 在global配置项定义
log 127.0.0.1 local{1-7} info #基于syslog记录日志到指定设备级别有(err、warning、info、debug)
listen web_port
bind 127.0.0.1:80
mode http
log global #开启当前web_port的日志功能默认不记录日志
server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5
# systemctl restart haproxy
```
### Rsyslog配置
```bash
[root@localhost ~]# vim /etc/rsyslog.conf
ModLoad imudpUDPServerRun 514
......
local3.* /var/log/haproxy.log
......
# systemctl restart rsyslog
```
### 验证HAProxy日志
- 重启syslog服务并访问app页面然后验证是否生成日志
```bash
[root@localhost ~]# tail -f /var/log/haproxy.log
Aug 14 20:21:06 localhost haproxy[18253]: Connect from 192.168.0.1:3050 to 10.0.0.7:80 (web_host/HTTP)
Aug 14 20:21:06 localhost haproxy[18253]: Connect from 192.168.0.1:3051 to 10.0.0.7:80 (web_host/HTTP)
Aug 14 20:21:06 localhost haproxy[18253]: Connect from 192.168.0.1:3050 to 10.0.0.7:80 (web_host/HTTP)
```
# Proxies配置
官方文档http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#4
- `defaults [<name>]`
- 默认配置项针对以下的frontend、backend和listen生效可以多个name也可以没有name
- `frontend <name>`
- 前端servername类似于Nginx的一个虚拟主机 server和LVS服务集群。
- `backend <name>`
- 后端服务器组等于nginx的upstream和LVS中的RS服务器
- `listen <name>`
- 将frontend和backend合并在一起配置相对于frontend和backend配置更简洁生产常用
注意name字段只能使用大小写字母数字-(dash)_(underscore). (dot)和 :'(colon),并且严格区分大小写
![image-20210713125712794](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210713125712794.png)
## Proxies配置-defaults
- defaults 配置参数
```bash
option redispatch #当server Id对应的服务器挂掉后强制定向到其他健康的服务器重新派发
option abortonclose #当服务器负载很高时,自动结束掉当前队列处理比较久的链接,针对业务情况选择开启
option http-keep-alive #开启与客户端的会话保持
option forwardfor #透传客户端真实IP至后端web服务器
mode http|tcp #设置默认工作类型,使用TCP服务器性能更好减少压力
timeout http-keep-alive 120s #session 会话保持超时时间,此时间段内会转发到相同的后端服务器
timeout connect 120s #客户端请求从haproxy到后端server最长连接等待时间(TCP连接之前)默认单位ms
timeout server 600s #客户端请求从haproxy到后端服务端的请求处理超时时长(TCP连接之后)默认单位ms如果超时会出现502错误此值建议设置较大些访止502错误
timeout client 600s #设置haproxy与客户端的最长非活动时间默认单位ms建议和timeout server相同
timeout check 5s #对后端服务器的默认检测超时时间
default-server inter 1000 weight 3 #指定后端服务器的默认设置
```
## Proxies配置-frontend
- frontend 配置参数
```bash
bind #指定HAProxy的监听地址可以是IPV4或IPV6可以同时监听多个IP或端口可同时用于listen字段中
#格式:
bind [<address>]:<port_range> [, ...] [param*]
#注意如果需要绑定在非本机的IP需要开启内核参数net.ipv4.ip_nonlocal_bind=1
```
- 范例
```bash
listen http_proxy #监听http的多个IP的多个端口和sock文件
bind :80,:443,:8801-8810
bind 10.0.0.1:10080,10.0.0.1:10443
bind /var/run/ssl-frontend.sock user root mode 600 accept-proxy
listen http_https_proxy #https监听
bind :80
bind :443 ssl crt /etc/haproxy/site.pem #公钥和私钥公共文件
listen http_https_proxy_explicit #监听ipv6、ipv4和unix sock文件
bind ipv6@:80
bind ipv4@public_ssl:443 ssl crt /etc/haproxy/site.pem
bind unix@ssl-frontend.sock user root mode 600 accept-proxy
listen external_bind_app1 #监听file descriptor
bind "fd@${FD_APP1}"
```
- 生产示例
```bash
frontend web_port #可以采用后面形式命名:业务-服务-端口号
bind :80,:8080
bind 10.0.0.7:10080,:8801-8810,10.0.0.17:9001-9010
mode http|tcp #指定负载协议类型
use_backend <backend_name> #调用的后端服务器组名称
```
## Proxies配置-backend
- 定义一组后端服务器backend服务器将被frontend进行调用。
```bash
mode http|tcp #指定负载协议类型,和对应的frontend必须一致
option #配置选项
server #定义后端real server
```
注意option后面加 httpchksmtpchk,mysql-check,pgsql-checkssl-hello-chk方法可用于实现更多应用层检测功能。
### option 配置
```bash
check #对指定real进行健康状态检查如果不加此设置默认不开启检查
addr <IP> #可指定的健康状态监测IP可以是专门的数据网段减少业务网络的流量
port <num> #指定的健康状态监测端口
inter <num> #健康状态检查间隔时间默认2000 ms
fall <num> #后端服务器从线上转为线下的检查的连续失效次数默认为3
rise <num> #后端服务器从下线恢复上线的检查的连续有效次数默认为2
weight <weight> #默认为1最大值为2560表示不参与负载均衡但仍接受持久连接
backup #将后端服务器标记为备份状态,只在所有非备份主机down机时提供服务类似Sorry Server
disabled #将后端服务器标记为不可用状态,即维护状态,除了持久模式,将不再接受连接
redirect prefix http://www.baidu.com/ #将请求临时(302)重定向至其它URL只适用于http模式
redir http://www.baidu.com #将请求临时(302)重定向至其它URL只适用于http模式
maxconn <maxconn> #当前后端server的最大并发连接数
backlog <backlog> #当前端服务器的连接数达到上限后的后援队列长度注意不支持backend
```
## frontend+backend配置实例
![image-20210713170548860](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210713170548860.png)
- node1和node2搭建一个网站
```bash
[root@node1 ~]# yum -y install httpd
[root@node1 ~]# echo "node1 ..." > /var/www/html/index.html
[root@node1 ~]# systemctl start httpd
[root@node1 ~]# systemctl enable httpd
```
- haproxy配置文件
```bash
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
global
maxconn 100000
chroot /apps/haproxy
stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin
#uid 99
#gid 99
user haproxy
group haproxy
daemon
#nbproc 4
#cpu-map 1 0
#cpu-map 2 1
#cpu-map 3 2
#cpu-map 4 3
pidfile /var/lib/haproxy/haproxy.pid
log 127.0.0.1 local2 info
defaults
option http-keep-alive
option forwardfor
maxconn 100000
mode http
timeout connect 300000ms
timeout client 300000ms
timeout server 300000ms
listen stats
mode http
bind 0.0.0.0:9999
stats enable
log global
stats uri /haproxy-status
stats auth admin:123456
frontend test-http
bind :80
mode tcp
use_backend test-http-nodes
backend test-http-nodes
mode tcp
default-server inter 1000 weight 6
server web1 192.168.88.20:80 check inter 3000 fall 3 rise 5 weight 2 addr 192.168.88.20 port 3306
server web2 192.168.88.30:80 check inter 3000 fall 3 rise 5
[root@haproxy ~]# systemctl restart haproxy
```
- 验证结果
![image-20210713171531848](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210713171531848.png)
- 因为检查node1的3306接口并没有启动所以node1始终是down的
![image-20210713172303463](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210713172303463.png)
## listen替代frontend+backend
使用listen替换上面的frontend和backend的配置方式可以简化设置通常只用于TCP协议的应用
```bash
listen WEB_PORT_80
bind :80
mode http
option forwardfor
# 这个选项的作用是启用或禁用将原始客户端的IP地址添加到HTTP请求的X-Forwarded-For头部中。
server web1 192.168.88.20:80 check inter 3000 fall 3 rise 5
server web2 192.168.88.30:80 check inter 3000 fall 3 rise 5
[root@haproxy ~]# systemctl restart haproxy
```
## 使用子配置文件保存配置
当业务众多时,将所有配置都放在一个配置文件中,会造成维护困难。可以考虑按业务分类,将配置信息拆分,放在不同的子配置文件中,从而达到方便维护的目的。
- 创建子配置目录
```bash
[root@haproxy ~]# mkdir /etc/haproxy/conf.d/
```
- 创建子配置文件注意必须为cfg后缀
```bash
[root@haproxy ~]# vim /etc/haproxy/conf.d/test.cfg
listen WEB_PORT_80
bind :80
mode http
option forwardfor
balance roundrobin
server web1 192.168.88.20:80 check inter 3000 fall 3 rise 5
server web2 192.168.88.30:80 check inter 3000 fall 3 rise 5
```
- 添加子配置目录到unit文件中
```bash
[root@haproxy ~]# vim /lib/systemd/system/haproxy.service
[Unit]
Description=HAProxy Load Balancer
After=syslog.target network.target
[Service]
ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/conf.d/ -c -q
ExecStart=/usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/conf.d/ -p /var/lib/haproxy/haproxy.pid
ExecReload=/bin/kill -USR2 $MAINPID
[Install]
WantedBy=multi-user.target
[root@haproxy ~]# systemctl daemon-reload
[root@haproxy ~]# systemctl restart haproxy
```
# HAProxy调度算法
HAProxy通过固定参数`balance`指明对后端服务器的调度算法该参数可以配置在listen或backend选项中。
HAProxy的调度算法分为静态和动态调度算法但是有些算法可以根据参数在静态和动态算法中相互转换。
官方文档:[http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#4-balance](http://www.yunweipai.com/go?_=55fbd0b441aHR0cDovL2Nib250ZS5naXRodWIuaW8vaGFwcm94eS1kY29udi8yLjEvY29uZmlndXJhdGlvbi5odG1sIzQtYmFsYW5jZQ==)
## 静态算法
静态算法按照事先定义好的规则轮询公平调度不关心后端服务器的当前负载、链接数和响应速度等且无法实时修改权重只能靠重启HAProxy生效。
### static-rr
static-rr基于权重的轮询调度不支持权重的运行时利用socat进行动态调整及后端服务器慢启动其后端主机数量没有限制相当于LVS中的 wrr
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance static-rr
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 2 check inter 3000 fall 2 rise 5
```
### first
first根据服务器在列表中的位置自上而下进行调度但是其只会当第一台服务器的连接数达到上限新请求才会分配给下一台服务因此会忽略服务器的权重设置此方式使用较少
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance first
server web1 192.168.88.20:80 maxconn 2 weight 1 check inter 3000 fall 2 rise 5
server web2 192.168.88.30:80 weight 1 check inter 3000 fall 2 rise 5
```
测试访问效果,同时运行下面命令,观察结果
```bash
while true;do curl http://192.168.88.10/index.html ; sleep 0.1;done
```
## 动态算法
动态算法基于后端服务器状态进行调度适当调整优先调度至当前负载较低的服务器且权重可以在haproxy运行时动态调整无需重启。
### roundrobin
roundrobin基于权重的轮询动态调度算法支持权重的运行时调整不同于lvs中的rr轮询模式HAProxy中的roundrobin支持慢启动(新加的服务器会逐渐增加转发数)其每个后端backend中最多支持4095个real server支持对real server权重动态调整roundrobin为默认调度算法
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance roundrobin
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 2 check inter 3000 fall 2 rise 5
```
支持动态调整权重
```bash
# echo "get weight web_host/web1" | socat stdio /var/lib/haproxy/haproxy.sock
1 (initial 1)
# echo "set weight web_host/web1 3" | socat stdio /var/lib/haproxy/haproxy.sock
# echo "get weight web_host/web1" | socat stdio /var/lib/haproxy/haproxy.sock
3 (initial 1)
```
### leastconn
leastconn加权的最少连接的动态支持权重的运行时调整和慢启动即当前后端服务器连接最少的优先调度(新客户端连接)比较适合长连接的场景使用比如MySQL等场景。
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance leastconn
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
```
### random
在1.9版本开始增加一个叫做random的负载平衡算法其基于随机数作为一致性hash的key随机负载平衡对于大型服务器场或经常添加或删除服务器非常有用支持weight的动态调整weight较大的主机有更大概率获取新请求
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance random
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
```
## 其他算法
其它算法即可作为静态算法,又可以通过选项成为动态算法
### source
源地址hash基于用户源地址hash并将请求转发到后端服务器后续同一个源地址请求将被转发至同一个后端web服务器。此方式当后端服务器数据量发生变化时会导致很多用户的请求转发至新的后端服务器默认为静态方式但是可以通过hash-type支持的选项更改
这个算法一般是在不插入Cookie的TCP模式下使用也可给拒绝会话cookie的客户提供最好的会话粘性适用于session会话保持但不支持cookie和缓存的场景
源地址有两种转发客户端请求到后端服务器的服务器选取计算方式分别是取模法和一致性hash
#### map-base取模法
map-based取模法对source地址进行hash计算再基于服务器总权重的取模最终结果决定将此请求转发至对应的后端服务器。此方法是静态的即不支持在线调整权重不支持慢启动可实现对后端服务器均衡调度。缺点是当服务器的总权重发生变化时即有服务器上线或下线都会因总权重发生变化而导致调度结果整体改变hash-type 指定的默认值为此算法
> 所谓取模运算就是计算两个数相除之后的余数10%7=3, 7%4=3 map-based算法基于权重取模hash(source_ip)%所有后端服务器相加的总权重
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode tcp
log global
balance source
hash-type map-based
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 3
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 3
[root@haproxy ~]#echo "set weight web_host/10.0.0.27 10" | socat stdio /var/lib/haproxy/haproxy.sock
Backend is using a static LB algorithm and only accepts weights '0%' and '100%'.
[root@haproxy ~]#echo "set weight web_host/10.0.0.27 0" | socat stdio /var/lib/haproxy/haproxy.sock
[root@haproxy conf.d]#echo "get weight web_host/10.0.0.27" | socat stdio /var/lib/haproxy/haproxy.sock
0 (initial 1)
```
#### 一致性hash
一致性哈希当服务器的总权重发生变化时对调度结果影响是局部的不会引起大的变动hashomod n 该hash算法是动态的支持使用 socat等工具进行在线权重调整支持慢启动
> 1、key1=hash(source_ip)%(2^32) [0---4294967295]
> 2、keyA=hash(后端服务器虚拟ip)%(2^32)
> 3、将key1和keyA都放在hash环上将用户请求调度到离key1最近的keyA对应的后端服务器
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode tcp
log global
balance source
hash-type consistent
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
```
### uri
基于对用户请求的URI的左半部分或整个uri做hash再将hash结果对总权重进行取模后根据最终结果将请求转发到后端指定服务器适用于后端是缓存服务器场景默认是静态也可以通过hash-type指定map-based和consistent来定义使用取模法还是一致性hash。
注意:此算法是应用层,所有只支持 mode http ,不支持 mode tcp
```bash
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
左半部分:/<path>;<params>
整个uri/<path>;<params>?<query>#<frag>
```
- uri 取模法配置示例
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance uri
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
```
- uri 一致性hash配置示例
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance uri
hash-type consistent
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
```
### url_param
url_param对用户请求的url中的 params 部分中的一个参数key对应的value值作hash计算并由服务器总权重相除以后派发至某挑出的服务器通常用于追踪用户以确保来自同一个用户的请求始终发往同一个real server如果无没key将按roundrobin算法
```bash
假设:
url = http://www.test.com/foo/bar/index.php?key=value
则:
host = "www.test.com"
url_param = "key=value"
```
- url_param取模法配置示例
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance url_param userid #url_param hash
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
```
- url_param一致性hash配置示例
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance url_param userid #对url_param的值取hash
hash-type consistent
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
```
### hdr
针对用户每个http头部(header)请求中的指定信息做hash此处由 name 指定的http首部将会被取出并做hash计算然后由服务器总权重取模以后派发至某挑出的服务器如无有效的值则会使用默认的轮询调度。
- hdr取模法配置示例
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance hdr(User-Agent)
#balance hdr(host)
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
```
- 一致性hash配置示例
```bash
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance hdr(User-Agent)
hash-type consistent
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5
```
### rdp-cookie
rdp-cookie对远windows远程桌面的负载使用cookie保持会话默认是静态也可以通过hash-type指定map-based和consistent来定义使用取模法还是一致性hash。
- rdp-cookie取模法配置示例
```bash
listen RDP
bind 10.0.0.7:3389
balance rdp-cookie
mode tcp
server rdp0 10.0.0.17:3389 check fall 3 rise 5 inter 2000 weight 1
```
- rdp-cookie一致性hash配置示例
```bash
[root@haproxy ~]#cat /etc/haproxy/conf.d/windows_rdp.cfg
listen magedu_RDP_3389
bind 172.16.0.100:3389
balance rdp-cookie
hash-type consistent
mode tcp
server rdp0 10.0.0.200:3389 check fall 3 rise 5 inter 2000 weight 1
[root@haproxy ~]#hostname -I
10.0.0.100 172.16.0.100
```
## 算法总结
```bash
static-rr--------->tcp/http 静态
first------------->tcp/http 静态
roundrobin-------->tcp/http 动态
leastconn--------->tcp/http 动态
random------------>tcp/http 动态
以下静态和动态取决于hash_type是否consistent
source------------>tcp/http
Uri--------------->http
url_param--------->http
hdr--------------->http
rdp-cookie-------->tcp
```
## 各算法使用场景
```bash
first #使用较少
static-rr #做了session共享的web集群
roundrobin
random
leastconn #数据库
source #基于客户端公网IP的会话保持
uri--------------->http #缓存服务器CDN服务商蓝汛、百度、阿里云、腾讯
url_param--------->http
hdr #基于客户端请求报文头部做下一步处理
rdp-cookie #很少使用
```
# HAProxy状态页
通过web界面显示当前HAProxy的运行状态
官方帮助http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#4-stats%20admin
- 状态页配置项
```bash
stats enable #基于默认的参数启用stats page
stats hide-version #将状态页中haproxy版本隐藏
stats refresh <delay> #设定自动刷新时间间隔,默认不自动刷新
stats uri <prefix> #自定义stats page uri默认值/haproxy?stats
stats realm <realm> #账户认证时的提示信息示例stats realm HAProxy\ Statistics
stats auth <user>:<passwd> #认证时的账号和密码可使用多次默认no authentication可有多行用户
stats admin { if | unless } <cond> #启用stats page中的管理功能
```
- 启用状态页
```bash
listen stats
bind :9999
stats enable
#stats hide-version
stats uri /haproxy-status
stats realm HAPorxy\ Stats\ Page
stats auth haadmin:123456 #两个用户
stats auth admin:123456
#stats refresh 30s
stats admin if TRUE #安全原因,不建议打开
```
- 状态页信息
```bash
pid = 27134 (process #1, nbproc = 1, nbthread = 1) #pid为当前pid号process为当前进程号nbproc和nbthread为一共多少进程和每个进程多少个线程
uptime = 0d 0h00m04s #启动了多长时间
system limits: memmax = unlimited; ulimit-n = 200029 #系统资源限制:内存/最大打开文件数/
maxsock = 200029; maxconn = 100000; maxpipes = 0 #最大socket连接数/单进程最大连接数/最大管道数maxpipes
current conns = 2; current pipes = 0/0; conn rate = 2/sec; bit rate = 0.000 kbps #当前连接数/当前管道数/当前连接速率
Running tasks: 1/14; idle = 100 % #运行的任务/当前空闲率
active UP #在线服务器
backup UP #标记为backup的服务器
active UP, going down #监测未通过正在进入down过程
backup UP, going down #备份服务器正在进入down过程
active DOWN, going up #down的服务器正在进入up过程
backup DOWN, going up #备份服务器正在进入up过程
active or backup DOWN #在线的服务器或者是backup的服务器已经转换成了down状态
not checked #标记为不监测的服务器
active or backup DOWN for maintenance (MAINT) #active或者backup服务器人为下线的
active or backup SOFT STOPPED for maintenance #active或者backup被人为软下线(人为将weight改成0)
```
![image-20210713183017871](%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/image-20210713183017871.png)
## backend server信息
| session rate(每秒的连接会话信息) | Errors(错误统计信息) |
| ---------------------------------- | --------------------------------- |
| cur:每秒的当前会话数量 | Req:错误请求量 |
| max:每秒新的最大会话数量 | conn:错误链接量 |
| limit:每秒新的会话限制量 | Resp:错误响应量 |
| sessions(会话信息) | Warnings(警告统计信息) |
| cur:当前会话量 | Retr:重新尝试次数 |
| max:最大会话量 | Redis:再次发送次数 |
| limit: 限制会话量 | |
| Total:总共会话量 | Server(real server信息) |
| LBTot:选中一台服务器所用的总时间 | Status:后端机的状态包括UP和DOWN |
| Last和服务器的持续连接时间 | LastChk:持续检查后端服务器的时间 |
| Wght:权重 | |
| Bytes(流量统计) | Act:活动链接数量 |
| In:网络的字节输入总量 | Bck:备份的服务器数量 |
| Out:网络的字节输出总量 | Chk:心跳检测时间 |
| Dwn:后端服务器连接后都是DOWN的数量 | |
| Denied(拒绝统计信息) | Dwntme:总的downtime时间 |
| Req:拒绝请求量 | Thrtle:server 状态 |
| Resp:拒绝回复量 | |
## 利用状态页实现haproxy服务器的健康性检查
- 通过curl 命令对haproxy的状态页的访问实现健康检查
```bash
[root@haproxy ~]# curl -I http://admin:123456@192.168.88.10:9999/haproxy-status
HTTP/1.1 200 OK
cache-control: no-cache
content-type: text/html
[root@haproxy ~]# curl -I -u admin:123456 http://192.168.88.10:9999/haproxy-status
HTTP/1.1 200 OK
cache-control: no-cache
content-type: text/html
```