之前团队的nginx管理,都是运维同学每次去修改配置文件,然后重启,非常不方便,一直想找一个可以方便管理nginx集群的工具,翻遍web,未寻到可用之物,于是自己设计开发了一个。

效果预览

  1. 集群group管理界面

可以管理group的节点,配置文件,修改后可以一键重启所有节点,且配置文件出错时会提示错误,不会影响线上服务。

2.集群Node节点管理

3 .集群Node节点日志查看

  1. 生成的配置文件预览

  1. vhost管理

设计思路

数据结构:

一个nginxGroup,拥有多个NginxNode,共享同一份配置文件。

分布式架构:Manager节点+agent节点+web管理

每个nginx机器部署一个agent,agent启动后自动注册到manager,通过web可以设置agent所属group,以及管理group的配置文件。

配置文件变更后,manager生成配置文件,分发给存活的agent,检验OK后,控制agent重启nginx。

关键技术点

分布式管理

一般分布式可以借助zookeeper等注册中心来实现,作为java项目,其实使用EurekaServer就可以了:

manager加入eureka依赖:

    <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency> <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

然后在入口程序添加 @EnableEurekaServer

agent 添加注册配置:

eureka:
instance:
prefer-ip-address: true
client:
service-url:
defaultZone: http://admin:admin@ip:3002/eureka/

manager 节点获取存活的agent,可以通过EurekaServerContextHolder来获取注册的agent,同时可以通过定时任务自动发现新节点。

public class NginxNodeDiscover {

    private static final String AGENT_NAME = "XNGINXAGENT";

    private PeerAwareInstanceRegistry getRegistry() {
return getServerContext().getRegistry();
} private EurekaServerContext getServerContext() {
return EurekaServerContextHolder.getInstance().getServerContext();
} @Autowired
NginxNodeRepository nginxNodeRepository; @Scheduled(fixedRate = 60000)
public void discoverNginxNode() {
List<String> nodes = getAliveAgents();
nodes.stream().forEach(node->{
if(!nginxNodeRepository.findByAgent(node).isPresent()){
NginxNode nginxNode = new NginxNode();
nginxNode.setAgent(node);
nginxNode.setName(node);
nginxNodeRepository.save(nginxNode);
}
});
} public List<String> getAliveAgents() {
List<String> instances = new ArrayList<>();
List<Application> sortedApplications = getRegistry().getSortedApplications();
Optional<Application> targetApp = sortedApplications.stream().filter(a->a.getName().equals(AGENT_NAME)).findFirst();
if(targetApp.isPresent()){
Application app = targetApp.get();
for (InstanceInfo info : app.getInstances()) {
instances.add(info.getHomePageUrl());
}
}
return instances;
}
}

RPC调用

manager 需要控制agent,按最简单的方案,agent提供rest服务,从Eureka获取地址后直接调用就可以了,另外可以借助feign来方便调用。

定义接口:

public interface NginxAgentManager {

    @RequestLine("GET /nginx/start")
RuntimeBuilder.RuntimeResult start() ; @RequestLine("GET /nginx/status")
RuntimeBuilder.RuntimeResult status() ; @RequestLine("GET /nginx/reload")
RuntimeBuilder.RuntimeResult reload() ; @RequestLine("GET /nginx/stop")
RuntimeBuilder.RuntimeResult stop(); @RequestLine("GET /nginx/testConfiguration")
RuntimeBuilder.RuntimeResult testConfiguration(); @RequestLine("GET /nginx/kill")
RuntimeBuilder.RuntimeResult kill() ; @RequestLine("GET /nginx/restart")
RuntimeBuilder.RuntimeResult restart() ; @RequestLine("GET /nginx/info")
NginxInfo info(); @RequestLine("GET /nginx/os")
OperationalSystemInfo os() ; @RequestLine("GET /nginx/accesslogs/{lines}")
List<NginxLoggerVM> getAccesslogs(@Param("lines") int lines); @RequestLine("GET /nginx/errorlogs/{lines}")
List<NginxLoggerVM> getErrorLogs(@Param("lines") int lines); }

