首页 > 其他分享 >让运维无忧,实战解析巡检报告功能实现方案

让运维无忧,实战解析巡检报告功能实现方案

时间:2024-03-06 17:45:23浏览次数:29  
标签:巡检 面板 无忧 运维 ... PanelModel panels 目录 panel

随着大数据技术的演进和信息安全性需求的提升,数据规模的持续扩张为数据运维工作带来了严峻考验。面对海量数据所形成的繁重管理压力,运维人员面临效率瓶颈,而不断攀升的人力成本也使得单纯依赖扩充运维团队来解决问题变得不再实际可行。

由此可见,智能化与高效便捷是运维发展的必然方向。袋鼠云所推出的巡检报告功能,正是为了顺应这一目标,致力于提供优化的解决方案。

什么是巡检报告?

巡检报告是指对某一个系统或设备进行全面检查,并把检查结果及建议整理成报告的过程。巡检报告通常用于评估系统或设备的运行状况与性能,为发现问题、优化系统、提高效率、降低故障率等方面提供参考。

file

本文将详细阐述巡检报告的各项功能特性和其实现方案,为有此类需求的用户提供实用的参考依据。

巡检报告实现功能

● 自定义布局

· 报告中的面板可进行拖拽改变布局

· 在拖拽的过程中限制拖拽区域,只允许在同一父级内进行拖拽,不允许跨目录移动,不允许改变目录的级别,比如把一级目录移动到另一个一级目录内,变成二级目录

● 目录可收缩展开

· 目录支持收缩展开,收缩时隐藏所有子面板,展开时显示所有子面板

· 移动目录时,子面板跟随移动

· 改变目录后,同步更新右侧的目录面板

· 生成目录编号

file

● 右侧目录树

· 生成目录编号

· 支持锚点滚动

· 支持展开收缩

· 与左侧报告联动

file

● 数据面板

· 根据日期范围获取指标数据

· 通过图表的形式展示指标信息

· 查看详情,删除

· 各面板的请求设计,支持刷新请求

file

file

● 面板导入

· 统计目录下选择的面板数量

· 导入新面板时,不能破坏已有布局,新面板只能跟在旧面板后

· 导入已有面板时,需要进行数据比较,有数据变更需要重新获取最新的数据

file

● 保存

在保存前,所有影响布局相关的操作,都是临时的,包括导入面板。只有在点击保存后,才会把当前数据提交给后端进行保存。

● 支持 pdf 和 word 导出

file

巡检报告实现方案

那么,这一套巡检报告功能究竟是如何实现的呢?下面将从数据结构设计组件设计、目录、面板等方面进行逐一介绍。

数据结构设计

先看看使用扁平结构下的图示:

file

在扁平结构下,确定子项只需要找到下一个 row 面板,对于多级目录下也是同理,只是对一级目录需要额外处理。

扁平结构虽然实现起来较为简单,但为了满足特定需求,即限制目录的拖拽。限制目录需要一个比较清晰的面板层级关系,很显然,树状数据结构能够非常贴切且清晰地描述一个数据的层级结构。

file

组件设计

与传统组件编程有所区别。在实现上对渲染和数据处理进行了分离,分为两块:

· React 组件:主要负责页面渲染

· Class : 负责数据的处理

file

DashboardModel

class DashboardModel {
    id: string | number;
    panels: PanelModel[]; // 各个面板
    // ...
}

PanelModel

class PanelModel {
    key?: string;
    id!: number;
    gridPos!: GridPos; // 位置信息
    title?: string;
    type: string;
    panels: PanelModel[]; // 目录面板需要维护当前目录下的面板信息
    // ...
}

每一个 Dashboard 组件对应一个 DashboardModel,每一个 Panel 组件对应一个 PanelModel

React 组建根据类实例中的数据进行渲染。实例生产后,不会轻易的销毁,或者改变引用地址,这让依赖实例数据进行渲染的 React 组件无法触发更新渲染。

需要一个方式,在实例内数据发生改变后,由我们手动触发组件的更新渲染。

● 组件渲染控制

由于我们之前采用的是 Hooks 组件,不像 Class 组件可以通过调用 forceUpdate 方法触发组件。

而在 react18 中有一个新特性 useSyncExternalStore,可以让我们订阅外部的数据,如果数据发生改变了,会触发组件的渲染。

实际上 useSyncExternalStore 触发组件渲染的原理就是在内部维护了一个 state,当更改了 state 值,则引起了外部组件的渲染。

