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

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

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

  1. 通过Lua脚本结合nginx,也就是Openresty方案;
  2. 给nginx的每个server额外添加一个端口,每次通过调用这个端口修改upstream;
  3. 给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条策略截图如下:
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截图如下:
到此Consul集群部署完成。
 
 
二、nginx改造
1. 升级nginx
下载nginx相关模块:

注意:

  • 下载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名称,截图如下:

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的更多相关文章

  1. windows环境下基于nginx搭建rtmp服务器

    基于nginx搭建rtmp服务器需要引入rtmp模块,引入之后需重新编译nginx linux环境几个命令行就能实现编译,笔者未尝试,网上有很多教程. windows环境还需要安装一系列的编译环境,例 ...

  2. Nginx 针对上游服务器缓存

    L:99 nginx缓存 : 定义存放缓存的载体 proxy_cache 指令 Syntax: proxy_cache zone | off; Default: proxy_cache off; Co ...

  3. nginx 加工上游服务器返回的内容,并返回给客户端

    禁用上游响应头部功能 Syntax: proxy_ignore_headers field ...; Default: — Context: http, server, location 功能介绍:某 ...

  4. nginx 与上游服务器建立连接的相关设置

    向上游服务建立联系 Syntax: proxy_connect_timeout time; #设置TCP三次握手超时时间,默认60秒:默认超时后报502错误 Default: proxy_connec ...

  5. Nginx 当上游服务器返回失败时的处理办法

    陶辉95课 Syntax: proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503  ...

  6. 基于nginx实现web服务器的双机热备

    1.适用场景 对于部署重要的服务,会使用两台服务器,互相备份,共同执行同一服务.当一台服务器出现故障时,可以由另一台服务器承担服务任务,从而在不需要人工干预的情况下,自动保证系统能持续提供服务.双机热 ...

  7. windows下搭建基于nginx的rtmp服务器

    https://blog.csdn.net/fireroll/article/details/51985688 Windows机器配置:Windows7旗舰版 64位Intel(R) Core(TM) ...

  8. 基于Nginx dyups模块的站点动态上下线并实现简单服务治理

    简介 今天主要讨论一下,对于分布式服务,站点如何平滑的上下线问题. 分布式服务 在分布式服务下,我们会用nginx做负载均衡, 业务站点访问某服务站点的时候, 统一走nginx, 然后nginx根据一 ...

  9. Tengine:基于Nginx的衍生版

    engine是由淘宝网发起的Web服务器项目.它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性.Tengine的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了很好的检验 ...

随机推荐

  1. Java 操作 HBase 教程

    Java 操作 HBase 教程 一.简介 二.hbase-client 引入 三.连接操作 四.表操作 五.运行测试 相关博文原文地址: 博客园:美码师:HBase(2) Java 操作 HBase ...

  2. SQL操作数据——SQL组成,查询基础语法,where,Oracle常用函数等

    SQL组成 DML数据操作语言 DCL数据控制语言 DQL数据查询语言 DDL数据定义语言 查询基础语法 记录筛选 where 子句 记录筛选 where 子句 实例练习 实例练习 Select语句中 ...

  3. Spring5源码,Spring DispatecherServlet的生命周期

    一.前端控制器模式 二.DispatcherServlet的执行链 三.DispatcherServlet 1.策略初始化 2.请求预处理 3.请求处理 4.视图解析 5.处理调度请求 - 视图渲染 ...

  4. JS:replace

    JavaScript中replace() 方法如果直接用str.replace("-","!") 只会替换第一个匹配的字符. 而str.replace(/-/g ...

  5. REST架构及其介绍

    概述     REST是英文Representational State Transfer的缩写,中文翻译:表述性状态转移.     他是由Roy Thomas Fielding博士在他的论文 < ...

  6. (14)Linux绝对路径和相对路径

    Linux 系统中,文件是存放在目录中的,而目录又可以存放在其他的目录中,因此,用户(或程序)可以借助文件名和目录名,从文件树中的任何地方开始,搜寻并定位所需的目录或文件. 说明目录或文件名位置的方法 ...

  7. .NET 微服务

    前文传送门: 什么是云原生? 现代云原生设计理念 Microservices 微服务是构建现代应用程序的一种流行的体系结构样式,云原生系统拥抱微服务. 微服务是由一组(使用共享结构交互的.独立的小块服 ...

  8. Codeforces Round #656 (Div. 3) C. Make It Good

    题目链接:https://codeforces.com/contest/1385/problem/C 题意 去除一个数组的最短前缀使得余下的数组每次从首或尾部取元素可以排为非减序. 题解一 当两个大数 ...

  9. WPF 之路由事件和附加事件(六)

    一.消息驱动与直接事件模型 ​ 事件的前身是消息(Message).Windows 是消息驱动的系统,运行其上的程序也遵循这个原则.消息的本质就是一条数据,这条消息里面包含着消息的类别,必要的时候还记 ...

  10. 四、Jmeter 集合点(实际场景应用)

    一.jmeter集合点的作用域及作用范围 先明确一些概念:1)定时器是在每个sampler(采样器)之前执行的,而不是之后: 是的,你没有看错,不管这个定时器的位置放在sampler之后,还是之下,它 ...