广告位 · 页头(后台-模块管理-新建模块后替换此区域)

如何用 Vue 3 工程化对接 Ceph 分布式存储系统?

Ceph 集群从测试走向生产后,Web 管理入口的缺失会让故障定位和日常运维成本明显上升。Ceph 自带的 Dashboard 虽然覆盖了基础监控,但在 UI 风格、权限隔离和业务集成上往往无法满足团队定制需求。借助 Vue 3 的工程化能力,可以快速搭建一个面向 Ceph 的分布式存储管理平台,把存储池、OSD、RADOS Gateway 和监控数据统一到一个可维护的前端项目里。

如何用 Vue 3 工程化对接 Ceph 分布式存储系统?

本文从工程化视角出发,介绍如何用 Vue 3、TypeScript、Pinia 和 Axios 构建 Ceph 存储管理前端,覆盖项目初始化、请求封装、核心模块实现以及生产环境优化,帮助团队降低 Ceph 的日常运维复杂度。

一、为什么需要在 Vue 3 中工程化 Ceph 管理端

Ceph 作为分布式存储系统,核心组件包括 RADOS、RBD、CephFS 和 RADOS Gateway,这些组件本身只提供命令行工具和有限的 Web Dashboard。当集群规模超过几十个 OSD、存储池数量增多后,仅靠命令行检查 ceph -s 或 ceph osd pool ls 很难快速发现异常节点和容量瓶颈。团队需要统一的 Web 界面来查看集群健康状态、管理存储池配额、监控 OSD 的读写延迟。

自建管理前端的优势在于可以按照业务场景裁剪功能。比如只给应用管理员暴露对象存储桶的创建和配额调整入口,而将 OSD 上下线操作留给存储管理员。Vue 3 的组件化开发模式非常适合这种分区清晰的运维面板。工程化也不是简单请求几个接口,而是要把认证、状态管理、错误处理和性能优化纳入统一框架,避免在多个页面中重复编写登录和不稳定的调用逻辑。

另一个关键在于 Ceph 的 API 并不统一。Dashboard 提供基于 JWT 的 REST API,RADOS Gateway 则暴露 S3 兼容接口和 Admin Ops API。直接用浏览器调 S3 接口会遇到跨域和签名问题,需要由前端工程通过代理层或后端来收敛。这就引出了下一节的工程化基础建设。

二、项目基础:Vue 3 工程化与请求层封装

初始化一个 Vue 3 项目可以使用 Vite 的官方模板,TypeScript 支持可以显著提高接口数据结构的可维护性。安装 Axios、Pinia、Vue Router、Element Plus 以及可视化库 ECharts,能够覆盖管理台常见的表格、表单和图表需求。

npm create vite@latest ceph-admin -- --template vue-ts
cd ceph-admin
npm install axios pinia vue-router element-plus echarts aws-sdk

请求层是工程化的重点。Ceph Dashboard 的登录接口返回短期 token,后续接口需要在请求头中携带 Authorization 字段。统一封装 Axios 实例可以集中处理 token 注入、超时和 401 跳转,而不是在每个组件里重复编写。下面的封装把响应直接解包,业务层拿到的就是接口数据,出错时由拦截器统一提示并清理过期凭证。