基于这个思路我们简单的实现了一个能够触发组件渲染的 useForceUpdate 方法

export function useForceUpdate() {
    const [_, setValue] = useState(0);
    return debounce(() => setValue((prevState) => prevState + 1), 0);
}

虽说实现了 useForceUpdate,但是在实际使用的过程中,还需要在组件销毁时移除事件。而 useSyncExternalStore 已经内部已经实现了,直接使用即可。

useSyncExternalStore(dashboard?.subscribe ?? (() => {}), dashboard?.getSnapshot ?? (() => 0));

useSyncExternalStore(panel?.subscribe ?? (() => {}), panel?.getSnapshot ?? (() => 0));

根据 useSyncExternalStore 使用,分别添加了 subscribe 和 getSnapshot 方法

class DashboardModel {  // PanelModel 一样 
    count = 0;

    forceUpdate() {
        this.count += 1;
        eventEmitter.emit(this.key);
    }

    /**
     * useSyncExternalStore 的第一个入参,执行 listener 可以触发组件的重渲染
     * @param listener
     * @returns
     */
    subscribe = (listener: () => void) => {
        eventEmitter.on(this.key, listener);
        return () => {
            eventEmitter.off(this.key, listener);
        };
    };

    /**
     * useSyncExternalStore 的第二个入参,count 在这里改变后触发diff的通过。
     * @param listener
     * @returns
     */
    getSnapshot = () => {
        return this.count;
    };
}

当改变数据后,需要触发组件的渲染,只需要执行 forceUpdate 即可。

面板

● 面板拖拽

市面上比较大众的拖拽插件有以下几个:

· react-beautiful-dnd

· react-dnd

· react-grid-layout

经过比较后,发现 react-grid-layout 非常适合用来做面板的拖拽功能。react-grid-layout 本身使用简单,基本无上手门槛,最终决定使用 react-grid-layout。详细说明可以查看该链接:https://github.com/react-grid-layout/react-grid-layout

在面板布局改变后触发 react-grid-layout 的 onLayoutChange 方法,可以拿到布局后的所有面板最新的位置数据。

const onLayoutChange = (newLayout: ReactGridLayout.Layout[]) => {
    for (const newPos of newLayout) {
        panelMap[newPos.i!].updateGridPos(newPos);
    }
    dashboard!.sortPanelsByGridPos();
};

PanelMap 是一个 map,key 为 Panel.key, value 为面板,是在我们组件渲染时就已经准备好了。

const panelMap: Record<PanelModel['key'], PanelModel> = {};

要更新面板布局数据,可通过 PanelMap 准确定位到相应的面板,并进一步调用其 updateGridPos 方法执行布局更新操作。

到这,我们只是完成了面板本身数据更新,还需要执行仪表盘的 sortPanelsByGridPos 方法,对所有的面板进行排序。

class DashboardModel {
    sortPanelsByGridPos() {
        this.panels.sort((panelA, panelB) => {
            if (panelA.gridPos.y === panelB.gridPos.y) {
                return panelA.gridPos.x - panelB.gridPos.x;
            } else {
                return panelA.gridPos.y - panelB.gridPos.y;
            }
        });
    }
    // ...
}

● 面板拖动范围

目前的拖动范围是整个仪表盘,可随意拖动,绿色是仪表盘可拖拽区域,灰色为面板。如下:

file

如果需要限制就需要改成如下图的结构:

file

在原本的基础上,以目录为单位区分,绿色为整体的可移动区域,黄色为一级目录块,可在绿色区域拖动,拖动时以整个黄色块进行拖动,紫色为二级目录块,可在当前黄色区域内拖动,不可脱离当前黄色块,灰色的面板只能在当前目录下拖动。

需要在原先数据结构基础上进行改造:

file

class PanelModel {
    dashboard?: DashboardModel; // 当前目录下的 dashboard
    // ...
}

● 面板的导入设计

file

后端返回的数据是一颗有着三级层级的树,我们拿到后,在数据上维护成 ModuleMap, DashboardMap 和 PanelMap 3个Map。

import { createContext } from 'react';

export interface Module { // 一级目录
    key: string;
    label: string;
    dashboards?: string[];
    sub_module?: Dashboard[];
}

export interface Dashboard { // 二级目录
    key: string;
    dashboard_key: string;
    label: string;
    panels?: number[];
    selectPanels?: number[];
    metrics?: Panel[];
}

