首页 > 其他分享 >Grafana+OpenSearch+Spring Boot集成(四) 【反向代理Grafana实现嵌入】

Grafana+OpenSearch+Spring Boot集成(四) 【反向代理Grafana实现嵌入】

时间:2022-11-28 23:34:53浏览次数:55  
标签:node http OpenSearch opensearch Spring grafana proxy import Grafana


实际开发中,可能会需要将单个Grafana图表嵌入到自己的项目中,通常最简单的方式是Grafana启用匿名登录,但这种方式可能会存在安全问题。
本节将介绍如何通过Api key反向代理Grafana网页,然后嵌入到Web应用中。其中有两种反向代理方式,一种是借助第三方库通过Java后端代理,一种是通过nginx代理

Grafana配置

docler-compose.yml文件内容见Grafana+OpenSearch+Spring Boot集成(一) 【基础环境配置】

1、在docker-compose.yml同级目录下创建Grafana/grafana.ini文件,添加允许嵌入的配置和服务端配置

[security]
#设置为true,允许通过iframe方式嵌入grafana panle
allow_embedding = true

[server]
# Protocol (http, https, h2, socket)
protocol = http
# 端口,默认为3000
http_port = 3000
# 域名,默认为localhost
domain = localhost
# 完整的url,通常会添加一个子路径grafana方便访问静态资源
root_url = %(protocol)s://%(domain)s:%(http_port)s/grafana/
# 如果root_url添加了子路径,需要设置为true
serve_from_sub_path = true

2、在docker-compose.yml中添加- ./grafana/grafana.ini:/etc/grafana/grafana.ini将grafana.ini挂载到docker容器中,覆盖默认的配置文件

3、运行docker-compose up -d命令更新配置,然后重启一下grafana docker容器

4、在Grafana面板左侧找到Api keys,创建一个Api key

创建完毕后记得将key立即复制保存,用于后续反向代理配置

代理方式一

1、IDEA新建一个Spring Boot项目,依赖勾选上Spring Web和Thymeleaf。

3、添加smiley-http-proxy-servlet的依赖到pom.yml文件中

		<dependency>
			<groupId>org.mitre.dsmiley.httpproxy</groupId>
			<artifactId>smiley-http-proxy-servlet</artifactId>
			<version>1.12.1</version>
		</dependency>

4、在application.yml文件中添加反向代理的配置以及thymeleaf的配置

spring:
  thymeleaf:
    template-resolver-order: 0
    prefix: classpath:/templates/
    suffix: .html
    mode: HTML
    encoding: UTF-8
    cache: false
    servlet:
      content-type: text/html