import axios from 'axios';
import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
const request: AxiosInstance = axios.create({
  baseURL: '/api',
  timeout: 15000,
});
request.interceptors.request.use((config: InternalAxiosRequestConfig) => {
  const token = localStorage.getItem('ceph_token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});
request.interceptors.response.use(
  (response) => response.data,
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem('ceph_token');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);
export default request;

Pinia 的状态管理主要用来缓存存储池和 OSD 的列表数据。如果每个页面挂载时都重新拉取完整集群信息,Dashboard API 会承受不小的压力。把数据放在 store 中,页面复用同一份状态,同时通过 loading 标记避免并发重复请求。这样在切换路由时,已经加载过的存储池列表可以立即渲染。

import { defineStore } from 'pinia';
import request from '@/utils/request';
interface Pool {
  pool_name: string;
  type: string;
  size: number;
  pg_num: number;
  used_bytes: number;
}
export const useCephStore = defineStore('ceph', {
  state: () => ({
    pools: [] as Pool[],
    osds: [] as any[],
    loading: false,
  }),
  actions: {
    async fetchPools() {
      this.loading = true;
      try {
        const res = await request.get('/pool');
        this.pools = res as Pool[];
      } finally {
        this.loading = false;
      }
    },
  },
});

三、核心运维模块:存储池、OSD 与 S3 桶管理

存储池管理是 Ceph 运维中最常用的功能。通过 Dashboard 的 /api/pool 接口可以获取所有存储池的副本数、PG 数量和已使用容量。前端需要把这些原始数据处理成易读的格式,例如将字节数转换为 KB、MB、GB。用 Element Plus 的 <el-table> 组件渲染列表,同时支持按类型筛选,可以快速定位到某个 RBD 或 CephFS 池。

<template>
  <el-table :data="store.pools" v-loading="store.loading">
    <el-table-column prop="pool_name" label="存储池" />
    <el-table-column prop="type" label="类型" width="120" />
    <el-table-column prop="size" label="副本数" width="100" />
    <el-table-column prop="pg_num" label="PG 数量" width="120" />
    <el-table-column label="使用量">
      <template #default="{ row }">
        {{ formatSize(row.used_bytes) }}
      </template>
    </el-table-column>
  </el-table>
</template>
<script setup lang="ts">
import { onMounted } from 'vue';
import { useCephStore } from '@/store/ceph';
const store = useCephStore();
function formatSize(bytes: number): string {
  if (!bytes) return '0 B';
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
  let index = 0;
  let size = bytes;
  while (size >= 1024 && index < units.length - 1) {
    size /= 1024;
    index++;
  }
  return `${size.toFixed(2)} ${units[index]}`;
}
onMounted(() => {
  store.fetchPools();
});
</script>

OSD 的读写延迟和状态直接影响整个集群的可用性。通过接口获取 OSD 列表后,前端可以用 ECharts 饼图展示在线和离线的 OSD 数量。轮询频率建议设置为 15 到 30 秒一次,避免给 Dashboard 带来额外负担。如果集群规模很大,还可以把 OSD 数据按照主机分组,形成树状表格,方便定位到具体磁盘。

import * as echarts from 'echarts';
import request from '@/utils/request';
export async function renderOsdChart(el: HTMLElement) {
  const osdMap = await request.get('/osd');
  const states = osdMap.osds.map((osd: any) => osd.state);
  const upCount = states.filter((s: string) => s === 'up').length;
  const downCount = states.length - upCount;
  const chart = echarts.init(el);
  chart.setOption({
    tooltip: { trigger: 'item' },
    series: [{
      type: 'pie',
      radius: '60%',
      data: [
        { value: upCount, name: '在线' },
        { value: downCount, name: '离线' }
      ]
    }]
  });
}

对象存储桶管理则主要面向 RADOS Gateway 的 S3 接口。前端可以直接使用 AWS SDK for JavaScript 完成桶的创建、删除和配额调整。不过浏览器直连 S3 会暴露访问密钥,所以更稳妥的做法是由后端或服务端代理签名。Vue 3 工程化中通常把 S3 调用封装成一个独立模块,只暴露高层方法,组件不直接接触密钥和签名逻辑。

四、生产环境的安全与性能优化

开发阶段可以通过 Vite 的本地代理把 /api 转发到 Ceph Dashboard 地址,从而绕过浏览器的跨域限制。生产部署时建议使用 Nginx 反向代理,集中处理 HTTPS、证书校验和请求头转发。下面的 Nginx 配置同时支持前端静态资源托管和 API 代理,避免直接暴露 Ceph Dashboard 的 8443 端口。

server {
    listen 80;
    server_name ceph-admin.internal;
    location /api/ {
        proxy_pass https://127.0.0.1:8443/api/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_ssl_verify off;
    }
    location / {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
    }
}

安全层面除了 token 的短期失效机制,还要注意前端存储 token 的方式。虽然 localStorage 使用简单,但容易受到 XSS 攻击。更严格的方案是把 token 放在 HttpOnly Cookie 中,由服务端在登录后写入,前端不直接读取凭证。即便使用本地存储,也应设置较短的过期时间和统一的退出清理逻辑。对于 S3 密钥,绝对不要打包进前端代码,必须通过后端生成预签名 URL 或者使用短期 STS 凭证。

大集群的渲染性能也不可忽视。如果存储池或 OSD 列表包含上千条记录,直接渲染会阻塞页面。虚拟滚动组件可以减少 DOM 节点数量,搜索和筛选操作加上防抖能够显著降低请求频率。对于集群健康状态的展示,可以用 WebSocket 或 SSE 推送替代定时轮询,减少无意义的流量消耗。缓存方面可以把变化不频繁的集群拓扑信息缓存 5 分钟,而容量和延迟数据保持更短的刷新周期。

总体来看,Vue 3 工程化对接 Ceph 的核心不仅是调用几个 API,而是通过统一的请求层、状态管理和安全代理把分散的存储组件纳入可维护的前端体系。当底层 Ceph 集群升级或接口变化时,只需调整封装层,无需改动各个页面组件,这也是工程化带来的长期收益。

广告位 · 文章底部(后台-模块管理替换此区域)