实际开发中,可能会需要将单个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图表,控制台也无任何错误