在Backstage中集成Grafana

在Backstage中集成Grafana

Zave Genshin Impact

注意
本文档基于 Backstage 1.32.0 版本编写,并采用自 1.24.0 版本以来官方推荐使用的新后端系统(New BackEnd System)。Backstage 仍处于不断更新发展中,本文下述步骤可能将不适用于后续更新版本。

一、开发前准备

  1. 确认 Grafana 与 Backstage 服务能够互相 ping 通
  2. 确认 Backstage 已经完成 Azure 账户登录的集成;若还没有完成本部分工作,请参考此文后再继续接下来的步骤:在Backstage中集成Microsoft Azure进行身份验证并实现伪SSO
  3. 确认 Grafana 支持使用 Microsoft Azure 登录
  4. 开发参考文档:

二、创建自定义插件

(1)新建自定义插件模板

从项目的根目录开始:

1
yarn new

使用上面这个命令后,命令行界面将会出现如下的选项页:

1
2
3
4
5
6
7
What do you want to create? (use arrow keys)
plugin - A new frontend plugin
backend-module - A new backend module that extends an existing backend plugin with additional features
node-library - A new node-library package, exporting shared functionality for backend plugins and modules
web-library - A new web-library package, exporting shared functionality for frontend plugins
plugin-common - A new isomorphic common plugin package
plugin-node - A new Node.js library plugin package

直接选择第一项,这一项表示在前端新建一个插件。

后面的操作可以参照此文章的第四部分(1)小节进行:在Backstage中集成Jenkins

(2)整理插件结构

将插件的结构整理为如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
grafana/
├── dev/
│ └── index.tsx
├── src/
│ ├── components/
│ │ └── page.tsx
│ ├── index.ts
│ ├── plugin.test.ts
│ ├── plugin.ts
│ ├── routes.ts
│ └── setupTests.ts
├── .eslintrc.js
├── package.json
└── README.md

其中 src/components 文件夹下的 page.tsx 文件是整个插件的核心,里面包含插件页面的组成以及插件处理逻辑。

src 文件夹下的 index.ts、plugin.ts、routes.ts 负责处理路由和将插件向外暴露。

(3)编写插件页面及其业务逻辑

编辑 plugins/grafana/src/components/page.tsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import React from 'react';
import { useState, useEffect } from 'react';
import { Page, Content } from '@backstage/core-components';
import { identityApiRef, useApi, microsoftAuthApiRef } from '@backstage/core-plugin-api';
import PermissionDenied from '../../../../packages/app/src/components/custom/PermissionDenied';

export const MainPage = () => {
// useApi作为hook必须在顶层调用
const microsoftAuthApi = useApi(microsoftAuthApiRef);
const IdentityApi = useApi(identityApiRef);

const [localSrc, setLocalSrc] = useState('');
const [loading, setLoading] = useState(true);
const [backstageCredentials, setBackstageCredentials] = useState('');
const [isMicroLogin, setIsMicroLogin] = useState(true);

useEffect(() => {
const getDataByMicrosoftIdToken = async () => {
try {
const microsoftCredentials = await microsoftAuthApi.getIdToken();
setLocalSrc(
`https://grafana.xxxxxx.com.cn/d/ee080bb6-9a05-4a8b-84fb-871fd9a14c16/new-dashboard?orgId=1&auth_token=${microsoftCredentials}`
);
} catch (error) {
// 当没有登录过MicroSoft账号时,进入此界面将自动弹出请求登录窗口,此时点击reject all,则进行此处理
console.log('Failed to fetch Microsoft credentials:', error);
} finally {
setIsMicroLogin(false);
setLoading(false);
}
};

const getBackStageUserIdentity = async () => {
try {
const bkstCredentials = await IdentityApi.getBackstageIdentity();
setBackstageCredentials(bkstCredentials.userEntityRef);

if (bkstCredentials.userEntityRef !== 'user:development/guest') {
getDataByMicrosoftIdToken();
}
} catch (error) {
console.log('Failed to fetch Backstage credentials:', error);
}
};

getBackStageUserIdentity();
}, []);

// 若登录账号为guest访客账号或未进行MicroSoft账号登录认证,则将无权限访问
if (backstageCredentials === 'user:development/guest' || !isMicroLogin) {
return (
<Page themeId="tool">
<Content>
<PermissionDenied />
</Content>
</Page>
);
}

return (
<Page themeId="tool">
<Content>
{loading ? (
<div>Loading...</div>
) : (
<iframe
src={localSrc}
title="Grafana"
height="100%"
width="100%"
style={{ border: 0 }}
/>
)}
</Content>
</Page>
);
};

