Nginx-xdclass

2020年11月4日

01 阿里云Linux服务器上Nginx安装

进入Nginx官网:

把下载好的nginx-1.18.0.tar.gz放到/usr/local/software/server下(下载的软件建议放到/usr/local/下面,为了便于管理,后面两个文件夹是自己创建的)

  • 安装依赖
yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel
  • 安装nginx
tar -zxvf nginx-1.18.0.tar.gz

./configure

make

make install
  • 默认安装路径
/usr/local/nginx
  • 启动nginx
# sbin放的是二进制文件
cd /usr/local/nginx/sbin
# 启动nginx
./nginx
  • 查看nginx进程:ps -ef | grep "nginx"

删除进程:kill -9 进程id

  • 阿里云网络安全组配置80端口,防火墙开放端口(这步我没有做)

02 Nginx核心基础知识

2.1 Nginx目录文件

  • 源码编译安装后,默认目录:/usr/local/nginx

- nginx
- - conf #所有配置文件目录
- - - nginx.conf #默认的主要的配置文件
- - - nginx.conf.default #默认模板
- - html #编译安装时Nginx的默认站点目录
- - - 50x.html #错误页面
- - - index.html #默认首页
- - logs #nginx默认的日志路径,包括错误日志及访问日志
- - - access.log #nginx访问日志
- - - error.log #错误日志
- - - error.log #nginx启动后的进程id
- - sbin #nginx命令的目录
- - - nginx #启动命令
  • 常见命令
#默认配置文件启动
./nginx 

#重启,加载默认配置文件
./nginx -s reload 

#启动指定某个配置文件
./nginx -c /usr/local/nginx/conf/nginx.conf

#停止
./nginx -s stop 

#关闭进程,nginx有master process和worker process,先关闭master process
ps -ef | grep "nginx"
kill -9 PID

2.2 Nginx配置文件

  • 全局配置(events及其上面这些)
  • server主机设置(http那一块,server虚拟主机的配置)
  • location(URL匹配特定位置的设置)
#每个配置项由配置指令和指令参数2个部分构成


#指定Nginx Worker进程运行以及用户组
#user  nobody;

#推荐和CPU的核数保持一致(例如:前端发起请求到后端是由worker进行处理,并不是越多越好,如果超过了CPU核数,则要进行上下切换,消耗性能)
worker_processes  1;

#错误日志的 存放路径/错误日志 日志级别
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#进程PID存放路路径
#pid        logs/nginx.pid;