proxy:
  grafana:
    # 需要代理的url前缀
    servlet_url: /grafana/*
    # grafana url
    target_url: http://localhost:3000
    # grafana api key, 添加一个Bearer前缀
    key: 'Bearer eyJrIjoiV3NOSXk5Q2Y5RFdLWEZmZDBvQUg3VDRYdTFFVHh4TjciLCJuIjoiVGVzdCIsImlkIjoxfQ=='

5、添加ProxyConfiguration反向代理配置文件

package com.grafanademo.config;

import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.mitre.dsmiley.httpproxy.ProxyServlet;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;

import javax.servlet.Servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class ProxyConfiguration {
    @Value("${proxy.grafana.servlet_url}")
    private String servletUrl;

    @Value("${proxy.grafana.target_url}")
    private String targetUrl;

    @Value("${proxy.grafana.key}")
    private String key;

    @Bean
    public ServletRegistrationBean<Servlet> proxyServletRegistration() {
        // 设置需要反向代理的url
        ServletRegistrationBean<Servlet> registrationBean = new ServletRegistrationBean<>(getServlet(), servletUrl);
        Map<String, String> params = new HashMap<>();
        params.put("targetUri", targetUrl);
        //如果需要打印日志,将值设置为true
        params.put("log", "false");
        registrationBean.setInitParameters(params);
        return registrationBean;
    }

    public Servlet getServlet() {
        return new ProxyServlet() {
            @Override
            protected HttpResponse doExecute(HttpServletRequest servletRequest, HttpServletResponse servletResponse,
                                             HttpRequest proxyRequest) throws IOException {
                // 可以在这之前执行一些其他操作,比如鉴权等...
                
                // 设置Authorization请求头,值为Grafana中创建的api key值
                proxyRequest.setHeader("Authorization", key);
                return super.doExecute(servletRequest, servletResponse, proxyRequest);
            }
        };
    }
}

6、添加thymeleaf的配置文件


package com.grafanademo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.templateresolver.StringTemplateResolver;

import javax.annotation.PostConstruct;

@Configuration
public class ThymeleafConfiguration {

    private TemplateEngine templateEngine;

    @Autowired
    public void setTemplateEngine(TemplateEngine templateEngine) {
        this.templateEngine = templateEngine;
    }

    @PostConstruct
    public void init() {
        templateEngine.addTemplateResolver(new StringTemplateResolver());
    }
}

7、点击创建的grafana panel的标题处的下拉框,点击share


然后复制当前panel的地址,从/grafana开始复制

8、在resource/templates目录下添加一个grafana.html文件,添加一个iframe标签,src值为刚才复制的url

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .chart-embedded {
            width: 800px;
            height: 300px;
            border: 1px solid #ccc;
        }
    </style>
</head>
<body>
<iframe src="/grafana/d-solo/gRHix6OVk/new-dashboard?orgId=1&from=1669542468651&to=1669564068651&panelId=2" class="chart-embedded"></iframe>
</body>
</html>

9、添加一个Controller,用来返回Thymeleaf模板

package com.grafanademo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class GrafanaController {

    @GetMapping("/grafanaPage")
    public String grafanaPage() {
        return "grafana";
    }
}

10、启动程序,然后访问localhost:8080/grafanaPage,可以看到grafana图表成功显示在当前页面中

存在问题

实际上这种代理方式存在一个问题,无法代理ws,控制台会一直打印连接WebSocket失败的错误


为了避免这个问题,只能在grafana.ini中添加live的配置,将max_connections 设置为0关闭live

[live]
# 0 disables Live, -1 means unlimited connections.
max_connections = 0

当然还有另一个办法,就是采用nginx进行反向代理

代理方式二

1、添加nginx docker容器配置到docker-compose.yml文件中,完整内容为:

version: '3'
services:
  #opensearch 集群 节点1
  opensearch-node-1:
    #如果java jdk版本是1.8,则需要安装1.3+的版本
    #参考:https://opensearch.org/docs/2.3/opensearch/install/compatibility/
    image: opensearchproject/opensearch:1.3.3
    container_name: opensearch-node-1
    environment:
      - cluster.name=opensearch-cluster
      - node.name=opensearch-node-1
      - discovery.seed_hosts=opensearch-node-1,opensearch-node-2
      - cluster.initial_master_nodes=opensearch-node-1,opensearch-node-2
      - bootstrap.memory_lock=true
      - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
      - "DISABLE_SECURITY_PLUGIN=true" #禁用安全插件,这样可以免密登录
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536 # 为OpenSearch用户打开的最大文件数,设置为至少65536
        hard: 65536
    volumes:
      # 挂载数据到本地
      - opensearch-data-1:/usr/share/opensearch/data
    ports:
      - 9200:9200
      - 9600:9600
    expose:
      - 9200
      - 9600
    networks:
      # 所有节点配置的网络都为同一个,这样访问其他容器服务时可以直接用node名称作为domain
      - opensearch-grafana-network
  # opensearch 集群 节点2,配置同节点1
  opensearch-node-2:
    image: opensearchproject/opensearch:1.3.3
    container_name: opensearch-node-2
    environment:
      - cluster.name=opensearch-cluster
      - node.name=opensearch-node-2
      - discovery.seed_hosts=opensearch-node-1,opensearch-node-2
      - cluster.initial_master_nodes=opensearch-node-1,opensearch-node-2
      - bootstrap.memory_lock=true
      - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
      - "DISABLE_SECURITY_PLUGIN=true"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    volumes:
      - opensearch-data-2:/usr/share/opensearch/data
    networks:
      - opensearch-grafana-network
  # opensearch仪表盘,可以使用它的dev tool来可视化查看数据
  # 如果不需要,这一段可以注释掉
  opensearch-dashboards-1:
    image: opensearchproject/opensearch-dashboards:1.3.3
    container_name: opensearch-dashboards
    ports:
      - 5601:5601
    expose:
      - "5601"
    environment:
      - 'OPENSEARCH_HOSTS=["http://opensearch-node-1:9200","http://opensearch-node-2:9200"]'
      - "DISABLE_SECURITY_DASHBOARDS_PLUGIN=true"
    networks:
      - opensearch-grafana-network
  # grafana 配置
  grafana:
    image: grafana/grafana:latest
    ports:
      - 3000:3000
    volumes:
      - grafana-data-1:/var/lib/grafana
      - ./grafana/grafana.ini:/etc/grafana/grafana.ini
    environment:
      - TERM=linux
      - "GF_RENDERING_CALLBACK_URL=http://grafana:3000/"
    networks:
      - opensearch-grafana-network

  #nginx
  web:
    image: nginx
    volumes:
      - ./nginx/:/etc/nginx/conf.d/
    ports:
      - 80:80
    environment:
      - NGINX_PORT=80
    networks:
      - opensearch-grafana-network

volumes:
  opensearch-data-1:
  opensearch-data-2:
  grafana-data-1:

networks:
  opensearch-grafana-network:

2、在docker-compose.yml同级目录下,新增配置文件nginx/defualt.cong。PS:上一步中已经添加了挂载这个文件的设置

map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}

server {
  listen 80;

# Proxy Grafana
  location /grafana/ {
    proxy_buffer_size 128k;
    proxy_buffers   32 128k;
    proxy_busy_buffers_size 128k;

    add_header Access-Control-Allow-Origin '*';
    add_header Access-Control-Allow-Methods '*';
    add_header Access-Control-Allow-Credentials true;
    #grafana api key
    set $auth 'Bearer eyJrIjoiV3NOSXk5Q2Y5RFdLWEZmZDBvQUg3VDRYdTFFVHh4TjciLCJuIjoiVGVzdCIsImlkIjoxfQ==';

    proxy_set_header     Host $host;
    proxy_set_header     Authorization $auth;
    proxy_set_header     X-Real-IP $remote_addr;
    proxy_set_header     X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header     X-Forwarded-Proto $scheme;

    proxy_pass http://grafana:3000;
    proxy_redirect default;
  }

  # Proxy Grafana Live WebSocket connections.
  location /grafana/api/live/ {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $http_host;

    add_header Access-Control-Allow-Origin '*';
    add_header Access-Control-Allow-Methods '*';
    add_header Access-Control-Allow-Credentials true;
    #grafana api key
    set $auth 'Bearer eyJrIjoiV3NOSXk5Q2Y5RFdLWEZmZDBvQUg3VDRYdTFFVHh4TjciLCJuIjoiVGVzdCIsImlkIjoxfQ==';

    proxy_set_header     Authorization $auth;
    proxy_set_header     X-Real-IP $remote_addr;
    proxy_set_header     X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header     X-Forwarded-Proto $scheme;
    proxy_pass http://grafana:3000;
  }
}

3、同方式一,创建一个Spring boot项目,只需要添加thymeleaf配置(参考方式一第4、6、9步)。然后将grafana.html中的src添加一个前缀http://localhost

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .chart-embedded {
            width: 800px;
            height: 300px;
            border: 1px solid #ccc;
        }
    </style>
</head>
<body>
<iframe src="http://localhost/grafana/d-solo/gRHix6OVk/new-dashboard?orgId=1&from=1669542468651&to=1669564068651&panelId=2" class="chart-embedded"></iframe>
</body>
</html>

4、启动程序,访问localhost:8080/grafanaPage,将可以看到嵌入的grafana图表,控制台也无任何错误

标签:node,http,OpenSearch,opensearch,Spring,grafana,proxy,import,Grafana
From: https://www.cnblogs.com/rachel-x/p/16930993.html

相关文章

  • Spring之SpringContext
    一、概述1.SpringContext概念创建上下文并将BeanPostProcessor加载到spring2.SpringApplicationContext概念Spring通过应用上下文(ApplicationContext)装载bean的定义......
  • Spring6 正式发布!重磅更新,是否可以拯救 Java
    简介SpringFramework6和SpringBoot3是一个跨越式的升级整个框架支持的最低JDK版本直接跨越到JDK17,无论框架层还是基础设施层都做了巨大的改变,Spring6.0新框架具体......
  • Spring之SpringContext
    一、概述1.SpringContext概念创建上下文并将BeanPostProcessor加载到spring2.SpringApplicationContext概念Spring通过应用上下文(ApplicationContext)装载bean的定......
  • Spring MVC文件请求处理详解:MultipartResolver
    org.springframework.web.multipart.MultipartResolver是Spring-Web针对RFC1867实现的多文件上传解决策略。1使用场景前端上传文件时,无论是使用比较传统的表单,还是使用F......
  • java:[5,51] 程序包com.alibaba.dru id.spring.boot.autoconfigure不存在
    一、报错信息二、解决方案mvn-Uidea:idea ......
  • Spring--案例:百度网盘密码数据兼容处理
    案例再度来袭也就是说,在百度网盘的密码复制时,后面即使有空格也能提取成功(trim方法)案例的实现:也就是实现存在空格时,也能输出true;现在的话:那么,我们应该如何使得它忽......
  • Spring--案例:测试业务层接口万次执行效率
    案例来袭这样,并不能够分辨出哪个的效率是哪个可以利用pjp进行这样的操作:结果:......
  • Spring Data JPA基本使用
    JPA简介JPA即JavaPersistenceAPI。是一款持久层框架,是JDK5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。JPA的对象关系映射(ORM)模型是......
  • Prometheus+Grafana监控JVM实战
    1.什么是JMXExporter? JMXExporter利用Java的JMX机制来读取JVM运行时的一些监控数据,然后将其转换为Prometheus所认知的metrics格式,以便让Prometheus对......
  • Spring框架自学之路——事务管理
    目录​​目录​​​​前言​​​​介绍​​​​案例说明​​​​案例准备及问题分析​​​声明式事务管理​​​事务管理器​​​基于XML配置声明式事务​​​XML配置​​​......