agent 实现功能:

@RestController
@RequestMapping("/nginx")
public class NginxResource { ... @PostMapping("/update")
@Timed
public String update(@RequestBody NginxConf conf){
if(conf.getSslDirectives()!=null){
for(SslDirective sslDirective : conf.getSslDirectives()){
nginxControl.conf(sslDirective.getCommonName(),sslDirective.getContent());
}
}
return updateConfig(conf.getConf());
} @GetMapping("/accesslogs/{lines}")
@Timed
public List<NginxLoggerVM> getAccesslogs(@PathVariable Integer lines) {
return nginxControl.getAccessLogs(lines);
} }

manager 调用;

先生成一个Proxy实例,其中nodeurl是agent节点的url地址

    public NginxAgentManager getAgentManager(String nodeUrl){
return Feign.builder()
.options(new Request.Options(1000, 3500))
.retryer(new Retryer.Default(5000, 5000, 3))
.requestInterceptor(new HeaderRequestInterceptor())
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(NginxAgentManager.class, nodeUrl);
}

然后调用就简单了,比如要启动group:

public void start(String groupId){
operateGroup(groupId,((conf, node) -> {
NginxAgentManager manager = getAgentManager(node.getAgent()); String result = manager.update(conf);
if(!result.equals("success")){
throw new XNginxException("node "+ node.getAgent()+" update config file failed!");
} RuntimeBuilder.RuntimeResult runtimeResult = manager.start();
if(!runtimeResult.isSuccess()){
throw new XNginxException("node "+ node.getAgent()+" start failed,"+runtimeResult.getOutput());
}
}));
} public void operateGroup(String groupId,BiConsumer<NginxConf,NginxNode> action){ List<String> alivedNodes = nodeDiscover.getAliveAgents();
if(alivedNodes.size() == 0){
throw new XNginxException("no alived agent!");
}
List<NginxNode> nginxNodes = nodeRepository.findAllByGroupId(groupId);
if(nginxNodes.size() ==0){
throw new XNginxException("the group has no nginx Nodes!");
} NginxConf conf = nginxConfigService.genConfig(groupId); for(NginxNode node : nginxNodes){ if(!alivedNodes.contains(node.getAgent())){
continue;
} action.accept(conf, node);
}
}

Nginx 配置管理

nginx的核心是各种Directive(指令),最核心的是vhost和Location。

我们先来定义VHOST:

public class VirtualHostDirective implements Directive {

	private Integer port = 80;
private String aliases;
private boolean enableSSL;
private SslDirective sslCertificate;
private SslDirective sslCertificateKey;
private List<LocationDirective> locations; private String root;
private String index;
private String access_log;
}

其中核心的LocationDirective,设计思路是passAddress存储location的目标地址,可以是url,也可以是upstream,通过type来区分,同时如果有upstream,则通过proxy来设置负载信息。

public class LocationDirective {

	public static final String PROXY = "PROXY";
public static final String UWSGI = "UWSGI";
public static final String FASTCGI = "FASTCGI";
public static final String COMMON = "STATIC"; private String path; private String type = COMMON; private ProxyDirective proxy; private List<String> rewrites; private String advanced; private String passAddress; }

再来看ProxyDirective,通过balance来区分是普通的url还是upstream,如果是upstream,servers存储负载的服务器。

public class ProxyDirective implements Directive {

	public static final String BALANCE_UPSTREAM = "upstream";
public static final String BALANCE_URL = "url"; private String name; private String strategy; /**
* Upstream balance type : upsteam,url
*/
private String balance = BALANCE_UPSTREAM; private List<UpstreamDirectiveServer> servers; }

历史数据导入

已经有了配置信息,可以通过解析导入系统,解析就是常规的文本解析,这里不再赘述。

核心思想就是通过匹配大括号,将配置文件分成block,然后通过正则等提取信息,比如下面的代码拆分出server{...}

