Nginx动态配置
Last updated
Last updated
在使用upstream实现负载均衡时,假如upstream列表有变更,那么就需要到服务器进行手动修改。有没有这样一种方式:后端服务上线时,将其自动注册到upstream列表中;后端服务下线时,自动从upstream列表中摘除?答案就是使用配置中心。
这里以Consul为例。
Consul是一款开源的分布式服务注册与发现系统,通过HTTP API可以使得服务注册、发现实现起来非常简单,它支持如下特性:
服务注册:服务实现者可以通过HTTP API或DNS方式,将服务注册到Consul。
服务发现:服务消费者可以通过HTTP API或DNS方式,从Consul获取服务的IP和PORT。
故障检测:支持如TCP、HTTP等方式的健康检查机制,从而当服务有故障时自动摘除。
K/V存储:使用K/V存储实现动态配置中心,其使用HTTP长轮询实现变更触发和配置更改。
多数据中心:支持多数据中心,可以按照数据中心注册和发现服务,即支持只消费本地机房服务,使用多数据中心集群还可以避免单数据中心的单点故障。
Raft算法:Consul使用Raft算法实现集群数据一致性。
通过Consul可以管理服务注册与发现,接下来需要有一个与Nginx部署在同一台机器的Agent来实现Nginx配置更改和Nginx重启功能。我们有Confd或者Consul-template两个选择,而Consul-template是Consul官方提供的,我们就选择它了。其使用HTTP长轮询实现变更触发和配置更改(使用Consul的watch命令实现)。也就是说,我们使用Consul-template实现配置模板,然后拉取Consul配置渲染模板来生成Nginx实际配置。
除Consul外,还有一个选择是etcd3,其使用了gRPC和protobuf可以说是一个亮点。不过,etcd3目前没有提供多数据中心、故障检测、Web管理界面。
整体架构图如下:
1.首先,upstream
服务启动,我们通过管理后台向Consul
注册服务。
2.在Nginx
机器上部署并启动Consul-template Agent
,其通过长轮询监听服务变更。
3.Consul-template
监听到变更后,动态修改upstream
列表。
4.Consul-template
修改完upstream
列表后,调用重启Nginx
脚本重启Nginx
。
首先我们要启动Consul-Server。
./consul agent -server -bootstrap-expect 1-data-dir /tmp/consul -bind 0.0.0.0-client 0.0.0.0
此处需要使用data-dir指定Agent状态存储位置,
bind指定集群通信的地址,
client指定客户端通信的地址(如Consul-template与Consul通信)。
在启动时还可以使用-ui-dir ./ui/指定Consul Web UI目录,实现通过Web UI管理Consul,
然后访问如http://127.0.0.1:8500即可看到控制界面。
使用如下HTTP API注册服务。
上面指令中,Datacenter指定数据中心,Address指定服务IP,Service.Id指定服务唯一标识,Service.Service指定服务分组,Service.tags指定服务标签(如测试环境、预发环境等),Service.Port指定服务端口。
通过如下HTTP API摘除服务。
通过如下HTTP API发现服务。
接下来我们需要在Consul-template机器上添加一份配置模板item.jd.tomcat.ctmpl
。
service 指定格式为:标签.服务@数据中心,然后通过循环输出Address和Port,从而生成Nginx upstream配置。
接下来启动Consul-template:
使用consul指定Consul服务器客户端通信地址,template格式是“配置模板:目标配置文件:脚本”,即通过配置模板更新目标配置文件,然后调用脚本重启Nginx。
直接通过Nginx include
指令将/usr/servers/nginx/conf/domains/item.jd.tomcat
包含到nginx.conf
配置文件即可,restart.sh
脚本代码如下所示。
即如果Nginx没有启动,则启动,否则重启。
Java真实服务中进行服务注册与摘除
项目中添加Consul Java Client
:
代码中进行服务注册与摘除:
到此我们就实现了动态upstream
负载均衡,upstream
服务启动后自动注册到Nginx
,upstream
服务停止时,自动从Nginx
上摘除。
通过Consul
+ Consul-template
方式,每次发现配置变更都需要reload nginx
,而reload是有一定损耗的。而且,如果你需要长连接支持的话,那么当reload nginx
时长连接所在worker进程会进行优雅退出,并当该worker进程上的所有连接都释放时,进程才真正退出(表现为worker进程处于worker process is shutting down)。因此,如果能做到不reload就能动态更改upstream,那么就完美了。对于社区版Nginx目前有三个选择:Tengine的Dyups模块、微博的Upsync和使用OpenResty的balancer_by_lua。微博使用Upsync+Consul实现动态负载均衡,而又拍云使用其开源的slardar(Consul + balancer_by_lua)实现动态负载均衡。
todo
使用Nginx实现HTTP动态负载均衡—《亿级流量网站架构核心技术》