Keepalived+LVS+nginx搭建nginx高可用集群

news2024/10/6 2:26:15

一、简介  

        nginx是一款非常优秀的反向代理工具,支持请求分发,负载均衡,以及缓存等等非常实用的功能。在请求处理上,nginx采用的是epoll模型,这是一种基于事件监听的模型,因而其具备非常高效的请求处理效率,单机并发能力能够达到上百万。nginx接收到的请求可以通过负载均衡策略分发到其下一级的应用服务器,这些服务器一般是以集群方式部署的,因而在性能不足的情况下,应用服务器可以通过加机器的方式扩展流量。此时,对于一些特大型的网站,性能的瓶颈就来自于nginx了,因为单机的nginx的并发能力是有上限的,而nginx本身是不支持集群模式的,因而此时对nginx的横向扩展就显得尤为重要。

        keepalived是一款服务器状态检测和故障切换的工具。在其配置文件中,可以配置主备服务器和该服务器的状态检测请求。也就是说keepalived可以根据配置的请求,在提供服务期间不断向指定服务器发送请求,如果该请求返回的状态码是200,则表示该服务器状态是正常的,如果不正常,那么keepalived就会将该服务器给下线掉,然后将备用服务器设置为上线状态。
lvs是一款用于四层负载均衡的工具。所谓的四层负载均衡,对应的是网络七层协议,常见的如HTTP协议是建立在七层协议上的,而lvs作用于四层协议上,也即:传输层,网络层,数据链路层和物理层。这里的传输层主要协议有TCP和UDP协议,也就是说lvs主要支持的方式是TCP和UDP。也正是因为lvs是处于四层负载均衡上的,因而其处理请求的能力比常见的服务器要高非常多,比如nginx的请求处理就是建立在网络七层上的,lvs的负载均衡能力是nginx的十倍以上。
通过上面的介绍,我们可以发现,在特大型网站中,应用服务器是可以横向扩容的,而nginx是不支持横向扩容的,此时nginx就会成为性能瓶颈。而lvs是一款负载均衡工具,因而如果我们结合lvs和nginx,那么就可以通过部署多台nginx服务器,通过lvs的负载均衡能力,将请求均衡的分发到各个nginx服务器上,再由nginx服务器分发到各个应用服务器,这样,我们就实现了nginx的横向扩展了。由于nginx本质上也是一款应用服务器,因而其也有可能宕机,因而这里结合keepalived就可以实现nginx的故障检测和服务切换。也就是说,通过keepalived+lvs+nginx,我们实现了nginx的高可用集群模式。

        在上面的介绍中,我们会注意到,虽然keepalived+lvs+nginx实现了nginx的集群模式,但是在我们使用nginx的时候,其本身是有一个ip和端口的,默认监听的端口是80和443,那么lvs是如何实现将请求分发给具有不同ip和端口的nginx服务器的呢?这里就是通过虚拟ip实现的,所谓虚拟ip就是对外提供一个公共的ip,外部客户端请求的都是这个ip,lvs在接收到虚拟ip的请求之后,通过配置的调度器和负载均衡策略,选择一个目标nginx服务器,然后将请求转发给该服务器。这里lvs有两个概念,就是调度器和负载均衡策略,所谓的调度器指的是lvs将会以何种方式处理请求和响应数据,其主要有三种调度器:

  • Virtual Server via Network Address Translation(VS/NAT):这种方式的主要原理是,用户发送请求到虚拟ip上后,lvs会根据负载均衡算法选择一个目标处理服务,然后将请求报文中的目标ip地址修改为计算得到的目标服务器,并且发送给该服务器。对于响应的报文,调度器会将目标服务器返回的响应数据中的源地址修改为虚拟ip地址。通过这种方式,对客户端而言,其形式上面向的是一台服务器。不过这种方式的缺点在于,所有的响应数据都需要通过调度器,如果请求量比较大的情况下,那么调度器就会成为整个系统的瓶颈。
  • Virtual Server via IP Tunneling(VS/TUN):这种方式主要解决的就是VS/NAT中,响应数据会经过调度器的问题。同VS/NAT一样 ,调度器还是会接收请求的数据,并且将报文中的目标ip修改为目标服务的ip,但是在目标服务处理完数据之后,其会直接将响应报文中的源ip修改为虚拟ip,然后将请求发送给客户端。通过这种方式,响应数据就由各个目标服务进行了处理,而无需通过调度器进行返回,这种方式会大大提高系统的吞吐量,而且由于一般请求报文比响应报文小很多,调度器也只需要处理请求报文,那么系统的整体负载将会被均摊到各个服务器上。
  • Virtual Server via Direct Routing(VS/DR):这种方式相对于VS/TUN,其主要区别在于,VS/NAT是将请求报文中的ip地址修改为目标服务的ip地址,而VS/DR则是直接将请求报文中的MAC地址修改为目标地址,这种方式效率会更高,因为VS/TUN中的ip地址最终还是需要转换为MAC地址来发送数据的。

