Nginx + keepAlived实现服务高可用
Nginx是什么?
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。
其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、简单的配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx 1.0.4发布。
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。描述来源百度百科
什么是高可用?
“高可用性”HA(High Availability)通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性。如果一个系统能够一直提供服务,那么这个可用性则是百分之百,但是天有不测风云。所以我们只能尽可能的去减少服务的故障。
什么是KeepAlived?
KeepAlived简介
KeepAlived是Linux下一个轻量级别的高可用解决方案。高可用(High Avalilability,HA),其实两种不同的含义:广义来讲,是指整个系统的高可用行,狭义的来讲就是之主机的冗余和接管,它与HeartBeat RoseHA 实现相同类似的功能,都可以实现服务或者网络的高可用,但是又有差别,HeartBeat是一个专业的、功能完善的高可用软件,它提供了HA 软件所需的基本功能,比如:心跳检测、资源接管,检测集群中的服务,在集群节点转移共享IP地址的所有者等等。HeartBeat功能强大,但是部署和使用相对比较麻烦,与HeartBeat相比,KeepAlived主要是通过虚拟路由冗余来实现高可用功能,虽然它没有HeartBeat功能强大,但是KeepAlived部署和使用非常的简单,所有配置只需要一个配置文件即可以完成。
VRRP协议与工作原理
在现实的网络环境中。主机之间的通信都是通过配置静态路由或者(默认网关)来完成的,而主机之间的路由器一旦发生故障,通信就会失效,因此这种通信模式当中,路由器就成了一个单点瓶颈,为了解决这个问题,就引入了VRRP协议。
熟悉网络的学员对VRRP协议应该不陌生,它是一种主备模式的协议,通过VRRP可以在网络发生故障时透明的进行设备切换而不影响主机之间的数据通信,这其中涉及到两个概念:物理路由器和虚拟路由器。
VRRP可以将两台或者多台物理路由器设备虚拟成一个虚拟路由,这个虚拟路由器通过虚拟IP(一个或者多个)对外提供服务,而在虚拟路由器内部十多个物理路由器协同工作,同一时间只有一台物理路由器对外提供服务,这台物理路由设备被成为:主路由器(Master角色),一般情况下Master是由选举算法产生,它拥有对外服务的虚拟IP,提供各种网络功能,如:ARP请求,ICMP 数据转发等,而且其它的物理路由器不拥有对外的虚拟IP,也不提供对外网络功能,仅仅接收MASTER的VRRP状态通告信息,这些路由器被统称为“BACKUP的角色”,当主路由器失败时,处于BACKUP角色的备份路由器将重新进行选举,产生一个新的主路由器进入MASTER角色,继续提供对外服务,整个切换对用户来说是完全透明的。
每个虚拟路由器都有一个唯一的标识号,称为VRID,一个VRID与一组IP地址构成一个虚拟路由器,在VRRP协议中,所有的报文都是通过IP多播方式发送的,而在一个虚拟路由器中,只有处于Master角色的路由器会一直发送VRRP数据包,处于BACKUP角色的路由器只会接受Master角色发送过来的报文信息,用来监控Master运行状态,一一般不会发生BACKUP抢占的情况,除非它的优先级更高,而当MASTER不可用时,BACKUP也就无法收到Master发过来的信息,于是就认定Master出现故障,接着多台BAKCUP就会进行选举,优先级最高的BACKUP将称为新的MASTER,这种选举角色切换非常之快,因而保证了服务的持续可用性。
什么是KeepAlived
KeepAlived起初是为LVS设计的,专门用来监控集群系统中各个服务节点的状态,它根据TCP/IP参考模型的第三、第四层、第五层交换机制检测每个服务节点的状态,如果某个服务器节点出现异常,或者工作出现故障,KeepAlived将检测到,并将出现的故障的服务器节点从集群系统中剔除,这些工作全部是自动完成的,不需要人工干涉,需要人工完成的只是修复出现故障的服务节点。后来KeepAlived又加入了VRRP的功能,VRRP(Vritrual Router Redundancy Protocol,虚拟路由冗余协议)出现的目的是解决静态路由出现的单点故障问题,通过VRRP可以实现网络不间断稳定运行,因此KeepAlived一方面具有服务器状态检测和故障隔离功能,另外一方面也有HA cluster功能,下面介绍一下VRRP协议实现的过程。
KeepAlived的工作原理
上面我们介绍了KeepAlived通过VRRP实现高可用性的工作原理,而KeepAlived作为一个高性能集群软件,它还能实现对集群中服务器运行状态的监控以及故障隔离,下面我们介绍一下KeepAlived对服务器运行状态和故障隔离的工作原理。
KeepAlived工作在TCP/IP 参考模型的 三层、四层、五层,也就是分别为:网络层,
传输层和应用层,根据TCP、IP参数模型隔层所能实现的功能,KeepAlived运行机制如下:
在网络层:我们知道运行这4个重要的协议,互联网络IP协议,互联网络可控制报文协议ICMP、地址转换协议ARP、反向地址转换协议RARP,在网络层KeepAlived在网络层采用最常见的工作方式是通过ICMP协议向服务器集群中的每一个节点发送一个ICMP数据包(有点类似与Ping的功能),如果某个节点没有返回响应数据包,那么认为该节点发生了故障,KeepAlived将报告这个节点失效,并从服务器集群中剔除故障节点。
在传输层:提供了两个主要的协议:传输控制协议TCP和用户数据协议UDP,传输控制协议TCP可以提供可靠的数据输出服务、IP地址和端口,代表TCP的一个连接端,要获得TCP服务,需要在发送机的一个端口和接收机的一个端口上建立连接,而KeepAlived在传输层里利用了TCP协议的端口连接和扫描技术来判断集群节点的端口是否正常,比如对于常见的WEB服务器80端口。或者SSH服务22端口,KeepAlived一旦在传输层探测到这些端口号没有数据响应和数据返回,就认为这些端口发生异常,然后强制将这些端口所对应的节点从服务器集群中剔除掉。
在应用层:可以运行FTP,TELNET,SMTP,DNS等各种不同类型的高层协议,KeepAlived的运行方式也更加全面化和复杂化,用户可以通过自定义KeepAlived工作方式,例如:可以通过编写程序或者脚本来运行KeepAlived,而KeepAlived将根据用户的设定参数检测各种程序或者服务是否允许正常,如果KeepAlived的检测结果和用户设定的不一致时,KeepAlived将把对应的服务器从服务器集群中剔除
常见的互联网结构
上图代理服务器的Nginx可以和代理Web-Service的不为同一台。
从上图可以看出在生产环境上很多时候是以Nginx
做反向代理对外提供服务,但是Nginx
难免会遇见故障,如果Nginx
服务器宕机那么所有对外提供的接口都将导致无法访问。系统还有很多地方都能实现高可用,我今天分享的是实现服务的Nginx服务的高可用。
所以我们实现【客户端层】到【反向代理层】的高可用,是通过反向代理层的冗余来实现的。以nginx为例:有两台nginx,一台对线上提供服务,另一台冗余以保证高可用,常见的实践是keepalived存活探测,相同virtual IP提供服务。
故障说明
例如上图的典型多模块互联网结构图,如果我们是前后端分离的项目,后台服务有多个模块,那么这时通常的做法是通过一个Nginx服务器反向代理这些模块分别部署的服务器,对外提供一个统一的URL地址供Web-Service端调用,Nginx根据Web-Service调用的请求URL,转发请求给对应的服务器上的服务。如果这些转发URL请求的Nginx服务器挂了,那么就无法再进行请求转发,Web-Service端也就会请求报错。
准备工作
两台虚拟机IP为:192.168.102.132/133地址以你的实际地址为准
更换yum源:
下载wget(下载新的CentOS-Base.repo 时用到也可以不下载;使用curl代替)
yum install wget
备份源
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
下载新的CentOS-Base.repo 到 /etc/yum.repos.d/ (CentOS7)
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
清除缓存
yum clean all
生成缓存
yum makecache
更新
yum update
安装JDK8
去Oracle官网下载JDK8(PS:注意选择Linux对应的版本下载)
创建目录
mkdir /usr/local/java/
下载完成后,使用xftp上传到虚拟机/usr/local/java/目录下,进入/usr/local/java/执行解压命令
tar -zxvf jdk-8u301-linux-x64.tar.gz
解压完成后/usr/local/java/目录下的文件
设置环境变量
vim /etc/profile
在末尾添加
export JAVA_HOME=/usr/local/java/jdk1.8.0_301 #你对应的路径,如我上图解压完成的就是/usr/local/java/jdk1.8.0_301export JRE_HOME=${JAVA_HOME}/jreexport CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/libexport PATH=${JAVA_HOME}/bin:$PATH
保存退出
使环境变量生效
source /etc/profile
创建软链接
ln -s /usr/local/java/jdk1.8.0_301/bin/java /usr/bin/java
检查
java -version
出现下图说明安装成功
安装Tomcat
在Tomcat官网下载Tomcat9安装包
创建目录
mkdir /usr/local/tomcat/
下载完成后,使用xftp上传到虚拟机/usr/local/tomcat/目录下,进入/usr/local/tomcat/执行解压命令
tar -zxvf apache-tomcat-9.0.53.tar.gz
解压完成后/usr/local/tomcat/目录下的文件
进入tomcat的bin目录
cd apache-tomcat-9.0.53/bin/
创建setenv.sh
vi setenv.sh
输入
CATALINA_HOME=/usr/local/tomcat/apache-tomcat-9.0.53 #你的Tomcat安装目录CATALINA_BASE=/usr/local/tomcat/apache-tomcat-9.0.53 #你的Tomcat安装目录JAVA_HOME/usr/local/java/jdk1.8.0_301 #你的Java安装目录JRE_HOME=/usr/local/java/jdk1.8.0_301/jre/ #你的Java安装目录下的jre目录CATALINA_PID=/usr/local/tomcat/apache-tomcat-9.0.53/tomcat.pid #你的Tomcat安装目录JAVA_OPTS="-server -XX:PermSize=256M -XX:MaxPermSize=1024m -Xms512M -Xmx1024M -XX:MaxNewSize=256m"
保存退出
给setenv.sh权限
chmod +x setenv.sh
切换至/usr/lib/systemd/system
cd /usr/lib/systemd/system
创建tomcat.service
vi tomcat.service
输入
[Unit]Description=TomcatAfter=syslog.target network.target remote-fs.target nss-lookup.target[Service]Type=forkingPIDFile=/usr/local/tomcat/apache-tomcat-9.0.53/tomcat.pid #和前面配置的CATALINA_PID一致ExecStart=/usr/local/tomcat/apache-tomcat-9.0.53/bin/startup.sh #/usr/local/tomcat/apache-tomcat-9.0.53你的Tomcat安装目录ExecReload=/bin/kill -s HUP $MAINPIDExecStop=/bin/kill -s QUIT $MAINPIDPrivateTmp=true[Install]WantedBy=multi-user.target
保存退出
配置开机启动
systemctl enable tomcat
启动Tomcat
systemctl start tomcat
停止Tomcat
systemctl stop tomcat
重启Tomcat
systemctl restart tomcat
在游览器输入你的IP地址:8080看到一只小喵咪表示Tomcat安装成功并成功启动;如果不能访问检查是不是没有开放端口,因为是虚拟机所以我直接关闭防火墙也就行了,如果是生产环境不建议关闭防火墙,应该是开放对应的端口,如果是云服务器,例如阿里云ECS服务器,在云服务器的安全组里面配置开放对应的端口就可以了
查看防火墙状态
firewall-cmd --state
停止防火墙
systemctl stop firewalld.service
禁止防火墙开机启动
systemctl disable firewalld.service
CentOS7开放端口
firewall-cmd --zone=public --add-port=8080/tcp --permanent # 开放8080端口
CentOS7关闭端口
firewall-cmd --zone=public --remove-port=8080/tcp --permanent #关闭82080端口
重启防火墙
firewall-cmd --reload
开放端口后再次访问,就可以看到小猫咪了如下图
安装Nginx
安装所需插件
yum -y install gcc
yum install -y pcre pcre-devel
yum install -y zlib zlib-devel
yum install -y openssl openssl-devel
这些插件都是安装Nginx所必须的,具体每个插件的作用就不做详细说明,有兴趣的自己百度就可以了。
下载Nginx,在Nginx官网下载地址下载你需要的版本就可以了,这里我下载的是最新的nginx-1.20.1最新的稳定版本
创建目录
mkdir /usr/local/nginx/
下载完成后,使用xftp上传到虚拟机/usr/local/nginx/目录下,进入/usr/local/nginx/执行解压命令
tar -zxvf nginx-1.20.1.tar.gz
解压完成后/usr/local/nginx/目录下的文件,,解压完成应该只有下图红框框出来的两个文件,其余的是在nginx安装完成后有的
进入刚刚解压出来的nginx-1.20.1文件夹
cd nginx-1.20.1
执行以下三个命令
./configure #配置Nginx编译选项,默认即可
make #编译
make install #编译
三个命令执行完成后切换到/usr/local/nginx安装目录
cd /usr/local/nginx/
进入conf配置目录,配置nginx.conf文件(后面做高可用时需修改,现在暂时不需要)
切换目录到/usr/local/nginx/sbin下面
cd /usr/local/nginx/sbin
执行
./nginx
查看nginx服务是否启动成功
ps -ef | grep nginx
如上图所示表示启动成功,在游览器里面输入你的IP地址,应该能看到下面的这张图
如果不行,参考上面Tomcat安装,开放端口80
配置nginx开机启动
进入/lib/systemd/system/目录,创建nginx.service文件
cd /lib/systemd/system/
vim nginx.service
输入
[Unit]Description=nginx After=network.target [Service] Type=forking ExecStart=/usr/local/nginx/sbin/nginxExecReload=/usr/local/nginx/sbin/nginx reloadExecStop=/usr/local/nginx/sbin/nginx quitPrivateTmp=true [Install] WantedBy=multi-user.target
保存退出
执行systemctl enable nginx.service使nginx开机启动
systemctl enable nginx.service
启动nginx
systemctl start nginx.service
结束nginx
systemctl stop nginx.service
重启nginx
systemctl restart nginx.service
安装keepalived
yum
方式直接安装即可,该方式会自动安装依赖:
yum -y install keepalived
yum
方式安装的会生产配置文件在/etc/keepalived
下:
切换到/etc/keepalived目录编辑keepalived.conf
cd /etc/keepalived
vi keepalived.conf
编辑keepalived.conf为以下内容(PS:当前操作的虚拟机为主机,后面还有一台虚拟机作为备机,两个虚拟机环境一致,唯一配置不同的就是keepalived.conf文件主机的state为MASTER;备机的为BACKUP)
#检测脚本vrrp_script chk_http_port { script "/usr/local/src/check_nginx_pid.sh" #心跳执行的脚本,检测nginx是否启动 interval 2 #(检测脚本执行的间隔,单位是秒) weight 2 #权重}#vrrp 实例定义部分vrrp_instance VI_1 { state MASTER # 指定keepalived的角色,MASTER为主,BACKUP为备 interface ens33 # 当前进行vrrp通讯的网络接口卡(当前centos的网卡) 用ifconfig查看你具体的网卡 virtual_router_id 66 # 虚拟路由编号,主从要一直 priority 100 # 优先级,数值越大,获取处理请求的优先级越高 advert_int 1 # 检查间隔,默认为1s(vrrp组播周期秒数) #授权访问 authentication { auth_type PASS #设置验证类型和密码,MASTER和BACKUP必须使用相同的密码才能正常通信 auth_pass 1111 } track_script { chk_http_port #(调用检测脚本) } virtual_ipaddress { 192.168.102.199 # 定义虚拟ip(VIP),可多设,每行一个 }}
virtual_ipaddress
里面可以配置vip,在线上通过vip来访问服务。
interface
需要根据服务器网卡进行设置通常查看方式
ip addr
authentication
配置授权访问后备机也需要相同配置
进入/usr/local/src目录创建心跳执行的脚本
cd /usr/local/src/
vim check_nginx_pid.sh
输入
#!/bin/bash#检测nginx是否启动了A=`ps -C nginx --no-header |wc -l` if [ $A -eq 0 ];then #如果nginx没有启动就启动nginx systemctl start nginx #重启nginx if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then #nginx重启失败,则停掉keepalived服务,进行VIP转移 killall keepalived fifi
保存退出
脚本授权
chmod 775 check_nginx_pid.sh
至此我们需要的环境已经搭建完成,现在我们编写一个简单的服务,然后发布来测试一下,我用的是IntelliJ IDEA创建的一个Spring Boot项目
创建测试项目
创建过程如下
填写你的项目信息
如下为我的配置信息,勾选packaging为War会自动帮我们配置打包移除内置Spring Boot内置的Tomcat
勾选你需要的依赖,因为是测试项目不需要任何依赖,所以直接点击Finish,等待项目创建完成
创建完成后我们在目录下建一个controller文件夹,在下面建一个TestHighAvailabilityController.java文件,结构如下图
这里有点注意事项;我们新创建的项目的配置文件为application.properties文件,我们修改其为application.yml然后在里面写入
server: port: 9000 #配置项目启动端口,默认为8080
我们在TestHighAvailabilityController.java写入一下代码
package com.tan.controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * @description: 高可用测试接口 * @author: tan * @email: tan2511217211@163.com * @date: 2021/9/23 23:18 **/@RestController@RequestMapping("/api")public class TestHighAvailabilityController { @GetMapping("/test") public String test(){ return "Message from IP: 192.168.102.132/3"; }}
这里我为了后面能直观的看到高可用实现了请求切换,所以我的返回语句分别是Message from IP: 192.168.102.133和Message from IP: 192.168.102.132
打两次包,把
返回信息为Message from IP: 192.168.102.132打的包放到192.168.102.132这个IP的服务器上;
返回信息为Message from IP: 192.168.102.133打的包放到192.168.102.133这个IP的服务器上;
编写完执行打包命令(可使用命令,亦可以直接在IDEA里面使用插件的打包命令,如果使用IDEA的maven插件按下图依次执行)
然后把打包生成的war包分别上传至两台虚拟机
然后配置Nginx反向代理
在nginx.conf文件里面新增一个location配置,写入以下内容
location ^~/highAvailability/ { #highAvailability为你的请求前缀 proxy_pass http://192.168.102.132:9000/highAvailability/; #你的服务地址}
如下图所示
192.168.102.133的服务器只需把IP地址变更就行,和上面的配置一样
配置Tomcat
因为使用的是外部部署的方式,所以我们在application.yml里面配置的
server: port: 9000
没有效果,我们需要在安装的Tomcat里面修改Tomcat配置才有效
进入Tomcat安装目录
cd /usr/local/tomcat/apache-tomcat-9.0.53
创建一个webapps1文件夹,里面存放我们刚刚上传的war包
mkdir webapps1/
进入conf目录修改server.xml文件,我们需要在server.xml里面新增我们刚刚创建的webapps1配置,新增内容如下
</Service><Service name="Catalina"> <Connector port="9000" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <Engine name="Catalina" defaultHost="localhost"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps1" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine></Service>
具体如下图所示
与默认配置唯一的区别就是appBase,两台虚拟机配置一样
配置完成后重启Tomcat,Nginx,KeepAlived然后再游览器访问我们在KeepAlived配置的VIP(我设置的为192.168.102.199)地址
可以看到这时返回的是IP为192.168.102.132虚拟机服务的信息,这时我们停掉192.168.102.132虚拟机的nginx服务
再次访问发现返回信息已经变成192.168.102.133虚拟机服务返回的信息了,如下图所示
至此,我们就已经实现了服务的高可用。
文章不足之处,可以在下面评论交流