在这里的例子中,页面的实现主要靠使用 iframe 显示 Grafana 界面。此外,此处依靠 URL 中明文携带 Microsoft Azure IdToken 的方式进行身份验证,其安全性仍待考量。Microsoft Azure IdToken 的获取依靠 Backstage 自带的 Auth 系列 Api 获取,前提是使用 Azure 账号登录 Backstage,或者在 Backstage 中绑定了自己的 Azure 账号,这就是为什么需要提前集成好 Azure 的原因。

当然,为了拥有更好的安全性,此处还额外添加了针对访客用户的访问限制,在访客用户登录并试图访问 Grafana 时,将会显示如下界面:

这个页面的编写如下:

注:请注意本文件的存放位置!

编辑 packages/app/src/components/custom/PermissionDenied.tsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import React from "react";
import { Button, Container, Typography } from '@material-ui/core';

const PermissionDenied = () => {
return (
<Container style={{ textAlign: 'center', marginTop: '100px' }}>
<img
src="https://static.vecteezy.com/system/resources/previews/002/846/473/large_2x/cloud-computing-technology-vector.jpg"
style={{ width: '30%', margin: '20px auto', display: 'block', maxWidth: '100%' }}
alt="Permission Denied"
/>
<Typography variant="h2" gutterBottom>
Permission Defined
</Typography>
<Typography variant="h5" gutterBottom>
您尚未有权限访问
</Typography>
<Button
variant="contained"
color="primary"
href='/'
style={{ marginTop: '20px' }}
>
返回主页
</Button>
</Container>
);
};

export default PermissionDenied;

除了从插件自身层面进行权限控制,我们也可以从路由层面进行权限访问控制,具体实现可以参考这篇文章:Backstage三层权限架构设计与实现

当然,除了上述简单的权限判断,也可以去添加实现更为复杂的权限判断,此处就不再赘述了。

(4)修改其他文件

我们还需要对插件中的其他几个文件进行如下的修改:

编辑 src/index.ts:

1
export { grafanaPlugin, grafanaPage } from './plugin';

编辑 src/plugin.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { createRoutableExtension, createPlugin } from '@backstage/core-plugin-api';
import { rootRouteRef } from './routes';

export const grafanaPlugin = createPlugin({
id: 'grafana',
routes: {
root: rootRouteRef,
},
});

export const grafanaPage = grafanaPlugin.provide(
createRoutableExtension({
name: 'grafanaPage',
component: () => import('./components/page').then(m => m.MainPage),
mountPoint: rootRouteRef,
}),
);

主要是修改了一些命名,调整了文件导入路径等。

(5)将插件导入到前端界面中

我们需要在 packages/app 文件夹下的 App.tsx 文件中添加如下内容:

编辑 packages/app/src/App.tsx:

1
2
3
4
5
6
7
8
9
10
// ...other import
import { grafanaPage } from '@internal/backstage-plugin-grafana';

// ...others
const routes = (
<FlatRoutes>
{/* ...others */}
<Route path="/grafana" element={<GrafanaPage />} />
</FlatRoutes>
);

其中 path 的值最好与给插件的取名保持一致,element 的值对应的是上一步 index.ts 中所导出的 xxxxPage。

还需要在 packages/app/components/Root 文件夹下的 Root.tsx 文件中添加如下内容:

编辑 src/components/Root/Root.tsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<SidebarPage>
{/* ...others */}
<Sidebar>
<SidebarGroup label="Menu" icon={<MenuIcon />}>
{/* ...others */}
<SidebarScrollWrapper>
{/* Items in this group will be scrollable if they run out of space */}
<SidebarItem icon={DashboardIcon} to="grafana" text="Grafana" />
{/* ...others */}
</SidebarScrollWrapper>
</SidebarGroup>
</Sidebar>
{/* ...others */}
</SidebarPage>

完成上面的步骤后,就能够在 Backstage 的主界面左侧菜单栏中看到 Grafana 对应的入口了。

此时,在使用了 Azure 登录 Backstage 的情况下,点击进入 Grafana 插件,就可以直接进入面板,而无需再进行二次登录。

Grafana 插件展示效果

至此,我们就在 Backstage 中完成了一个前端插件的开发。

三、补充说明

(1)Grafana 官方插件

Grafana 在 Backstage 中其实已经拥有官方的插件,我们只需根据其说明文档编写少量的配置,就能够实现集成。但是考虑到本系统的需求且为了避免进行二次登录,才重新设计并开发了这个新插件。

Grafana 官方插件集成指南:Grafana | Backstage 集成指南

  • 标题: 在Backstage中集成Grafana
  • 作者: Zave
  • 创建于 : 2026-09-05 12:00:00
  • 更新于 : 2026-09-05 12:00:00
  • 链接: https://zavex.cn/post/18707.html
  • 版权声明: 版权所有 © Zave,禁止转载。
评论