export interface Panel {
    expr: Expr[]; // 数据源语句信息
    label: string;
    panel_id: number;
}

type Expr = {
    expr: string;
    legendFormat: string;
};

export const DashboardContext = createContext({
    moduleMap: new Map<string, Module>(),
    dashboardMap: new Map<string, Dashboard>(),
    panelMap: new Map<number, Panel>(),
});

我们在渲染模块时,遍历 ModuleMap ,并通过 Module 内的 dashboards 信息找到二级目录。

在交互上设置一级目录不可选中,当选中二级目录时,通过二级目录 Dashboard 的 panels 找到相关的面板渲染到右侧区域。

对于这3个 Map 的操作,维护在 useHandleData 中,导出:

{
    ...map, // moduleMap、dashboardMap、panelMap
    getData, // 生成巡检报告的数据结构
    init: initData, // 初始化 Map
}

● 面板选中回填

在进入面板管理时,需要回填已选中的面板,我们可以通过 getSaveModel 获取到当前巡检报告的信息,把对应的选中信息存放到 selectPanels 中。

现在我们只需要改变 selectPanels 中的值,就可以做到对应面板的选中。

● 面板选中重置

直接遍历 DashboardMap,并把每个 selectPanels 重置。

dashboardMap.forEach((dashboard) => {
    dashboard.selectPanels = [];
});

● 面板插入

在我们选中面板后,对选中面板进行插入时,有几种情况:

· 巡检报告原本存在的面板,这次也选中,在插入时会比较数据,如果数据发生改变,需要根据最新的数据源信息进行请求,并渲染

· 巡检报告原本存在的面板,这次未选中,在插入时,需要删除掉未选中的面板

· 新选中的面板,在插入时,在对应目录的末尾进行插入

添加新面板需要,与目录收缩类似,不同的是:

· 目录收缩针对的只有一个目录,而插入在针对的是整体

· 目录收缩是直接从子节点开始向上冒泡,而插入是先从根节点开始向下插入,插入完成后在根据最新的目录数据,更新一遍布局

class DashboardModel {
    update(panels: PanelData[]) {
        this.updatePanels(panels); // 更新面板
        this.resetDashboardGridPos(); // 重新布局
        this.forceUpdate();
    }

    /**
     * 以当前与传入的进行对比,以传入的数据为准,并在当前的顺序上进行修改
     * @param panels
     */
    updatePanels(panels: PanelData[]) {
        const panelMap = new Map();
        panels.forEach((panel) => panelMap.set(panel.id, panel));

        this.panels = this.panels.filter((panel) => {
            if (panelMap.has(panel.id)) {
                panel.update(panelMap.get(panel.id));
                panelMap.delete(panel.id);
                return true;
            }
            return false;
        });

        panelMap.forEach((panel) => {
            this.addPanel(panel);
        });
    }

    addPanel(panelData: any) {
        this.panels = [...this.panels, new PanelModel({ ...panelData, top: this })];
    }

    resetDashboardGridPos(panels: PanelModel[] = this.panels) {
        let sumH = 0;
        panels?.forEach((panel: any | PanelModel) => {
            let h = ROW_HEIGHT;
            if (isRowPanel(panel)) {
                h += this.resetDashboardGridPos(panel.dashboard.panels);
            } else {
                h = panel.getHeight();
            }

            const gridPos = {
                ...panel.gridPos,
                y: sumH,
                h,
            };
            panel.updateGridPos({ ...gridPos });
            sumH += h;
        });

        return sumH;
    }
}

class PanelModel {
    /**
     * 更新
     * @param panel
     */
    update(panel: PanelData) {
        // 数据源语句发生变化需要重新获取数据
        if (this.target !== panel.target) {
            this.needRequest = true;
        }

        this.restoreModel(panel);

        if (this.dashboard) {
            this.dashboard.updatePanels(panel.panels ?? []);
        }

        this.needRequest && this.forceUpdate();
    }
}

● 面板请求

needRequest 控制面板是否需要进行请求,如果为 true 在面板下一次进行渲染时,会进行请求,请求的处理也放在了 PanelModel 中。

import { Params, params as fetchParams } from '../../components/useParams';

class PanelModel {
    target: string; // 数据源信息

    getParams() {
        return {
            targets: this.target,
            ...fetchParams,
        } as Params;
    }

    request = () => {
        if (!this.needRequest) return;
        this.fetchData(this.getParams());
    };

    fetchData = async (params: Params) => {
        const data = await this.fetch(params);
        this.data = data;
        this.needRequest = false;
        this.forceUpdate();
    };
    
    fetch = async (params: Params) => { /* ... */ }
}

我们数据渲染组件一般层级较深,而请求时会需要时间区间等外部参数,对于这部分参数采用全局变量的方式,用 useParams 进行维护。上层组件使用 change 修改参数,数据渲染组件根据抛出的 params 进行请求。

export let params: Params = {
    decimal: 1,
    unit: null,
};

function useParams() {
    const change = (next: (() => Params) | Params) => {
        if (typeof next === 'function') params = next();
        params = { ...params, ...next } as Params;
    };

    return { params, change };
}

export default useParams;

● 面板刷新

从根节点向下查找,找到叶子节点,在触发对应的请求。

file

class DashboardModel {
    /**
     * 刷新子面板
     */
    reloadPanels() {
        this.panels.forEach((panel) => {
            panel.reload();
        });
    }
}

class PanelModel {
    /**
     * 刷新
     */
    reload() {
        if (isRowPanel(this)) {
            this.dashboard.reloadPanels();
        } else {
            this.reRequest();
        }
    }

    reRequest() {
        this.needRequest = true;
        this.request();
    }
}

● 面板的删除

对于面板的删除,我们只需要在对应的 Dashboard 下进行移除,删除后会改变当前 Dashboard 高度,这块的处理与下文的目录收缩一致。

class DashboardModel {
    /**
     * @param panel 删除的面板
     */
    removePanel(panel: PanelModel) {
        this.panels = this.filterPanelsByPanels([panel]);

        // 冒泡父容器,减少的高度
        const h = -panel.gridPos.h;
        this.top?.changeHeight(h);

        this.forceUpdate();
    }

    /**
     * 根据传入的面板进行过滤
     * @param panels 需要过滤的面板数组
     * @returns 过滤后的面板
     */
    filterPanelsByPanels(panels: PanelModel[]) {
        return this.panels.filter((panel) => !panels.includes(panel));
    }
    // ...
}

● 面板的保存

与后端沟通后,当前巡检报告数据结构由前端自主维护,最终给后端一个字符串就好。获取到目前的面板数据,用 JSON 进行转换即可。

面板的信息获取过程,先从根节点出发,遍历至叶子结点,再从叶子结点开始,一层层向上进行返回,也就是回溯的过程。

class DashboardModel {
    /**
     * 获取所有面板数据
     * @returns
     */
    getSaveModel() {
        const panels: PanelData[] = this.panels.map((panel) => panel.getSaveModel());
        return panels;
    }
    // ...
}

// 最终保存时所需要的属性,其他的都不需要
const persistedProperties: { [str: string]: boolean } = {
    id: true,
    title: true,
    type: true,
    gridPos: true,
    collapsed: true,
    target: true,
};

class PanelModel {
    /**
     * 获取所有面板数据
     * @returns
     */
    getSaveModel() {
        const model: any = {};

        for (const property in this) {
            if (persistedProperties[property] && this.hasOwnProperty(property)) {
                model[property] = cloneDeep(this[property]);
            }
        }
        model.panels = this.dashboard?.getSaveModel() ?? [];

        return model;
    }
    // ...
}

● 面板详情展示

file

对面板进行查看时,可修改时间等,这些操作会影响到实例中的数据,需要对原数据与详情中的数据进行区分。

通过对原面板数据的重新生成一个 PanelModel 实例,对这个实例进行任意操作,都不会影响到原数据。

const model = panel.getSaveModel();
const newPanel = new PanelModel({ ...model, top: panel.top }); // 创建一个新的实例
setEditPanel(newPanel); // 设置为详情

在 dom 上,详情页面是采用绝对定位,覆盖着巡检报告。

目录

● 目录收缩展开

为目录面板维护一个 collapsed 属性用来控制面板的隐藏显示。

class PanelModel {
    collapsed?: boolean; // type = row
    // ...
}

// 组件渲染
{!collapsed && <DashBoard dashboard={panel.dashboard} serialNumber={serialNumber} />}

对目录进行收缩展开时,会改变自身的高度,现在还需要把这个改变的高度同步给上一级的仪表盘。

上一级需要做的就是类似我们控制目录的处理。如下,控制第一个二级目录收缩

file

当面板发生变更时,需要通知上级面板,进行对应的操作。

file