1. 环境准备

1. VMware;
2. 4台CentOs7虚拟主机:172.16.28.130, 172.16.28.131, 172.16.28.132, 172.16.28.133
3. 系统服务:LVS, Keepalived
4. Web服务器:nginx
5. 集群搭建:LVS DR模式

2. 软件安装

在四台虚拟机上,我们以如下方式搭建集群:

172.16.28.130 lvs+keepalived
172.16.28.131 lvs+keepalived
172.16.28.132 nginx
172.16.28.133 nginx

         这里我们使用172.16.28.130 和172.16.28.131 两台机器作为lvs+keepalived 的工作机器,也就是说这两台机器的作用主要是进行负载均衡和故障检测和下线的;我们使用172.16.28.132和172.16.28.133 两台机器作为应用服务器,主要是对外提供服务的。这四台服务器作为整个后端集群服务,并且对外提供的虚拟ip是172.16.28.120 。需要说明的是,这里的keepalived 所检测的服务是两台lvs 服务器,这两台服务器,一台作为master服务器,一台作为backup服务器,两者在负载均衡的配置上是完全一样的。在正常情况下,客户端请求虚拟ip的时候,lvs 会将该请求转发到master服务器上,然后master服务器根据配置的负载均衡策略选择一台应用服务器,并且将请求发送给该应用服务器进行处理。如果在某个时刻,lvs的master服务器由于故障宕机了,keepalived就会检测到该故障,并且进行故障下线,然后将backup机器上线用于提供服务,从而实现故障转移的功能。

2.1 lvs+keepalived安装

在172.16.28.130 和172.16.28.131 上安装ipvs和keepalived:

# 安装nginx

sudo yum install nginx

需要注意的是,在两台nginx服务器上需要将防火墙关闭,否则lvs+keepalived的两台机器就无法将请求发送到两台nginx服务器上来:

# 关闭防火墙

systemctl disable firewalld.service

查看两台负载均衡机器是否支持lvs:

sudo lsmod |grep ip_vs

# 如果看到如下结果,则说明是支持的

[zhangxufeng@localhost ~]$ sudo lsmod|grep ip_vs
ip_vs 145497 0
nf_conntrack 137239 1 ip_vs
libcrc32c 12644 3 xfs,ip_vs,nf_conntrack

如果上述命令没有任何结果,则执行sudo ipvsadm 命令启动ipvs之后,再通过上述命令进行查看即可。启动ipvs之后,我们就可以在/etc/keepalived/ 目录下编辑keepalived.conf 文件,我们以172.16.28.130 机器作为master机器,master节点配置如下:

# Global Configuration
global_defs {
lvs_id director1 # 指定lvs的id
}
# VRRP Configuration
vrrp_instance LVS {
state MASTER # 指定当前节点为master节点
interface ens33 # 这里的ens33是网卡的名称,通过ifconfig或者ip addr可以查看
virtual_router_id 51 # 这里指定的是虚拟路由id,master节点和backup节点需要指定一样的
priority 151 # 指定了当前节点的优先级,数值越大优先级越高,master节点要高于backup节点
advert_int 1 # 指定发送VRRP通告的间隔,单位是秒
authentication {
auth_type PASS # 鉴权,默认通过
auth_pass 123456 # 鉴权访问密码
}
virtual_ipaddress {
172.16.28.120 # 指定了虚拟ip
}
}
# Virtual Server Configuration - for www server
# 后台真实主机的配置
virtual_server 172.16.28.120 80 {
delay_loop 1 # 健康检查的时间间隔
lb_algo rr # 负载均衡策略,这里是轮询
lb_kind DR # 调度器类型,这里是DR
persistence_time 1 # 指定了持续将请求打到同一台真实主机的时间长度
protocol TCP # 指定了访问后台真实主机的协议类型
# Real Server 1 configuration
# 指定了真实主机1的ip和端口
real_server 172.16.28.132 80 {
weight 1 # 指定了当前主机的权重
TCP_CHECK {
connection_timeout 10 # 指定了进行心跳检查的超时时间
nb_get_retry 3 # 指定了心跳超时之后的重复次数
delay_before_retry 3 # 指定了在尝试之前延迟多长时间
}
}
# Real Server 2 Configuration
real_server 172.16.28.133 80 {
weight 1 # 指定了当前主机的权重
TCP_CHECK {
connection_timeout 10 # 指定了进行心跳检查的超时时间
nb_get_retry 3 # 指定了心跳超时之后的重复次数
delay_before_retry 3 # 指定了在尝试之前延迟多长时间
}
}
}

