服务器运维(三十九)日服务器mysql错误日志分析工具—东方仙盟

日志分析

核心代码

科技修仙・洞察 MySQL 错误日志异常玄机 ------ 日志分析的安全价值与实践必要性

在数字化修仙的时代,MySQL 作为数据存储的核心法器,其运行状态直接关乎整个业务仙盟的稳定与安全。而 MySQL 错误日志,正是洞察数据库运行异常、捕捉安全威胁的 "照妖镜"------ 忽视它,就如同修仙者闭目修炼,极易被隐藏的安全妖邪趁虚而入;善用它,则能提前感知风险、快速定位问题,筑牢数据安全的仙法屏障。

一、为何 MySQL 错误日志分析是安全防护的 "必修课"?

MySQL 错误日志记录了数据库运行全过程中的告警、异常、错误等核心信息,从配置异常到访问攻击,从文件缺失到恶意扫描,所有 "异常玄机" 都会在此留下痕迹。对其进行常态化分析,是数据库安全防护中不可省略的关键环节,核心必要性体现在三点:

1. 提前感知隐蔽的安全威胁

很多数据库攻击并非 "明火执仗",而是以 "试探性扫描""伪装访问" 的形式潜伏 ------ 比如陌生 IP 的 DNS 解析失败警告、IP 反向解析异常、主机名不匹配等日志条目,看似是普通警告,实则是黑客探测数据库漏洞的前兆。若能通过日志分析及时捕捉这类低危异常,就能在攻击升级前封堵入口,避免从 "试探" 演变为 "入侵"。

2. 快速定位安全事故根源

当数据库出现访问异常、数据读取失败甚至服务中断时,错误日志是最直接的 "破案线索":

  • 若日志中频繁出现 "二进制日志文件缺失",可快速定位是系统文件被篡改或删除,属于高危系统文件异常;
  • 若发现 "RSA 密钥文件缺失",能立刻判断是认证配置被破坏,需紧急修复密钥配置;
  • 若大量陌生 IP 触发 "DNS 解析失败",则可确定是批量扫描攻击,需及时封禁恶意 IP。没有日志分析,排查这类问题往往需要逐行核对配置、遍历网络记录,耗时耗力且易遗漏关键线索。

3. 构建常态化安全监控体系

数据库安全并非 "一次性加固" 就能一劳永逸,而是需要持续监控。通过对错误日志的定期分析,可建立安全基线:统计异常类型分布、攻击 IP 数量、高危事件频次,形成数据库安全的 "健康档案"。比如某 IP 短期内多次触发低危异常,可预判其恶意倾向;若高危异常(如密钥缺失、日志文件损坏)从无到有,需立刻启动应急响应,避免安全防线崩溃。

二、MySQL 错误日志分析的核心价值:从 "被动救火" 到 "主动防御"

1. 缩短安全事故处置时间

传统的数据库问题排查,往往是 "出问题再找原因",耗时数小时甚至数天;而基于错误日志的精准分析,可直接通过 "异常类型 + 攻击 IP + 时间维度" 的三维定位,将问题排查时间从 "小时级" 压缩到 "分钟级"。比如通过日志分析工具,能一键统计攻击 IP 的出现次数、主要异常类型,快速锁定恶意来源,无需人工逐行筛选日志。

2. 分类管控不同等级风险

MySQL 错误日志中的异常并非 "一视同仁",通过分析可按危险等级(高危 / 中危 / 低危 / 信息)分类处置:

  • 高危异常(如二进制日志缺失、密钥文件丢失):需立即处理,避免数据库服务瘫痪或认证体系失效;
  • 中危异常(如未知系统错误):需限期排查,防止演变为高危问题;
  • 低危异常(如扫描类警告):需汇总分析,批量封禁恶意 IP 或优化访问策略;
  • 信息级日志:作为基线参考,辅助判断数据库正常运行状态。这种分级管控,让安全资源聚焦在最关键的风险点,避免 "一刀切" 式的防护导致资源浪费。

3. 追溯安全事件全链路