增加一个 top 用来获取到父级实例

class DashboardModel {
    top?: null | PanelModel; // 最近的 panel 面板

    /**
     * 面板高度变更,同步修改其他面板进行对应高度 Y 轴的变更
     * @param row 变更高度的 row 面板
     * @param h 变更高度
     */
    togglePanelHeight(row: PanelModel, h: number) {
        const rowIndex = this.getIndexById(row.id);

        for (let panelIndex = rowIndex + 1; panelIndex < this.panels.length; panelIndex++) {
            this.panels[panelIndex].gridPos.y += h;
        }
        this.panels = [...this.panels];

        // 顶级 dashBoard 容器没有 top
        this.top?.changeHeight(h);
        this.forceUpdate();
    }
    // ...
}

class PanelModel {
    top: DashboardModel; // 最近的 dashboard 面板

    /**
     * @returns h 展开收起影响的高度
     */
    toggleRow() {
        this.collapsed = !this.collapsed;
        let h = this.dashboard?.getHeight();
        h = this.collapsed ? -h : h;
        this.changeHeight(h);
    }

    /**
     *
     * @param h 变更的高度
     */
    changeHeight(h: number) {
        this.updateGridPos({ ...this.gridPos, h: this.gridPos.h + h }); // 更改自身面板的高度
        this.top.togglePanelHeight(this, h); // 触发父级变更
        this.forceUpdate();
    }
    // ...
}

整理流程与冒泡类型,一直到最顶级的 Dashboard。展开收缩同理。

file

● 右侧目录渲染

锚点/序号

· 锚点采用 Anchor + id 选中组件

· 序号根据每次渲染进行生成

采用发布订阅管理渲染

每当仪表盘改变布局的动作时,右侧目录就需要进行同步更新,而任意一个面板都有可能需要触发右侧目录的更新。

如果我们采用实例内维护对应组件的渲染事件,有两个问题:

· 需要进行区分,比如刷新面板时,不需要触发右侧目录的渲染

· 每个面板如何订阅右侧目录的渲染事件

最终采用了发布订阅者模式,对事件进行管理。

class EventEmitter {
    list: Record<string, any[]> = {};

    /**
     * 订阅
     * @param event 订阅事件
     * @param fn 订阅事件回调
     * @returns
     */
    on(event: string, fn: () => void) {}

    /**
     * 取消订阅
     * @param event 订阅事件
     * @param fn 订阅事件回调
     * @returns
     */
    off(event: string, fn: () => void) {}

    /**
     * 发布
     * @param event 订阅事件
     * @param arg 额外参数
     * @returns
     */
    emit(event: string, ...arg: any[]) {
}
eventEmitter.emit(this.key); // 触发面板的订阅事件

eventEmitter.emit(GLOBAL); // 触发顶级订阅事件,就包括右侧目录的更新

pdf/word 导出

pdf 导出由 html2Canvas + jsPDF 实现。需要注意的是,当图片过长 pdf 会对图片进行切分,有可能出现切分的是内容区域的情况。需要手动计算面板的高度,是否超出当前文档,如果超出需要我们提前进行分割,添加到下一页中,尽可能把目录面板和数据面板一块切分。

word 导出由 html-docx-js 实现, 需要保留目录的结构,并可以在面板下添加总结,这就需要我们分别对每一个面板进行图片的转换。

实现的思路是根据 panels 遍历,找到目录面板就是用 h1、h2 标签插入,如果是数据面板,在数据面板中维护一个 ref 的属性,能让我们拿到当前面板的 dom 信息,根据这个进行图片转换,并为 base64 的格式(word 只支持 base64 的图片插入)。

写在最后

当前版本的巡检报告尚处于初级阶段,并非最终形态,随着后续的迭代升级,我们将逐步添加包括总结说明在内的多项功能。

采用目前方式实现后,未来若需进行 UI 界面调整时,只需针对性地修改相关 UI 组件即可,例如新增饼图、表格等内容。而在数据交互层面的改动,则仅需进入 DashboardModel 和 PanelModel 中进行必要的更新。此外,针对特定场景,我们还可以灵活抽离出专用类来进行处理,以确保整个迭代过程更加模块化和高效化。

《数栈产品白皮书》下载地址:https://www.dtstack.com/resources/1004?src=szsm

《数据治理行业实践白皮书》下载地址:https://www.dtstack.com/resources/1001?src=szsm

想了解或咨询更多有关大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:https://www.dtstack.com/?src=szbky

标签:巡检,面板,无忧,运维,...,PanelModel,panels,目录,panel
From: https://www.cnblogs.com/DTinsight/p/18057165

相关文章

