feat: add alert panels for NAS, Ingress, Istio charts
CI/CD / build-and-push (push) Successful in 1m26s
Details
CI/CD / build-and-push (push) Successful in 1m26s
Details
- NAS: 80% warning, 90% critical (same as disk) - Ingress/Istio: Z-score peak detection for traffic spikes - All placed next to their corresponding charts with scroll Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2375e886e7
commit
9462f39f81
|
|
@ -10,6 +10,7 @@ import NodeTable from '@/components/report/NodeTable';
|
|||
import NasTable from '@/components/report/NasTable';
|
||||
import NetworkTable from '@/components/report/NetworkTable';
|
||||
import AlertPanel from '@/components/report/AlertPanel';
|
||||
import NetworkAlertPanel from '@/components/report/NetworkAlertPanel';
|
||||
import ErrorBoundary from '@/components/ErrorBoundary';
|
||||
|
||||
function getDefaultRange() {
|
||||
|
|
@ -172,18 +173,33 @@ export default function Home() {
|
|||
</div>
|
||||
|
||||
{data.metrics.nas && data.metrics.nas.length > 0 && (
|
||||
<ErrorBoundary fallback="NAS 차트 오류">
|
||||
<TimeSeriesChart title="NAS 사용률" instances={data.metrics.nas} unit="%" />
|
||||
</ErrorBoundary>
|
||||
<div className="grid grid-cols-[1fr_280px] gap-4">
|
||||
<ErrorBoundary fallback="NAS 차트 오류">
|
||||
<TimeSeriesChart title="NAS 사용률" instances={data.metrics.nas} unit="%" />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary fallback="NAS 주의 구간 오류">
|
||||
<AlertPanel instances={data.metrics.nas} type="nas" height={300} />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ErrorBoundary fallback="Ingress Network 차트 오류">
|
||||
<NetworkChart title="Ingress Network Traffic" data={data.metrics.ingress} />
|
||||
</ErrorBoundary>
|
||||
<div className="grid grid-cols-[1fr_280px] gap-4">
|
||||
<ErrorBoundary fallback="Ingress Network 차트 오류">
|
||||
<NetworkChart title="Ingress Network Traffic" data={data.metrics.ingress} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary fallback="Ingress 주의 구간 오류">
|
||||
<NetworkAlertPanel data={data.metrics.ingress} height={300} />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
|
||||
<ErrorBoundary fallback="Istio Network 차트 오류">
|
||||
<NetworkChart title="Istio Network Traffic" data={data.metrics.istio} />
|
||||
</ErrorBoundary>
|
||||
<div className="grid grid-cols-[1fr_280px] gap-4">
|
||||
<ErrorBoundary fallback="Istio Network 차트 오류">
|
||||
<NetworkChart title="Istio Network Traffic" data={data.metrics.istio} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary fallback="Istio 주의 구간 오류">
|
||||
<NetworkAlertPanel data={data.metrics.istio} height={300} />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
|
||||
<ErrorBoundary fallback="NodeTable 오류">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { formatTimestamp, formatPercent } from '@/lib/formatters';
|
|||
|
||||
interface AlertPanelProps {
|
||||
instances: MetricInstance[];
|
||||
type: 'cpu' | 'memory' | 'disk';
|
||||
type: 'cpu' | 'memory' | 'disk' | 'nas';
|
||||
height?: number;
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ function generateAlerts(instances: MetricInstance[], type: string): Alert[] {
|
|||
message: `${formatTimestamp(peak.startTime)} ~ ${formatTimestamp(peak.endTime)} Peak (max: ${formatPercent(peak.maxValue)})`,
|
||||
});
|
||||
});
|
||||
} else if (type === 'disk') {
|
||||
} else if (type === 'disk' || type === 'nas') {
|
||||
if (inst.stats.max > 80) {
|
||||
alerts.push({
|
||||
level: inst.stats.max > 90 ? 'critical' : 'warning',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
'use client';
|
||||
|
||||
import type { NetworkMetrics } from '@/types/metrics';
|
||||
import { formatTimestamp, formatBytesPerSec } from '@/lib/formatters';
|
||||
|
||||
interface NetworkAlertPanelProps {
|
||||
data: NetworkMetrics;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
interface Alert {
|
||||
level: 'warning' | 'critical';
|
||||
instance: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export default function NetworkAlertPanel({ data, height = 300 }: NetworkAlertPanelProps) {
|
||||
const alerts: Alert[] = [];
|
||||
|
||||
[...data.receive, ...data.transmit].forEach((inst) => {
|
||||
const direction = data.receive.includes(inst) ? 'RX' : 'TX';
|
||||
const label = `${direction} ${inst.instance || 'total'}`;
|
||||
|
||||
inst.peaks.peaks.forEach((peak) => {
|
||||
alerts.push({
|
||||
level: 'warning',
|
||||
instance: label,
|
||||
message: `${formatTimestamp(peak.startTime)} ~ ${formatTimestamp(peak.endTime)} 트래픽 급증 (max: ${formatBytesPerSec(peak.maxValue)})`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (alerts.length === 0) {
|
||||
return (
|
||||
<div className="bg-green-50 border border-green-200 rounded-lg p-3 flex items-center justify-center" style={{ height }}>
|
||||
<p className="text-green-700 text-xs">특이사항 없음</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white border rounded-lg flex flex-col" style={{ height }}>
|
||||
<div className="px-3 py-2 border-b bg-gray-50 flex-shrink-0">
|
||||
<h4 className="text-xs font-medium text-gray-600">주의 구간 ({alerts.length}건)</h4>
|
||||
</div>
|
||||
<ul className="divide-y divide-gray-100 overflow-y-auto flex-1">
|
||||
{alerts.map((alert, idx) => (
|
||||
<li key={idx} className="px-3 py-2 flex items-start gap-2">
|
||||
<span className="mt-1 inline-block w-1.5 h-1.5 rounded-full flex-shrink-0 bg-yellow-500" />
|
||||
<div className="text-xs">
|
||||
<span className="font-medium text-gray-800">{alert.instance}</span>
|
||||
<p className="text-gray-500">{alert.message}</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue