简单的 nginx 学习
1. nginx的安装
1.1 下载安装包
去官网下载对应的nginx包,推荐使用稳定版本,上传nginx到linux系统
1.2 安装依赖环境
-
安装gcc环境
yum install gcc-c++
-
安装PCRE库,用于解析正则表达式
yum install -y pcre pcre-devel
-
zlib压缩和解压缩依赖
yum install -y zlib zlib-devel
-
SSL 安全的加密的套接字协议层,用于HTTP安全传输,也就是https
yum install -y openssl openssl-devel
-
解压,需要注意,解压后得到的是源码,源码需要编译后才能安装
tar -zxvf nginx-1.26.2.tar.gz
-
编译之前,先创建nginx临时目录,如果不创建,在启动nginx的过程中会报错
mkdir /var/temp/nginx -p
-
在nginx目录,输入如下命令进行配置,目的是为了创建makefile文件
./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
命令 解释 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临时目录 -
make编译以及install安装
make
make install
-
启动服务
# 进入到nginx安装目录 cd /usr/local/nginx/sbin # 启动服务 ./nginx # 停止服务 ./nginx -s stop # 重新加载 ./nginx -s reload
-
打开浏览器,访问虚拟机所处内网ip即可打开nginx默认页面,显示如下便表示安装成功
注意事项:
如果在云服务器安装,需要开启默认的nginx端口:80
如果在虚拟机安装,需要关闭防火墙
本地win或mac需要关闭防火墙
2. 首页访问过程
查看 nginx/conf
下的 nginx.conf
文件
server {
# 监听端口
listen 80;
# 监听服务ip地址
server_name localhost;
# 映射地址, /映射到下面配置地址
location / {
# 相对目录地址 html与conf目录同级
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
上面访问过程如下:
客户端请求http://192.168.138.129:80/
=> Nginx服务器 => 监听80端口 => 找到对应服务server => 映射路由地址 / => HTML资源
3. nginx基础认知
3.1 nginx进程模型
同步与异步,阻塞与非阻塞
-
同步阻塞: 客户端发送请求给服务端,此时服务端处理任务时间很久,则客户端则被服务端堵塞了,所以客户端会一直等待服务端的响应,此时客户端不能做其他任何事,服务端也不会接受其他客户端的请求。这种通信机制比较简单粗暴,但是效率不高。
-
同步非阻塞: 客户端发送请求给服务端,此时服务端处理任务时间很久,这个时候虽然客户端会一直等待响应,但是服务端可以处理其他的请求,过一会回来处理原先的。这种方式很高效,一个服务端可以处理很多请求,不会在因为任务没有处理完而堵着,所以这是非阻塞的。
-
异步阻塞: 客户端发送请求给服务端,此时服务端处理任务时间很久,但是客户端不会等待服务器响应,它可以做其他的任务,等服务器处理完毕后再把结果响应给客户端,客户端得到回调后再处理服务端的响应。这种方式可以避免客户端一直处于等待的状态,优化了用户体验,其实就是类似于网页里发起的ajax异步请求。
-
异步非阻塞: 客户端发送请求给服务端,此时服务端处理任务时间很久,这个时候的任务虽然处理时间会很久,但是客户端可以做其他的任务,因为他是异步的,可以在回调函数里处理响应;同时服务端是非阻塞的,所以服务端可以去处理其他的任务,如此,这个模式就显得非常的高效了。
进程模型架构图
主要分为两个进行模块
-
master: 主进程,领导者,数量为1
管理worker,如果worker异常停止master则会重新启动,一般master发送信号到worker服务
./nginx -s stop ./nginx -s quit ./nginx -s reload # 检查修改配置文件是否正确 ./nginx -t
-
worker:工作进程,为领导者服务,默认数量为1
可以修改,在
nginx.conf
文件中,修改worker_processes
即可,重新加载nginx
后发现worker
数量已经修改# 检查修改配置文件是否正确 nginx -t # 重新加载 nginx -s reload
-
worker数量设置
nginx的worker进程数量通常设置为与服务器的CPU核心数量相等。如果你的服务器有多个CPU或者超线程(HT)技术,通常推荐设置为CPU核心数的2倍,或者如果你的应用是CPU密集型的,那么worker数可以设置为CPU核心数的1.5-2倍。
设置worker进程数量的指令是
worker_processes
,在Nginx配置文件中(通常是nginx.conf
),你可以找到或添加这样的行:# 设置为auto会让Nginx自动检测CPU核心数量。 worker_processes auto; # 或者具体的CPU核心数量 # 假设你的服务器有4个CPU核心,如果你想手动指定,可以替换为具体的数字,例如: worker_processes 4;
3.2 nginx处理web请求
3.2.1 nginx的worker的抢占机制
nginx的worker的抢占机制处理流程图:
处理过程具体如下:
- 服务启动,首先创建(fork)三个worker进程
- client发起请求,三个worker发起争抢,此处使用互斥锁(锁与client是对应的),worker1抢到,则2,3则不进行处理
- worker1完整处理好请求,返回客户端响应数据,则释放锁信息
3.2.2 服务器处理机制对比
传统服务器的事件处理流程图:
处理过程如下:
- master创建多个worker
- client1请求worker1但是请求阻塞了,client2,3则不可以进行处理,则需要worker2进行处理
- 如果请求特别耗时,并且请求量大,则需要很多worker进行处理
nginx服务器事件处理流程图:
处理过程如下:
-
master创建多个worker,接收服务请求
-
client1发送请求到worker1,发现阻塞了,client2来了,没有关系,worker使用的是epoll异步模型,多路复用,不会阻塞,可以先处理client2,3请求
-
一个worker一般可以处理6~8万个请求,在
nginx.conf
配置文件中,可以配置worker的连接模型以及连接数,默认为1024
3.3 nginx 配置文件结构
nginx结构图如下:
具体配置文件结构格式如下配置文件所示
# 设置worker进程的用户,指的linux中的用户,会涉及到nginx操作目录或文件的一些权限,默认为nobody
user root;
# worker进程工作数设置,一般来说CPU有几个,就设置几个,或者设置为N-1也行
worker_processes 2;
# nginx 日志级别debug | info | notice | warn | error | crit | alert | emerg,错误级别从左到右越来越大
error_log logs/error.log notice;
# 设置nginx进程 pid
pid logs/nginx.pid;
# 设置工作模式
events {
# 默认使用epoll
use epoll;
# 每个worker允许连接的客户端最大连接数
worker_connections 1024*10;
}
# http 是指令块,针对http网络传输的一些指令配置
http {
# include 引入外部配置,提高可读性,避免单个配置文件过大
# 也可以导入其他server信息,将server信息设置为单个server.conf文件,进行导入 include server1.conf
include mime.types;
# 设定日志格式,main 为定义的格式名称,如此 access_log 就可以直接使用这个变量了
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# sendfile使用高效文件传输,提升传输性能。启用后才能使用tcp_nopush,是指当数据表累积一定大小后才发送,提高了效率。
sendfile on;
tcp_nopush on;
# gzip启用压缩,html/js/css压缩后传输会更快
gzip on;
# default_type 指令用来配置 Nginx 响应前端请求默认的 MIME 类型。默认是 text 文本。
default_type application/octet-stream;
# keepalive_timeout设置客户端与服务端请求的超时时间,保证客户端多次请求的时候不会重复建立新的连接,节约资源损耗。
keepalive_timeout 65;
# server可以在http指令块中设置多个虚拟主机
server {
# 监听端口
listen 80;
# localhost、ip、域名
server_name localhost;
# 请求路由映射,匹配拦截
location / {
# 请求位置
root html;
# 首页设置
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server ......
}
日志参数配置详情:
参数名 | 参数意义 |
---|---|
$remote_addr | 客户端ip |
$remote_user | 远程客户端用户名,一般为:’-’ |
$time_local | 时间和时区 |
$request | 请求的url以及method |
$status | 响应状态码 |
$body_bytes_send | 响应客户端内容字节数 |
$http_referer | 记录用户从哪个链接跳转过来的 |
$http_user_agent | 用户所使用的代理,一般来时都是浏览器 |
$http_x_forwarded_for | 通过代理服务器来记录客户端的ip |
3.4 pid打开失败处理
偶尔执行nginx
命令的时候会提示 pid没有等问题,具体解决方式如下所示
-
执行
./nginx -s reload
提示/var/run/nginx/nginx.pid failed 没有那个文件或者目录
, 我们则重新创建对应目录即可mkdir /var/run/nginx
-
执行
./nginx -s reload
发现提示我们invalid PID number in /var/run/nginx/nginx.pid
,我们则手动指定nginx.conf配置文件即可# 配置nginx配置文件目录 ./nginx -c /usr/local/nginx/conf/nginx.conf # 重启nginx ./nginx -s reload
3.5 nginx常用命令
-
关闭命令
# 快速关闭 ./nginx -s stop # 优雅停止,执行完用户请求再停止,针对http请求 ./nginx -s quit
-
配置文件修改检查命令
# 检查配置文件修改是否正确 ./nginx -t
-
检查版本号
# 版本信息 ./nginx -v # 具体信息包含版本信息 ./nginx -V
-
帮助命令
# 帮助命令 ./nginx -h ./nginx -?
-
指定自定义配置文件
./nginx -c /usr/local/nginx/conf/nginx.conf
3.6 日志备份切割
3.6.1 手动切割
正常以天作为切割单位,创建一个shell可执行文件:cut_my_log.sh,内容为:
#!/bin/bash
LOG_PATH="/var/log/nginx/"
# 获取昨天日期时间
RECORD_TIME=$(date -d "yesterday" +%Y-%m-%d+%H:%M)
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
为cut_my_log.sh
添加可执行的权限:
# 添加执行权限
chmod +x cut_my_log.sh
# 执行切割
./cut_my_log.sh
3.6.2 自动切割
自动切割日志需要使用定时任务,需要安装定时任务
yum install crontabs
使用crontab -e
编辑并且添加一个新的定时任务
crontab -e
# 编辑内容新增如下内容:
*/1 * * * * /usr/local/nginx/sbin/cut_my_log.sh
# 退出后重启定时模块
service crond restart
crontab常用命令如下:
命令 | 作用 |
---|---|
service crond start | 启动服务 |
service crond stop | 关闭服务 |
service crond restart | 重启服务 |
service crond reload | 重新载入配置 |
crontab -e | 编辑任务 |
crontab -l | 查看任务列表 |
cron表达式是,分为5或6个域,每个域代表一个含义,如下所示:
分 | 时 | 日 | 月 | 星期 | 年 | |
---|---|---|---|---|---|---|
取值范围 | 0-59 | 0-23 | 1-31 | 1-12 | 1-7 | 2019、2020… |
常用表达式如下:
- 每分钟
*/1 * * * *
- 每日凌晨
59 23 * * *
- 每日凌晨1点
0 1 * * *
3.7 搭建静态资源服务器
上传前端服务dist以及一个mp3文件到服务器目录
开始配置nginx.conf配置文件
# 我使用的是root用戶操作,所以需要修改use属性
use root;
# 在nginx.conf的http模块下,引入静态资源conf
include static.conf
编辑 static.conf
server {
listen 90;
server_name localhost;
# 资源源码
location / {
root /root/nginx/dist;
index index.html;
}
# 最后拼接的路径为 /root/nginx,location后面路径会拼接到root地址后面
location /nginx {
root /root/;
}
}
最后进行访问 http://192.168.138.129:90/
也可以使用別名进行设置
server {
listen 90;
server_name localhost;
# 资源源码
location / {
root /root/nginx/dist;
index index.html;
}
# 使用别名访问,当访问/static时候,会转发到alias路径
location /static {
# root /root/;
alias /root/nginx;
}
}
再次访问 http://192.168.138.129:90/static/gwr.mp3 结果如下所示
3.8 Gzip提高请求效率
开启 gzip
压缩功能,提高传输效率,提高带宽,gzip与server平级
# 开启gzip
gizp on
# 限制最小压缩,小于1个字节文件不会被压缩
gzip_min_length 1;
# 设置压缩级别(压缩比,文件越大压缩越多,但是cpu会占用过多)
gzip_comp_level 3;
# 压缩类型
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-http-php image/jpeg image/gif image/png application/json;
3.9 location配置
3.9.1 locaton匹配规则
-
前缀匹配
前缀匹配:默认的匹配方式,不带修饰符,匹配 URI 的前缀 location /api { # 处理 /api 及其子路径的请求 }
-
精准匹配
=
location = / { root html; index index.html; } # = 具体某一个文件,最终访问路径 ip:port/root/nginx/gwr.mp3 location = /nginx/gwr.mp3 { root /root; }
-
正则表达式
# `~*` 正则表达式不区分大小写,访问路径 ip:port/root/nginx/xxx.mp3|xxx.jpg|xxx.jpeg location ~* \.(mp3|png|jpg|jpeg) { root /root/nginx; } # `~` 正则表达式区分大小写 location ~ \.(mp3|png|jpg|jpeg) { root /root/nginx; } # `^~` 正则表达式以某个字符开头请求,请求路径为 ip:port/root/nginx/xxx.mp3 ...... location ^~ /nginx { root /root; }
-
目录匹配
# 以斜杠 / 结尾的匹配模式,用于匹配特定的目录。 location /images/ { # 处理 /images/ 目录下的请求 }
location /images/
其匹配规则如下:
精确匹配:如果请求路径完全匹配 /images/,则会进入该 location 块。
前缀匹配:如果请求路径以 /images/ 开头,则也会进入该 location 块。具体来说,以下几种情况都会匹配到 location /images/:
- /images/
- /images/image1.jpg
- /images/subdir/image2.png
因此,location /images/ 要求请求路径必须以 /images/ 开头,但不一定是完全相同的路径,以我们公司测试环境为例,页面请求路径信息如下所示
http://ip:8901/queryModel/query/launderMoneyCustomer/countData
对应的nginx配置信息如下,其接收客户端以
/queryModel/
为开始的请求,根据规则匹配转发到http://ip:11200/dics/demo/queryModel/
,此路径为网关地址,后期网关会解析此访问路径具体的nginx.conf主配置如下:
# 用户配置,指定运行 Nginx 的用户 user root; # 设置工作进程数,自动根据系统核心数来决定 worker_processes auto; # 启用正则表达式的 JIT 编译,以加快处理速度 pcre_jit on; # 错误日志配置,只记录警告及以上级别的错误 error_log logs/error.log warn; # PID 文件位置 pid logs/nginx.pid; # 事件模块配置 events { # 每个工作进程的最大并发连接数 worker_connections 65535; } # HTTP 部分配置 http { # 包含 MIME 类型文件 include mime.types; # 默认 MIME 类型 default_type application/octet-stream; # 分别配置main,json两种日志格式配置,后面方便使用 log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; # JSON 格式的日志格式 log_format json escape=json '{"@timestamp":"$time_iso8601",' '"host":"$hostname",' '"server_ip":"$server_addr",' '"client_ip":"$remote_addr",' '"xff":"$http_x_forwarded_for",' '"domain":"$host",' '"url":"$uri",' '"referer":"$http_referer",' '"args":"$args",' '"upstreamtime":"$upstream_response_time",' '"responsetime":"$request_time",' '"request_method":"$request_method",' '"status":"$status",' '"size":"$body_bytes_sent",' '"request_length":"$request_length",' '"protocol":"$server_protocol",' '"upstreamhost":"$upstream_addr",' '"upstreamtime":"$upstream_response_time",' '"upstreamstatus":"$upstream_status",' '"file_dir":"$request_filename",' '"http_user_agent":"$http_user_agent"' '}'; # 访问日志配置,使用 JSON 格式 access_log logs/access.log json; # 启用 sendfile 优化 sendfile on; # TCP 无推送选项(已注释) # tcp_nopush on; # 保持连接的超时时间 keepalive_timeout 120; # 启用 gzip 压缩(已注释) # gzip on; # FastCGI 缓冲区大小 固定配置即可 fastcgi_buffer_size 128k; fastcgi_buffers 8 128k; fastcgi_busy_buffers_size 128k; fastcgi_temp_file_write_size 128k; # 客户端请求头缓冲区大小 固定配置即可 client_header_buffer_size 128k; # 客户端请求体缓冲区大小 固定配置即可 client_body_buffer_size 4m; # 允许请求头中包含下划线 固定配置即可 underscores_in_headers on; # 定义一个服务器块 server { # 监听端口 8000 listen 8000; # 服务器名称 server_name localhost; # 处理 /vts_metrics 请求,非必要配置 location /vts_metrics { # 显示虚拟主机流量状态 vhost_traffic_status_display; # 设置显示格式为 HTML vhost_traffic_status_display_format html; } # 处理 /sts_metrics 请求,非必要配置 location /sts_metrics { # 显示流媒体服务器流量状态 stream_server_traffic_status_display; # 设置显示格式为 HTML stream_server_traffic_status_display_format html; } } # 包含其他虚拟主机配置文件 include vhosts/*.conf; }
具体的转发规则在
nginx.conf
同目录的/vhosts
目录下如下,其中一个credit.conf的内容如下:# 定义一个服务器块 server { # 监听端口 8901 上的 IPv4 连接 listen 8901; # 监听端口 8901 上的 IPv6 连接 listen [::]:8901; # 设置服务器名称,用于虚拟主机区分 server_name localhost; # 设置默认文档根目录 root html/front-credit; # 指定默认索引文件 index index.html index.htm; # 处理 /download 路径下的请求 location /download { # 设置索引文件 index index.html index.htm; # 设置文档根目录为 /data/htdocs root /data/htdocs; } # 处理 /queryModel/ 路径下的请求,有多个模块,除路径其余基本相同,此处只记录一个 location /queryModel/ { # 将请求代理到后端服务器 proxy_pass http://ip:11200/dics/demo/queryModel/; # 关闭自动重定向 proxy_redirect off; # 设置代理请求头 proxy_set_header HOST $host; # 设置 Host 头为客户端请求中的 Host proxy_set_header X-Real-IP $remote_addr; # 设置 X-Real-IP 头为客户端的真实 IP 地址 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 设置 X-Forwarded-For 头为客户端的真实 IP 地址(如果经过多个代理) # 使用 HTTP/1.1 版本进行代理 proxy_http_version 1.1; # 设置代理请求头 Connection 为 "Keep-Alive" proxy_set_header Connection "Keep-Alive"; # 设置代理相关的超时时间 proxy_connect_timeout 60; # 建立连接的超时时间 proxy_send_timeout 120; # 发送数据的超时时间 proxy_read_timeout 120; # 接收数据的超时时间 } # 设置访问日志文件的位置和格式 access_log logs/dics-credit.log json; }
gateway网关获取到请求信息,进行路径信息切割替换,最终转发到query模块后端服务,gateway具体的配置参数如下表所示
我们测试环境还有另一个前端服务配置信息如下,此处记录一下方便日后复习:
# 定义一个服务器块(server block) server { # 监听端口8905上的IPv4连接 listen 8905; # 同时监听端口8905上的IPv6连接 listen [::]:8905; # 设置服务器名称,用于虚拟主机区分 server_name localhost; # 设置默认文档根目录 root html/front-clm; # 指定默认索引文件 index index.html index.htm; # 定义一个位置块(location block),处理以"/api/"开头的所有请求 location /api/ { # 将请求代理到后端服务器 proxy_pass http://xxx.xxx.xxx.xxx:18602/; # 关闭自动重定向 proxy_redirect off; # 设置代理请求头 proxy_set_header HOST $host; # 设置Host头为客户端请求中的Host proxy_set_header X-Real-IP $remote_addr; # 设置X-Real-IP头为客户端的真实IP地址 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 设置X-Forwarded-For头为客户端的真实IP地址(如果经过多个代理) # 使用HTTP/1.1版本进行代理 proxy_http_version 1.1; # 设置代理请求头Connection为"Keep-Alive" proxy_set_header Connection "Keep-Alive"; # 设置代理相关的超时时间 proxy_connect_timeout 60; # 建立连接的超时时间 proxy_send_timeout 120; # 发送数据的超时时间 proxy_read_timeout 120; # 接收数据的超时时间 } # 设置访问日志文件的位置和格式 access_log logs/dics-clm.log json; }
由上面的配置逻辑,我们可以整理出具体的请求逻辑如下:
- 客户端发起请求 http://ip:8901/queryModel/query/launderMoneyCustomer/countData 到nginx服务
- nginx服务匹配
/queryModel/
路径转发到网关,请求变为 http://ip:11200/dics/demo/queryModel/query/launderMoneyCustomer/countData - 请求网关根据切割要求切割
/dics/demo/queryModel
,请求变为 http://query-server:port/query/launderMoneyCustomer/countData,其中query-server
为gateway获取注册到nacos上的服务名称
3.9.2 location 的优先级
当有多个 location 块匹配同一个请求时,nginx 会根据以下规则来确定使用哪个 location 块:
-
首先检查是否有精确匹配 (
=
)。 -
如果没有,检查是否有带有正则表达式的匹配 (
~
或~*
),并使用第一个匹配的规则。 -
如果没有正则表达式匹配,则使用最长的前缀匹配。
3.9.3 location 的应用场景
location 块可以用于多种场景,以下是一些例子:
-
静态资源服务
这个 location 块用于服务 /usr/share/nginx/html/static/ 目录下的静态文件
# 访问/static/目录信息转发到别名路径下,获取静态资源 location /static/ { alias /usr/share/nginx/html/static/; }
-
代理转发
所有以 /api/ 开头的请求都会被代理到后端服务器,我们配合
# 定义一个名为 tomcats 的 upstream,用于负载均衡多个后端服务器 upstream tomcats { # 添加三个后端服务器地址 server 192.168.138.129:8081; server 192.168.138.129:8082; server 192.168.138.129:8083; } # 配置一个 Nginx 服务器块 server { # 监听 8080 端口 listen 8080; # 设置服务器名称 server_name localhost; # 配置 location 块来处理根路径的请求 location / { # 将请求代理到前面定义的 tomcats 上游服务器组 proxy_pass http://tomcats; } }
-
错误页面重定向
当发生 404 错误时,Nginx 会重定向到 /404.html 页面
# 当发生 404 错误时,返回 /404.html 页面 error_page 404 /404.html; location = /404.html { # 指定 404 页面的根目录为 /usr/share/nginx/html root /usr/share/nginx/html; # 该 location 块仅对内部重定向有效,外部请求无法直接访问这个路径 internal; }
-
基于请求方法的处理
根据请求方法的不同,将请求重写到不同的 location块进行处理。
location /api { if ($request_method = POST) { # 如果请求方法是 POST,则重写请求路径到 /api/post rewrite ^ /api/post last; } } location /api/post { # 处理 POST 请求 }
-
限制访问
这个 location 块限制只有来自特定 IP 范围的请求才能访问 /restricted/ 路径。
location /restricted/ { # 允许来自 192.168.138.0/24 子网的请求访问该路径(192.168.138.1~192.168.138.254) allow 192.168.138.0/24; # 拒绝所有其他客户端的请求 deny all; }
3.9.4 location 的嵌套
location 块可以嵌套,这意味着你可以在一个 location 块内部定义另一个 location 块。这通常用于对特定的路径进行更细粒度的控制
在这个例子中,/admin 路径下的请求首先匹配外层的 location /,然后进一步匹配内层的 location /admin,如果请求的文件以 .php 结尾,则还会匹配最内层的 location ~ .php$。
location / {
# 默认处理
location /admin {
# 对 /admin 路径的特殊处理
location ~ \.php$ {
# 对 /admin 路径下 PHP 文件的处理
}
}
}
3.9.5 location的指令
在 location 块内部,你可以使用各种指令来控制请求的处理方式,例如:
-
proxy_pass:将请求代理到后端服务器。
-
rewrite:重写请求的 URI。
-
try_files:尝试不同的文件或路径,直到找到一个存在的。
-
root 和 alias:设置请求文件的基本目录。
-
index:指定目录索引文件。
-
autoindex:启用或禁用目录列表。
-
error_page:定义错误页面。
-
auth_basic 和 auth_basic_user_file:设置 HTTP 基本认证
下面我们编写一个示例配置文件:
server {
# listen 80;:指定 Nginx 监听 HTTP 请求的端口号,这里是 80 端口,即标准的 HTTP 端口。
listen 80;
# 定义服务器名,当客户端请求的Host与这个名称匹配时,就会使用这个server块的配置。
server_name example.com;
# 设置网站的根目录,所有未命名的 location 块(即默认的 location /)都会从这个目录下寻找资源
root /var/www/html;
# 这个 location 块精确匹配根路径 /,并且配置了 index index.html;,这意味着当访问根路径时,Nginx 会自动寻找 /var/www/html 目录下的 index.html 文件来响应请求。
location = / {
# 设置默认索引文件为 index.html
index index.html;
}
# 这个 location 块用于处理所有以 /static/ 开头的请求
location /static/ {
# 将请求的 URI 替换为指定的路径,也就是说,所有对 /static/ 的请求都会映射到实际的目录 /var/www/static/
alias /var/www/static/;
}
# 代理到后端应用服务器,这个 location 块用于处理所有以 /api/ 开头的请求。
location /api/ {
# 将所有匹配的请求代理到名为 backend_server 的后端服务器。通常,backend_server 需要在 http 或 upstream 块中定义
proxy_pass http://backend_server;
# 设置代理请求的 Host 头部为客户端的原始请求中的 Host 值。
proxy_set_header Host $host;
# 设置 X-Real-IP 头部为客户端的 IP 地址。
proxy_set_header X-Real-IP $remote_addr;
# 设置 X-Forwarded-For 头部,这是一个用于标识请求来源的链式头部,可以追踪请求穿过多个代理的路径
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 设置 X-Forwarded-Proto 头部为请求使用的协议(http 或 https)
proxy_set_header X-Forwarded-Proto $scheme;
}
# 这个 location 块使用正则表达式匹配所有以 .php 结尾的文件
location ~ \.php$ {
# 包含一个外部配置文件,通常这个文件包含了处理 PHP 文件所需的 FastCGI 参数
include snippets/fastcgi-php.conf;
# 指定 FastCGI 传递的目标,这里是 PHP-FPM 监听的 Unix Socket 文件
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
}
# 定义当发生 404 错误时,Nginx 应该返回哪个页面
error_page 404 /404.html;
# 这个 location 块精确匹配 /404.html 路径
location = /404.html {
# 设置 /404.html 文件的根目录,因为这是一个内部重定向,所以需要明确指定根目录。
root /var/www/html;
# 标记这个 location 块只能从内部重定向访问,而不能直接通过外部请求访问
internal;
}
}
3.10 DNS域名解析
具体解析流程参考如下:
用户请求到后端服务的过程参考如下:
后续可以本地修改hosts文件,也可以使用工具switch-hosts进行修改
4. nginx进阶知识
4.1 nginx跨域
跨域如何发生的,我本地打开浏览器访问慕课网,在慕课网中操作,发起ajax请求,访问网站网址都是www.imooc.com开头,没有跨域问题,但是现在我发起了一个访问淘宝的请求,此时发生跨域问题,因为请求地址前缀修改为www.tabobao.com。
我们需要实现CORS跨域共享(Corss-Origin Resource Sharing),允许浏览器向夸Origin的服务器发起js请求获取资源信息,具体的实现方式如下
- Jsonp
- SpringBoot Cors
- Nginx
使用nginx进行跨域配置
server {
listen 90;
server_name localhost;
#允许跨域请求的域,*代表所有
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' *;
# 资源源码
location / {
root /root/nginx/dist;
index index.html;
}
# 使用别名访问,当访问/static时候,会转发到alias路径
location /static {
# root /root/;
alias /nginx/root/;
}
}
这段代码与springboot代码功能相似,SpringBoot实现如下:
/**
* 设置跨域配置信息
*/
@Bean
public CorsFilter corsFilter() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允许所有来源的跨域请求
corsConfiguration.addAllowedOrigin("*");
// 允许发送凭证(cookies等)
corsConfiguration.setAllowCredentials(true);
// 允许所有请求方法(GET, POST, PUT, DELETE等)
corsConfiguration.addAllowedMethod("*");
// 允许所有请求头
corsConfiguration.addAllowedHeader("*");
// 暴露所有头部信息
corsConfiguration.addExposedHeader("*");
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
// 将跨域配置信息应用到所有路径
configSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(configSource);
}
4.2 静态资源防盗链
可以验证发起请求的请求域名是否复核要求,符合要求则继续向下进行请求,否则返回 404
server {
listen 90;
server_name localhost;
# 允许跨域请求的域,*代表所有
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' *;
# 资源防盗链,对源站点验证,只有xxx.com后缀域名发起请求才能通过验证
valid_referers *.xxx.com;
# 非法引入会进入下方判断
if ($invalid_referer) {
# 自定义码
return 409;
}
# 资源源码
location / {
root /root/nginx/dist;
index index.html;
}
# 使用别名访问,当访问/static时候,会转发到alias路径
location /static {
# root /root/;
alias /nginx/root/;
}
}
因为域名不匹配,原有地址访问mp3资源现在已经不能进行访问了 http://192.168.138.129:90/static/gwr.mp3
4.3 nginx模块化体系
主要模块如下图所示:
具体模块执行作用:
- event module: 事件机制,操作系统执行机制默认epoll模式
- phase handler: 处理客户端的一些请求,还要处理响应内容的响应
- out filter: 在phase handler处理之后,把内容进行一些自己处理过滤,再响应到浏览器端,比如gzip
- upstream: 反向代理模块,将真正的请求转发到真实的服务器地址去响应真实的内容
- load balancer: 负载均衡器,实现集群,实现负载均衡算法等
- extend module: 集成模块,实现第三方内容模块
4.4 网络模型
Nginx负载均衡的时候,其实Nginx是七层负载均衡,后续我们还会涉及到LVS,是四层负载均衡,七层和四层是什么概念呢?这就必须提到网络模型。网络模型是计算机网络基础的一部分内容,一般大学计算机系都会讲到此知识点,并且会作为考点;其实在面试过程中有时候也会被问到。所以我们还是有必要来复习或学习一下这块的一些重要知识的。
网络模型就是 OSI(Open System Interconnect),意思为开放网络互联,是由国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)共同出版的,他是一种网络互联模型,也是一种规范。
网络模型分为七层,也就是当用户发起请求到服务器接收,会历经七道工序,或者说用户利用互联网发送消息给另一个用户,也会历经七道工序。这七层可以分为如下:
层级 | 名称 | 说明 |
---|---|---|
第七层 | 应用层 | 与用户行为交互 |
第六层 | 表示层 | 定义数据格式以及数据加密 |
第五层 | 会话层 | 创建、管理以及销毁会话 |
第四层 | 传输层 | 创建、管理请求端到响应端(端到端)的连接 |
第三层 | 网络层 | 请求端的IP地址 |
第二层 | 数据链路层 | 提供介质访问与链路管理 |
第一层 | 物理层 | 传输介质,物理媒介 |
以上七层每层可以与上下相邻层进行通信。每一层都是非常复杂的,我们不在这里深究,我们以举例的形式来阐述每一层是干嘛的。
-
应用层
这是面向用户的,最靠近用户,为了让用户和计算机交互,在计算机里会有很多软件,比如eclipse,idea,qq,nginx等,这些都是应用软件,用户可以通过这些应用软件和计算机交互,交互的过程其实就是接口的调用,应用层为用户提供了交互的接口,以此为用户提供交互服务。那么在这一层最常见的协议有:HTTP,HTTPS,FTP,SMTP,POP3等。Nginx在本层,为七层负载均衡。
举例:我要寄一封信给远在天边的老外LiLei,我会打开快递软件下单,这个时候我是用户,快递软件就是应用服务,是建立在计算机上的,提供给用户交互的一种服务或称之为手段。
-
表示层
该层提供数据格式编码以及加密功能,确保请求端的数据能被响应端的应用层识别。
举例:我写中文给LiLei,他看不懂,这个时候我就会使用翻译软件把中文翻译成英文,随后信中涉及到一些比较隐私的信息我会加密一下,这个时候翻译软件和加密器就充当了表示层的作用,他用于显示用户能够识别的内容。
-
会话层
会话可以理解为session,请求发送到接受响应的这个过程之间存在会话,会话层就充当了这一过程的管理者,从创建会话到维护会话最后销毁会话。
举例:我每次写信给LiLei都会记录在一个小本本上,寄信时间日期,收信时间日期,这本小本本上存有每次通信记录,这个小本本就相当于是一个会话的管理者。又或者说,我们平时在打电话,首先需要拨打电话,这是建立会话,对方接听电话,此时正在通话(维持并管理会话),通话结束后会话销毁,那么这也是一次会话的生命周期。
-
传输层
该层建立端到端的连接,他提供了数据传输服务,在传输层通信会涉及到端口号,本层常见的协议为TCP、UDP,LVS就是在传输层,也就是四层负载均衡。
举例:我和LiLei通信过程中会借助快递公司,快递公司会分配快递员取件和寄件,那么这个快递员则充当传输层的作用。
-
网络层
网络通信的时候必须要有本机IP和对方的IP,请求端和响应端都会有自己的IP的,IP就相当于你家地址门牌号,在网络上云服务器有固定的公网IP,普通计算机也有,只不过是动态IP,运营商每天会分配不同的IP给你的计算机。所以网络层也能称之为IP层,IP是互联网的基础根本。能提供IP分配的设备则为路由器或交换机。
举例:对于拥有固定IP的云服务来说,他们都是由腾讯云、阿里云等这样的供应商提供的,他们为云服务器提供固定ip;电信、移动、联调等运营商为你的计算机动态分配ip,每天都不同;则这些供应商和运营商都是网络层。同理,快递员由物流公司分配和管理,那么物流公司就是网络层咯。
-
数据链路层
这一层会提供计算机MAC地址,通信的时候会携带,为了确保请求投递正确,所以他会验证检测MAC地址,以确保请求响应的可靠性。
举例:快递员在投递派送的时候,他(或客服)会预先提前打电话给你,确认你家地址对不对、有没有人、货到付款有没有准备好钱等等,这个时候快递员(或客服)就充当了数据链路层的职责。
-
物理层
端到端请求响应过程中的媒介,物理介质,比如网线、中继器等等设备,都是你在端到端交互过程中不可缺少的基础设备。
举例:快递员在投递的过程中,你写的信会历经一些交通运输工具,比如首先通过飞机运输到国外,在海关统一拿到信以后会通过汽车运输到LiLei所在城市的物流集散地,最后快递员通过三轮电频车寄到LiLei家里,这个时候,飞机、汽车、三轮电瓶车都是物理层的媒介。
以上就是七层网络模型,大家理解其意义即可。需要注意的是Nginx存在于第七层,属于七层负载均衡;而第四层会有LVS,属于四层负载均衡。而关于七层和四层的区别我会在下一节来说一说。
4.5 集群负载均衡
4.5.1 基础环境
负载均衡图逻辑图:
我们使用tomcat构建集群进行负载集群测试,三个tomcat服务使用不同端口号,在同一服务器上分别是
- tomcat1 http://192.168.138.129:8081/
- tomcat2 http://192.168.138.129:8082/
- tomcat3 http://192.168.138.129:8083/
我们使用后docker进行启动服务,分别启动启动三台tomcat服务
docker run --name tomcat1 -it -d -p 8081:8080 tomcat:8.5.32
docker run --name tomcat2 -it -d -p 8082:8080 tomcat:8.5.32
docker run --name tomcat3 -it -d -p 8083:8080 tomcat:8.5.32
访问地址tomcat首页信息http://192.168.138.129:8081/ ~ http://192.168.138.129:8083/
修改nginx.conf配置文件,我们将配置配在新的tomcat.conf
中,复制原有的static.conf
,并且在nginx.conf
中incloud tomcat.conf
# 定义一个名为 tomcats 的 upstream,用于负载均衡多个后端服务器
upstream tomcats {
# 添加三个后端服务器地址
server 192.168.138.129:8081;
server 192.168.138.129:8082;
server 192.168.138.129:8083;
}
# 配置一个 Nginx 服务器块
server {
# 监听 8080 端口
listen 8080;
# 设置服务器名称
server_name localhost;
# 配置 location 块来处理根路径的请求
location / {
# 将请求代理到前面定义的 tomcats 上游服务器组
proxy_pass http://tomcats;
}
}
我们通过新配置的tomcat地址进行访问,http://192.168.138.129:8080/
4.5.2 轮训策略
-
默认请求策略轮训,不需要配置即生效
-
加权重轮训,根据服务器节点配置,进行权重分配
具体配置非常检点,既在upstream 配置server后面加上weight即可,权重weight默认都为1,权重越小,服务获取流量越小
# 定义一个名为 tomcats 的 upstream,用于负载均衡多个后端服务器 upstream tomcats { # 添加三个后端服务器地址 server 192.168.138.129:8081 weight=1; server 192.168.138.129:8082 weight=2; server 192.168.138.129:8083 weight=5; }
upstream还有其他指令参数如下
-
max_conns 现在每台server连接数
# worker进程设置1个,便于测试观察成功的连接数 worker_processes 1; upstream tomcats { # 添加三个后端服务器地址 server 192.168.138.129:8081 max_conns=2; server 192.168.138.129:8082 max_conns=2; server 192.168.138.129:8083 max_conns=2; }
-
slow_start 慢启动,此配置为商业版配置,需要付费使用
upstream tomcats { # 添加三个后端服务器地址,设置该服务器的慢启动时间为 60 秒。在这段时间内,服务器将逐渐增加接收的请求量。 server 192.168.138.129:8081 weight=2 slow_start=60s; server 192.168.138.129:8082 weight=2; server 192.168.138.129:8083 weight=2; }
-
down 用于标记服务不可用
upstream tomcats { # 添加三个后端服务器地址 server 192.168.138.129:8081 down; server 192.168.138.129:8082 max_conns=2; server 192.168.138.129:8083 max_conns=2; }
-
backup 表示当前服务器节点是备用机,只有在其他的服务器都宕机以后,自己才会加入到集群中,被用户访问到:
upstream tomcats { # 添加三个后端服务器地址 server 192.168.138.129:8081 backup; server 192.168.138.129:8082 max_conns=2; server 192.168.138.129:8083 max_conns=2; }
注意 :
backup
参数不能使用在hash
和random load balancing
中 -
max_fails
:表示失败几次,则标记server已宕机,剔出上游服务。fail_timeout
:表示失败的重试时间。# 则代表在15秒内请求某一server失败达到2次后,则认为该server已经挂了或者宕机了,随后再过15秒,这15秒内不会有新的请求到达刚刚挂掉的节点上, # 而是会请求到正常运作的server,15秒后会再有新请求尝试连接挂掉的server,如果还是失败,重复上一过程,直到恢复。 upstream tomcats { # 添加三个后端服务器地址 server 192.168.138.129:8081 backup; server 192.168.138.129:8082 max_conns=2 max_fails=2 fail_timeout=15s; server 192.168.138.129:8083 max_conns=2 max_fails=2 fail_timeout=15s; }
-
keepalived : 设置长连接处理的数量,节省创建连接资源
proxy_http_version:设置长连接http版本为1.1
proxy_set_header:清除
connection header
信息upstream tomcats { server 192.168.138.129:8081 backup; server 192.168.138.129:8082 max_conns=2; server 192.168.138.129:8083 max_conns=2; keepalive 32; } server { listen 8080; server_name localhost; location / { proxy_pass http://tomcats; proxy_http_version 1.1; proxy_set_header Connection ""; } }
-
-
ip_hash 负载均衡策略
策略实现略缩过程图:
ip_hash
可以保证用户访问可以请求到上游服务中的固定的服务器,前提是用户ip没有发生更改。使用ip_hash的注意点:不能把后台服务器直接移除,只能标记down
.# 定义一个名为 tomcats 的 upstream,用于负载均衡多个后端服务器 upstream tomcats { # 使用 ip_hash; # 添加三个后端服务器地址 server 192.168.138.129:8081; server 192.168.138.129:8082; server 192.168.138.129:8083; } # 配置一个 Nginx 服务器块 server { # 监听 8080 端口 listen 8080; # 设置服务器名称 server_name localhost; # 配置 location 块来处理根路径的请求 location / { # 将请求代理到前面定义的 tomcats 上游服务器组 proxy_pass http://tomcats; } }
使用ip_hash的时候,如果节点增加减少会发生请求取模重新计算,为了解决这种情况,出现了一致性hash算法,简单的实现原理图如下
服务器减少逻辑如下,如果节点3挂了,受影响的只有在节点2~节点3访问的用户,而且后期这些用户访问的节点将变更为节点4
增加节点的情况下,只有红框中标注的人受影响,这样可以保证最少的人受影响
nginx 1.18.0 后版本可以直接使用hash指令 配置一致性hash 并且支持故障转移 只需要修改配置文件
upstream tomcats{ hash $request_uri consistent; server 192.168.138.129:8082; server 192.168.138.129:8083; }
-
url_hash,使用请求的
hash(url) % node_counts =index
,# 定义一个名为 tomcats 的 upstream,用于负载均衡多个后端服务器 upstream tomcats { # 使用url_hash算法 hash $request_uri; # 添加三个后端服务器地址 server 192.168.138.129:8081; server 192.168.138.129:8082; server 192.168.138.129:8083; }
-
least_conn 最小连接数,哪一台服务器的连接数少,便去请求哪一台服务器
# 定义一个名为 tomcats 的 upstream,用于负载均衡多个后端服务器 upstream tomcats { # 使用最小连接数算法 least_conn; # 添加三个后端服务器地址 server 192.168.138.129:8081; server 192.168.138.129:8082; server 192.168.138.129:8083; }
4.6 缓存信息
整体缓存操作过程图:
浏览器缓存可以在nginx中控制,我们访问浏览器,发现请求接口返回状态码为304,则表示他使用的是缓存信息
我们可以在location中设置过期时间,设置浏览器缓存,具体配置如下:
server {
listen 90;
server_name localhost;
#允许跨域请求的域,*代表所有
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' *;
# 资源源码
location / {
root /root/nginx/dist;
index index.html;
# 设置资源过期时间,浏览器缓存过期时间10s
expires 10s;
# 设置晚上10点半过期
expires @22h30m
# 距离现在时间一个小时之前,缓存已经失效
expires -1h;
# 不设置缓存
expires epoch;
# 关闭缓存
expires off;
# 最大缓存时间,永不过期
expires max
}
}
设置反向代理缓存信息,减少nginx服务器请求目标服务器
# 定义一个名为 tomcats 的 upstream,用于负载均衡多个后端服务器
upstream tomcats {
server 192.168.138.129:8081;
server 192.168.138.129:8082;
server 192.168.138.129:8083;
}
# 配置一个 Nginx 服务器块
server {
# 监听 8080 端口
listen 8080;
# 设置服务器名称
server_name localhost;
# 配置 location 块来处理根路径的请求
location / {
# 将请求代理到前面定义的 tomcats 上游服务器组
proxy_pass http://tomcats;
# 启用缓存,和keys_zone一致
proxy_cache mycache;
# 针对200和304状态码缓存时间为8小时
proxy_cache_valid 200 304 8h;
}
}
# proxy_cache_path 设置缓存目录
# keys_zone 设置共享内存以及占用空间大小
# max_size 设置缓存大小
# inactive 超过此时间则被清理
# use_temp_path 临时目录,使用后会影响nginx性能
proxy_cache_path /usr/local/nginx/upstream_cache keys_zone=mycache:5m max_size=1g inactive=1m use_temp_path=off;
页面 http://192.168.138.129:8080/ 访问后,观察目录/usr/local/nginx/upstream_cache
中已经有缓存信息
4.7 https配置
要在nginx中配置https,就必须安装ssl模块,也就是: http_ssl_module,新增ssl模块(原来的那些模块需要保留)
./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
需要使用腾讯云或者阿里云进行域名网站备案,然后开启ssl后证书。获取之后下ssl证书 *.crt 和 私钥 *.key 拷贝到/usr/local/nginx/conf目录中,注意我们申请的一般是学习使用为免费,有效期是1年,域名只有一级域名有效。
配置HTTPS,新增 server 监听 443 端口,配置之后重新加载nginx既可使用https访问服务了,具体配置如下:
server {
# https默认端口
listen 443;
server_name localhost;
# 开启ssl
ssl on;
# 配置ssl证书
ssl_certificate 1_www.imoocdsp.com_bundle.crt;
# 配置证书秘钥
ssl_certificate_key 2_www.imoocdsp.com.key;
# ssl会话cache
ssl_session_cache shared:SSL:1m;
# ssl会话超时时间
ssl_session_timeout 5m;
# 配置加密套件,写法遵循 openssl 标准
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://tomcats/;
index index.html index.htm;
}
}
腾讯云Nginx配置https文档地址:https://cloud.tencent.com/document/product/400/35244
5.nginx 高可用
高可用集群类型有很多种类,常用的种类如下:
- 负载均衡(LB):包括 LVS、HAProxy、nginx(http/upstream, stream/upstream),用于分配网络流量。
- 高可用集群(HA):例如数据库、Redis 等,确保服务持续可用。 高性能集群(HPC):专注于提供强大的计算能力。
我们使用的nginx高可用简单示意过程图如下所示:
5.1 keepalived基本概念
我们测试环境使用的是keepalived进行热备实现,keepalived 概念如下
负载均衡是一种在真实集群之间分配 IP 流量的方法 服务器,提供一个或多个高可用性虚拟服务。在设计负载均衡拓扑时,必须考虑负载均衡器本身以及后面的真实服务器的可用性。
Keepalived 为负载均衡和高可用性提供了框架, 框架依赖于众所周知且广泛使用的 Linux 虚拟服务器(IPVS)内核模块,提供第 4 层负载均衡。 Keepalived 实现了一组健康检查器,以动态和自适应的方式 根据服务器池的运行状况维护和管理负载均衡的服务器池。
高可用性是通过虚拟冗余路由协议实现的 (VRRP)。VRRP是路由器故障切换的基础砖块。keepalived也 实现了一组到 VRRP 有限状态机的钩子 提供低级和高速协议交互。每个Keepalived 框架可以单独使用,也可以一起使用,以提供弹性基础设施。
keepalived具体的作用如下:
- 组件是免费组件
- LVS系统的健康检查
- 实施 VRRPv2 堆栈以处理负载均衡器故障切换
VRRP 相关概念如下具体解释如下
专业名称 | 解释 |
---|---|
Virtual Router | 虚拟路由器 |
虚拟路由器标识 | VRID(0-255) ,唯一标识虚拟路由器 |
VIP | Virtual IP 虚拟ip |
物理路由器 | master :主设备 backup :备用设备 priority :优先级 |
安全认证方式 | 无认证或简单字符认证或采用预共享密钥以及MD5 认证。 |
工作模式 | 主/备:单虚拟路由器模式 主/主:分为主/备(虚拟路由器 1)和备/主(虚拟路由器 2)两种情况。 |
5.2 keepalived双机主备原理
配置keepalived后,用户不再直接访问nginx,而是通过虚拟ip访问nginx,选择nginx的主master节点进行访问,如果master节点出现什么问题无法访问,keepalived会自动切换访问备用nginx服务节点,保证服务高可用HA
5.3 keepalived安装
我们虚拟主机配置信息如下
服务名称 | 主机ip端口 |
---|---|
keepalived | 192.168.138.129 |
keepalived_backup | 192.168.138.130 |
nginx1 | 192.168.138.129 |
nginx2 | 192.168.138.130 |
keepalived下载地址 https://www.keepalived.org/download.html ,我们本次使用的是 2.3.0 版本
上传 keepalived-2.3.0.tar.gz 到预订目标服务器,192.168.138.129
解压tar包,解压后进入到解压出来的目录,看到会有configure,那么就可以做配置了(配置安装和nginx一模一样)
tar -zxvf keepalived-2.3.0.tar.gz
进入到keepalived包中,进行调用 ./configure
配置
# prefix:keepalived安装的位置
# sysconf:keepalived核心配置文件所在位置,固定位置,改成其他位置则keepalived启动不了,/var/log/messages中会报错
./configure --prefix=/usr/local/keepalived --sysconf=/etc
配置过程中可能会出现警告信息,如下所示,需要安装libnl/libnl-3依赖
yum -y install libnl libnl-devel
再次执行 ./configure
命令,发现错误已经不提示了,正常安装成功
./configure --prefix=/usr/local/keepalived --sysconf=/etc
开始进行安装
make && make install
进入到/etc/keepalived
,该目录下为keepalived核心配置文件,如果找不到使用命令whereis keepalived
进行查找
5.4 keepalived核心配置
我们查看keepalived配置文件内容/etc/keepalived/keepalived.conf.sample文件中,我们复制一个keepalived.conf.sample到keepalived.conf
cd /etc/keepalived
cp keepalived.conf.sample keepalived.conf
我们使用命令查看vim keepalived.conf
文件,进行内容整理
-
全局配置信息
global_defs { # 配置通知邮件的相关设置 # 当发生故障切换时,Keepalived会向下面定义的邮箱地址发送通知邮件 notification_email { acassen@firewall.loc failover@firewall.loc sysadmin@firewall.loc } # 定义发送邮件的发件人地址 notification_email_from Alexandre.Cassen@firewall.loc # 指定SMTP服务器的IP地址 smtp_server 192.168.200.1 # 设置SMTP连接超时时间(秒) smtp_connect_timeout 30 # 定义路由器ID,用于标识运行Keepalived服务的主机 # 此ID在集群中必须是全局唯一的 router_id LVS_DEVEL # 禁用对通告消息中IP地址的检查 vrrp_skip_check_adv_addr # 启用严格的VRRP模式 vrrp_strict # 设置发送免费ARP的间隔时间为0秒,意味着禁用此功能 vrrp_garp_interval 0 # 设置发送GARP消息的间隔时间为0秒,意味着禁用此功能 vrrp_gna_interval 0 }
我们对其进行修改,删除无用配置信息,最终修改后如下
global_defs { # 定义路由器ID,用于标识运行Keepalived服务的主机 router_id keep_129 }
-
VRRP实例信息
# 计算机节点 vrrp_instance VI_1 { # 设置当前实例的状态为MASTER,当前129位主节点master,备用机则为BACKUP state MASTER # 指定使用的网络接口 interface eth0 # 设置虚拟路由器的ID,保证主备一致 virtual_router_id 51 # 设置优先级,值越大优先级越高 priority 100 # 设置VRRP通告间隔时间(秒) advert_int 1 # 配置身份验证 authentication { # 设置认证类型为密码认证 auth_type PASS # 设置认证密码 auth_pass 1111 } # 配置虚拟IP地址列表 virtual_ipaddress { 192.168.200.16 192.168.200.17 192.168.200.18 } # 允许接收发送到上述VIP的流量 accept }
删除无用信息,修改后信息如下
# 计算机节点 vrrp_instance VI_1 { # 设置当前实例的状态为MASTER,当前129位主节点master,备用机则为BACKUP state MASTER # 指定使用的网络接口 interface ens33 # 设置虚拟路由器的ID,保证主备一致 virtual_router_id 51 # 优先级,master挂掉了,值越大优先级越高,越高几率成为master priority 100 # 主备之间检查时间间隔,VRRP通告间隔时间(秒) advert_int 1 # 配置身份验证,防止非法节点的进入 authentication { # 设置认证类型为密码认证 auth_type PASS # 设置认证密码 auth_pass 1111 } # 配置虚拟IP地址列表 virtual_ipaddress { 192.168.138.160 } # 允许接收发送到上述VIP的流量 accept }
-
启动keepalived服务
进入到
/usr/local/keepalived/sbin
目录,执行./keepalived即可cd /usr/local/keepalived/sbin ./keepalived
查看keepalived是否正常启动,查看虚拟ip是否正常
ps -ef | grep keepalived ip addr
5.5 注册keepalive为系统服务
之前已经配置了虚拟ip 192.168.138.160,那么我们便可以使用这个ip访问我们的nginx首页
拷贝/root/keepalived/keepalived-2.3.0/keepalived/etc/init.d/keepalived
配置文件到系统/etc/init.d
目录下,以及拷贝sysconfig/keepalived
到/etc/sysconfig/
目录下,如果提示是否覆盖,选择覆盖即可
cd /root/keepalived/keepalived-2.3.0/keepalived/etc
cp init.d/keepalived /etc/init.d/
cp sysconfig/keepalived /etc/sysconfig/
执行系统命令,将keepalived注册为系统服务
systemctl daemon-reload
# 启动/重启/停止 start/restart/stop
systemctl start keepalived.service
# 检查keepalived是否注册为系统服务
systemctl -l | grep keepalived
5.6 keepalived双机主备配置
我们使用129服务已经设置了keepalived的主master节点,现在设置130为从节点,按照129安装keepalived同样在130节点上安装keepalived,从节点设置也是非常简单,主要配置如下
global_defs {
# 唯一标识符,全局唯一
router_id keep_130
}
vrrp_instance VI_1 {
# 备用节点
state BACKUP
interface ens33
virtual_router_id 51
priority 80
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
# 对外提供虚拟ip,与主节点相同
virtual_ipaddress {
192.168.138.160
}
# 允许接收发送到上述VIP的流量
accept
}
启动keepalived服务,然后查看ip信息,发现160还没有上来,这是因为主节点还在,备用节点还没有提供服务
cd /usr/local/keepalived/sbin
./keepalived
我们停止129对应的keepalived服务,然后再次观察ip信息,发现160虚拟ip已经出现了,再次访问则显示访问的就是130的nginx服务了,因为160对应的ip已经是130上的keepalived提供的了
# 129停止keepalived服务
systemctl stop keepalived
之后我们再次启动keepalived服务,模拟129服务恢复,发现再次访问160重新切换回了129服务器,并且查看130对应的ip,发现160虚拟ip已经消失不见了
systemctl start keepalived
5.7 配置nginx7*24不间断提供服务
我们需要编写简单的脚本,主要是监听nginx服务是否正常,如果不正常则尝试进行重启,如果重启失效,则切备用的nginx对外提供服务,具体实现过程如下:
-
增加Nginx重启检测脚本
vim /etc/keepalived/check_nginx_alive_or_not.sh #!/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 /etc/keepalived/check_nginx_alive_or_not.sh
-
配置keepalived监听nginx脚本
! Configuration File for keepalived global_defs { # 定义路由器ID,用于标识运行Keepalived服务的主机,要求全局唯一 router_id LVS_DEVEL } # 定义一段自定义脚本 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 { # 设置当前实例的状态为MASTER,当前129位主节点master,备用机则为BACKUP state MASTER # 指定使用的网络接口 interface ens33 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } # 监听脚本 track_script { # 追踪 nginx 脚本 check_nginx_alive } # 提供虚拟ip virtual_ipaddress { 192.168.138.160 } # Allow packets addressed to the VIPs above to be received accept }
然后重启keepalived服务,停止nginx服务然后再次访问nginx页面,发现129nginx还是在提供服务,那么已经实现了nginx的自动检测功能
systemctl restart keepalived /usr/local/nginx/sbin/nginx -s stop
这种配置其实也有问题,我们的keepalived以及nginx如果都不出问题,则我们的备用服务器则永远不会被访问到,整体设计存在一个资源浪费的问题,这时候我们需要使用双主热备
,架构图如下,两个nginx互为主备
5.8 双主热备实现
服务器可以使用双主热备进行实现,这里以他腾讯云为例子,上海节点有两个独立的云服务器,他们都拥有独立的ip
点击云解析,进行域名解析,域名解析需要先进行备案,我们查看wxfanhual.com
这个域名,点击进行查看
我们可以看到我们在这个域名下,配置了两个ip,并且使用主机记录为www以及类型A访问就会映射到两个不同的ip
我们点击负载均衡,查看负载均衡配置信息为均等负载,就是轮训,我们也可以对负载逻辑进行修改,重新分配权重信息
我们点击域名设置,里面会有一个DNS服务器信息,我们购买域名之后,会提供DNS服务器,域名经过DNS服务器解析之后获取到不同的ip,然后再展示服务器中不同的内容的。
我们本地具体应该如何配置呢,其实就是配置两组不同的主备配置,既129与130服务器keepalived互为主备,具体配置如下:
-
主节点配置信息
global_defs { router_id keep_129 } vrrp_instance VI_1 { state MASTER interface ens33 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.138.160 } } vrrp_instance VI_2 { state BACKUP interface ens33 virtual_router_id 52 priority 80 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.138.170 } }
-
备用节点配置
global_defs { router_id keep_130 } vrrp_instance VI_1 { state BACKUP interface ens33 virtual_router_id 51 priority 80 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.138.160 } } vrrp_instance VI_2 { state MASTER interface ens33 virtual_router_id 52 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.138.170 } }
-
重启服务
# 重启Keepalived systemctl restart keepalived