前言
使用集群是网站解决高并发、海量数据问题的常用手段。当一台服务器的处理能力、存储空间不足时,不要企图去换更强大的服务器,对大型网站而言,不管多么强大的服务器,都满足不了网站持续增长的业务需求。这种情况下,更恰当的做法是增加一台服务器分担原有服务器的访问及存储压力。通过负载均衡调度服务器,将来自浏览器的访问请求分发到应用服务器集群中的任何一台服务器上,如果有更多的用户,就在集群中加入更多的应用服务器,使应用服务器的负载压力不再成为整个网站的瓶颈。
摘自《大型网站技术架构_核心原理与案例分析》
认识Nginx+keepalived
keepalived:Keepalived主要用作RealServer的健康状态检查以及LoadBalance主机和BackUP主机之间failover的实现。提到keepalived我们就会想到一个名词就是高可用,何为高可用? 高可用就是通过设计来保证机器的高度可用性。举个例子比如现在我们手头有两台互联互通的机器,如果我们有一台在跑服务的时候突然宕机了那我们保证服务的正常运行就要把服务向另一台机器转移了,那如果这个时候机器可以自动来接管,而不影响服务那我们就称之为高可用。
既然知道了高可用的概念,我们就来看一下keepalived是怎么做到的高可用,那我们就要看下keepalived的工作原理:通过vrrp协议实现。
通过上图我们可以看到,client端在向server端发送请求的时候会通过vrrp协议的vip(虚拟ip)来进行交互。服务端本身都会有物理ip为什么还需要在添加一个虚拟ip呢?高可用是指在两台或者两台机器以上的集群概念,我们为了保证client端请求的稳定性我们引入vip这样虚拟ip他就可以再master宕掉后自动漂移到backup机器上。
keepalived的工作方式:抢占式,非抢占式(下面具体实现里会有两种模式的详细配置介绍)
Nginx
-
nginx是一个高性能的http和反向代理服务器,特点是占有内存少,并发能力强,在中国大陆许多知名的大公司网站也都在nginx例,例如百度,京东,淘宝,网易,腾讯等
-
Nginx 的特点
1.跨平台:Nginx可以大多数Unix like Os编译运行,而且也有Windows的移植版本。
2.非阻塞、高并发连接:数据复制时,磁盘I/O的第一阶段是非阻塞的。官方测试能够支撑5万并发连接,在实际生产环境中跑到2~3万并发连接数.(这得益于Nginx使用了最新的epoll模型)
3.事件驱动:通信机制采用epoll模型,支持更大的并发连接。
4.master/worker结构:一个master进程,生成一个或多个worker进程
5.内存消耗小:处理大并发的请求内存消耗非常小。在3万并发连接下,开启的10个Nginx 进程才消耗150M内存(15M*10=150M)
6.成本低廉:Nginx为开源软件,可以免费使用。而购买F5 BIG-IP、NetScaler等硬件负载均衡交换机则需要十多万至几十万人民币
7.内置的健康检查功能:如果 Nginx Proxy 后端的某台 Web 服务器宕机了,不会影响前端访问。
8.节省带宽:支持 GZIP 压缩,可以添加浏览器本地缓存的 Header 头。
9.稳定性高:用于反向代理,宕机的概率微乎其微。 -
Nginx的内部(进程)模型
具体实现
Nginx+keepalived的安装部署及启停流程
Nginx安装及配置(主备)
- 安装编译Nginx所需依赖包
sudo apt-get install gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel (先更新源,ubuntu某些包的名称与centos不一样需要查一下) ubuntu : gcc g++ make automake autoconf libtool libpcre3 libpcre3-dev zlib1g zlib1g.dev openssl libssl-dev - 下载编译安装Nginx
cd /usr/local/src/
wget http://nginx.org/download/nginx-1.13.0.tar.gz
cd /usr/local/src/
tar -zxvf nginx-1.13.0.tar.gz
cd nginx-1.13.0
./configure --prefix=/usr/local/nginx
make && make install - NGINX配置(重要)
user darwin darwin;
worker_processes 4;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 4096;
}
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 50m;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
gzip on;
upstream prod {
server 10.3.140.7:9101;
server 10.3.140.7:9141;
server 10.3.140.7:9400;
server 10.3.140.7:9181;
#server 10.3.140.8:9102;
#server 10.3.140.8:9142;
#server 10.3.140.8:9182;
#server 10.3.140.8:9401;
}
#server {
#listen 8080;
#server_name 10.3.140.7 localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
#location / {
#root html;
#proxy_pass http://prod;
#index index.html index.htm;
#}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
#error_page 500 502 503 504 /50x.html;
#location = /50x.html {
#root html;
#}
#proxy_connect_timeout 600;
#proxy_read_timeout 600;
#proxy_send_timeout 600;
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
#}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
server {
listen 8080;
#listen 443;
ssl on;
charset gbk;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#ssl_protocols TLSv1;
ssl_certificate /usr/local/nginx/conf/server.crt;
ssl_certificate_key /usr/local/nginx/conf/server.key;
server_name trnetsvr.iqubic.net;
#server_name 10.3.140.7 trnetsvr.iqubic.net;
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 3m;
ssl_ciphers AESGCM:ALL:!DH:!EXPORT:!RC4:+HIGH:!MEDIUM:!LOW:!aNULL:!eNULL;
ssl_prefer_server_ciphers on;
#error_page 497 400 https://$host:8080$request_url;
location / {
#root /usr/local/nginx/html/;
proxy_pass http://prod;
index index.html index.html;
}
}
}
具体配置信息请查阅nginx的官方文档。
如果需要添加https的反向代理的需要安装编译openssl模块。(流程请自行百度)
- 检查系统防火墙并打开对应的端口
vi /etc/sysconfig/iptables
Nginx -A INPUT -m state --state NEW -m tcp -p tcp --dport 5000 -j ACCEPT
service iptables restart
- nginx 启停及测试
查看nginx版本信息
/usr/local/nginx/sbin/nginx -V
测试nginx是否配置成功
/usr/local/nginx/sbin/nginx -t
启动nginx
/usr/local/nginx/sbin/nginx
停止nginx
sudo pkill nginx
动态加载nginx配置(在不停服务的情况下更新配置)
/usr/local/nginx/sbin/nginx -s reload
Keepalived安装及配置(以较麻烦的Ubuntu为例,centos也适用)
- 下载 keepalived,keepalived官方下载地址
- 解压安装步骤
tar zxvf keepalived-2.0.15.tar.gz
cp -r keepalived-2.0.15
/usr/local/src
cd /usr/local/src/keepalived-2.0.15/
./configure --prefix=/usr/local/keepalived
make && make install
- 使keepalived命令能直接使用
cp /usr/local/keepalived/sbin/keepalived /sbin/ - 创建配置文件并修改(重要)
mkdir -p /etc/keepalived
cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/
vim /etc/keepalived/keepalived.conf
cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/
bal_defs {
notification_email {
wanghao5276@163.com
bal_defs {
notification_email {
wanghao5276@163.com
}
notification_email_from wanghao5276@163.com
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id LVS_DEVEL
}
vrrp_script chk_nginx {
script "/home/darwin/check_nginx.sh"
interval 2
weight 2
}
# VIP1
vrrp_instance VI_1 {
state MASTER
interface bond0
lvs_sync_daemon_inteface eth0
virtual_router_id 150
priority 100
advert_int 5
nopreempt
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.3.140.10
}
track_script {
chk_nginx
}
smtp_alert
}
配置具体信息请查询keepalived官方文档。
- 创建检查nginx状态的脚本
- 实时监测nginx进程并瞬间重启nginx的脚本(注意权限)
#!/bin/bash
#auto check nginx process
#by author wanghao
A=`ps -C nginx --no-header | wc -l`
if [ $A -eq 0 ];then
/usr/local/nginx/sbin/nginx #检测nginx进程数为0,重启下nginx
if [`ps -C nginx --no-header | wc -l` -eq 0];then #如果nginx重启失败了,则关掉keepalived转移vip
killall keepalived
fi
fi
2. 检测keepalived进程并重启脚本
#!/bin/bash
#auto check nginx process
#by author wanghao
A=`ps -C keepalived --no-header | wc -l`
if [ $A -eq 0 ];then
sudo /etc/init.d/keepalived start #检测keepalived进程数为0,重启下keepalived
python /etc/keepalived/sendmail.py
fi
3. 发送邮件脚本(python实现)
#-*- coding: UTF-8 -*-
import smtplib
import socket
import time
from email.header import Header
from email.mime.text import MIMEText
# 第三方 SMTP 服务
mail_host = "smtp.163.com" # SMTP服务器
mail_user = "wanghao5276@163.com" # 用户名
mail_pass = "TUNTWNHQWGCVUNQG" # 授权密码,非登录密码
sender = 'wanghao5276@163.com' # 发件人邮箱(最好写全, 不然会失败)
receivers = [ '1214088649@qq.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
# 获取本机计算机名称
hostname = socket.gethostname()
# 获取本机ip
#-*- coding: UTF-8 -*-
import smtplib
import socket
import time
from email.header import Header
from email.mime.text import MIMEText
# 第三方 SMTP 服务
mail_host = "smtp.163.com" # SMTP服务器
mail_user = "wanghao@163.com" # 用户名
mail_pass = "TUNTWNHQWGCVUNQG" # 授权密码,非登录密码
sender = 'wanghao@163.com' # 发件人邮箱(最好写全, 不然会失败)
receivers = [ 'wanghao@qq.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
# 获取本机计算机名称
hostname = socket.gethostname()
# 获取本机ip
ip_str = socket.gethostbyname(hostname)
content = "keepalived: %s change to state,please check and restore. Start method: sudo /etc/init.d/keepalived start.And check the status." % (ip_str)
title = 'Nginx-Keepalived服务告警信息-{}'.format(time.asctime( time.localtime(time.time()) )) # 邮件主题
def sendEmail():
message = MIMEText(content, 'plain', 'utf-8') # 内容, 格式, 编码
message['From'] = "{}".format(sender)
message['To'] = ",".join(receivers)
message['Subject'] = title
try:
smtpObj = smtplib.SMTP_SSL(mail_host, 465) # 启用SSL发信, 端口一般是465
smtpObj.login(mail_user, mail_pass) # 登录验证
smtpObj.sendmail(sender, receivers, message.as_string()) # 发送
print("mail has been send successfully.")
except smtplib.SMTPException as e:
print(e)
def send_email2(SMTP_host, from_account, from_passwd, to_account, subject, content):
email_client = smtplib.SMTP(SMTP_host)
email_client.login(from_account, from_passwd)
# create msg
msg = MIMEText(content, 'plain', 'utf-8')
msg['Subject'] = Header(subject, 'utf-8') # subject
msg['From'] = from_account
msg['To'] = to_account
email_client.sendmail(from_account, to_account, msg.as_string())
email_client.quit()
if __name__ == '__main__':
sendEmail()
设置服务启动
复制服务脚本
cp keepalived-2.0.15/keepalived/etc/init.d/keepalived /etc/init.d/
#注意:上面的服务启动脚本是在源文件目录,而不是安装目录(/usr/local/keepalived)下,这是2.0之后的变化。
修正相关配置问题
vim /etc/init.d/keepalived 可以看到下图内容,其中有3个地方有问题。
图中1: 由于 ubuntu下没有 /etc/rc.d/init.d/functions,需要为其建立软链接
mkdir -p /etc/rc.d/init.d
ln -s /lib/lsb/init-functions /etc/rc.d/init.d/functions
图中2:拷贝相应文件的源配置文件
注释内容有介绍 ,这个源配置文件(在里面设置keepalived启动参数)
mkdir /etc/sysconfig
cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
图中3:安装daemon,并修改命令
-
安装 daemon: apt install daemon
-
图中3的命令 修改为daemon – keepalived ${KEEPALIVED_OPTIONS} #加了一个“–”
说明:命令里的变量 ${KEEPALIVED_OPTIONS} 是在/etc/sysconfig/keepalived里设置的,默认内容如
这里的-D 代表记录详细日志。那么命令 daemon keepalived ${KEEPALIVED_OPTIONS}的结果是
daemon keepalived -D
这个命令是有问题的,其中的-D本来是给keepalived用的,但这样组合后被认为是daemon命令的参数。这会导致服务不能启动。 如果不修改,会提示启动失败,但却不输出具体信息。但可以通过查看 /var/log/syslog 找到错误信息
$Starting keepalived: daemon: option requires an argument -- 'D' 。这是一个底层错误,所以只在系统日志里看到。
执行 daemon --help, 可以看到帮助信息
可以看出, daemon命令的-D参数是需要一个path参数的,所以会出现系统日志里的错误。
由 usage: daemon [options] [--] [cm arg...],可知正确的命令格式应该是:daemon -- keepalived -D
所以上面力中标示的第3处,应该修改为 daemon -- keepalived ${KEEPALIVED_OPTIONS}
注意:每次修改/etc/init.d/keepalived后,需要重新运行 systemctl daemon-reload 重新加载服务脚本
启动测试(两台都启动)
systemctl daemon-reload #重新加载服务
service keepalived start #启动keealived服务
service keepalived status #可以查看运行状态
ip a #查看是否占用了VIP,只有主服务器可以占用,主有问题时,才会漂移到从服务器,并且从服务器状态变为MASTER
如果启动出现问题,除了这里的输出信息,还可以通过 /var/log/syslog来查看问题
正常状态是
-
在任意一台服务器上关闭nginx,会发现keepalived自动重启nginx
-
关闭任意一台服务器,VIP漂移到另一台服务器
本文章以理论-实践的一整套搭建Nginx+keepalived实现HA高可用的主从热备,负载均衡,反向代理流程。由于本人水平有限,文章中难免存在一些错误或疏漏,恳请读者批评指正,在评论区讨论交流。