# 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会话共享(NFS,MySQL,memcache,redis,file) - 在解决负载均衡会话问题,我们需要了解session和cookie的区别。 - 浏览器端存的是cookie每次浏览器发请求到服务端时,报文头是会自动添加cookie信息的。 - 服务端会查询用户的cookie作为key去存储里找对应的value(session) - 同一域名下的网站的cookie都是一样的,所以无论几台服务器,无论请求分配到哪一台服务器上同一用户的cookie是不变的。也就是说cookie对应的session也是唯一的。所以,这里只要保证多台业务服务器访问同一个共享存储服务器(NFS,MySQL,memcache,redis,file)就行了。 ## 会话保持配置 测试网站选择我们之前搭建过的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分为三种类型: - LB:Load Balancing,负载均衡。调度负载,按照算法调度。 - HA:High Availiablity,高可用,避免SPOF(single 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% - HPC:High-performance computing,高性能 www.top500.org ## 分布式系统 分布式存储: Ceph,GlusterFS,FastDFS,MogileFS 分布式计算:hadoop,Spark 分布式常见应用 分布式应用-服务按照功能拆分,使用微服务 分布式静态资源–静态资源放在不同的存储集群上 分布式数据和存储–使用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 - 软件(小公司) - lvs:Linux Virtual Server,阿里四层SLB (Server Load Balance)使用下四层功能:物理层 数据链路层 - nginx:支持七层调度,阿里七层SLB使用Tengine - haproxy:支持七层调度 - ats:Apache Traffic Server,yahoo捐助给apache - perlbal:Perl 编写 - pound ### 基于工作的协议层次划分 - 传输层(通用):DNAT和DPORT - LVS:Linux Virtual Server - nginx:stream - haproxy:mode tcp - 应用层(专用):针对特定协议,常称为 proxy server - http:nginx, httpd, haproxy(mode http), … - fastcgi:nginx, httpd, … - mysql:mysql-proxy, … ### 负载均衡的会话保持 - session sticky:同一用户调度固定服务器 - Source IP:LVS sh算法(对某一特定服务而言) - Cookie - session replication:每台服务器拥有全部session - session multicast cluster (内存消耗大) - session server:专门的session服务器 - Memcached,Redis (只放session,共享)也存在单点失败,即也要做集群哨兵机制 ## HA 高可用集群实现 keepalived:vrrp协议 Ais:应用接口规范 heartbeat cman+rgmanager(RHCS) coresync_pacemaker # Linux Virtual Server简介 ## LVS介绍 - LVS:Linux 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 负载均衡的应用场景为高访问量的业务,提高应用程序的可用性和可靠性。 ## 应用于高访问量的业务 如果您的应用访问量很高,您可以通过配置监听规则将流量分发到不同的云服务器ECS(Elastic 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 ## LVS集群类型中的术语 VS:Virtual Server,Director Server(DS), Dispatcher(调度器),Load Balancer RS:Real Server(lvs), upstream server(nginx)上游服务器, backend server(haproxy) CIP:Client IP VIP:Virtual serve IP VS外网的IP DIP:Director IP VS内网的IP RIP:Real 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模式 image-20210712080133506 lvs-nat:本质是多目标IP的DNAT,通过将请求报文中的目标地址和目标端口修改为某挑出的RS的RIP和PORT实现转发 - RIP和DIP应在同一个IP网络,且应使用私网地址;RS的网关要指向DIP - 请求报文和响应报文都必须经由Director转发,Director易于成为系统瓶颈 - 支持端口映射,可修改请求报文的目标PORT - VS必须是Linux系统,RS可以是任意OS系统 image-20210712080410013 image-20210712080545578 ### LVS的DR模式 image-20210712080949453 ​ LVS-DR:Direct 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),将报文发往挑选出的目标RS;RS直接响应给客户端(源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-nat:RIP的网关要指向DIP - lvs-fullnat:RIP和DIP未必在同一IP网络,但要能通信 - lvs-dr与lvs-tun: - 请求报文要经由Director,但响应报文由RS直接发往Client - lvs-dr:通过封装新的MAC首部实现,通过MAC网络转发 - lvs-tun:通过在原IP报文外封装新IP头实现转发,支持远距离通信 ## LVS调试算法 ipvs scheduler:根据其调度时是否考虑各RS当前的负载状态 分为两种:静态方法和动态方法 ### 静态方法 - 仅根据算法本身进行调度 - RR:roundrobin,轮询 不考虑机器的性能好坏,轮询调度 - WRR:Weighted RR,加权轮询 权重越高,优先级高,调度更多资源给它 优先级高 - SH:Source Hashing,实现session sticky,源IP地址hash;将来自于同一个IP地址的请求始终发往第一次挑中的RS,从而实现会话绑定 - DH:Destination Hashing;目标地址哈希,第一次轮询调度至RS,后续将发往同一个目标地址的请求始终转发至第一次挑中的RS,典型使用场景是正向代理缓存场景中的负载均衡,如:宽带运营商 ### 动态方法 主要根据每RS当前的负载状态及调度算法进行调度Overhead=value 较小的RS将被调度 - LC:least connections 适用于长连接应用(最少连接 ) - `Overhead=activeconns *256+inactiveconns` - WLC:Weighted LC,默认调度方法(加权最少连接 ) - `Overhead=(activeconns * 256+inactiveconns)/weight` - SED:Shortest Expection Delay,初始连接高权重优先(最少期望延迟) - `Overhead=(activeconns+1)*256/weight` - NQ:Never Queue,第一轮均匀分配,后续SED(从不排队调度方法) - LBLC:Locality-Based LC,动态的DH算法,使用场景:根据负载状态实现正向代理 - LBLCR:LBLC with Replication,带复制功能的LBLC,解决LBLC负载不均衡问题,从负载重的复制到负载轻的RS ### 内核版本 4.15版本后新增调度算法 - FO(Weighted Fail Over)调度算法,在此FO算法中,遍历虚拟服务所关联的真实服务器链表,找到还未过载(未设置IP_VS_DEST_F_OVERLOAD标志)的且权重最高的真实服务器,进行调度。方便调式上下线。 - OVF(Overflow-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 -f:firewall 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模式案例 image-20210712100318910 实验环境说明: 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.6:DNS解析支持,HTTP连接多路复用…… - 1.5:开始支持SSL,IPV6,会话保持…… 从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/ github:https://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功能 image-20210713100058631 ## 支持功能 - TCP 和 HTTP反向代理 - SSL/TSL服务器 - 可以针对HTTP请求添加cookie,进行路由后端服务器 - 可平衡负载至后端服务器,并支持持久连接 - 支持所有主服务器故障切换至备用服务器 - 支持专用端口实现监控服务 - 支持停止接受新连接请求,而不影响现有连接 - 可以在双向添加,修改或删除HTTP报文首部 - 响应报文压缩 - 支持基于pattern实现连接请求的访问控制 - 通过特定的URI为授权用户提供详细的状态信息 - 支持http反向代理 - 支持动态程序的反向代理 - 支持基于数据库的反向代理 ## 不具备的功能 - 正向代理--squid,nginx - 缓存代理--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 cannot be specified using 'proto' keyword) h2 : mode=HTTP side=FE|BE mux=H2 fcgi : mode=HTTP side=BE mux=FCGI : mode=HTTP side=FE|BE mux=H1 : 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 []` - 默认配置项,针对以下的frontend、backend和listen生效,可以多个name也可以没有name - `frontend ` - 前端servername,类似于Nginx的一个虚拟主机 server和LVS服务集群。 - `backend ` - 后端服务器组,等于nginx的upstream和LVS中的RS服务器 - `listen ` - 将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 [
]: [, ...] [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 #调用的后端服务器组名称 ``` ## Proxies配置-backend - 定义一组后端服务器,backend服务器将被frontend进行调用。 ```bash mode http|tcp #指定负载协议类型,和对应的frontend必须一致 option #配置选项 server #定义后端real server ``` 注意:option后面加 httpchk,smtpchk,mysql-check,pgsql-check,ssl-hello-chk方法,可用于实现更多应用层检测功能。 ### option 配置 ```bash check #对指定real进行健康状态检查,如果不加此设置,默认不开启检查 addr #可指定的健康状态监测IP,可以是专门的数据网段,减少业务网络的流量 port #指定的健康状态监测端口 inter #健康状态检查间隔时间,默认2000 ms fall #后端服务器从线上转为线下的检查的连续失效次数,默认为3 rise #后端服务器从下线恢复上线的检查的连续有效次数,默认为2 weight #默认为1,最大值为256,0表示不参与负载均衡,但仍接受持久连接 backup #将后端服务器标记为备份状态,只在所有非备份主机down机时提供服务,类似Sorry Server disabled #将后端服务器标记为不可用状态,即维护状态,除了持久模式,将不再接受连接 redirect prefix http://www.baidu.com/ #将请求临时(302)重定向至其它URL,只适用于http模式 redir http://www.baidu.com #将请求临时(302)重定向至其它URL,只适用于http模式 maxconn #当前后端server的最大并发连接数 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 ​ 一致性哈希,当服务器的总权重发生变化时,对调度结果影响是局部的,不会引起大的变动,hash(o)mod 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 ://:@:/;?# 左半部分:/; 整个uri:/;?# ``` - 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 #设定自动刷新时间间隔,默认不自动刷新 stats uri #自定义stats page uri,默认值:/haproxy?stats stats realm #账户认证时的提示信息,示例:stats realm HAProxy\ Statistics stats auth : #认证时的账号和密码,可使用多次,默认:no authentication,可有多行用户 stats admin { if | unless } #启用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 ```