基于nginx实现上游服务器动态自动上下线无需reload的实现方法


Posted in Servers onMarch 31, 2021

网上关于nginx的介绍有很多,这里讲述的是上游服务(如下图的Java1服务)在没有“网关”的情况下,如何通过nginx做到动态上下线。

基于nginx实现上游服务器动态自动上下线无需reload的实现方法

传统的做法是,手动修改nginx的upstream文件,将Java1的配置注释或者标记为down,然后reload nginx生效。当然可以做成脚本自动化修改,然而对于一个繁忙的nginx来说,贸然reload轻则响应缓慢,重则雪崩丢失流量。

那么怎样做到nginx动态加载upstream配置呢?网上大体有3种方案:

  • 通过Lua脚本结合nginx,也就是Openresty方案;
  • 给nginx的每个server额外添加一个端口,每次通过调用这个端口修改upstream;
  • 给nginx添加数据库,upstream数据放在数据库中,通过修改数据库数据实现修改upstream配置。

对于一个正在运行的生产环境nginx来说,第3个方案无疑是成本最低的。下面让我们具体看一下:

技术方案:nginx1.16+nginx_upstream_check_module+nginx-upsync-module+consul

说明:

  • 这里的consul就是上面所说的数据库,它不只是key/value类型的库,还有一个简洁的web管理页面,可以很方便的管理键值对数据;
  • nginx_upstream_check_module是阿里开源的针对上游服务的健康检测模块;
  • nginx-upsync-module是微博开源的可以与consul/etcd结合的模块。

下面分别通过consul集群部署、nginx改造、创建upstream数据3个方面逐一讨论实施细节。

一、部署consul集群

官网:https://www.consul.io/

假设用下面3台机器组成一个Consul集群:

192.168.21.11
192.168.21.12
192.168.21.13
192.168.21.14 # 这个IP为代理IP,用于代理上面3台机器

1. 准备工作

从官网下载consul压缩包,分别上传到上面3台服务器,这里的consul版本为1.8.4:

unzip consul_1.8.4_linux_amd64.zip
mv consul /usr/local/bin/
[root@nginx-11 tmp]# consul
Usage: consul [--version] [--help] <command> [<args>]

Available commands are:
 acl Interact with Consul's ACLs
 agent Runs a Consul agent
 catalog Interact with the catalog
 ....

3台机器分别创建consul数据、日志、配置文件目录:

mkdir -p /data/consul/{data,log}
mkdir /etc/consul

2.生成consul配置文件

下面以192.168.21.11的配置文件为例:

[root@nginx-11 tmp]# cat /etc/consul/config.json
{
 "datacenter":"dc1",
 "primary_datacenter":"dc1",
 "bootstrap_expect":3,
 "start_join":[
 "192.168.21.11",
 "192.168.21.12",
 "192.168.21.13"
 ],
 "retry_join":[
 "192.168.21.11",
 "192.168.21.12",
 "192.168.21.13"
 ],
 "advertise_addr": "192.168.21.11",
 "bind_addr": "192.168.21.11",
 "client_addr": "0.0.0.0",
 "server":true,
 "connect":{
 "enabled":true
 },
 "node_name":"192.168.21.11",
 "ui": true,
 "data_dir":"/data/consul/data",
 "enable_script_checks":false,
 "enable_local_script_checks":true,
 "log_file":"/data/consul/log/",
 "log_level":"info",
 "log_rotate_bytes":100000000,
 "log_rotate_duration":"24h",
 "encrypt":"a2zC4ItisuFdpl7IqwoYz3GqwA5W1w2CxjNmyVbuhZ4=",
 "acl":{
 "enabled":true,
 "default_policy":"deny",
 "enable_token_persistence":true,
 "enable_key_list_policy":true,
 "tokens":{
 "master":"6c95012f-d086-4ef3-b6b9-35b60f529bd0"
 }
 }
}

说明:

  • 另外2台服务器的配置文件,分别将上面的advertise_addr、bind_addr、node_name对应值修改为对应IP,其他配置不需要改变;
  • 参数 "bootstrap_expect":3 意为希望部署一个3个节点的集群,请根据实际情况配置;
  • encrypt与tokens对应的值,3台机器应保持一致,encrypt值可以通过consul keygen命令生成,token值可以通过uuidgen命令生成,也可以都通过这2个工具生成;
  • 相关参数的理解可以参考:https://juejin.im/post/6844903860717240334

3. 创建consul集群

分别在3台机器上启动consul即可:

consul agent -config-file=/etc/consul/config.json &

通过浏览器访问http://192.168.21.14:8500(或者任意一个IP:Port)即可访问consul后台界面,输入上面master的tokens值可以看到里面具体内容。