错误日志记录了异常事件的时间、来源 IP、具体内容,是安全事件溯源的核心依据。当发生数据泄露、未授权访问等事故时,可通过日志回溯:

  • 首次异常出现的时间,判断攻击发起节点;
  • 攻击 IP 的访问轨迹,定位恶意来源;
  • 异常类型的演变,分析黑客的攻击路径(如从扫描到尝试破解,再到篡改文件)。完整的溯源能力,不仅能快速止损,还能为后续加固策略提供精准依据。

完整代码

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>东方仙盟服务器日志分析工具之mysql错误日志</title>
   
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>东方仙盟服务器日志分析工具之mysql错误日志</h1>
            <p class="subtitle">科技修仙 · 洞察MySQL错误日志异常玄机</p>
        </div>
        
        <div class="log-input">
            <h2 style="margin-bottom: 10px;"><span>fairyalli_strico_paper</span> 请输入MySQL错误日志内容</h2>
            <textarea id="logInput" placeholder="请粘贴MySQL错误日志内容..." style="height:200px;"></textarea>
            <button id="analyzeBtn"><span>fairyalli_strico_search</span>开始分析日志</button>
            <button id="clearBtn" class="clear-btn"><span>fairyalli_strico_垃圾桶</span>清空内容</button>
        </div>
        
        <div class="results">
            <!-- 基础统计卡片 -->
            <div class="stat-card">
                <h2><span>fairyalli_strico_stat</span>日志统计概览</h2>
                <div class="stats-grid">
                    <div class="stat-item">
                        <p class="stat-label">总日志条目数</p>
                        <p id="totalLogs" class="stat-value">0</p>
                    </div>
                    <div class="stat-item">
                        <p class="stat-label">异常类型数</p>
                        <p id="errorTypeCount" class="stat-value">0</p>
                    </div>
                    <div class="stat-item">
                        <p class="stat-label">攻击IP数</p>
                        <p id="attackIpCount" class="stat-value">0</p>
                    </div>
                    <div class="stat-item">
                        <p class="stat-label">高危异常数</p>
                        <p id="highRiskCount" class="stat-value">0</p>
                    </div>
                </div>
                <p class="time-range" id="timeRange">时间范围:-- -- --</p>
            </div>
            
            <!-- 异常分析卡片 -->
            <div class="stat-card">
                <h2><span>fairyalli_strico_search</span> 异常分析结果 (按危险等级排序)</h2>
                <table id="errorAnalysisTable">
                    <thead>
                        <tr>
                            <th>异常类型</th>
                            <th>攻击类型</th>
                            <th>出现次数</th>
                            <th>危险等级</th>
                            <th>典型IP/主机</th>
                            <th>首次出现时间</th>
                        </tr>
                    </thead>
                    <tbody id="errorAnalysisBody">
                        <tr>
                            <td colspan="6" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
                        </tr>
                    </tbody>
                </table>
            </div>
            
            <!-- IP攻击统计 -->
            <div class="stat-card">
                <h2><span>fairyalli_strico_ok</span>攻击IP统计</h2>
                <table id="ipStatsTable">
                    <thead>
                        <tr>
                            <th>攻击IP/主机名</th>
                            <th>出现次数</th>
                            <th>主要异常类型</th>
                            <th>危险等级</th>
                            <th>首次出现时间</th>
                        </tr>
                    </thead>
                    <tbody id="ipStatsBody">
                        <tr>
                            <td colspan="5" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
                        </tr>
                    </tbody>
                </table>
            </div>
            
            <!-- 详细日志列表 -->
            <div class="stat-card">
                <h2><span>fairyalli_strico_notepenh</span>所有日志详细列表</h2>
                <table id="detailTable">
                    <thead>
                        <tr>
                            <th>时间</th>
                            <th>异常类型</th>
                            <th>攻击IP/主机</th>
                            <th>危险等级</th>
                            <th>日志内容</th>
                        </tr>
                    </thead>
                    <tbody id="detailBody">
                        <tr>
                            <td colspan="5" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>

    <script>
        // 获取DOM元素
        const logInput = document.getElementById('logInput');
        const analyzeBtn = document.getElementById('analyzeBtn');
        const clearBtn = document.getElementById('clearBtn');
        const totalLogs = document.getElementById('totalLogs');
        const errorTypeCount = document.getElementById('errorTypeCount');
        const attackIpCount = document.getElementById('attackIpCount');
        const highRiskCount = document.getElementById('highRiskCount');
        const timeRange = document.getElementById('timeRange');
        const errorAnalysisBody = document.getElementById('errorAnalysisBody');
        const ipStatsBody = document.getElementById('ipStatsBody');
        const detailBody = document.getElementById('detailBody');

        // 解析MySQL错误日志的函数
        function parseMysqlErrorLog(logContent) {
            const logEntries = [];
            const lines = logContent.split('\n').map(line => line.trim()).filter(line => line);
            
            // 定义正则表达式
            const logLineRegex = /^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+(\d+)\s+\[(ERROR|WARNING|NOTE)\]\s+(.*)$/;
            const ipRegex = /IP address\s+'([\d\.]+)'|Host name\s+'([^\']+)'|Hostname\s+'([^\']+)'/;
            const binlogErrorRegex = /Failed to open log \(file\s+'[^']+', errno 2\)|Could not open log file|File\s+'[^']+' not found/;
            const resolveErrorRegex = /could not be resolved: Name or service not known/;
            const ipResolveWarningRegex = /has been resolved to the host name\s+'[^']+', which resembles IPv4-address itself/;
            const hostnameMismatchRegex = /does not resolve to/;
            
            for (const line of lines) {
                const match = line.match(logLineRegex);
                if (match) {
                    const [, logTime, pid, logLevel, message] = match;
                    let ip = '';
                    let hostname = '';
                    let errorType = '';
                    let attackType = '';
                    let riskLevel = 'info';
                    
                    // 提取IP或主机名
                    const ipMatch = message.match(ipRegex);
                    if (ipMatch) {
                        ip = ipMatch[1] || '';
                        hostname = ipMatch[2] || ipMatch[3] || '';
                    }
                    
                    // 分类异常类型
                    if (logLevel === 'ERROR') {
                        if (binlogErrorRegex.test(message)) {
                            errorType = '二进制日志文件缺失';
                            attackType = '系统文件异常';
                            riskLevel = 'high'; // 高危
                        } else if (message.includes('RSA private key file not found') || message.includes('RSA public key file not found')) {
                            errorType = 'RSA密钥文件缺失';
                            attackType = '认证配置异常';
                            riskLevel = 'medium'; // 中危
                        } else {
                            errorType = '未知错误';
                            attackType = '系统错误';
                            riskLevel = 'medium'; // 中危
                        }
                    } else if (logLevel === 'WARNING') {
                        if (resolveErrorRegex.test(message)) {
                            errorType = 'DNS解析失败';
                            attackType = '可疑IP访问/扫描';
                            riskLevel = 'low'; // 低危
                        } else if (ipResolveWarningRegex.test(message)) {
                            errorType = 'IP反向解析异常';
                            attackType = '扫描器探测';
                            riskLevel = 'low'; // 低危
                        } else if (hostnameMismatchRegex.test(message)) {
                            errorType = '主机名解析不匹配';
                            attackType = '域名欺诈/扫描';
                            riskLevel = 'low'; // 低危
                        } else {
                            errorType = '未知警告';
                            attackType = '可疑访问';
                            riskLevel = 'low'; // 低危
                        }
                    } else { // NOTE
                        errorType = '系统信息';
                        attackType = '系统日志';
                        riskLevel = 'info'; // 信息级
                    }
                    
                    logEntries.push({
                        time: logTime,
                        pid: pid,
                        level: logLevel,
                        message: message,
                        ip: ip,
                        hostname: hostname,
                        errorType: errorType,
                        attackType: attackType,
                        riskLevel: riskLevel,
                        riskLevelText: {
                            'high': '高危',
                            'medium': '中危',
                            'low': '低危',
                            'info': '信息'
                        }[riskLevel]
                    });
                }
            }
            
            return logEntries;
        }

        // 分析日志的主函数
        function analyzeLogs() {
            // 清空之前的结果
            errorAnalysisBody.innerHTML = '';
            ipStatsBody.innerHTML = '';
            detailBody.innerHTML = '';
            
            // 获取日志内容
            const logContent = logInput.value;
            if (!logContent.trim()) {
                // 清空统计数据
                totalLogs.textContent = '0';
                errorTypeCount.textContent = '0';
                attackIpCount.textContent = '0';
                highRiskCount.textContent = '0';
                timeRange.textContent = '时间范围:暂无数据';
                
                errorAnalysisBody.innerHTML = `
                    <tr>
                        <td colspan="6" style="text-align: center; color: #999;">暂无日志数据</td>
                    </tr>
                `;
                ipStatsBody.innerHTML = `
                    <tr>
                        <td colspan="5" style="text-align: center; color: #999;">暂无日志数据</td>
                    </tr>
                `;
                detailBody.innerHTML = `
                    <tr>
                        <td colspan="5" style="text-align: center; color: #999;">暂无日志数据</td>
                    </tr>
                `;
                return;
            }
            
            // 解析日志
            const logEntries = parseMysqlErrorLog(logContent);
            const totalCount = logEntries.length;
            
            // 更新基础统计
            totalLogs.textContent = totalCount;
            
            if (totalCount === 0) {
                errorTypeCount.textContent = '0';
                attackIpCount.textContent = '0';
                highRiskCount.textContent = '0';
                timeRange.textContent = '时间范围:日志解析失败,请检查格式';
                
                errorAnalysisBody.innerHTML = `
                    <tr>
                        <td colspan="6" style="text-align: center; color: #ff6600;">日志解析失败,请检查MySQL错误日志格式</td>
                    </tr>
                `;
                ipStatsBody.innerHTML = `
                    <tr>
                        <td colspan="5" style="text-align: center; color: #ff6600;">日志解析失败,请检查MySQL错误日志格式</td>
                    </tr>
                `;
                detailBody.innerHTML = `
                    <tr>
                        <td colspan="5" style="text-align: center; color: #ff6600;">日志解析失败,请检查MySQL错误日志格式</td>
                    </tr>
                `;
                return;
            }
            
            // 统计高危异常数
            const highRiskEntries = logEntries.filter(entry => entry.riskLevel === 'high');
            highRiskCount.textContent = highRiskEntries.length;
            
            // 按异常类型分组统计
            const errorTypeStats = {};
            logEntries.forEach(entry => {
                const key = entry.errorType;
                if (!errorTypeStats[key]) {
                    errorTypeStats[key] = {
                        count: 0,
                        attackType: entry.attackType,
                        riskLevel: entry.riskLevel,
                        riskLevelText: entry.riskLevelText,
                        ips: new Set(),
                        firstOccurrence: entry.time,
                        entries: []
                    };
                }
                errorTypeStats[key].count++;
                errorTypeStats[key].entries.push(entry);
                if (entry.ip) {
                    errorTypeStats[key].ips.add(entry.ip);
                }
                // 记录首次出现时间
                if (entry.time < errorTypeStats[key].firstOccurrence) {
                    errorTypeStats[key].firstOccurrence = entry.time;
                }
            });
            
            // 按IP分组统计
            const ipStats = {};
            logEntries.forEach(entry => {
                const ipOrHost = entry.ip || entry.hostname || '未知';
                if (ipOrHost === '未知') return;
                
                if (!ipStats[ipOrHost]) {
                    ipStats[ipOrHost] = {
                        count: 0,
                        errorTypes: new Set(),
                        riskLevel: entry.riskLevel,
                        riskLevelText: entry.riskLevelText,
                        firstOccurrence: entry.time
                    };
                }
                ipStats[ipOrHost].count++;
                ipStats[ipOrHost].errorTypes.add(entry.errorType);
                // 更新最高风险等级
                const riskOrder = { 'high': 3, 'medium': 2, 'low': 1, 'info': 0 };
                if (riskOrder[entry.riskLevel] > riskOrder[ipStats[ipOrHost].riskLevel]) {
                    ipStats[ipOrHost].riskLevel = entry.riskLevel;
                    ipStats[ipOrHost].riskLevelText = entry.riskLevelText;
                }
                // 记录首次出现时间
                if (entry.time < ipStats[ipOrHost].firstOccurrence) {
                    ipStats[ipOrHost].firstOccurrence = entry.time;
                }
            });
            
            // 更新统计数据
            errorTypeCount.textContent = Object.keys(errorTypeStats).length;
            attackIpCount.textContent = Object.keys(ipStats).length;
            
            // 计算时间范围
            const allTimes = logEntries.map(entry => entry.time);
            if (allTimes.length > 0) {
                const minTime = new Date(Math.min(...allTimes.map(t => new Date(t).getTime())));
                const maxTime = new Date(Math.max(...allTimes.map(t => new Date(t).getTime())));
                timeRange.textContent = `时间范围:${minTime.toLocaleString()} 至 ${maxTime.toLocaleString()}`;
            } else {
                timeRange.textContent = '时间范围:无法解析';
            }
            
            // 生成异常分析表格(按危险等级和出现次数排序)
            const riskOrder = { 'high': 3, 'medium': 2, 'low': 1, 'info': 0 };
            const sortedErrorTypes = Object.entries(errorTypeStats)
                .map(([errorType, stats]) => ({
                    errorType,
                    ...stats,
                    ipsArray: Array.from(stats.ips).slice(0, 3) // 取前3个IP
                }))
                .sort((a, b) => {
                    // 先按危险等级排序,再按出现次数降序
                    const riskDiff = riskOrder[b.riskLevel] - riskOrder[a.riskLevel];
                    return riskDiff !== 0 ? riskDiff : b.count - a.count;
                });
            
            let errorAnalysisHtml = '';
            sortedErrorTypes.forEach(item => {
                const levelClass = `level-${item.riskLevel}`;
                errorAnalysisHtml += `
                    <tr>
                        <td class="type-cell">${item.errorType}</td>
                        <td class="type-cell">${item.attackType}</td>
                        <td class="count-cell">${item.count}</td>
                        <td class="level-cell ${levelClass}">${item.riskLevelText}</td>
                        <td class="ip-cell">${item.ipsArray.join(', ') || '无'}</td>
                        <td class="time-cell">${item.firstOccurrence}</td>
                    </tr>
                `;
            });
            errorAnalysisBody.innerHTML = errorAnalysisHtml;
            
            // 生成IP攻击统计表格
            const sortedIpStats = Object.entries(ipStats)
                .map(([ip, stats]) => ({
                    ip,
                    ...stats,
                    errorTypesArray: Array.from(stats.errorTypes).join(', ')
                }))
                .sort((a, b) => {
                    // 先按危险等级排序,再按出现次数降序
                    const riskDiff = riskOrder[b.riskLevel] - riskOrder[a.riskLevel];
                    return riskDiff !== 0 ? riskDiff : b.count - a.count;
                });
            
            let ipStatsHtml = '';
            sortedIpStats.forEach(item => {
                const levelClass = `level-${item.riskLevel}`;
                ipStatsHtml += `
                    <tr>
                        <td class="ip-cell">${item.ip}</td>
                        <td class="count-cell">${item.count}</td>
                        <td class="type-cell">${item.errorTypesArray}</td>
                        <td class="level-cell ${levelClass}">${item.riskLevelText}</td>
                        <td class="time-cell">${item.firstOccurrence}</td>
                    </tr>
                `;
            });
            ipStatsBody.innerHTML = ipStatsHtml;
            
            // 生成详细日志列表
            let detailHtml = '';
            logEntries.sort((a, b) => new Date(b.time) - new Date(a.time)).forEach(entry => {
                const levelClass = `level-${entry.riskLevel}`;
                detailHtml += `
                    <tr>
                        <td class="time-cell">${entry.time}</td>
                        <td class="type-cell">${entry.errorType}</td>
                        <td class="ip-cell">${entry.ip || entry.hostname || '无'}</td>
                        <td class="level-cell ${levelClass}">${entry.riskLevelText}</td>
                        <td class="log-message">${entry.message}</td>
                    </tr>
                `;
            });
            detailBody.innerHTML = detailHtml;
        }

        // 清空内容
        function clearContent() {
            logInput.value = '';
            totalLogs.textContent = '0';
            errorTypeCount.textContent = '0';
            attackIpCount.textContent = '0';
            highRiskCount.textContent = '0';
            timeRange.textContent = '时间范围:-- -- --';
            
            errorAnalysisBody.innerHTML = `
                <tr>
                    <td colspan="6" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
                </tr>
            `;
            ipStatsBody.innerHTML = `
                <tr>
                    <td colspan="5" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
                </tr>
            `;
            detailBody.innerHTML = `
                <tr>
                    <td colspan="5" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
                </tr>
            `;
        }

        // 绑定事件
        analyzeBtn.addEventListener('click', analyzeLogs);
        clearBtn.addEventListener('click', clearContent);
        
        // 支持按Ctrl+Enter快速分析
        logInput.addEventListener('keypress', function(e) {
            if (e.key === 'Enter' && e.ctrlKey) {
                analyzeLogs();
            }
        });
    </script>
	<script>
 const 东方仙盟图标替换 = new 东方仙盟_前端渲染_图标替换();