上面是master节点上keepalived的配置,对于backup节点,其配置与master几乎是一致的,只是其state和priority参数不同。如下是backup节点的完整配置:

# Global Configuration
global_defs {
lvs_id director2 # 指定lvs的id
}
# VRRP Configuration
vrrp_instance LVS {
state BACKUP # 指定当前节点为master节点
interface ens33 # 这里的ens33是网卡的名称,通过ifconfig或者ip addr可以查看
virtual_router_id 51 # 这里指定的是虚拟路由id,master节点和backup节点需要指定一样的
priority 150 # 指定了当前节点的优先级,数值越大优先级越高,master节点要高于backup节点
advert_int 1 # 指定发送VRRP通告的间隔,单位是秒
authentication {
auth_type PASS # 鉴权,默认通过
auth_pass 123456 # 鉴权访问密码
}
virtual_ipaddress {
172.16.28.120 # 指定了虚拟ip
}
}
# Virtual Server Configuration - for www server
# 后台真实主机的配置
virtual_server 172.16.28.120 80 {
delay_loop 1 # 健康检查的时间间隔
lb_algo rr # 负载均衡策略,这里是轮询
lb_kind DR # 调度器类型,这里是DR
persistence_time 1 # 指定了持续将请求打到同一台真实主机的时间长度
protocol TCP # 指定了访问后台真实主机的协议类型
# Real Server 1 configuration
# 指定了真实主机1的ip和端口
real_server 172.16.28.132 80 {
weight 1 # 指定了当前主机的权重
TCP_CHECK {
connection_timeout 10 # 指定了进行心跳检查的超时时间
nb_get_retry 3 # 指定了心跳超时之后的重复次数
delay_before_retry 3 # 指定了在尝试之前延迟多长时间
}
}
# Real Server 2 Configuration
real_server 172.16.28.133 80 {
weight 1 # 指定了当前主机的权重
TCP_CHECK {
connection_timeout 10 # 指定了进行心跳检查的超时时间
nb_get_retry 3 # 指定了心跳超时之后的重复次数
delay_before_retry 3 # 指定了在尝试之前延迟多长时间
}
}
}

将master和backup配置成完全一样的原因是,在master宕机时,可以根据backup的配置进行服务的无缝切换。

         在lvs+keepalived机器配置完成之后,我们下面配置两台应用服务器的nginx配置。这里我们是将nginx作为应用服务器,在其配置文件中配置返回状态码为200,并且会将当前主机的ip返回,如下是其配置:

worker_processes auto;
# pid /run/nginx.pid;
events {
worker_connections 786;
}
http {
server {
listen 80;
# 这里是直接返回200状态码和一段文本
location / {
default_type text/html;
return 200 "Hello, Nginx! Server zhangxufeng@172.16.28.132\n";
}
}
}
worker_processes auto;
# pid /run/nginx.pid;
events {
worker_connections 786;
}
http {
server {
listen 80;
# 这里是直接返回200状态码和一段文本
location / {
default_type text/html;
return 200 "Hello, Nginx! Server zhangxufeng@172.16.28.133\n";
}
}
}

可以看到,两台机器返回的文本中主机ip是不一样的。nginx配置完成后,可以通过如下命令进行启动:
sudo nginx
在启动nginx之后,我们需要配置虚拟ip,这是因为我们使用的lvs调度器是DR模式,前面我们讲到过,这种模式下,对客户端的响应是真实服务器直接返回给客户端的,而真实服务器需要将响应报文中的源ip修改为虚拟ip,这里配置的虚拟ip就是起这个作用的。我们编辑/etc/init.d/lvsrs 文件,写入如下内容:

#!/bin/bash
ifconfig lo:0 172.16.28.120 netmask 255.255.255.255 broadcast 172.16.28.120 up
route add -host 172.16.28.120 dev lo:0
echo "0" > /proc/sys/net/ipv4/ip_forward
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
exit 0
  • lo:表示当前主机真实网卡的名称;
  • 172.16.28.120:表示虚拟ip;