  • MySQL 数据库巡检都有哪些内容
    一套正常运行的系统是一个复杂的系统工程,牵涉到主机、操作系统、网络、数据库、中间件、底层存储,还有系统的核心:应用。任何层面的故障都可能造成系统的不可用。今天聊一聊数据库层面的巡检问题。数据库巡检的目的:保障数据库的正常运行,保证数据的安全性,完整性、可靠性。这篇文章......
  • 图扑数字孪生水电站,水力发电可视化运维
    自水轮机的早期发明被用于农业灌溉,到18世纪末期的工业革命促使水轮机技术的改良,再到19世纪末水利发电的崛起,直至今日,智慧水电站数字孪生技术正处于蓬勃发展之中。通过整合物联网、大数据、云计算等现代信息技术,水电站数字孪生可用于设计、建造、运行和维护等各个环节。能实时......
  • 中国联通全球托管运维服务:助力企业无忧应对数据中心运维挑战
    在全球化背景下,企业的信息化进程不断加快,数据中心作为支撑关键业务的核心基础设施,在全球范围内的布局与运维变得愈发重要。然而,企业在设立异地或海外数据中心时,常常面临资源有限、人力短缺等问题,特别是在目标地缺乏专业的IT工程师团队时,如何确保数据中心的稳定运行与高效管理成为......
  • SQL server数据库相关运维脚本
    SQLserver数据库相关运维脚本创建数据库USEmaster;goDECLARE@database_nameNVARCHAR(50);DECLARE@data_fileNVARCHAR(50);DECLARE@sqlNVARCHAR(MAX);--数据库名SET@database_name='数据库名';--数据文件存放路径,该路径必须存在。--路径中不允许使用空格等字......
  • 运维一款月变更70+次的服务,是一种什么体验?
    应用平台AppStage运维中心为华为D服务提供的全链路监控,支持多种日志接入,可支撑业务核心场景实现“1分钟感知,20分钟定界,30分钟解决”,使变更效率提升90%,业务可用性保持99.95%!本文分享自华为云社区《运维一款月变更70+次的服务,是一种什么体验?》,作者:开天aPaaS小助手。面对一款业务......
  • 7. LCD1602自动巡检方式显示16路温度数据的编程实现
    在多路温度数据,多路压力数据或者多路其它数据采集的过程中,我们借用一个屏幕来显示的时候,一般选用两种模式:自动巡检模式:上电的时候默认的就是这种模式,上节课使用了4个菜单,也就是4个屏,每个屏采集了4路数据,这样的话就相当于让其处于自动巡检的模式,每隔一定的时间来切换一个界面,从而......
  • 安全运维:cmd命令大全(108个)
    1、calc:启动计算器2、appwiz.cpl:程序和功能3、certmgr.msc:证书管理实用程序4、charmap:启动字符映射表5、chkdsk.exe:Chkdsk磁盘检查(管理员身份运行命令提示符)6、cleanmgr:打开磁盘清理工具7、cliconfg:SQLSERVER客户端网络实用工具8、cmstp:连接管理器配置文件安装程序9......
  • VMware Aria Operations 8.16 - 多云 IT 运维管理
    VMwareAriaOperations8.16-多云IT运维管理通过统一的高性能平台,实现跨私有云、混合云和多云环境的IT运维管理。请访问原文链接:https://sysin.org/blog/vmware-aria-operations/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org自动驾驶式IT运维管理VMwar......
  • 【调研】运维监控平台文章资源收集
    https://www.zabbix.com/OpManager智能网络监控软件卓豪https://www.manageengine.cn/network-monitoring/ZABBIX实时监控数万台设备,采集百万级指标http://grandage.cn/?bd_vid=9690304849173985139Prometheus普罗米修斯是一个开源的服务监控系统和时序数据库,其提供了通用......
  • 运维工作新时代:自主编码实现运维自动化的转型之旅
    引言随着业务系统和底层中间件服务的复杂度不断增加,传统手工运维方式面临着诸多挑战和限制。人工编写运维脚本显得非常低效,同时手动执行运维操作存在着巨大风险。在此情况下,推动运维自动化成为运维人员必须落地实施的工作。运维同学如果可以有地方自主通过编码的方式,实现各种自动......