东方仙盟图标替换.替换整页(); 
</script>
</body>
</html>

三、实战启示:让日志分析成为数据库安全的 "第一道防线"

对于企业级数据库运维而言,手动翻阅海量日志既低效又易遗漏,如同用凡眼寻找微尘中的妖邪。借助自动化的 MySQL 错误日志分析工具(如 "东方仙盟服务器日志分析工具"),可实现:

  1. 一键解析日志内容,自动统计总条目数、异常类型数、攻击 IP 数、高危异常数;
  2. 按危险等级排序展示异常分析结果,快速聚焦核心风险;
  3. 汇总攻击 IP 的访问行为,批量识别恶意来源;
  4. 展示全量日志详情,支持时间维度追溯。

这种智能化分析,让数据库运维从 "被动响应" 转向 "主动预判",真正将错误日志转化为安全防护的 "仙法利器"。

总结

MySQL 错误日志并非简单的 "运行记录",而是数据库安全的 "预警雷达" 与 "破案卷宗"。常态化的日志分析,既是及时发现安全威胁的必要手段,也是快速定位问题、追溯攻击轨迹的核心支撑。在数据安全日益重要的今天,忽视错误日志分析,就等于放弃了数据库安全的第一道防线;唯有善用日志分析工具,洞察每一条异常背后的 "玄机",才能让 MySQL 数据库在数字化修仙之路上,始终保持稳定、安全的运行状态,守护业务仙盟的核心数据资产。