编写完成后运行该脚本文件即可。然后将两台lvs+keepalived机器上的keepalived服务启动起来即可:

sudo service keepalived start

最后可以通过如下命令查看配置的lvs+keepalived的策略:

[zhangxufeng@localhost keepalived]$ sudo ipvsadm -ln IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 172.16.28.120:80 rr -> 172.16.28.132:80 Route 1 0 0
2.2 集群测试

根据上述步骤,我们配置完成了一个lvs+keepalived+nginx的集群。在浏览器中,我们可以访问http://172.16.28.120 即可看到如下响应:

Hello, Nginx! Server zhangxufeng@172.16.28.132

多次刷新浏览器之后,可以看到浏览器中显示的文本切换如下,这是因为lvs的负载均衡策略产生的:

Hello, Nginx! Server zhangxufeng@172.16.28.133
3. 小结

本文首先对lvs和keepalived的工作原理进行了讲解,分别介绍了其工作的几种模式,然后对lvs+keepalived+nginx搭建nginx集群的方式进行详细讲解,并且说明了其中所需要注意的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1601568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt 4 QPushButton

Qt 常用控件 QPushButton 实例 Push Button:命令按钮。 入口文件 main.cpp #include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);MainWindow w;w.show();return a.exec(); }头文件 mainwindow.h …

【C 数据结构】单链表

文章目录 【 1. 基本原理 】1.1 链表的节点1.2 头指针、头节点、首元节点 【 2. 链表的创建 】2.0 创建1个空链表&#xff08;仅有头节点&#xff09;2.1 创建单链表&#xff08;头插入法&#xff09;*2.2 创建单链表&#xff08;尾插入法&#xff09; 【 3. 链表插入元素 】【…

rabbitmq每小时自动重启

引言 找了半天&#xff0c;最后通过系统日志发现是因为执行 systemctl restart rabbitmq-server 命令无法返回回调 systemctl 导致超时&#xff0c;自动关机。怀疑是 rabbitmq 与 systemctl 冲突&#xff0c;后 mq 升级版本已修复&#xff0c;可参考&#xff1a;https://github…

golang-基础语法

make 和 new 的区别 make 和 new 都是用来分配内存 make 只能对 slice map channel 进行初始化结构体实例。new 可以对任意类型进行初始化make 用于分配数据对象的具体实例&#xff0c;new 用于分配数据类型的默认值&#xff0c;并返回该数据的指针。 new 出来的 slice 、ma…

springboot Logback 不同环境,配置不同的日志输出路径

1.背景&#xff1a; mac 笔记本开发&#xff0c;日志文件写到/data/logs/下&#xff0c;控制台报出&#xff1a;Failed to create parent directories for [/data/logs/........... 再去手动在命令窗口创建文件夹data&#xff0c;报Read-only file system 2.修改logback-spri…

(C++) 稀疏表Sparse Table

目录 一、介绍 1.1 倍增 1.2 稀疏表ST 二、原理 三、代码实现 3.1 创建稀疏表 3.2 初始化数值 3.3 ST查询 一、介绍 1.1 倍增 倍增的思想是在数据空间特别大的时候&#xff0c;快速进行查找搜索而使用的。例如想要在一个数据量为n的递增数组中查找到等于x的下标&#x…

算法|基础算法|高精度算法

基础算法|位运算 1.高精度加法 2.高精度减法 3.高精度乘法 4.高精度除法 心有猛虎&#xff0c;细嗅蔷薇。你好朋友&#xff0c;这里是锅巴的C\C学习笔记&#xff0c;常言道&#xff0c;不积跬步无以至千里&#xff0c;希望有朝一日我们积累的滴水可以击穿顽石。 高精度加法 …

图灵奖得主AviWigderson:随机性与AI深度融合,引领计算科学新篇章

近日&#xff0c;理论计算机科学领域的杰出代表Avi Wigderson教授荣获了享有“计算机界诺贝尔奖”美誉的图灵奖&#xff0c;以表彰他对计算中随机性和伪随机性研究的杰出贡献。这一荣誉不仅彰显了Wigderson教授在计算理论领域的卓越成就&#xff0c;也为当前热门的AI和深度学习…

打破常规,重新定义PMP备考之路