private List<String> blocks() {
List<String> blocks = new ArrayList<>();
List<String> lines = Arrays.asList(fileContent.split("\n")); AtomicInteger atomicInteger = new AtomicInteger(0);
AtomicInteger currentLine = new AtomicInteger(1);
Integer indexStart = 0;
Integer serverStartIndex = 0;
for (String line : lines) {
if (line.contains("{")) {
atomicInteger.getAndIncrement();
if (line.contains("server")) {
indexStart = currentLine.get() - 1;
serverStartIndex = atomicInteger.get() - 1;
}
} else if (line.contains("}")) {
atomicInteger.getAndDecrement();
if (atomicInteger.get() == serverStartIndex) {
if (lines.get(indexStart).trim().startsWith("server")) {
blocks.add(StringUtils.join(lines.subList(indexStart, currentLine.get()), "\n"));
}
}
}
currentLine.getAndIncrement();
}
return blocks;
}

配置文件生成

配置文件生成,一般是通过模板引擎,这里也不例外,使用了Velocity库。

    public static StringWriter mergeFileTemplate(String pTemplatePath, Map<String, Object> pDto) {
if (StringUtils.isEmpty(pTemplatePath)) {
throw new NullPointerException("????????????");
}
StringWriter writer = new StringWriter();
Template template;
try {
template = ve.getTemplate(pTemplatePath);
} catch (Exception e) {
throw new RuntimeException("????????", e);
}
VelocityContext context = VelocityHelper.convertDto2VelocityContext(pDto);
try {
template.merge(context, writer);
} catch (Exception e) {
throw new RuntimeException("????????", e);
}
return writer;
}

定义模板:

#if(${config.user})user ${config.user};#end
#if(${config.workerProcesses}== 0 )
worker_processes auto;
#else
worker_processes ${config.workerProcesses};
#end
pid /opt/xnginx/settings/nginx.pid; events {
multi_accept off;
worker_connections ${config.workerConnections};
} ...

生成配置文件;

    public static StringWriter buildNginxConfString(ServerConfig serverConfig, List<VirtualHostDirective> hostDirectiveList, List<ProxyDirective> proxyDirectiveList) {
Map<String,Object> map = new HashMap<>();
map.put("config",serverConfig);
map.put("upstreams", proxyDirectiveList);
map.put("hosts",hostDirectiveList);
return VelocityHelper.mergeFileTemplate(NGINX_CONF_VM, map);
}

管理web

管理web基于ng-alain框架,typescript+angular mvvm开发起来,和后端没有本质区别

开发相对简单,这里不赘述。

小结

目前只实现了基本的管理功能,后续可根据需要再继续补充完善,比如支持业务、负责人等信息管理维护。


作者:Jadepeng

出处:jqpeng的技术记事本--http://www.cnblogs.com/xiaoqi

您的支持是对博主最大的鼓励,感谢您的认真阅读。

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