#事件模块指令,用来指定Nginx的IO模型,Nginx支持的有select、poll、kqueue、epoll 等。不同的是epoll用在Linux平台上,而kqueue用在BSD系统中,对于Linux系统,epoll工作模式是首选。
events {
    #异步非阻塞,提升IO性能
    use epoll;
    # 定义Nginx每个进程的最大连接数,作为服务器来说:worker_connections * worker_processes;
    # 作为反向代理来说,最大并发数量应该是worker_connections * worker_processes/2。因为反向代理服务器,每个并发会建立与客户端的连接和与后端服务的连接,会占用两个连接。
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #自定义服务日志(如下为日志模板)
    #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; #main就是上面的模板名称

    #是否开启高效传输模式on开启off关闭
    sendfile        on;
    
    #减少网络报文段的数量
    #tcp_nopush     on;

    #客户端连接保持活动的超时时间,超过这个时间之后,服务器会关闭该连接
    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    #虚拟主机的配置
    server {
        #虚拟主机的服务端口
        listen       80;
        #用来指定IP地址或域名,多个域名之间用空格分开
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        #URL地址匹配(除了前面的域名之外,就是具体的路径)
        location / {
            #服务默认启动目录(root指的的存储路径;html即/usr/local/nginx/html,相对路径)
            root   html;
            #默认访问文件,按照顺序找
            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 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       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

03 Nginx虚拟主机-搭建前端静态服务器

什么是虚拟主机?

指在一台物理主机服务器上划分出多个磁盘空间,每个磁盘空间都是一个虚拟主机,每台虚拟主机都可以对外提供Web服务,并且互不干扰,就类似虚拟机。利用虚拟主机把多个不同域名的网站部署在同一台服务器上,节省了服务器硬件成本和相关的维护费用。

注意:配置之前,拷贝一份默认的出来,nginx配置容易出错且肉眼难看出来,自己仔细核对。

  • Nginx虚拟主机配置

nginx.conf中的一个server就是一个虚拟主机;

在win10下,进入C:\Windows\System32\drivers\etc,修改hosts文件:

47.115.23.41 dawson.com
47.115.23.41 rose.com

通过域名进行区分,不在location进行区分;

server {
    listen       80;
    server_name  dawson.com;

    location / {
        root   html;
        index  xdclass.html;
    }
}

server {
    listen       80;
    server_name  rose.com;

    location / {
        root   html;
        index  xdclass.html;
    }
}

复制nginx/html/下的index.html,命名为xdclass.html,并修改内容cp index.html xdclass.html

则访问47.115.23.41、dawson.com、rose.com都能得到:

之后访问便会提示未备案(可能是出于安全问题)

04 配置文件-图片服务器

图片服务器:学javaweb、node、或者其他基础web项目,基本都是图片上传到项目本身,这个是实际项目中很少用;公司一般会使用图片服务器或者云厂商提供的CDN。

前端提交图片 -> 后端处理 -> 存储到图片服务器 -> 拼接好访问路径存储到数据库和返回前端;

  • 本地图片上传到usr/local/software/img,配置专属访问路径

注意location后面的路径可以写成/app/img或者/app/img/,都能访问成功;但是要确保alias后面的路径/usr/local/software/img/是正确的,如果写成usr/local/software/img/则不行;

server {
    listen       80;
    server_name  47.115.23.41;

    location /app/img {
        alias /usr/local/software/img/;
    }
}

注意:

  • 在location / 中配置root目录;
  • 在location /path中配置alias虚拟目录,目录后面的"/"符号一定要带上;

05 Nginx挖掘access.log日志

5.1 Nginx访问日志的用处

用处:

  • 统计站点访问ip来源、某个时间段的访问频率;
  • 查看访问最频的页面、Http响应状态码、接口性能;
  • 接口秒级访问量、分钟访问量、小时和天访问量;
  • ...

默认配置:

#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; #main就是上面的模板名称

案例:

122.70.148.18 - - [04/Aug/2020:14:46:48+0800] "GET /user/api/v1/product/order/query_state?product_id=1&token=xdclasseyJhbGciOJEHTTP/1.1"
200 48 "https://xdclass.net/"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/84.0.4147.89 Safari/537.36"

默认配置解析:

  • $remote_addr:对应的是真实日志里的122.70.148.18,即客户端的IP。
  • $remote_user:对应的是第二个中杠"-",没有远程用户,所以用"-"填充。
  • [$time_local]:对应的是[04/Aug/2020:14:46:48+0800]。
  • “$request”:对应的是"GET /user/api/v1/product/order/query_state?product_id=1&token=xdclasseyJhbGciOJEHTTP/1.1"。
  • $status:对应的是200状态码,200表示正常访问。
  • $body_bytes_sent:对应的是48字节,即响应body的大小。
  • “$http_referer”:对应的是"https://xdclass.net/",若是直接打开域名浏览的时,referer就会没有值,为"-"。
  • “$http_user_agent”: 对应的是"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/84.0.4147.89 Safari/537.36"。
  • “$http_x_forwarded_for”: 对应的是"-"或者空。

5.2 Nginx站点访问量、高频URL统计

access_temp.log示例:(head -n 100 filename,查看前100条数据;head filename,查看前10条数据)

预备Liunx命令

  • awk:是文本处理工具,默认按照空格切分,$N 是第切割后第N个,从1开始;
  • sort:命令用于将文本文件内容加以排序,-n按照数值排,-r按照倒序来排;案例的sort -n 是按照第一列的数值大小进行排序,从小到大,倒序就是sort -rn
  • uniq:去除重复出现的行列, -c 在每列旁边显示该行重复出现的次数。

需求1:查看访问最频繁的前5个IP

awk '{print $1}' access_temp.log  | sort -n | uniq -c | sort -rn | head -n 5

需求2:统计访问最多的URL前5名

cat access_temp.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -5

用需求1的方法也能得到一样的结果

awk '{print $7}' access_temp.log | sort -n | uniq -c | sort -rn | head -n 5

5.3 自定义日志格式,统计接口响应耗时

日志格式增加$request_time

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for" $request_time';
server {
    listen       80;
    server_name  47.115.23.41;

    location / {
        root   html;
        index  xdclass.html;
    }

    access_log  logs/time_access.log  main;

}                  

访问47.115.23.41/xxx/xxx,可以得到time_access.log日志示例:

  • 统计耗时接口,列出传输时间超过5秒的接口
cat time_access.log | awk '($NF > 5){print $7}' | sort -n | uniq -c | sort -rn

06 Nginx配置集群应用-负载均衡策略

6.1 Linux服务器安装JDK8和jar包上传

安装JDK8:

  • 下载:https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
  • 上传到阿里云服务器的/usr/local/software/jdk目录下,没有的目录直接创建;
  • 配置全局环境变量
    • tar -zxvf jdk-8u181-linux-x64.tar.gz
    • 重命名为jdk8:mv jdk1.8.0_181/ jdk8
    • 配置环境变量,后source /etc/profile立即生效,java -version查看是否安装好
JAVA_HOME=/usr/local/software/jdk/jdk8
CLASSPATH=$JAVA_HOME/lib
PATH=$PATH:$JAVA_HOME/bin
export PATH JAVA_HOME CLASSPATH

注意:配置环境变量的时候,等号=两边不能有空格,不然报错;

6.2 后端应用集群构建-SpringBoot应用和接口说明

  • demo-1.jar监听8080端口;
  • demo-2.jar监听8081端口;

接口说明:(要在阿里云中开启端口访问权限才能调用接口成功)

进程启动:

  • 直接启动:java -jar demo-1.jar
  • 守护进程方式启动:nohup java -jar demo-1.jar &

6.3 Nginx负载均衡upstream

负载均衡(Load Balance)

  • 分布式系统中一个非常重要的概念,当访问的服务具有多个实例时,需要根据某种“均衡”的策略决定请求发往哪个节点,这就是所谓的负载均衡;
  • 原理是将数据流量分摊到多个服务器执行,减轻每台服务器的压力,从而提高了数据的吞吐量。

负载均衡的种类

  • 通过硬件来进行解决,常见的硬件有NetScaler、F5、Radware和Array等商用的负载均衡器,但比较昂贵;
  • 通过软件来进行解决,常见的软件有LVS、Nginx等,它们是基于Linux系统并且开源的负载均衡策略;
  • 目前性能和成本来看,Nginx是目前多数公司选择使用的;

配置案例:

upstream lbs {
    server 47.115.23.41:8080;
    server 47.115.23.41:8081;
}
    
server {
    listen       80;
    server_name  47.115.23.41;

    location /api/ {
        proxy_pass http://lbs;
        proxy_redirect default;    
    }

}
  • upstream一般放在虚拟主机server上面,lbs为取的一个名字,在下面引用;
  • 请求流程:用户通过域名server_name请求到了虚拟主机server,然后与location中的路径匹配,如果满足,则proxy_pass到upstream里面,有很多个server,会通过负载均衡策略(默认为轮询),分发到某个server。
  • proxy_redirect default;可以不加这行;

刷新,轮询

6.4 Nginx常见的负载均衡策略

  1. 节点轮询(默认)
    • 简介:每个请求按顺序分配到不同的后端服务器;
    • 场景:会造成可靠性低和负载分配不均衡,适合静态文件服务器;
  • weight权重配置
    • 简介:weight和访问比率成正比,数字越大,分配得到的流量越高;
    • 场景:服务器性能差异大的情况使用;
upstream lbs {
    server 47.115.23.41:8080 weight=5;
    server 47.115.23.41:8081 weight=10;
}
server {
    listen       80;
    server_name  47.115.23.41;

    location /api/ {
        proxy_pass http://lbs;
        proxy_redirect default;    
    }
}
  1. ip_hash(固定分发)
    • 简介:根据请求按访问ip的hash结果分配,这样每个用户就可以固定访问一个后端服务器;
    • 场景:服务器业务分区、业务缓存、Session需要单点的情况;
upstream lbs {
        ip_hash;
        server 47.115.23.41:8080;
        server 47.115.23.41:8081;
}
server {
    listen       80;
    server_name  47.115.23.41;

    location /api/ {
        proxy_pass http://lbs;
        proxy_redirect default;    
    }
}

upstream还可以为每个节点设置状态值:

  • down表示当前的server暂时不参与负载;
  • server 47.115.23.41:8080 down;
  • backup其它所有的非backup机器down的时候,会请求backup机器,这台机器压力会最轻,配置也会相对低;
  • server 47.115.23.41:8080 backup;

注释:测试backup的时候,可以kill没有加backup的那个server,然后就会发现之后就会调用加了backup的server了。

6.5 Nginx后端节点可用性探测和配置实操

如果某个应用挂了,请求不应该继续分发过去;

  • max_fails:允许请求失败的次数,默认为1,当超过最大次数时就不会请求;
  • fail_timeout:max_fails次失败后,暂停的时间,默认为10s;

参数解释

  • max_fails=N:设定Nginx与后端节点通信尝试失败的次数;
  • fail_timeout参数定义的时间内,如果失败的次数达到此值,Nginx就认为这个节点不可用;
  • 在下一个fail_timeout时间段到来前,服务器不会再被尝试;
  • 失败的尝试次数默认是1,如果设为0就会停止统计尝试次数,认为服务器是一直可用的;

具体什么是nginx认为的失败呢?

  • 可以通过指令proxy_next_upstream来配置什么是失败的尝试。
  • 注意默认配置时,http_404状态不被认为是失败的尝试。
upstream lbs {
        server 47.115.23.41:8080 max_fails=2 fail_timeout=60s;
        server 47.115.23.41:8081 max_fails=2 fail_timeout=60s;
}
server {
    listen       80;
    server_name  47.115.23.41;

    location /api/ {
        proxy_pass http://lbs;
        proxy_next_upstream error timeout http_500 http_503 http_404;    
    }
}

案例实操:

  • kill一个节点后,然后访问接口大于10次,如果访问不到,Nginx会把这个节点剔除;
  • 重启这个节点,在fail_timeout周期里面不会再获取流量;

07 Nginx经典应用

7.1 Nginx全局异常兜底数据返回

任何接口都是可能出错,4xx、5xx等。如果业务没有做好统一的错误管理,直接暴露给用户,无疑是看不懂所以假如后端某个业务出错,nginx层也需要进行转换让前端知道Http响应是200,其实是将错误的状态码定向至200,返回了全局兜底数据。

upstream lbs {
        server 47.115.23.41:8080 max_fails=2 fail_timeout=60s;
        server 47.115.23.41:8081 max_fails=2 fail_timeout=60s;
}
server {
    listen       80;
    server_name  47.115.23.41;

    location / {
        proxy_pass http://lbs;
        proxy_redirect default;

	    #save the real ip of user
        proxy_set_header HOST $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        proxy_next_upstream error timeout http_503 non_idempotent;

	    #turn on configuration of error intercept, must be turned on
        proxy_intercept_errors on;
    }

    access_log  logs/time_access.log  main;

    #if not add "=200", then return the original http error code; 
    #otherwise if there is an error status code such as 500, it will return to the user 200 status and jump to "/default_api"
    error_page   404 500 502 503 504 =200 /default_api;
    location = /default_api {
        default_type application/json;
        return 200 '{"code":"-1","msg":"invoke fail, not found"}';
    }
}

注意:

  • nginx.conf中最好不要复制中文进去,不然可能会出现乱码,所以注释写成英文;
  • 如果正常访问,都能得到正确的结果;如果访问出错,都会返回200 OK以及自定义Json给前端,前端可通过"code":"-1"来给用户进行提示错误信息;(这样做的目的是防止后端未定义错误状态码,或者定义不全,而给用户返回一些看不懂的信息。)

7.2 Nginx封禁恶意IP

  • 网络攻击常见的有:TCP洪水攻击、注入攻击、DOS等,比较难防的有DDOS等;
  • 数据安全,防止对手爬虫恶意爬取,封禁IP;
  • 一般就是封禁IP
    • linux server的层面封禁IP:iptables(推荐)
    • nginx的层面封禁IP ,方式多种(但 request还是会打进来,让nginx 返回 403, 占用资源)

Nginx作为网关,可以有效的封禁IP:

  • 单独网站屏蔽IP的方法,把include xxx; 放到网址对应的在server{}语句块,虚拟主机;
  • 所有网站屏蔽IP的方法,把include xxx; 放到http{}语句块。

在/usr/local/nginx/conf目录下touch blacklist.conf用于专门存放封禁的IP,格式为deny ip地址;;例如:(先在nginx.conf中记录的access.log中找到访问的ip,把它加入黑名单进行测试)

  • blacklist.conf
deny 14.30.22.148;
  • 然后在nginx.conf引入
http {
    ......
    include blacklist.conf;
    server {
    ......    
    }

}

  • ./nginx -s reload:重新加载配置,不中断服务;
  • 拓展:自动化封禁ip思路
    • 编写shell脚本;
    • AWK统计access.log,记录每秒访问超过60次的ip,然后配合nginx或者iptables进行封禁;
    • crontab定时跑脚本;

7.3 Nginx配置浏览器跨域

跨域:浏览器同源策略1995年,同源政策由Netscape公司引入浏览器。目前,所有浏览器都实行这个政策。 最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页“同源”。

所谓“同源”指的是“三个相同”:

  • 协议相同:http https
  • 域名相同:www.xdclass.net
  • 端口相同:80 81

In a word:浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域。

浏览器控制台跨域提示:No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

也就是用postman调试接口没问题,前端也没问题,但是数据显示不出来,这可能是跨域的问题;

解决办法:

  • JSONP(目前很少使用了)

  • Http响应头配置允许跨域(业界常用,有如下两种方法)

    • nginx层配置
    • 后端代码中处理,通过拦截器配置
  • Nginx开启跨域配置(location下配置,一般会针对特定的接口,在header头信息里面加几个字段)

server {
    listen       80;
    server_name  47.115.23.41;

    location /api/ {
        add_header 'Access-Control-Allow-Origin' $http_origin;
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Headers' 'DNT,web-token,app-token,Authorization,Accept,Origin,Keep-Alive,User-Agent,X-Mx-ReqToken,X-Data-Type,X-Auth-Token,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header Access-Control-Allow-Methods 'GET,POST,OPTIONS';
        
      # if request method is OPTIONS, then return 200 OK, don't need to forward to back-end codes
	  if ($request_method = 'OPTIONS') {
           add_header 'Access-Control-Max-Age' 1728000;
           add_header 'Content-Type' 'text/plain; charset=utf-8';
		   add_header 'Content-Length' 0;
		   return 200;
	  }
        
        proxy_pass http://lbs;
        proxy_redirect default;

	    # save the real ip of user
        proxy_set_header HOST $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        proxy_next_upstream error timeout http_503 non_idempotent;

	    # turn on configuration of error intercept, must be turned on
        proxy_intercept_errors on;
    }
    
    location / {
        proxy_pass http://lbs;
        proxy_redirect default;
    }

    access_log  logs/time_access.log  main;

    # if not add "=200", then return the original http error code; 
    # otherwise if there is an error status code such as 500, it will return to the user 200 status and jump to "/default_api"
    error_page   404 500 502 503 504 =200 /default_api;
    location = /default_api {
        default_type application/json;
        return 200 '{"code":"-1","msg":"invoke fail, not found"}';
    }
}

  • add_header 'Access-Control-Allow-Origin' $http_origin;:允许的源,$http_origin表示任何一个源都可以传过去;
  • add_header 'Access-Control-Allow-Credentials' 'true';:请求的时候可以根据cookie来进行配置;
  • add_header 'Access-Control-Allow-Headers':常见的header有没有加进去;
  • add_header Access-Control-Allow-Methods 'GET,POST,OPTIONS';:哪些请求需要进行跨域;
  • if ($request_method = 'OPTIONS') {:预检请求,H5新规范,一般都会发一个OPTIONS请求,预检一下接口是否畅通;如果后端没有写好,即使配置了跨域,也会出现跨域问题;

7.4 路径匹配-Nginx的locatioin规则应用

语法规则:

location [ = | ~ | ~* | ^~ ] uri { ... }
location @name { ... }

语法规则很简单,一个location关键字,后面跟着可选的修饰符,后面是要匹配的字符,花括号中是要执行的操作。

修饰符:

  • =:表示精确匹配。只有请求的url路径与后面的字符串完全相等时,才会命中。
  • ~:表示该规则是使用正则定义的,区分大小写。
  • ~*:表示该规则是使用正则定义的,不区分大小写。
  • ^~:表示如果该符号后面的字符是最佳匹配,采用该规则,不再进行后续的查找。

匹配过程:

location有两种表示形式,一种是使用前缀字符,一种是使用正则。如果是正则的话,前面有~~*修饰符。

具体的匹配过程如下:

首先先检查使用前缀字符定义的location,选择最长匹配的项并记录下来。如果找到了精确匹配的location,也就是使用了=修饰符的location,结束查找,使用它的配置。然后按顺序查找使用正则定义的location,如果匹配则停止查找,使用它定义的配置。如果没有匹配的正则location,则使用前面记录的最长匹配前缀字符location。

基于以上的匹配过程,我们可以得到以下两点启示:

  1. 使用正则定义的location在配置文件中出现的顺序很重要。因为找到第一个匹配的正则后,查找就停止了,后面定义的正则就是再匹配也没有机会了。
  2. 使用精确匹配可以提高查找的速度。例如经常请求/的话,可以使用=来定义location。

总结:精准匹配 > 字符串匹配(若有多个匹配项匹配成功,那么选择匹配长的并记录) > 正则匹配

示例:

location = / {
    return 1;
}

location / {
    return 2;
}

location /user/ {
    return 3;
}

location ^~ /images/ {
    return 4;
}

location ~* \.(gif|jpg|jpeg)$ {
    return 5;
}

请求/精准匹配1,不再往下查找。

请求/index.html匹配2。首先查找匹配的前缀字符,找到最长匹配是配置2,接着又按照顺序查找匹配的正则。结果没有找到,因此使用先前标记的最长匹配,即配置2。

请求/user/index.html匹配3。首先找到最长匹配3,由于后面没有匹配的正则,所以使用最长匹配3。

请求/user/1.jpg匹配5。首先进行前缀字符的查找,找到最长匹配项3,继续进行正则查找,找到匹配项5。因此使用5。

请求/images/1.jpg匹配4。首先进行前缀字符的查找,找到最长匹配4。但是,特殊的是它使用了^~修饰符,不再进行接下来的正则的匹配查找,因此使用4。这里,如果没有前面的修饰符,其实最终的匹配是5。

请求/documents/index.html匹配2。因为2表示任何以/开头的URL都匹配。在上面的配置中,只有2能满足,所以匹配2。

location @name的用法

@用来定义一个命名location。主要用于内部重定向,不能用来处理正常的请求。其用法如下:

location / {
    try_files $uri $uri/ @custom
}
location @custom {
    # ...do something
}

上例中,当尝试访问url找不到对应的文件就重定向到我们自定义的命名location(此处为custom)。

值得注意的是,命名location中不能再嵌套其它的命名location。

URL尾部的/需不需要

关于URL尾部的/有三点也需要说明一下。第一点与location配置有关,其他两点无关。

  1. location中的字符有没有/都没有影响。也就是说/user/和/user是一样的。
  2. 如果URL结构是https://domain.com/的形式,尾部有没有/都不会造成重定向。因为浏览器在发起请求的时候,默认加上了/。虽然很多浏览器在地址栏里也不会显示/。这一点,可以访问baidu验证一下。
  3. 如果URL的结构是https://domain.com/some-dir/。尾部如果缺少/将导致重定向。因为根据约定,URL尾部的/表示目录,没有/表示文件。所以访问/some-dir/时,服务器会自动去该目录下找对应的默认文件。如果访问/some-dir的话,服务器会先去找some-dir文件,找不到的话会将some-dir当成目录,重定向到/some-dir/,去该目录下找默认文件。可以去测试一下你的网站是不是这样的。

7.5 地址重定向-Nginx的rewrite规则应用

https://www.cnblogs.com/tugenhua0707/p/10798762.html

rewrite功能就是,使用nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向。

语法:

一:理解“地址重写”与“地址转发”的含义。

地址重写与地址转发是两个不同的概念。

地址重写:是为了实现地址的标准化,比如我们可以在地址栏中中输入www.baidu.com我们也可以输入www.baidu.cn最后都会被重写到www.baidu.com上。浏览器的地址栏也会显示www.baidu.com

地址转发:它是指在网络数据传输过程中数据分组到达路由器或桥接器后,该设备通过检查分组地址并将数据转发到最近的局域网的过程。

因此地址重写和地址转发有以下不同点:

  1. 地址重写会改变浏览器中的地址,使之变成重写成浏览器最新的地址。而地址转发是不会改变浏览器的地址的。
  2. 地址重写会产生两次请求,而地址转发只会有一次请求。
  3. 地址转发一般发生在同一站点项目内部,而地址重写不受限制。
  4. 地址转发的速度比地址重定向快。(地址重写即是重定向)

二:理解Rewrite指令使用

该指令是通过正则表达式的使用来改变URI。可以同时存在一个或多个指令。需要按照顺序依次对URL进行匹配和处理。

rewrite只能放在server{},location{},if{}中,并且只能对域名后边的除去传递的参数外的字符串起作用,例如http://seanlook.com/a/we/index.php?id=1&u=str只对/a/we/index.php重写。其基本语法结构如下:

rewrite regex replacement [flag];
  • rewrite的含义:该指令是实现URL重写的指令。
  • regex的含义:用于匹配URI的正则表达式。
  • replacement:将regex正则匹配到的内容替换成replacement。
  • flag: flag标记。

flag有如下值:

  • last: 本条规则匹配完成后,继续向下匹配新的location URI 规则。(不常用)
  • break: 本条规则匹配完成即终止,不再匹配后面的任何规则(不常用)。
  • redirect: 返回302临时重定向,浏览器地址会显示跳转新的URL地址。
  • permanent: 返回301永久重定向。浏览器地址会显示跳转新的URL地址。

例子:

rewrite ^/(.*) http://www.baidu.com/$1 permanent;

说明:

  • rewrite为固定关键字,表示开始进行rewrite匹配规则。
  • regex为^/(.*),这是一个正则表达式,匹配完整的域名和后面的路径地址。
  • replacement就是http://www.baidu.com/$1这块了,其中$1是取regex部分()里面的内容。如果匹配成功后跳转到的URL。
  • flag就是permanent,代表永久重定向的含义,即跳转到http://www.baidu.com/$1地址上。
server {
    listen       80;
    server_name  47.115.23.41;
    
    location /api {
        rewrite ^/(.*) https://www.baidu.com permanent;
    }
    error_page   404 500 502 503 504 =200 /default_api;
    location = /default_api {
        default_type application/json;
        return 200 '{"code":"-1","msg":"invoke fail, not found"}';
    }
  • 访问47.115.23.41/api/v1/pub/web会跳转到https://www.baidu.com/api/v1/pub/web从而报错Not Found;(47.115.23.41/api/v1/pub/web,这个地址是java jar包启动的接口地址)
  • 访问47.115.23.41/aa/xx/xx/xx...;如果aa=api,则会直接跳转到https://www.baidu.com,否则会跳到自定义返回Json的错误页面;
  • 访问47.115.23.41或者47.115.23.41/会直接跳转到https://www.baidu.com
  • rewrite ^/(.*) https://www.baidu.com/$1 permanent;;除了47.115.23.41/会直接跳转到https://www.baidu.com,其它都不行;

2020年11月25日16:37:58,如果想访问具体的网址跳转到https://www.baidu.com,则可以如下设置:

location /api/v2 {
    rewrite ^/(.*) https://www.baidu.com permanent;
}

则访问47.115.23.41/api/v2就能跳转到https://www.baidu.com

常用正则表达式:

字符 描述
^ 匹配输入字符串的起始位置
$ 匹配输入字符串的结束位置
  • | 匹配前面的字符串0次或者多次
  • | 匹配前面的字符串1次或者多次
    ? | 匹配前面的字符串0次或者1次
    . | 匹配除“\n”之外的所有单个字符
    (pattern) | 匹配括号内的pattern

应用场景:

  • 非法访问跳转,防盗链
  • 网站更换新域名
  • http跳转https
  • 不同地址访问同一个虚拟主机的资源

7.6 Nginx配置Websocket反向代理

配置:

server {
    listen 80;
    server_name xdclass.net;
    location / {
        proxy_pass http://lbs;
        proxy_read_timeout 300s; //websocket空闲保持时长
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

核心是下面的配置,其他和普通反向代理没区别, 表示请求服务器升级协议为WebSocket:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

服务器处理完请求后,响应如下报文 #状态码为101:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: upgrade

08 Nginx业务接口性能优化

8.1 服务端缓存前置

常见的开发人员控制的缓存分类:

  • 数据库缓存
  • 应用程序缓存
  • Nginx网关缓存
  • 前端缓存

服务端缓存前置,让后端结果缓存离用户更近一步。

  • /root/cache:本地路径,用来设置Nginx缓存资源的存放地址;
  • levels=1:2
    • 默认所有缓存文件都放在上面指定的根路径中,可能影响缓存的性能,推荐指定为2级目录来存储缓存文件;1和2表示用1位和2位16进制来命名目录名称。第一级目录用1位16进制命名,如a;第二级目录用2位16进制命名,如3a。所以此例中一级目录有16个,二级目录有16*16=256个,总目录数为16 *256=4096个。
    • levels=1:1:1时,表示是三级目录,且每级目录数均为16个。
  • key_zone:在共享内存中定义一块存储区域来存放缓存的key和metadata。
  • max_size:最大缓存空间,如果不指定会使用掉所有磁盘空间。当达到disk上限后,会删除最少使用的cache。
  • inactive:某个缓存在inactive指定的时间内如果不访问,将会从缓存中删除。
  • proxy_cache_valid:配置nginx cache中的缓存文件的缓存时间,proxy_cache_valid 200 304 2m 对于状态为200和304的缓存文件的缓存时间是2分钟。
  • use_temp_path:建议为off,则nginx会将缓存文件直接写入指定的cache文件中。
  • proxy_cache:启用proxy cache,并指定key_zone,如果proxy_cache off表示关闭掉缓存。
  • add_header Nging-Cache:"$upstream_cache_status"用于前端判断是否是缓存,miss、hit、expired(缓存过期)、updating(更新,使用旧的应答)
upstream lbs {
        server 47.115.23.41:8080 max_fails=2 fail_timeout=60s;
        server 47.115.23.41:8081 max_fails=2 fail_timeout=60s;
}	
proxy_cache_path /root/cache levels=1:2 keys_zone=xd_cache:10m max_size=1g inactive=60m use_temp_path=off;
server {
    listen       80;
    server_name  47.115.23.41;

    location / {
        proxy_pass http://lbs;
        
        proxy_cache xd_cache;
        proxy_cache_valid 200 304 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_key $host$uri$is_args$args;
        add_header Nginx-Cache "$upstream_cache_status";
    }
}

请求后端json接口,通过控制台日志判断是否有到后端服务。

多次请求(即刷新页面),只会出现一条记录,走了缓存。

增加参数,又会走后端服务。

注意:

  • nginx缓存过期影响的优先级进行排序为:inactvie > 源服务器端Expires/max-age > proxy_cache_valid
  • 如果出现Permission denied修改nginx.conf,将第一行修改为user root

  • 默认情况下GET请求及HEAD请求会被缓存,而POST请求不会被缓存,并非全部都要缓存,可以过滤部分路径不用缓存;

缓存清空:

  • 直接rm删除

  • nginx_cache_purge是nginx的一个三方模块,主要是对nginx生产的缓存(proxy_cache)内容进行清理。

缓存命中率统计

  • 前端打点日志上报

  • nginx日志模板增加信息:$upstream_cache_status

8.2 Nginx性能优化之静态资源压缩

对文本、js和css文件等进行压缩,一般是压缩后的大小是原始大小的25%。

  • 压缩配置
#开启gzip,减少我们发送的数据量
gzip on;
gzip_min_length 1k;

#4个单位为16k的内存作为压缩结果流缓存
gzip_buffers 4 16k;

#gzip压缩比,可在1~9中设置,1压缩比最小,速度最快,9压缩比最大,速度最慢,消耗CPU
gzip_comp_level 4;

#压缩的类型
gzip_types application/javascript text/plain text/css application/json application/xml text/javascript;

#给代理服务器用的,有的浏览器支持压缩,有的不支持,所以避免浪费不支持的也压缩,所以根据客户端的HTTP头来判断,是否需要压缩
gzip_vary on;

#禁用IE6以下的gzip压缩,IE某些版本对gzip的压缩支持很不好
gzip_disable "MSIE [1-6].";

从xdclass中拷贝一份js,其大小为39KB;

location /static {
    alias /usr/local/software/static;
}

压缩后为14.7KB。

09 Nginx整合OpenResty

9.1 OpenResty介绍与安装

什么是OpenResty,为什么要用OpenResty?

由章亦春发起,是基于Ngnix和Lua的高性能web平台,内部集成精良的Lua库、第三方模块、依赖,开发者可以方便搭建能够处高并发、扩展性极高的动态web应用、web服务、动态网关。OpenResty将Nginx核心、LuaJIT、许多有用的Lua库和Nginx第三方模块打包在一起,Nginx是C语言开发,如果要二次扩展是很麻烦的,而基于OpenResty,开发人员可以使用Lua编程语言对Nginx核心模块进行二次开发拓展,性能强大,OpenResty可以快速构造出1万以上并发连接响应的超高性能Web应用系统。

官网:http://openresty.org

阿里、腾讯、新浪、酷狗音乐等都是 OpenResty 的深度用户。

让Web服务直接跑在Nginx服务内部,充分利用Nginx的非阻塞I/O模型,不仅仅对HTTP客户端请求,甚至于对远程后端诸如MySQL、Memcaches以及Redis等都进行一致的高性能响应。所以对于一些高性能的服务来说,可以直接使用OpenResty访问Mysql或Redis等,而不需要通过第三方语言(PHP、Python、Ruby)等来访问数据库再返回,这大大提高了应用的性能。

  • Lua脚本介绍

官网:http://www.lua.org/start.html

Lua由标准C编写而成,没有提供强大的库,但可以很容易的被C/C++代码调用,也可以反过来调用C/C++的函数。在应用程序中可以被广泛应用,不过Lua是一种脚本/动态语言,不适合业务逻辑比较重的场景,适合小巧的应用场景,代码行数保持在几十行到几千行。LuaJIT是采用C和汇编语言编写的Lua解释器与即时编译器。

什么是ngx_lua

ngx_lua是Nginx的一个模块,将Lua嵌入到Nginx中,从而可以使用Lua来编写脚本,部署到Nginx中运行,即Nginx变成了一个Web容器;开发人员就可以使用Lua语言开发高性能Web应用了。

  • OpenResty提供了常用的ngx_lua开发模块
    • lua-resty-memcached
    • lua-resty-mysql
    • lua-resty-redis
    • lua-resty-dns
    • lua-resty-limit-traffic

通过上述的模块,可以用来操作mysql数据库、redis、memcached等,也可以自定义模块满足其他业务需求,很多经典的应用,比如开发缓存前置、数据过滤、API请求聚合、AB测试、灰度发布、降级、监控、限流、防火墙、黑白名单等。

  • OpenResty安装

http://openresty.org/en/linux-packages.html#centos

9.2 高并发利器-Nginx+OpenResty第一个例子

http {
    server {
        listen       80;
        server_name  localhost;
    
        location / {
            default_type text/html;
            content_by_lua_block{
                ngx.say("hello ddhhdd");
            }
        }
}

使用其他方式:

http {
    # 虚拟机主机块,还需要配置lua文件扫描路径
    lua_package_path "$prefix/lualib/?.lua;;";
    lua_package_cpath "$prefix/lualib/?.so;;";
    server {
        listen       80;
        server_name  localhost;
    
        location / {
            default_type text/html;
            content_by_lua_file lua/xdclass.lua;
        }
}

启动Nginx(直接用openresty里面的nginx即可,默认安装了多个模块),启动OpenResty中的nginx前,先ps -ef | grep "nginx"查看一下是否有启动其他nginx

注意:如果需要指定配置文件,nginx -c 配置文件路径;比如./nginx-c/usr/local/nginx/conf/nginx.conf

访问curl 127.0.0.1;如果用浏览器访问会出现文件下载,因为没有Html头信息;(亲测不会--ddh)

9.3 Nginx内置变量和OpenResty请求阶段

nginx内置变量

名称 说明
$arg_name 请求中的name参数
$args 请求中的参数
$content_length HTTP请求信息里的"Content-Length"
$content_type 请求信息里的"Content-Type"
$host 请求信息中的"Host",如果请求中没有Host行,则等于设置的服务器名
$hostname 机器名使用gethostname系统调用的值
$http_cookie cookie信息
$http_referer 引用地址
$http_user_agent 客户端代理信息
$http_via 最后一个访问服务器的ip地址
$http_x_forwarded_for 相当于网络访问路径
$is_args 如果请求行带有参数,返回“?”,否则返回空字符串
$limit_rate 对连接速率的限制
$nginx_version 当前运行的nginx版本号
$pid worker进程的PID
$query_string 与$args相同
$remote_addr 客户端ip地址
$remote_port 客户端端口号
$request 用户请求
$request_method 请求的方法,比如"GET"、"POST"等
$request_uri 请求的URI,带参数
$scheme 所用的协议,比如http或者是https
$server_name 请求到达的服务器名
$server_port 请求到达的服务器端口号
$server_protocol 请求的协议版本,"HTTP/1.0"或"HTTP/1.1"
$uri 请求的URI,可能和最初的值有不同,比如经过重定向之类的
  • 处理阶段

Nginx对于请求的处理分多个阶段,从而让第三方模块通过挂载行为在不同的阶段来控制,大致如下:

  1. 初始化阶段(Initialization Phase)
    • init_by_lua_file
    • init_worker_by_lua_file
  2. 重写与访问阶段(Rewrite / Access Phase)
    • rewrite_by_lua_file
    • access_by_lua_file
  3. 内容生成阶段(Content Phase)
    • content_by_lua_file
  4. 日志记录阶段(Log Phase)

9.4 Nginx+OpenRestry开发网络访问限制

  • 生产环境-管理后台一般需要指定的网络才可以访问,网段/ip等
  • Nginx+OpenRestry+Lua开发

自带的已经开发好的lua库可以提供使用:

http {
    ......

    upstream lbs {
        server 47.115.23.41:8080;
        server 47.115.23.41:8081;
    }
    # 这里设置为off,是为了避免每次修改之后都要重新reload的麻烦。
    # 在生产环境上需要lua_code_cache设置成on。
    lua_code_cache off;
    
    # lua_package_path可以配置openresty的文件寻址路径,$prefix为openresty安装路径
    # 文件名使用“?”作为通配符,多个路径使用“;”分隔,默认的查找路径用“;;”
    # 设置纯Lua扩展库的搜寻路径
    lua_package_path "$prefix/lualib/?.lua;;";
    
    # 设置c编写的Lua扩展模块的搜寻路径(也可以用';;')
    lua_package_cpath "$prefix/lualib/?.so;;";
    
    server {
        listen       80;
        server_name  localhost;
        
        location / {
            proxy_pass http://lbs;
            access_by_lua_file lua/black_ip_list.lua;   
        }
    }
}

lua/black_ip_list.lua; 这个是什么路径?其实就是nginx下的路径,相对路径;

创建lua/black_ip_list.lua,把127.0.0.1加入黑名单:

local black_ips = {["127.0.0.1"]=true}
local ip = ngx.var.remote_addr
if true == black_ips[ip] then
    ngx.exit(ngx.HTTP_FORBIDDEN)
    return;
end

则访问127.0.0.1的时候会出现403 Forbidden:

访问其它的则可以:

9.5 Nginx+OpenResty实现资源下载限速

限速限流应用场景:

  • 下载限速:保护带宽及服务的IO资源;
  • 请求限流:防止恶意攻击,保护服务器及资源安全;
    • 限制某个用户在一个给定时间段内能够产生的HTTP请求数;
    • 限流用在保护上游应用服务器不被在同一时刻的大量用户访问;

OpenResty下载限速案例实操:

  • Nginx 有一个 $limit_rate ,这个反映的是当前请求每秒能响应的字节数,该字节数默认为配置文件中limit_rate指令的设值;
server {
    ......
    location /download {
    
        # 当前请求的响应上限是 每秒300K字节
        access_by_lua_block {
            ngx.var.limit_rate = "300K"
        }
        
        alias /usr/local/software/app;  
    }
}

以下载demo-1.jar为例子:

9.6 网盘静态资源下载限速实现原理

限制下载速度常用的是:漏桶原理和令牌桶原理;

什么是漏桶算法?

备注:如果是请求限流,请求先进入到漏桶里,漏桶以固定的速度出水,也就是处理请求,当水加的过快也就是请求过多,桶就会直接溢出,也就是请求被丢弃拒绝了,所以漏桶算法能强行限制数据的传输速率或请求数.

什么是令牌桶算法?

备注:只要突发并发量不高于桶里面存储的令牌数据,就可以充分利用好机器网络资源。如果桶内令牌数量小于被消耗的量,则产生的令牌的速度就是均匀处理请求的速度。

10 Ngnix高可用解决方案LVS+KeepAlived

10.1 全链路高可用之Nginx基础架构问题分析

  • 全链路高可用之Nginx反向代理单点故障分析
    • dns轮训多个ip,假如某个nginx挂了,怎么办?

即这里的问题是只有一个nginx,如果这个nginx挂了,就无法连接后面的服务节点;--ddh

  • Nginx集群架构(vip ,virtual ip)

这两个nginx,提供给用户的是同一个ip,即vip。--ddh

网络知识补充:

国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标准体系。

从低到高分别是:物理层 -> 数据链路层 -> 网络层 -> 传输层 -> 会话层 -> 表示层 -> 应用层

四层负载:工作在OSI第四层,也就是传输层;

七层负载:工作在最高层,也就是应用层;

  • F5、LVS(四层负载 tcp)

    • 用虚拟ip+port接收请求,再转发到对应的真实机器
  • HAproxy、Nginx(七层负载)

    • 用虚拟的url或主机名接收请求,再转向相应的处理服务器

10.2 业界主流的高可用方案Linux虚拟服务器LVS

什么是LVS?http://www.linuxvirtualserver.org/

LVS是Linux Virtual Server,Linux虚拟服务器,是一个虚拟的服务器集群系统项目,是由章文嵩博士成立,是中国国内最早出现的自由软件项目
之一。Linux2.4 内核以后,LVS 已经是 Linux 标准内核的一部分。

软件负载解决的两个核心问题是:选谁、转发

提供了10多种调度算法: 轮询、加权轮询、最小连接、目标地址散列、源地址散列等;

三种负载均衡转发技术:

  • NAT:数据进出都通过 LVS,前端的Master既要处理客户端发起的请求,又要处理后台RealServer的响应信息,将RealServer响应的信息再转发给客户端,容易成为整个集群系统性能的瓶颈;(支持任意系统且可以实现端口映射)
  • DR:移花接木,最高效的负载均衡规则,前端的Master只处理客户端的请求,将请求转发给RealServer,由后台的RealServer直接响应客户端,不再经过Master,性能要优于LVS-NAT;需要LVS和RS集群绑定同一个VIP(支持多数系统,不可以实现端口映射)
  • TUNL:隧道技术,前端的Master只处理客户端的请求,将请求转发给RealServer,然后由后台的RealServer直接响应客户端,不再经过Master;(支持少数系统,不可以实现端口映射)

10.3 业界主流的高可用方案keepalived(以后的部分没有亲自试验过)

什么是keepalived?

  • 核心:监控并管理 LVS 集群系统中各个服务节点的状态;

keepalived是一个类似于交换机制的软件,核心作用是检测服务器的状态,如果有一台Web服务器工作出现故障,keepalived将检测到并将有故障的服务器从系统中剔除,使用其他服务器代替该服务器的工作,当服务器工作正常后keepalived自动将服务器加入到服务器群中,这些工作全部自动完成。后来加入了vrrp(虚拟路由器冗余协议),除了为LVS提供高可用还可以为其他服务器比如Mysql、Haproxy等软件提供高可用方案。

  • 安装
yum install -y keepalived

# 路径
cd /etc/keepalived
  • 启动和查看命令
# 启动
service keepalived start

# 停止
service keepalived stop

# 查看状态
service keepalived status

# 重启
service keepalived restart

# 停止防火墙
systemctl stop firewalld.service

注意:如果有缺少依赖可以执行下面的命令

yum install -y gcc
yum install -y openssl-devel
yum install -y libnl libnl-devel
yum install -y libnfnetlink-devel
yum install -y net-tools
yum install -y vim wget

10.4 keepalived核心配置讲解

  • 配置/etc/keepalived/keepalived.conf(Master)
! Configuration File for keepalived

global_defs {
    # 设置lvs的id,在一个网络内应该是唯一的
    router_id LVS_DEVEL 
    # 允许执行外部脚本
    enable_script_security 
}


# 配置vrrp_script,主要用于健康检查及检查失败后执行的动作。
vrrp_script chk_real_server {
    # 健康检查脚本,当脚本返回值不为0时认为失败
    script "/usr/local/software/conf/chk_server.sh"
    # 检查频率,以下配置每2秒检查1次
    interval 2
    # 当检查失败后,将vrrp_instance的priority减小5
    weight -5
    # 连续监测失败3次,才认为真的健康检查失败。并调整优先级
    fall 3
    # 连续监测2次成功,就认为成功。但不调整优先级
    rise 2

    user root
}



# 配置对外提供服务的VIP vrrp_instance配置

vrrp_instance VI_1 {

    # 指定vrrp_instance的状态,是MASTER还是BACKUP主要还是看优先级。
    state MASTER

    # 指定vrrp_instance绑定的网卡,最终通过指定的网卡绑定VIP
    interface ens33

    # 相当于VRID,用于在一个网内区分组播,需要组播域内内唯一。
    virtual_router_id 51

    # 本机的优先级,VRID相同的机器中,优先级最高的会被选举为MASTER
    priority 100

    # 心跳间隔检查,默认为1s,MASTER会每隔1秒发送一个报文告知组内其他机器自己还活着。
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass 1111
    }

    # 定义虚拟IP(VIP)为192.168.159.100,可多设,每行一个
    virtual_ipaddress {
        192.168.159.100
    }

  # 本vrrp_instance所引用的脚本配置,名称就是vrrp_script 定义的容器名
  track_script {
      chk_real_server
    }
}

# 定义对外提供服务的LVS的VIP以及port
virtual_server 192.168.159.100 80 {
    # 设置健康检查时间,单位是秒
    delay_loop 6

    # 设置负载调度的算法为rr
    lb_algo rr

    # 设置LVS实现负载的机制,有NAT、TUN、DR三个模式
    lb_kind NAT

    # 会话保持时间
    persistence_timeout 50

    # 指定转发协议类型(TCP、UDP)
    protocol TCP

    # 指定real server1的IP地址

    real_server 192.168.159.145 80 {
        # 配置节点权值,数字越大权重越高
        weight 1

        # 健康检查方式
        TCP_CHECK {                
            # 连接超时
            connect_timeout 10
            # 重试次数
            retry 3           
            # 重试间隔
            delay_before_retry 3     
            # 检查时连接的端口
            connect_port 80          
        }

    }

}
  • 配置/etc/keepalived/keepalived.conf(Backup)
! Configuration File for keepalived

global_defs {
    # 设置lvs的id,在一个网络内应该是唯一的
    router_id LVS_DEVEL 
    # 允许执行外部脚本
    enable_script_security 
}


# 配置vrrp_script,主要用于健康检查及检查失败后执行的动作。
vrrp_script chk_real_server {
    # 健康检查脚本,当脚本返回值不为0时认为失败
    script "/usr/local/software/conf/chk_server.sh"
    # 检查频率,以下配置每2秒检查1次
    interval 2
    # 当检查失败后,将vrrp_instance的priority减小5
    weight -5
    # 连续监测失败3次,才认为真的健康检查失败。并调整优先级
    fall 3
    # 连续监测2次成功,就认为成功。但不调整优先级
    rise 2

    user root
}

# 配置对外提供服务的VIP vrrp_instance配置
vrrp_instance VI_1 {

    # 指定vrrp_instance的状态,是MASTER还是BACKUP主要还是看优先级。
    state BACKUP

    # 指定vrrp_instance绑定的网卡,最终通过指定的网卡绑定VIP
    interface ens33

    # 相当于VRID,用于在一个网内区分组播,需要组播域内内唯一。
    virtual_router_id 51

    # 本机的优先级,VRID相同的机器中,优先级最高的会被选举为MASTER
    priority 50

    # 心跳间隔检查,默认为1s,MASTER会每隔1秒发送一个报文告知组内其他机器自己还活着。
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass 1111
    }

    # 定义虚拟IP(VIP)为192.168.159.100,可多设,每行一个
    virtual_ipaddress {
        192.168.159.100
    }

  # 本vrrp_instance所引用的脚本配置,名称就是vrrp_script 定义的容器名
  track_script {
      chk_real_server
    }
}

# 定义对外提供服务的LVS的VIP以及port
virtual_server 192.168.159.100 80 {
    # 设置健康检查时间,单位是秒
    delay_loop 6

    # 设置负载调度的算法为rr
    lb_algo rr

    # 设置LVS实现负载的机制,有NAT、TUN、DR三个模式
    lb_kind NAT

    # 会话保持时间
    persistence_timeout 50

    # 指定转发协议类型(TCP、UDP)
    protocol TCP

    # 指定real server1的IP地址

    real_server 192.168.159.146 80 {
        # 配置节点权值,数字越大权重越高
        weight 1

        # 健康检查方式
        TCP_CHECK {                  
            # 连接超时
            connect_timeout 10
            # 重试次数
            retry 3           
            # 重试间隔
            delay_before_retry 3     
            # 检查时连接的端口
            connect_port 80          
        }

    }

}

配置注意:

  • router_id后面跟的自定义的ID在同一个网络下是一致的;
  • state后跟的MASTER和BACKUP必须是大写;否则会造成配置无法生效的问题;
  • interface网卡ID;要根据自己的实际情况来看,可以使用以下方式查询ip a查询;
  • 在BACKUP节点上,其keepalived.conf与Master上基本一致,修改state为BACKUP,priority值改小即可;
  • authentication主备之间的认证方式,一般使用PASS即可;主备的配置必须一致,不能超过8位;

10.5 准备Nginx+Lvs+KeepAlive相关软件环境

  • 配置Nginx,修改网页(即修改nginx的欢迎页用于测试)
  • 启动keepalived
# 启动
service keepalived start

# 停止
service keepalived stop

# 查看状态
service keepalived status

# 重启
service keepalived restart

# 停止防火墙
systemctl stop firewalld.service

10.6 Nginx+Lvs+KeepAlive高可用方案实施

根据需求配置多个节点;(本实验是一个Master节点,一个Backup节点)

  • 如果其中keepalived挂了,那就会把vip就会分发到另外一个keepalived节点(ip漂移),响应正常;
  • 如果某个realServer挂了,比如是Nginx挂了,那对应keepalived节点存活依旧可以转发过去,但是响应失败;

解决如上问题:

  • 脚本监听
# 配置vrrp_script,主要用于健康检查及检查失败后执行的动作
vrrp_script chk_real_server {
    # 健康检查脚本,当脚本返回值不为0时认为失败
    script "/usr/local/software/conf/chk_server.sh"
    # 检查频率,以下配置每2秒检查1次
    interval 2
    # 当检查失败后,将vrrp_instance的priority减小5
    weight -5
    # 连续监测失败3次,才认为真的健康检查失败。并调整优先级
    fall 3
    # 连续监测2次成功,就认为成功。但不调整优先级
    rise 2

    user root
}
  • chk_server.sh脚本内容(需要 `chmod +x chk_server.sh``)
# !/bin/bash
# 检查nginx进程是否存在
counter=$(ps -C nginx --no-heading|wc -l)
if [ "${counter}" -eq "0" ]; then
    service keepalived stop
    echo 'nginx server is died.......'
fi

常见问题:

  • vip能ping通,vip监听的端口不通?第一个原因:nginx1和nginx2两台服务器的服务没有正常启动;
  • vip ping不通? 核对是否出现裂脑,常见原因为防火墙配置所致导致多播心跳失败,核对keepalived的配置是否正确;

特别注意: 需要关闭selinux,不然sh脚本可能不生效;

  • 查看:getenforce
  • 关闭:setenforce 0

生产环境问题

  • 是否提供vip(virtual ip):阿里云(LBS)、华为云、腾讯云、AWS;
posted @ 2020-11-20 16:09  ddhhdd  阅读(389)  评论(0)    收藏  举报