今天我想和大家聊聊一个我们都不陌生的话题——PMP备考。你是不是也在备考的苦海中挣扎&#xff0c;或是听说过各种“速成”的神话&#xff1f;&#x1f914; 最近读到一篇文章&#xff08;来着圣略PMP培训讲师老杨&#xff09;&#xff0c;让我对PMP备考有了新的认识。原来&a…

如何用flutter写一个好的登录页面

编写一个好的登录页面是构建用户友好且安全的移动应用的重要一步。下面是使用Flutter编写一个好的登录页面的一些建议和步骤&#xff1a; 1. 设计用户界面 1.简洁明了的布局&#xff1a;确保界面简洁明了&#xff0c;不要过分复杂&#xff0c;避免用户感到困惑。 2.清晰的输入框…

1、IPEX-LLM(原名BigDL-LLM)环境配置

IPEX-LLM 是一个为Intel XPU (包括CPU和GPU) 打造的轻量级大语言模型加速库&#xff0c;在Intel平台上具有广泛的模型支持、最低的延迟和最小的内存占用。 您可以使用 IPEX-LLM 运行任何 PyTorch 模型&#xff08;例如 HuggingFace transformers 模型&#xff09;。在运行过程中…

Redis的IO模型 和 多线程问题

Redis中的线程和IO模型 什么是Reactor模式 &#xff1f;单线程Reactor模式流程单线程Reactor&#xff0c;工作者线程池多Reactor线程模式 Redis中的线程和IO概述socketI/O多路复用程序文件事件分派器文件事件处理器文件事件的类型总结 多线程问题1. Redis6.0之前的版本真的是单…

05节-51单片机-模块化编程

1.两种编程方式的对比 传统方式编程&#xff1a; 所有的函数均放在main.c里&#xff0c;若使用的模块比较多&#xff0c;则一个文件内会有很多的代码&#xff0c;不利于代码的组织和管理&#xff0c;而且很影响编程者的思路 模块化编程&#xff1a; 把各个模块的代码放在不同的…

学习java时候的笔记(十六)

常用API Math 是一个帮助我们用于进行数学计算的工具类 Math中常用的方法 方法名说明abs(int a)获取参数的绝对值abs(-1) > 1ceil(double b)向上取整1.1 > 2floor(double b)向下取整1.7>1round(float a)四舍五入max(int a, int b)取两个整数的最大值max(2,3) >…

LeetCode-924. 尽量减少恶意软件的传播【深度优先搜索 广度优先搜索 并查集 图 哈希表】

LeetCode-924. 尽量减少恶意软件的传播【深度优先搜索 广度优先搜索 并查集 图 哈希表】 题目描述&#xff1a;解题思路一&#xff1a;解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 给出了一个由 n 个节点组成的网络&#xff0c;用 n n 个邻接矩阵图…

02_对象树

#include "mypushbutton.h" #include <QDebug>MyPushButton::MyPushButton(QWidget *parent): QPushButton(parent) {qDebug()<<"我的按钮类构造调用"; }MyPushButton::~MyPushButton() {qDebug()<<"我的按钮类析构调用"; }交…

「JavaEE」线程

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;JavaEE &#x1f387;欢迎点赞收藏加关注哦&#xff01; 线程 &#x1f349;线程&#x1f34c;多线程&#x1f34c;线程与进程的联系&区别&#x1f34c;多线程编程&#x1f34c;创建线程&a…

spring02:DI(依赖注入)

spring02&#xff1a;DI&#xff08;依赖注入&#xff09; 文章目录 spring02&#xff1a;DI&#xff08;依赖注入&#xff09;前言&#xff1a;一、构造器注入&#xff08;constructor&#xff09;二、set注入&#xff1a;分析&#xff1a; 1. Student类&#xff1a;2. Addres…

【大语言模型】轻松本地部署Stable Diffusion

硬件要求&#xff1a; 配备至少8GB VRAM的GPU&#xff0c;如果你的电脑只有CPU&#xff0c;请看到最后。根据部署规模&#xff0c;需要足够的CPU和RAM。 软件要求&#xff1a; Python 3.7或更高版本。支持NVIDIA GPU的PyTorch。Hugging Face的Diffusers库。Hugging Face的Tr…

什么是神经网络和机器学习?【云驻共创】

什么是神经网络和机器学习&#xff1f; 一.背景 在当今数字化浪潮中&#xff0c;神经网络和机器学习已成为科技领域的中流砥柱。它们作为人工智能的支柱&#xff0c;推动了自动化、智能化和数据驱动决策的进步。然而&#xff0c;对于初学者和专业人士来说&#xff0c;理解神经…