XNginx - nginx 集群可视化管理工具的更多相关文章

  1. redis cluster集群web管理工具 relumin

    redis cluster集群web管理工具 relumin 下载地址 https://github.com/be-hase/relumin 只支持redis cluster模式 java环境 tar ...

  2. Docker Swarm(十)Portainer 集群可视化管理

    前言 搭建好我们的容器编排集群,那我们总不能日常的时候也在命令行进行操作,所以我们需要使用到一些可视化的工具,Docker图形化管理提供了很多工具,有Portainer.Docker UI.Shipy ...

  3. elk中es集群web管理工具cerebro

    cerebo是kopf在es5上的替代者 安装es虽然不能再root下运行,但是cerebro 可以 run as root is ok wget https://github.com/lmeneze ...

  4. Nginx集群session管理的两种方式

    1.IP_HASH 修改nginx配置文件 实现非常简单,但是绑定在一个服务器上了,不能跨越多个服务. 2.redis管理 管理session信息的空间,需要修改tomcat配置文件 下载相应的red ...

  5. MongoDB 生态 – 可视化管理工具

    工欲善其事,必先利其器,我们在使用数据库时,通常需要各种工具的支持来提高效率:很多新用户在刚接触 MongoDB 时,遇到的问题是『不知道有哪些现成的工具可以使用』,本系列文章将主要介绍 MongoD ...

  6. nginx 集群介绍

    nginx 集群介绍 完成一次请求的步骤 1)用户发起请求 2)服务器接受请求 3)服务器处理请求(压力最大) 4)服务器响应请求 缺点:单点故障 单台服务器资源有限 单台服务器处理耗时长 ·1)部署 ...

  7. Docker可视化管理工具对比(DockerUI、Shipyard、Rancher、Portainer)

    1.前言 谈及docker,避免不了需要熟练的记住好多命令及其用法,对于熟悉shell.技术开发人员而言,还是可以接受的,熟练之后,命令行毕竟是很方便的,便于操作及脚本化.但对于命令行过敏.非技术人员 ...

  8. MongoDB 可视化管理工具

    MongoDB 可视化管理工具 (2011年10月-至今)   正文 该项目从2011年10月开始开发,知道现在已经有整整5年了.MongoDB也从一开始的大红大紫到现在趋于平淡.MongoCola这 ...

  9. docker--docker 的web可视化管理工具

    12 docker 的web可视化管理工具 12.1 常用工具介绍 当 Docker 部署规模逐步变大后,可视化监控容器环境的性能和健康状态将会变得越来越 重要. Docker的图形化管理工具,提供状 ...

随机推荐

  1. windows下java环境变量的配置 javac不是内部或外部命令的问题

    安装配置JAVA JDK 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html . 下载你电脑对应的JDK,下 ...

  2. Vue 核心之数据劫持

    前端界空前繁荣,各种框架横空出世,包括各类mvvm框架横行霸道,比如Angular.Regular.Vue.React等等,它们最大的优点就是可以实现数据绑定,再也不需要手动进行DOM操作了,它们实现 ...

  3. jquery datetimepicker

    1.详细说明见:https://xdsoft.net/jqplugins/datetimepicker/ 语言选择中文,现在lang配置已经失效;可用: $.datetimepicker.setLoc ...

  4. 使用tomcat发布含有shtml文件的web程序

    平常都是使用html或jsp,当导入含有shtml的一个web项目时,向平常一样使用Jetty运行,访问到shtml文件时,直接在浏览器上显示出了源码. 查询,使用tomcat发布,修改tomcat的 ...

  5. SpringBoot中常见注解含义总结

    @RestController @RestController被称为一个构造型(stereotype)注解.它为阅读代码的开发人员提供建议.对于Spring,该类扮演了一个特殊角色.它继承自@Cont ...

  6. 【原创】大数据基础之Logstash(1)简介、安装、使用

    Logstash 6.6.2 官方:https://www.elastic.co/products/logstash 一 简介 Centralize, Transform & Stash Yo ...

  7. 【原创】大数据基础之Kerberos(2)hive impala hdfs访问

    1 hive # kadmin.local -q 'ktadd -k /tmp/hive3.keytab -norandkey hive/server03@TEST.COM'# kinit -kt / ...

  8. Url解码和编码 escape()、encodeURI()、encodeURIComponent()区别详解

    Server.UrlDecode;解码 Server.UrlEncode;编码 url编码是一种浏览器用来打包表单输入的格式.浏览器从表单中获取所有的name和其中的值 ,将它们以name/value ...

  9. 银联支付java版

    注:本文来源于:<  银联支付java版    > 银联支付java版 2016年09月18日 15:55:20 阅读数:2431 首先去银联官网注册测试支付账户   下载对应的demo[ ...

  10. Confluence 6 配置文件和key

    找到配置文件 缓存的配置文件是存储在 <confluence-home>/shared-home/config/cache-settings-overrides.properties 中的 ...