阿雪技术观

在科技发展浪潮中,我们不妨积极投身技术共享。不满足于做受益者,更要主动担当贡献者。无论是分享代码、撰写技术博客,还是参与开源项目维护改进,每一个微小举动都可能蕴含推动技术进步的巨大能量。东方仙盟是汇聚力量的天地,我们携手在此探索硅基生命,为科技进步添砖加瓦。

Hey folks, in this wild tech - driven world, why not dive headfirst into the whole tech - sharing scene? Don't just be the one reaping all the benefits; step up and be a contributor too. Whether you're tossing out your code snippets, hammering out some tech blogs, or getting your hands dirty with maintaining and sprucing up open - source projects, every little thing you do might just end up being a massive force that pushes tech forward. And guess what? The Eastern FairyAlliance is this awesome place where we all come together. We're gonna team up

相关推荐
信创DevOps先锋1 小时前
中国DevOps工具链选型新趋势:合规、协同与生态融合的平衡之道
运维·gitee·devops
Magic--1 小时前
深入解析管道:最基础的进程间通信(IPC)实现
java·服务器·unix
白鸽梦游指南2 小时前
docker镜像优化
linux·运维·docker
陳10302 小时前
Linux:基础开发工具
linux·运维·服务器
我爱学习好爱好爱2 小时前
Ansible 常用模块详解:cron、archive、unarchive实战
linux·服务器·ansible
IT界的老黄牛3 小时前
Prometheus + Grafana + AlertManager 监控体系搭建:Docker 一把梭
运维·grafana·prometheus
乌恩大侠3 小时前
【KrakenSDR】MATLAB接口
服务器·网络·matlab
阿干tkl3 小时前
K3s + Harbor 端口冲突问题解决方案(Harbor 使用 80 端口)
运维
qq_339191143 小时前
uv 设置系统默认版本, linux设置uv
linux·运维·uv
小猿姐3 小时前
当KubeBlocks遇上国产数据库之Kingbase:让信创数据库“飞得更高”
运维·数据库·云原生