注意:

  • 上面配置文件中的acl配置,“enable_key_list_policy”配置一定要加上,且值要配成“true”,否则匿名用户可能访问不到consul配置内容。

4. 为非管理员创建consul访问权限

1)创建访问策略

通过浏览器访问consul,点击ACL -> Access Controls -> Policies -> 右上角Create创建一个只读“upstreams”kv策略,名称为:readonlykv,Rules内容为:

key_prefix "upstreams/" {
 policy = "list"
}

创建一个可以写“upstreams”kv策略,名称为:writekv,Rules内容为:

key_prefix "upstreams/" {
 policy = "write"
}

创建好的2条策略截图如下:

基于nginx实现上游服务器动态自动上下线无需reload的实现方法

2)创建访问token

在匿名用户token中加入允许访问只读“upstreams”kv策略,用于允许nginx模块匿名读取consul配置:
点击00000002,在Policies中选择readonlykv即可。

创建可以写“upstreams”kv的token,用于脚本带此token修改consul配置:
通过浏览器访问consul,点击ACL -> Access Controls -> Tokens -> 右上角Create,在Policies中选择writekv。
修改/创建好的2条token截图如下:

基于nginx实现上游服务器动态自动上下线无需reload的实现方法

到此Consul集群部署完成。

二、nginx改造

1. 升级nginx

下载nginx相关模块:

nginx-upsync-module:https://github.com/weibocom/nginx-upsync-module

nginx_upstream_check_module:https://github.com/xiaokai-wang/nginx_upstream_check_module

注意:

  • 下载nginx_upstream_check_module模块时请一定到xiaokai-wang的GitHub上下载,千万不要到阿里的官方GitHub上下载,否则版本不兼容编译不过去;
  • 在对Nginx升级前请先做好数据备份。

1)对nginx_upstream_check_module打patch

cd nginx-1.16.0
patch -p1 < /usr/local/src/nginx-1.16/nginx_upstream_check_module-master/check_1.12.1+.patch

说明:我把下载的2个nginx模块源码包放在了/usr/local/src/nginx-1.16/路径下。

2)编译nginx

./configure --prefix=/usr/local/nginx --add-module=/usr/local/src/nginx-1.16/nginx_upstream_check_module-master --add-module=/usr/local/src/nginx-1.16/nginx-upsync-module-master ...

说明:

我把nginx安装在/usr/local/下面;

命令后面的省略号是你要安装的模块,请根据实际情况添加,通过nginx -V可以看到当前安装了哪些模块,然后加上去即可。

3)安装nginx

make
# 如果是平滑升级,该步不要执行 
make install

4)升级nginx

#再次备份nginx二进制文件
mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx16.old
#用新nginx二进制文件替换老的
cp objs/nginx /usr/local/nginx/sbin/
#查看已安装的nginx模块
/usr/local/nginx/sbin/nginx -V

提醒:经过测试发现nginx1.6通过reload或者发送kill -USR2命令,老的nginx进程并不会退出,需要重启nginx才可以生效,不知道是不是Bug。

/usr/local/nginx/sbin/nginx -s stop
#如果老的nginx进程仍未推出,使用kill -9强制杀掉ps -ef |grep nginx
#开启nginx
/usr/local/nginx/sbin/nginx 
# 说明:发送kill -USR2命令
kill -USR2 `cat /usr/local/nginx/logs/nginx.pid`

到此,nginx升级完成。

2. 配置nginx

1)首先配置nginx展示页面,用于快速了解nginx运行状态

