LVS+Nginx实现高可用集群
主从复制高可用Redis集群
Redis缓存雪崩,穿透
常见问题
Nginx(engine x)是一个高性能的 HTTP 和反向代理web 服务器,同时也提供 IMAP/POP3/SMTP服务。
来源:Netcraft
Nginx HTTP服务知识大全
Nginx 核心知识
# 安装依赖 yum install -y gcc-c++ # 安装 gcc 环境 yum install -y pcre pcre-devel # 安装 PRCE 库,用于解析正则表达式 yum install -y zlib zlib-devel yum install -y openssl openssl-devel # https协议 wget http://nginx.org/download/nginx-1.16.1.tar.gz # 下载稳定版 tar -zxvf nginx-1.16.1.tar.gz # 解压 mkdir -p /var/temp/nginx # 创建临时目录 cd nginx-1.16.1/ ./configure --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/proxy --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi --with-http_ssl_module make && make install cd /usr/local/nginx/sbin/ # conf 等目录均位于/usr/local/nginx/中 ./nginx # 启动 ./nginx -s stop # 直接停止 ./nginx -s quit # 优雅停止 ./nginx -s reload # 重新加载 ./nginx -t # 检测配置文件 ./nginx -v # 查看 Nginx 版本 ./nginx -V # 查看版本及编译信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 安装依赖 yum install -y gcc-c++ # 安装 gcc 环境 yum install -y pcre pcre-devel # 安装 PRCE 库,用于解析正则表达式 yum install -y zlib zlib-devel yum install -y openssl openssl-devel # https协议 wget http://nginx.org/download/nginx-1.16.1.tar.gz # 下载稳定版 tar -zxvf nginx-1.16.1.tar.gz # 解压 mkdir -p /var/temp/nginx # 创建临时目录 cd nginx-1.16.1/ ./configure --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/proxy --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi --with-http_ssl_module make && make install cd /usr/local/nginx/sbin/ # conf 等目录均位于/usr/local/nginx/中 ./nginx # 启动 ./nginx -s stop # 直接停止 ./nginx -s quit # 优雅停止 ./nginx -s reload # 重新加载 ./nginx -t # 检测配置文件 ./nginx -v # 查看 Nginx 版本 ./nginx -V # 查看版本及编译信息 |
命令 | 解释 |
---|---|
–prefix | 指定nginx安装目录 |
–pid-path | 指向nginx的pid |
–lock-path | 锁定安装文件,防止被恶意篡改或误操作 |
–error-log | 错误日志 |
–http-log-path | http日志 |
–with-http_gzip_static_module | 启用gzip模块,在线实时压缩输出数据流 |
–http-client-body-temp-path | 设定客户端请求的临时目录 |
–http-proxy-temp-path | 设定http代理临时目录 |
–http-fastcgi-temp-path | 设定fastcgi临时目录 |
–http-uwsgi-temp-path | 设定uwsgi临时目录 |
–http-scgi-temp-path | 设定scgi临时目录 |
ps -ef|grep nginx # 查看 Nginx 进程 # 修改 worker 进程数 vi conf/nginx.conf worker_processes 2;
1 2 3 4 |
ps -ef|grep nginx # 查看 Nginx 进程 # 修改 worker 进程数 vi conf/nginx.conf worker_processes 2; |
# vi conf/nginx.conf # 设置工作模式 events { # 默认使用 epoll,因此可省略 use epoll; # 每个 worker 允许连接客户端最大连接数,可根据实际情况进行修改 worker_connections 10240; }
1 2 3 4 5 6 7 8 |
# vi conf/nginx.conf # 设置工作模式 events { # 默认使用 epoll,因此可省略 use epoll; # 每个 worker 允许连接客户端最大连接数,可根据实际情况进行修改 worker_connections 10240; } |
使用了多路径复用器,在出现了阻塞时一个 worker 可以处理多个客户端请求
nginx.conf 配置结构
main 全局配置 events 配置工作模式及连接数 http http 模块相关配置 |_server 虚拟主机配置,可以有多个 |_location 路由规则,表达式 |_upstream 集群,内网服务器
1 2 3 4 5 6 |
main 全局配置 events 配置工作模式及连接数 http http 模块相关配置 |_server 虚拟主机配置,可以有多个 |_location 路由规则,表达式 |_upstream 集群,内网服务器 |
BIO:同步阻塞
NIO:同步非阻塞
AIO:异步非阻塞
user root; # worker进程的用户,默认为 nobody worker_processes 1; # 工作进程数,通常同 CPU 数量或 N-1 # error_log日志级别 debug|info|notice|warn|error|crit|alert|emerg,级别从左到右越来越大 pid logs/nginx.pid; # Nginx 进程 pid # http 指令块,对 http 网络传输的一些指令配置 http { } include mime.types; # include 引入外部配置,提高可读性,避免单个配置文件过大 # 日志格式中main为格式名称,供access_log等调用,日志中各项请见下表 # sendfile 使用高效文件传输,提升传输性能;启用后才能使用tcp_nopush,是指当数据表累积一定大小后才发送,提高了效率 sendfile on; tcp_nopush on; # keepalive_timeout设置客户端与服务端请求的超时时间,保证客户端多次请求的时候不会重复建立新的连接,节约资源损耗 keepalive_timeout 65;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
user root; # worker进程的用户,默认为 nobody worker_processes 1; # 工作进程数,通常同 CPU 数量或 N-1 # error_log日志级别 debug|info|notice|warn|error|crit|alert|emerg,级别从左到右越来越大 pid logs/nginx.pid; # Nginx 进程 pid # http 指令块,对 http 网络传输的一些指令配置 http { }
include mime.types; # include 引入外部配置,提高可读性,避免单个配置文件过大 # 日志格式中main为格式名称,供access_log等调用,日志中各项请见下表 # sendfile 使用高效文件传输,提升传输性能;启用后才能使用tcp_nopush,是指当数据表累积一定大小后才发送,提高了效率 sendfile on; tcp_nopush on; # keepalive_timeout设置客户端与服务端请求的超时时间,保证客户端多次请求的时候不会重复建立新的连接,节约资源损耗 keepalive_timeout 65; |
参数名 | 参数意义 |
---|---|
$remote_addr | 客户端ip |
$remote_user | 远程客户端用户名,一般为:’-’ |
$time_local | 时间和时区 |
$request | 请求的url以及method |
$status | 响应状态码 |
$body_bytes_send | 响应客户端内容字节数 |
$http_referer | 记录用户从哪个链接跳转过来的 |
$http_user_agent | 用户所使用的代理,一般来说都是浏览器 |
$http_x_forwarded_for | 通过代理服务器来记录客户端的ip |
# 日志切割脚本 # vi cut_my_log.sh #!/bin/bash LOG_PATH="/var/log/nginx/" RECORD_TIME=$(date -d "yesterday" +%Y-%m-%d) PID=/var/run/nginx/nginx.pid mv ${LOG_PATH}/access.log ${LOG_PATH}/access.${RECORD_TIME}.log mv ${LOG_PATH}/error.log ${LOG_PATH}/error.${RECORD_TIME}.log #向Nginx主进程发送信号,用于重新打开日志文件 kill -USR1 `cat $PID` # 手动切割 chmod u+x cut_my_log.sh ./cut_my_log.sh # 自动切割可通过定时任务,如未安装yum install -y crontabs crontab -e * * */1 * * /usr/local/nginx/sbin/cut_my_log.sh # crontab -l 查看 service crond restart # 重启
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 日志切割脚本 # vi cut_my_log.sh
#!/bin/bash LOG_PATH="/var/log/nginx/" RECORD_TIME=$(date -d "yesterday" +%Y-%m-%d) PID=/var/run/nginx/nginx.pid mv ${LOG_PATH}/access.log ${LOG_PATH}/access.${RECORD_TIME}.log mv ${LOG_PATH}/error.log ${LOG_PATH}/error.${RECORD_TIME}.log #向Nginx主进程发送信号,用于重新打开日志文件 kill -USR1 `cat $PID`
# 手动切割 chmod u+x cut_my_log.sh ./cut_my_log.sh # 自动切割可通过定时任务,如未安装yum install -y crontabs crontab -e * * */1 * * /usr/local/nginx/sbin/cut_my_log.sh # crontab -l 查看 service crond restart # 重启 |
静态文件两种配置方式(其中 xxx 为服务器中/home 下的静态文件目录名):
server { .. location /xxx{ root /home; } location /static{ alias /home/xxx; } }
1 2 3 4 5 6 7 8 9 10 |
server { .. location /xxx{ root /home; }
location /static{ alias /home/xxx; } } |
gzip 压缩配置示例
#开启 gzip 压缩功能以提高传输效率、节约带宽 gzip on; #限制最小压缩,小于1字节文件不进行压缩 gzip_min_length 1; #压缩比,级别为1-9,级别越高相应CPU 占用也越多 gzip_comp_level 3; #压缩文件类型 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/json;
1 2 3 4 5 6 7 8 |
#开启 gzip 压缩功能以提高传输效率、节约带宽 gzip on; #限制最小压缩,小于1字节文件不进行压缩 gzip_min_length 1; #压缩比,级别为1-9,级别越高相应CPU 占用也越多 gzip_comp_level 3; #压缩文件类型 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/json; |
location的匹配规则
空格:默认匹配、普通匹配 location / { root /home; } =:精确匹配 location = /xxx.png { root /home; } ~*:匹配正则表达式,不区分大小写 location ~* \.(GIF|jpg|png|jpeg) { root /home; } ~:匹配正则表达式,区分大小写 location ~ \.(GIF|jpg|png|jpeg) { root /home; } ^~:以某个字符路径开头 location ^~ /xxx/img { root /home; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
空格:默认匹配、普通匹配 location / { root /home; } =:精确匹配 location = /xxx.png { root /home; } ~*:匹配正则表达式,不区分大小写 location ~* \.(GIF|jpg|png|jpeg) { root /home; } ~:匹配正则表达式,区分大小写 location ~ \.(GIF|jpg|png|jpeg) { root /home; } ^~:以某个字符路径开头 location ^~ /xxx/img { root /home; } |
Hosts 文件管理工具(兼容多平台):SwitchHosts,通过它可避免手动去编辑系统 hosts 文件
CORS(Cross-Origin Resource Sharing)
解决跨域的方案Jsonp, SpingBoot Cors, Nginx
# SpringBoot CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("http://www.xxx.com"); # 设置是否发送 cookie 信息 config.addAllowCredentials(true); # 设置允许请求的方式 config.addAllowedMethod("*"); #设置允许的 header config.addAllowedHeader("*"); # Nginx server{ ... # 允许跨域请求的域,*代表所有 add_header 'Access-Control-Allow-Origin' *; # 允许带上 cookie 请求 add_header 'Access-Control-Allow-Credentials' 'true'; # 允许请求的方法,比如 GET/POST/PUT/DELETE add_header 'Access-Control-Allow-Methods' *; # 允许请求的 header add_header 'Access-Control-Allow-Headers' *; ... }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# SpringBoot CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("http://www.xxx.com"); # 设置是否发送 cookie 信息 config.addAllowCredentials(true); # 设置允许请求的方式 config.addAllowedMethod("*"); #设置允许的 header config.addAllowedHeader("*");
# Nginx server{ ... # 允许跨域请求的域,*代表所有 add_header 'Access-Control-Allow-Origin' *; # 允许带上 cookie 请求 add_header 'Access-Control-Allow-Credentials' 'true'; # 允许请求的方法,比如 GET/POST/PUT/DELETE add_header 'Access-Control-Allow-Methods' *; # 允许请求的 header add_header 'Access-Control-Allow-Headers' *; ... } |
Nginx静态资源防盗链
server{ .. # 对源站点验证 valid_referers *.xxx.com; # 非法引入会进入下方判断 if($invalid_referer) { return 404; } ... }
1 2 3 4 5 6 7 8 9 10 |
server{ .. # 对源站点验证 valid_referers *.xxx.com; # 非法引入会进入下方判断 if($invalid_referer) { return 404; } ... } |
四层负载均衡
七层负载均衡
DNS地域负载均衡
层级 | 名称 | 说明 |
---|---|---|
第七层 | 应用层 | 与用户行为交互 |
第六层 | 表示层 | 定义数据格式以及数据加密 |
第五层 | 会话层 | 创建、管理以及销毁会话 |
第四层 | 传输层 | 创建、管理请求端到响应端(端到端)的连接 |
第三层 | 网络层 | 请求端的IP地址 |
第二层 | 数据链路层 | 提供介质访问与链路管理 |
第一层 | 物理层 | 传输介质,物理媒介 |
集群配置
本地集群可使用多台虚拟机,或直接在单台Linux机器上使用 Docker
docker run -d -it --rm tomcat:8.0
1 |
docker run -d -it --rm tomcat:8.0 |
我们连续启动了3个Docker
#配置上游服务器 upstream xxx { server 172.17.0.2:8080; server 172.17.0.3:8080; server 172.17.0.4:8080; } server { listen: 80; server_name www.xxx.com; location / { proxy_pass http://xxx; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#配置上游服务器 upstream xxx { server 172.17.0.2:8080; server 172.17.0.3:8080; server 172.17.0.4:8080; }
server { listen: 80; server_name www.xxx.com; location / { proxy_pass http://xxx; } } |
Jmeter
下载链接:https://jmeter.apache.org/download_jmeter.cgi
下载后 Windows 双击 jmeter.bat,macOS双击 jmeter 执行文件即可打开可视化窗口
1、测试计划>线程组:配置线程(用户)数和循环次数
2、线程组>采样器>HTTP 请求:配置请求的域名、端口等
3、测试计划>监听器:添加查看结果树、聚合报告、用表格查看结果等
着重对比单机和集群的异常率(聚合报告),以了解所能负载的用户数,一般超过20%即超出了异常极限
负载均衡 – 轮询、权重
负载均衡默认使用轮询,平均逐一分配给集群内的服务器;可通过配置来设置加权轮询
# 加权轮询配置(数值越小,分配的流量越少) upstream xxx { server 172.17.0.2:8080 weight=1; server 172.17.0.3:8080 weight=3; server 172.17.0.4:8080 weight=5; }
1 2 3 4 5 6 |
# 加权轮询配置(数值越小,分配的流量越少) upstream xxx { server 172.17.0.2:8080 weight=1; server 172.17.0.3:8080 weight=3; server 172.17.0.4:8080 weight=5; } |
更多内容参见:官方文档,其中各参数的配置方法同上述的 weight,以下设置均位于 upstream 中
keepalive吞吐量,用于设置长连接处理的数量
示例:keepalive 32;
对于 http,还应进行如下设置(location 内):
proxy_http_version 1.1; # 设置长连接 http 版本为1.1 proxy_set_header Connection ""; # 清除 connection header 信息
1 2 |
proxy_http_version 1.1; # 设置长连接 http 版本为1.1 proxy_set_header Connection ""; # 清除 connection header 信息 |
hash 算法:用户ip哈希对服务端节点数取模获取下标:
hash(ip) % node_counts = index
数据库分表哈希算法同理,如将 ip 替换为 pid
配置:
upstream xxx { ip_hash; server ... }
1 2 3 4 |
upstream xxx { ip_hash; server ... } |
nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c
... iphp->addrlen = 3; ... for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) { hash = (hash * 113 + iphp->addr[i]) % 6271; } ...
1 2 3 4 5 6 7 |
... iphp->addrlen = 3; ... for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) { hash = (hash * 113 + iphp->addr[i]) % 6271; } ... |
根据以上 Nginx 的源码文件可以分析出 ip_hash实际上取的是 IP 的前3段,因此通过内网(如hash( 192 168 1 ))将访问同一台主机。
注意:使用 ip_hash 如果有服务器出现故障不能直接移除,而是应将其标记为 down
ip_hash 在有节点宕机时节点数就会发生变化,自然所有下标也即访问的主机也会发生变化 。这样用户会丢失原有的 session,缓存无效,基于这一缺点引入了一致性哈希算法。
一致性哈希算法在0到232-1之间,根据用户和服务器节点的哈希值按顺时针就近原则决定用户所访问的服务器节点,这样不论是增加服务器节点还是减少服务器节点,都只有少数用户受到影响,并且依然保持相同的原则。
url_hash 是根据请求 url 进行哈希,然后与节点数取模得出下标
hash(url) % node_counts = index
upstream xxx { hash $request_uri; server ... }
1 2 3 4 |
upstream xxx { hash $request_uri; server ... } |
url_hash 在 url 发生变化时(如请求链接后多一个/)请求的服务器就会发生变化
least_conn 是最小连接数,实际含义是将请求发送到 连接数/权重 值最小的服务器上,以避免有些服务器节点出现闲置的状况:
upstream xxx { least_conn; server ... }
1 2 3 4 |
upstream xxx { least_conn; server ... } |
location /static{ alias /xxx/yyy; #expires 10s; # 缓存10s后过期 expires @22h30m; # 指定缓存过期时间 # expires -1h; # 缓存1小时前失效,即不缓存 # expires epoch; # 过期时间为1 January,1970,00:00:01 GMT,同样不进行缓存 # expires off; # 不设置,会使用浏览器默认值 # expires max; # 过期时间设置为31 December 2037 23:59:59 GMT,“Cache-Control”的值为10年 }
1 2 3 4 5 6 7 8 9 |
location /static{ alias /xxx/yyy; #expires 10s; # 缓存10s后过期 expires @22h30m; # 指定缓存过期时间 # expires -1h; # 缓存1小时前失效,即不缓存 # expires epoch; # 过期时间为1 January,1970,00:00:01 GMT,同样不进行缓存 # expires off; # 不设置,会使用浏览器默认值 # expires max; # 过期时间设置为31 December 2037 23:59:59 GMT,“Cache-Control”的值为10年 } |
# 设置缓存保存的目录 # keys_zone 设置共享内存及占用的空间大小,mycache为自定义名称,在下方需使用 # max_size 设置缓存总大小 # inactive 缓存超出所指定的时间进行清理 proxy_cache_path /usr/local/nginx/upstream_cache keys_zone=mycache:5m max_size=1g inactive=30m use_temp_path=off; server { ... # 开启并使用缓存,mycache为上方所指定的名称 proxy_cache mycache; # 针对指定状态码的缓存过期时间 proxy_cache_valid 200 304 8h; ... }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 设置缓存保存的目录 # keys_zone 设置共享内存及占用的空间大小,mycache为自定义名称,在下方需使用 # max_size 设置缓存总大小 # inactive 缓存超出所指定的时间进行清理 proxy_cache_path /usr/local/nginx/upstream_cache keys_zone=mycache:5m max_size=1g inactive=30m use_temp_path=off;
server { ... # 开启并使用缓存,mycache为上方所指定的名称 proxy_cache mycache; # 针对指定状态码的缓存过期时间 proxy_cache_valid 200 304 8h; ... } |
首先要确定 Nginx有没安装了ssl 模块
nginx -V # 如未安装需进入Nginx 源码目录重新编译,在原命令基础上添加--with-http_ssl_module再进行编译安装 ./configure --prefix=/usr/local/nginx ... --with-http_ssl_module
1 2 3 4 |
nginx -V # 如未安装需进入Nginx 源码目录重新编译,在原命令基础上添加--with-http_ssl_module再进行编译安装
./configure --prefix=/usr/local/nginx ... --with-http_ssl_module |
配置示例
server { listen 443; ... # 开启ssl ssl on; # 配置ssl证书 ssl_certificate xxx.crt; ssl_certificate_key xxx.key; # ssl会话缓存 ssl_session_cache shared:SSL:1m; # ssl会话超时时间 ssl_session_timeout 5m; # 配置加密套件,写法遵循 openssl 标准 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; ssl_prefer_server_ciphers on; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
server { listen 443; ...
# 开启ssl ssl on; # 配置ssl证书 ssl_certificate xxx.crt; ssl_certificate_key xxx.key;
# ssl会话缓存 ssl_session_cache shared:SSL:1m; # ssl会话超时时间 ssl_session_timeout 5m;
# 配置加密套件,写法遵循 openssl 标准 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; ssl_prefer_server_ciphers on; } |
静态数据:css/js/html/images/audios/videos/…
动态数据:得到的响应可能会和上一次不同
实现方式
动静分离的问题
主、备 Nginx
Keepalived:
Keepalived安装
下载地址:keepalived.org
以当前版本2.0.19为例:
tar -zxvf keepalived-2.0.19.tar.gz cd keepalived-2.0.19/ ./configure --prefix=/usr/local/keepalived --sysconf=/etc make && make install
1 2 3 4 |
tar -zxvf keepalived-2.0.19.tar.gz cd keepalived-2.0.19/ ./configure --prefix=/usr/local/keepalived --sysconf=/etc make && make install |
主要配置
# vi /etc/keepalived/keepalived.conf lobal_defs { # 路由id: 当前安装Keepalived 节点主机的标识符,要求全局唯一 router_id LVS_151 } # 计算机节点 vrrp_instance VI_1 { # 表示状态,MASTER/BACKUP state MASTER # 当前实例绑定的网卡,根据实际状况配置 interface eth0 # 保证主备节点一致即可 virtual_router_id 51 # 优先级/权重,优先级高的在主节点故障时优先成为主机点 priority 100 # 主备之间同步检查的时间间隔,默认1s advert_int 1 # 认证授权的密码,防止非法节点的进入 authentication { auth_type PASS auth_pass 1111 } # 虚拟IP地址,可以有多个 virtual_ipaddress { 192.168.1.161 } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# vi /etc/keepalived/keepalived.conf lobal_defs { # 路由id: 当前安装Keepalived 节点主机的标识符,要求全局唯一 router_id LVS_151 }
# 计算机节点 vrrp_instance VI_1 { # 表示状态,MASTER/BACKUP state MASTER # 当前实例绑定的网卡,根据实际状况配置 interface eth0 # 保证主备节点一致即可 virtual_router_id 51 # 优先级/权重,优先级高的在主节点故障时优先成为主机点 priority 100 # 主备之间同步检查的时间间隔,默认1s advert_int 1 # 认证授权的密码,防止非法节点的进入 authentication { auth_type PASS auth_pass 1111 } # 虚拟IP地址,可以有多个 virtual_ipaddress { 192.168.1.161 } } |
启动和关闭服务
cd /usr/local/keepalived/sbin/ ./keepalived # 此时停止服务需使用 ps -ef|grep keepalived 获取进程号然后执行 kill -9 [pid] # 注册为系统服务,需进入源码目录拷贝一些文件 cd keepalived-2.0.19/keepalived/etc/ cp init.d/keepalived /etc/init.d/ cp sysconfig/keepalived /etc/sysconfig/ systemctl daemon-reload # 刷新服务 # 此时即可以系统服务操作 keepalived 了 systemctl start keepalived.service
1 2 3 4 5 6 7 8 9 10 |
cd /usr/local/keepalived/sbin/ ./keepalived # 此时停止服务需使用 ps -ef|grep keepalived 获取进程号然后执行 kill -9 [pid] # 注册为系统服务,需进入源码目录拷贝一些文件 cd keepalived-2.0.19/keepalived/etc/ cp init.d/keepalived /etc/init.d/ cp sysconfig/keepalived /etc/sysconfig/ systemctl daemon-reload # 刷新服务 # 此时即可以系统服务操作 keepalived 了 systemctl start keepalived.service |
本地测试可通过绑定hosts 文件指向虚拟 IP 地址,备服参照主服配置,state 设置为BACKUP、权重设置低于主服务器即可
编写检测脚本/etc/keepalived/check_nginx_alive_or_not.sh,在 Nginx 中断时自动启动服务:
#!/bin/bash A=`ps -C nginx --no-header |wc -l` # 判断Nginx是否宕机,如果宕机了,尝试重启 if [ $A -eq 0 ];then /usr/local/nginx/sbin/nginx # 等待一会再次检查Nginx,如果没有启动成功,则停止Keepalived,使其启动备用机 sleep 3 if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then killall keepalived fi fi
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/bin/bash
A=`ps -C nginx --no-header |wc -l` # 判断Nginx是否宕机,如果宕机了,尝试重启 if [ $A -eq 0 ];then /usr/local/nginx/sbin/nginx # 等待一会再次检查Nginx,如果没有启动成功,则停止Keepalived,使其启动备用机 sleep 3 if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then killall keepalived fi fi |
添加配置
chmod +x check_nginx_alive_or_not.sh # vi /etc/keepalived/keepalived.conf vrrp_script check_nginx_alive { script "/etc/keepalived/check_nginx_alive_or_not.sh" interval 2 # 每隔两秒运行上一行脚本 # weight 10 # 如果脚本运行成功,则升级权重+10 weight -10 # 如果脚本运行失败,则降低权重-10 } vrrp_instance VI_1 { ... track_script { check_nginx_alive # 追踪 Nginx 脚本 } ... }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
chmod +x check_nginx_alive_or_not.sh # vi /etc/keepalived/keepalived.conf
vrrp_script check_nginx_alive { script "/etc/keepalived/check_nginx_alive_or_not.sh" interval 2 # 每隔两秒运行上一行脚本 # weight 10 # 如果脚本运行成功,则升级权重+10 weight -10 # 如果脚本运行失败,则降低权重-10 }
vrrp_instance VI_1 { ... track_script { check_nginx_alive # 追踪 Nginx 脚本 } ... } |
以上使用的Keepalived 双机主备一个主要的缺点是在MASTER 运行正常的情况下,从服务器将保持闲置的状态,因此让我们来了解一下双主热备,即互为主备:
通过设置多个虚拟 IP 并使用 DNS 轮询的方式实现(阿里、腾讯等平台的域名解析中均可将A记录如 www 解析到不同域名并设置权重)
配置类似前面,将原有的配置拷贝一份,比如在备用服务器上再使用另一个虚拟 IP 配置其为主服务器:
vrrp_instance VI_2 { state MASTER interface eth0 virtual_router_id 52 priority 100 ... virtual_ipaddress { 192.168.1.162 } }
1 2 3 4 5 6 7 8 9 10 |
vrrp_instance VI_2 { state MASTER interface eth0 virtual_router_id 52 priority 100 ... virtual_ipaddress { 192.168.1.162 } } |
相应地原主服务器也需要再进行一份备用服务器的配置
官方网站:http://linux-vs.org/
为什么要使用LVS+Nginx?
LVS 的三种模式
LVS DR模式搭建
服务器示例如图所示
VIP:虚拟 IP
RIP:真实服务器 IP
# LVS及Nginx服务器上关闭网络配置管理器,避免本地服务器的网络接口冲突 systemctl stop NetworkManager systemctl disable NetworkManager # 虚拟IP配置(LVS) cd /etc/sysconfig/network-scripts/ # vi ifcfg-eth0:1(可拷贝ifcfg-eth0进行修改,根据不同机器eth0可能会存在变化,只需保留如下几行) BOOTPROTO="static" DEVICE="eth0:1" ONBOOT="yes" IPADDR=192.168.1.150 NETMASK=255.255.255.0 # 重启网络让配置生效 service network restart # 安装集群管理工具(LVS服务器) yum install -y ipvsadm # 查看集群信息 ipvsadm -Ln # 虚拟 IP 配置(Nginx) cd /etc/sysconfig/network-scripts/ cp ifcfg-lo ifcfg-lo:1 # vi ifcfg-lo:1,仅修改如下行,其余保留不变 DEVICE=lo:1 IPADDR=192.168.1.150 NETMASK=255.255.255.255 # 重启网络 ifup lo # 或 systemctl restart network
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# LVS及Nginx服务器上关闭网络配置管理器,避免本地服务器的网络接口冲突 systemctl stop NetworkManager systemctl disable NetworkManager
# 虚拟IP配置(LVS) cd /etc/sysconfig/network-scripts/ # vi ifcfg-eth0:1(可拷贝ifcfg-eth0进行修改,根据不同机器eth0可能会存在变化,只需保留如下几行) BOOTPROTO="static" DEVICE="eth0:1" ONBOOT="yes" IPADDR=192.168.1.150 NETMASK=255.255.255.0 # 重启网络让配置生效 service network restart
# 安装集群管理工具(LVS服务器) yum install -y ipvsadm # 查看集群信息 ipvsadm -Ln
# 虚拟 IP 配置(Nginx) cd /etc/sysconfig/network-scripts/ cp ifcfg-lo ifcfg-lo:1 # vi ifcfg-lo:1,仅修改如下行,其余保留不变 DEVICE=lo:1 IPADDR=192.168.1.150 NETMASK=255.255.255.255 # 重启网络 ifup lo # 或 systemctl restart network |
注:云服务器需购买负载均衡或虚拟 IP 才可实现配置, Nginx 服务器配置对应本地回环(lo)的原因是其处理响应
arp-ignore:ARP 响应级别(处理请求)
推荐配置为1
arp-announce:ARP通告行为(返回响应)
推荐配置为2
# vi /etc/sysctl.conf(Nginx服务器上) net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.default.arp_ignore = 1 net.ipv4.conf.lo.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2 net.ipv4.conf.default.arp_announce = 2 net.ipv4.conf.lo.arp_announce = 2 # 刷新配置 sysctl -p # 增加网关,用于接收数据报文,将到达本地的请求交给 lo 处理 route add -host 192.168.0.150 dev lo:1 # 若无命令执行yum install net-tools -y安装 route -n # 查看 route delete # 删除 # 设置开机配置,避免重启后配置失效 echo "route add -host 192.168.0.150 dev lo:1" >> /etc/rc.local
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# vi /etc/sysctl.conf(Nginx服务器上) net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.default.arp_ignore = 1 net.ipv4.conf.lo.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2 net.ipv4.conf.default.arp_announce = 2 net.ipv4.conf.lo.arp_announce = 2
# 刷新配置 sysctl -p
# 增加网关,用于接收数据报文,将到达本地的请求交给 lo 处理 route add -host 192.168.0.150 dev lo:1 # 若无命令执行yum install net-tools -y安装 route -n # 查看 route delete # 删除 # 设置开机配置,避免重启后配置失效 echo "route add -host 192.168.0.150 dev lo:1" >> /etc/rc.local |
LVS配置
ipvsadm -A -t 192.168.1.150:80 -s rr # 编辑:ipvsadm -E -t 192.168.1.150:80 -s rr -p 5 ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.171:80 -g ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.172:80 -g ipvsadm -S # 保存规则避免重启失效 # 相关参数说明请参见 ipvsadm -h # 查看 ipvsadm -Ln # 查看持久化连接: ipvsadm -Ln --persistent-conn # 查看请求过期时间及源IP和目标IP: ipvsadm -Lnc # 查看状态: ipvsadm -Ln --status # 设置tcp tcpfin udp 的过期时间(通常保持默认,以下设置用于测试) ipvsadm --set 1 1 1 # 查看过期时间 ipvsadm -Ln --timeout
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
ipvsadm -A -t 192.168.1.150:80 -s rr # 编辑:ipvsadm -E -t 192.168.1.150:80 -s rr -p 5 ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.171:80 -g ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.172:80 -g ipvsadm -S # 保存规则避免重启失效 # 相关参数说明请参见 ipvsadm -h # 查看 ipvsadm -Ln # 查看持久化连接: ipvsadm -Ln --persistent-conn # 查看请求过期时间及源IP和目标IP: ipvsadm -Lnc # 查看状态: ipvsadm -Ln --status
# 设置tcp tcpfin udp 的过期时间(通常保持默认,以下设置用于测试) ipvsadm --set 1 1 1 # 查看过期时间 ipvsadm -Ln --timeout |
-A:添加集群
-t:tcp协议
ip地址:设定集群的访问ip,也就是LVS的虚拟ip
-s:设置负载均衡的算法,rr表示轮询
-p:设置连接持久化的时间,默认300秒
-a:添加真实服务器
-r:真实服务器的ip地址
-g:设定DR模式
在主备 LVS 服务器上安装 Keepalived,方法参见上方Keepalived安装
global_defs { ... } vrrp_instance VI_1 { ... } # 配置集群地址访问的 IP+端口,端口和 Nginx 保持一致 virtual_server 192.168.1.150 80 { # 健康检查时间,单位:秒 delay_loop 6 # 配置负载均衡算法,默认是轮询 lb_algo rr # 设置LVS的模式:NAT|TUN|DR lb_kind DR # 设置会话持久化时间 persistence_timeout 10 # 协议 protocol TCP # 负载均衡真实服务器,即Nginx节点 IP real_server 192.168.1.171 80 { # 轮询的默认权重配比 weight 1 # 设置健康检查 TCP_CHECK { # 检查的端口 connect_port 80 # 超时时间 connect_timeout 2 # 重试次数(s) nb_get_retry 5 # 间隔时间(s) delay_before_retry 3 } } real_server 192.168.1.172 80 { weight 1 ... } } # 清除此前规则 ipvsadm -C systemctl restart keepalived
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
global_defs { ... }
vrrp_instance VI_1 { ... }
# 配置集群地址访问的 IP+端口,端口和 Nginx 保持一致 virtual_server 192.168.1.150 80 { # 健康检查时间,单位:秒 delay_loop 6 # 配置负载均衡算法,默认是轮询 lb_algo rr # 设置LVS的模式:NAT|TUN|DR lb_kind DR # 设置会话持久化时间 persistence_timeout 10 # 协议 protocol TCP # 负载均衡真实服务器,即Nginx节点 IP real_server 192.168.1.171 80 { # 轮询的默认权重配比 weight 1 # 设置健康检查 TCP_CHECK { # 检查的端口 connect_port 80 # 超时时间 connect_timeout 2 # 重试次数(s) nb_get_retry 5 # 间隔时间(s) delay_before_retry 3 } }
real_server 192.168.1.172 80 { weight 1 ... } }
# 清除此前规则 ipvsadm -C systemctl restart keepalived |
以上即实现了高可用的 LVS+Keepalived 方案
LVS 负载均衡算法
静态算法
根据 LVS 自有固定算法分发用户请求
1、轮询(Round Robin – rr):平均分配请求(同 Nginx的轮询)
2、加权轮询(Weight Round Robin – wrr): 按照权重比例分配用户请求,权重越高,分配请求越多(同 Nginx 的权重)
3、源地址散列(Source Hash – sh):同 IP 用户由相同 RS 处理(同 Nginx 的 ip_hash)
4、目标地址散列(Destination Hash – dh):根据不同 url 请求不同的 RS(同 Nginx的 url_hash)
动态算法
根据流量或服务器压力不同分配用户请求
1、最小连接数(Least Connections – lc):将请求分配给连接最小的服务器
2、 加权最小连接数(Weight Least Connections – wlc):用数值表示服务器处理性能,将请求分发到性能好且空闲的服务器
3、 最短期望延迟(Shortest Expected Delay – sed):是一种特殊的 wlc 算法,将请求交给运算结果最小的服务器,设服务器 A、B、C的权重为1、2、3,计算方式如下:
- A: (1+1)/1=2
- B: (1+2)/2=3/2
- C: (1+3)/3=4/3
4、 最少队列调试(Never Queue = nq):如有 RS 的连接数等于0,直接将请求分配过去,无需排队等待运算
注:RS(Real Server)
LVS 最常使用的负载均衡算法为 wlc 或 wrr
官方文档
1、nginx: [error] open() “/var/run/nginx/nginx.pid” failed (2: No such file or directory)
mkdir /var/run/nginx /usr/local/nginx/sbin/nginx -s reload # 未启动则直接执行/usr/local/nginx/sbin/nginx
1 2 |
mkdir /var/run/nginx /usr/local/nginx/sbin/nginx -s reload # 未启动则直接执行/usr/local/nginx/sbin/nginx |
2、nginx: [error] invalid PID number “” in “/var/run/nginx/nginx.pid”
/usr/local/nginx sbin/nginx -c conf/nginx.conf # 指定配置文件 sbin/nginx -s reload
1 2 3 |
/usr/local/nginx sbin/nginx -c conf/nginx.conf # 指定配置文件 sbin/nginx -s reload |
3、默认 Docker 容器中没有 vi 等编辑器,可进行安装,也可在宿主机执行如下命令,然后根据返回的路径编辑容器中的文件
docker inspect --format='{{.GraphDriver.Data.MergedDir}}' 容器 ID 或名称
1 |
docker inspect --format='{{.GraphDriver.Data.MergedDir}}' 容器 ID 或名称 |
4、*** WARNING – this build will not support IPVS with IPv6. Please install libnl/libnl-3 dev libraries to support IPv6 with IPVS.
yum -y install libnl libnl-devel
1 |
yum -y install libnl libnl-devel |