cat nginx.conf
 server {
 listen 80;
 server_name localhost;

 # 在server 80中展示upstream,相当于全局配置,其他配置文件不需要配置          # 浏览器访问http://nginx-ip:80/upstream_show能查看到nginx upstream的具体配置信息
 location = /upstream_show {
  upstream_show;
 }

 # 在server 80中展示check详情,相当于全局配置,其他配置文件不需要配置          # 浏览器访问http://nginx-ip:80/status能查看到上游服务的健康状态,报红即为有问题,白色即为正常
 location /status {
  check_status;
 }

 # 在server 80中展示nginx自带的状态,相当于全局配置,其他配置文件不需要配置          # nginx原生自带功能
 location /NginxStatus {
  stub_status on;
  access_log off;
  allow 192.168.0.0/16;
  deny all;
 }
 }
     # 引入具体server配置,每个server需要配置nginx-upsync-module模块的配置
 include /usr/local/nginx/conf/vhosts/*.conf;

2)server配置

http方式检测

upstream rs1 {
 server 127.0.0.1:11111;
 upsync 192.168.21.14:8500/v1/kv/upstreams/rs1/ upsync_timeout=6m upsync_interval=500ms upsync_type=consul strong_dependency=off;
 upsync_dump_path /usr/local/nginx/conf/servers/servers_rs1.conf;

 check interval=1000 rise=2 fall=2 timeout=3000 type=http default_down=false;
 check_http_send "HEAD /health.htm HTTP/1.0\r\n\r\n";
 check_http_expect_alive http_2xx http_3xx;
}

server {
 listen 80;
...

tcp方式检测(tcp为默认检测方式)

upstream rs2 {
 server 127.0.0.1:11111;
 upsync 192.168.21.14:8500/v1/kv/upstreams/rs2/ upsync_timeout=6m upsync_interval=500ms upsync_type=consul strong_dependency=off;
 upsync_dump_path /usr/local/nginx/conf/servers/servers_rs2.conf;

 check interval=1000 rise=2 fall=2 timeout=3000 type=tcp default_down=false;
}

server {
 listen 80;
...

说明:

  • 推荐使用http方式检测,http比tcp方式更准确,该检测方式为nginx_upstream_check_module提供,功能强大,参数简单解释:每隔1秒进行1次健康检查,每次超时时间为3秒,连续2次健康检查成功则认为这个上游服务健康,将会被上线或一直保持在线;连续2次健康检查失败则认为这个上游服务不健康,将会被剔除下线。“/health.htm”是上游服务的健康检查接口,通过它判断服务是否健康。具体参数解释可参考:http://tengine.taobao.org/document_cn/http_upstream_check_cn.html
  • 参数简单解释:nginx-upsync-module模块会每隔0.5秒向consul数据库检查一次配置,每次超时时间为6分钟。具体参数解释可参考:https://github.com/weibocom/nginx-upsync-module
  • nginx会在/usr/local/nginx/conf目录下面创建servers子目录,该子目录下会自动创建相关server配置文件。

到此,nginx配置修改完成。

三、创建upstream数据(consul键值对)

可以通过web页面或者脚本创建upstream数据,方法如下:

1. web页面操作

如果需要创建目录,在要创建的字段后面加上"/"即可,如:upstreams/ 。

"Key/Value"中必须先创建"upstreams"目录(后面有字母s),然后再创建对应的server名称,截图如下:

基于nginx实现上游服务器动态自动上下线无需reload的实现方法

2. 命令行操作

使用命令行时不需要先创建"upstreams/"目录,命令会自动创建目录以及server数据。

下面以上游服务Java1(IP为192.168.20.100,端口号为8080,upstream分组名称为rs1)为例:

添加记录

curl -X PUT http://192.168.21.14:8500/v1/kv/upstreams/rs1/192.168.20.100:8080?token=$token

上述命令执行后,会形成一条nginx的upstream默认配置信息,即:

server 192.168.20.100:8080 weight=1 max_fails=2 fail_timeout=10s;

可以通过下面命令自定义权重等值:

curl -X PUT -d "{\"weight\":100, \"max_fails\":2, \"fail_timeout\":10}" http://192.168.21.14:8500/v1/kv/upstreams/rs1/192.168.20.100:8080?token=$token
# 或者 
curl -X PUT -d '{"weight":100, "max_fails":2, "fail_timeout":10}' http://192.168.21.14:8500/v1/kv/upstreams/rs1/192.168.20.100:8080?token=$token

删除记录

curl -X DELETE http://192.168.21.14:8500/v1/kv/upstreams/rs1/192.168.20.100:8080?token=$token

更新权重

curl -X PUT -d "{\"weight\":100, \"max_fails\":2, \"fail_timeout\":10}" http://192.168.21.14:8500/v1/kv/upstreams/rs1/192.168.20.100:8080?token=$token
# 或者 
curl -X PUT -d '{"weight":100, "max_fails":2, "fail_timeout":10}' http://192.168.21.14:8500/v1/kv/upstreams/rs1/192.168.20.100:8080?token=$token

下线服务

curl -X PUT -d "{\"weight\":2, \"max_fails\":2, \"fail_timeout\":10, \"down\":1}" http://192.168.21.14:8500/v1/kv/upstreams/rs1/192.168.20.100:8080?token=$token
# 或者
curl -X PUT -d '{"weight":2, "max_fails":2, "fail_timeout":10, "down":1}' http://192.168.21.14:8500/v1/kv/upstreams/rs1/192.168.20.100:8080?token=$token

查看upstream rs1下面有哪些上游服务器

curl http://192.168.21.14:8500/v1/kv/upstreams/rs1?recurse

推荐使用命令行操作,建议将命令行组装成脚本实现DevOps

四、一点感悟

在改造该动态发现方案期间,遇到了很多问题,最棘手的一个问题是测试环境种nginx一直报错,upstream数据始终无法完整下载,经过各种排查还是没有发现问题,中间我怀疑过是consul的问题,换成了etcd还是同样的报错,最后通过抓包跟踪,发现是Linux内核参数配置不当,导致队列溢出tcp三次握手失败,影响nginx与consul通信。

很多方案理论上是没有问题的,甚至说有人已经成功运用了,但是实际上亲自实施的话还是会遇到各种各样的问题,有些甚至是致命的,这时候就需要耐心的解决。希望大家在看到这篇文章的时候也去动手试试,如果遇到了问题请静下心来耐心排查。

还有一个是,很多人说运维是不产生价值的,我认为这么说是不对的,运维需要体现的价值有很多,SRE就是其中的一种。

到此这篇关于基于nginx实现上游服务器动态自动上下线无需reload的文章就介绍到这了!


Tags in this post...

Servers 相关文章推荐
Apache站点配置SSL强制跳转443
Mar 09 Servers
Nginx开启Brotli压缩算法实现过程详解
Mar 31 Servers
nginx配置文件使用环境变量的操作方法
Jun 02 Servers
nginx请求限制配置方法
Jul 09 Servers
总结高并发下Nginx性能如何优化
Nov 01 Servers
Docker下安装Oracle19c
Apr 13 Servers
Ubuntu Server 安装Tomcat并配置systemctl
Apr 28 Servers
阿里云服务器Ubuntu 20.04上安装Odoo 15
May 20 Servers
关于windows server 2012 DC 环境 重启后蓝屏代码:0xc00002e2的问题
May 25 Servers
Linux下搭建SFTP服务器的命令详解
Jun 25 Servers
搭建zabbix监控以及邮件报警的超级详细教学
Jul 15 Servers
为什么 Nginx 比 Apache 更牛逼
Mar 31 #Servers
Nginx的rewrite模块详解
Mar 31 #Servers
nginx常用命令放入shell脚本详解
Mar 31 #Servers
详解如何修改nginx的默认端口
nginx前后端同域名配置的方法实现
Mar 31 #Servers
Nginx同一个域名配置多个项目的实现方法
Mar 31 #Servers
Apache压力测试工具的安装使用
You might like
php FPDF类库应用实现代码
2009/03/20 PHP
ecshop 订单确认中显示省市地址信息的方法
2010/03/15 PHP
由php的call_user_func传reference引发的思考
2010/07/23 PHP
php 操作数组(合并,拆分,追加,查找,删除等)
2012/07/20 PHP
php自定义的格式化时间示例代码
2013/12/05 PHP
Yii2 批量插入、更新数据实例
2017/03/15 PHP
关于JQuery($.load)事件的用法和分析
2013/04/09 Javascript
Jquery实现三层遍历删除功能代码
2013/04/23 Javascript
jquery复选框全选/取消示例
2013/12/30 Javascript
Jquery Uploadify上传带进度条的简单实例
2014/02/12 Javascript
javascript的回调函数应用示例
2014/02/20 Javascript
jquery实现图片切换代码
2016/10/13 Javascript
js实现文字向上轮播功能
2017/01/13 Javascript
jQuery插件form-validation-engine正则表达式操作示例
2017/02/09 Javascript
node.js连接MongoDB数据库的2种方法教程
2017/05/17 Javascript
详解使用mpvue开发github小程序总结
2018/07/25 Javascript
微信小程序点击图片实现长按预览、保存、识别带参数二维码、转发等功能
2019/07/20 Javascript
[00:43]DOTA2小紫本全民票选福利PA至宝全方位展示
2014/11/25 DOTA
[01:32]TI珍贵瞬间系列(一)
2020/08/26 DOTA
python使用自定义user-agent抓取网页的方法
2015/04/15 Python
Windows中安装使用Virtualenv来创建独立Python环境
2016/05/31 Python
python 根据正则表达式提取指定的内容实例详解
2016/12/04 Python
Python3实现发送QQ邮件功能(html)
2017/12/15 Python
PyCharm代码格式调整方法
2018/05/23 Python
简单了解python 生成器 列表推导式 生成器表达式
2019/08/22 Python
Python爬虫入门有哪些基础知识点
2020/06/02 Python
css3圆角样式分享自定义按钮样式
2013/12/27 HTML / CSS
德国大型的家具商店:Pharao24.de
2016/10/02 全球购物
武汉高蓝德国际.net机试
2016/06/24 面试题
Linux文件系统类型
2012/09/16 面试题
运动会入场词100字
2014/02/06 职场文书
金融学专科生自我鉴定
2014/02/21 职场文书
行政专员求职信范文
2014/05/03 职场文书
小学节能减排倡议书
2014/05/15 职场文书
三好学生个人先进事迹材料
2014/05/17 职场文书
公司外出活动方案
2014/08/14 职场文书