FEAT(app): add Grafana integration
- Add Grafana dashboard link - Enable monitoring access
This commit is contained in:
@@ -1,221 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import SectionHeader from "@/components/landing/section-header";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
type Section = "overview" | "resources" | "kubernetes" | "network";
|
||||
|
||||
const sections: { id: Section; label: string; panels: number }[] = [
|
||||
{ id: "overview", label: "Overview", panels: 8 },
|
||||
{ id: "resources", label: "Resources", panels: 8 },
|
||||
{ id: "kubernetes", label: "Kubernetes", panels: 4 },
|
||||
{ id: "network", label: "Network", panels: 6 },
|
||||
];
|
||||
|
||||
export default function MonitoringPage() {
|
||||
const [activeSection, setActiveSection] = useState<Section>("overview");
|
||||
|
||||
return (
|
||||
<main className="page-container">
|
||||
<SectionHeader
|
||||
title="Monitoring"
|
||||
description="Real-time Kubernetes cluster monitoring with Grafana dashboards"
|
||||
/>
|
||||
|
||||
{/* 섹션 선택 버튼 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{sections.map((section) => (
|
||||
<Button
|
||||
key={section.id}
|
||||
onClick={() => setActiveSection(section.id)}
|
||||
variant={activeSection === section.id ? "default" : "outline"}
|
||||
className="transition-all"
|
||||
>
|
||||
{section.label}
|
||||
<span className="ml-2 text-sm opacity-60">
|
||||
({section.panels} panels)
|
||||
</span>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Overview 섹션 */}
|
||||
{activeSection === "overview" && (
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{/* 왼쪽 2개 열 (CPU, RAM) */}
|
||||
<div className="flex gap-2">
|
||||
{/* Global CPU Usage 열 */}
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Global CPU Usage (id:77) - w:6, h:8 */}
|
||||
<div className="w-[400px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=77 */}
|
||||
</div>
|
||||
{/* CPU Usage (id:37) - w:6, h:4 */}
|
||||
<div className="w-[400px] h-[160px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=37 */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Global RAM Usage 열 */}
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Global RAM Usage (id:78) - w:6, h:8 */}
|
||||
<div className="w-[400px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=78 */}
|
||||
</div>
|
||||
{/* RAM Usage (id:39) - w:6, h:4 */}
|
||||
<div className="w-[400px] h-[160px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=39 */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 가운데 좁은 열 (Nodes, Namespace, Running Pods) - w:2 */}
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Nodes (id:63) - w:2, h:4 */}
|
||||
<div className="w-[133px] h-[160px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=63 */}
|
||||
</div>
|
||||
{/* Namespaces (id:59) - w:2, h:4 */}
|
||||
<div className="w-[133px] h-[160px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=59 */}
|
||||
</div>
|
||||
{/* Running Pods (id:62) - w:2, h:4 */}
|
||||
<div className="w-[133px] h-[160px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=62 */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 오른쪽 넓은 열 (Kubernetes Resource Count) */}
|
||||
{/* Kubernetes Resource Count (id:52) - w:10, h:12 */}
|
||||
<div className="w-[667px] h-[488px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=52 */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Resources 섹션 */}
|
||||
{activeSection === "resources" && (
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Row 1 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{/* Cluster CPU Utilization (id:72) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=72 */}
|
||||
</div>
|
||||
{/* Cluster Memory Utilization (id:55) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=55 */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 2 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{/* CPU Utilization by namespace (id:46) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=46 */}
|
||||
</div>
|
||||
{/* Memory Utilization by namespace (id:50) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=50 */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 3 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{/* CPU Utilization by instance (id:54) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=54 */}
|
||||
</div>
|
||||
{/* Memory Utilization by instance (id:73) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=73 */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 4 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{/* CPU Throttled seconds by namespace (id:82) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=82 */}
|
||||
</div>
|
||||
{/* CPU Core Throttled by instance (id:83) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=83 */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Kubernetes 섹션 */}
|
||||
{activeSection === "kubernetes" && (
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Row 1 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{/* Kubernetes Pods QoS classes (id:84) - w:12, h:9 */}
|
||||
<div className="w-[800px] h-[360px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=84 */}
|
||||
</div>
|
||||
{/* Kubernetes Pods Status Reason (id:85) - w:12, h:9 */}
|
||||
<div className="w-[800px] h-[360px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=85 */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 2 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{/* OOM Events by namespace (id:87) - w:12, h:9 */}
|
||||
<div className="w-[800px] h-[360px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=87 */}
|
||||
</div>
|
||||
{/* Container Restarts by namespace (id:88) - w:12, h:9 */}
|
||||
<div className="w-[800px] h-[360px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=88 */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Network 섹션 */}
|
||||
{activeSection === "network" && (
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Row 1 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{/* Global Network Utilization by device (id:44) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=44 */}
|
||||
</div>
|
||||
{/* Network Saturation - Packets dropped (id:53) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=53 */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 2 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{/* Network Received by namespace (id:79) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=79 */}
|
||||
</div>
|
||||
{/* Total Network Received by instance (id:80) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=80 */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 3 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{/* Network Received (without loopback) by instance (id:56) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=56 */}
|
||||
</div>
|
||||
{/* Network Received (loopback only) by instance (id:81) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=81 */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import About from '@/components/landing/about';
|
||||
import Skills from '@/components/landing/skills';
|
||||
import Projects from '@/components/landing/projects';
|
||||
import Contact from '@/components/landing/contact';
|
||||
import Grafana from '@/components/landing/grafana';
|
||||
import GrafanaPage from '@/components/landing/grafana';
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
@@ -20,9 +20,9 @@ export default function Home() {
|
||||
<div id="projects">
|
||||
<Projects />
|
||||
</div>
|
||||
{/* <div id="grafana"> */}
|
||||
{/* <Grafana /> */}
|
||||
{/* </div> */}
|
||||
<div id="monitoring">
|
||||
<GrafanaPage />
|
||||
</div>
|
||||
<div id="contact">
|
||||
<Contact />
|
||||
</div>
|
||||
|
||||
@@ -107,8 +107,6 @@ const PROJECTS_DATA: ProjectData[] = [
|
||||
},
|
||||
];
|
||||
|
||||
interface ProjectCardProps extends ProjectData {}
|
||||
|
||||
function ProjectCard({
|
||||
title,
|
||||
description,
|
||||
@@ -120,7 +118,7 @@ function ProjectCard({
|
||||
docsUrl,
|
||||
monitoringUrl,
|
||||
featured,
|
||||
}: ProjectCardProps) {
|
||||
}: ProjectData) {
|
||||
return (
|
||||
<Card className="overflow-hidden w-full p-0 hover:shadow-lg transition-shadow">
|
||||
{/* Image Section */}
|
||||
|
||||
@@ -61,7 +61,7 @@ export default function Contact() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<div className="bg-muted">
|
||||
<main className="flex flex-col items-center justify-center gap-16 p-4 tablet:p-8 py-20">
|
||||
<SectionHeader
|
||||
title="Get In Touch"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useState, useRef } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import SectionHeader from "@/components/landing/section-header";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
@@ -13,28 +14,74 @@ const sections: { id: Section; label: string; panels: number }[] = [
|
||||
{ id: "network", label: "Network", panels: 6 },
|
||||
];
|
||||
|
||||
export default function MonitoringPage() {
|
||||
export default function GrafanaPage() {
|
||||
const [activeSection, setActiveSection] = useState<Section>("overview");
|
||||
const { theme } = useTheme();
|
||||
const sectionRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
||||
// Grafana iframe URL 생성 함수
|
||||
const getGrafanaUrl = (panelId: number, themeType: "dark" | "light") => {
|
||||
const baseUrl = "https://grafana0213.kro.kr/d-solo/k8s_views_global/kubernetes-views-global";
|
||||
return `${baseUrl}?orgId=1&refresh=30s&theme=${themeType}&panelId=${panelId}`;
|
||||
};
|
||||
|
||||
// iframe 래퍼 컴포넌트
|
||||
const GrafanaPanel = ({ panelId }: { panelId: number }) => {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
||||
// Grafana 섹션이 화면에 보이지 않으면 로딩하지 않음
|
||||
// if (!isVisible) {
|
||||
// return (
|
||||
// <div className="relative w-full h-full">
|
||||
// <LoadingSkeleton />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-full">
|
||||
{/* {(!loaded || isLoading) && <LoadingSkeleton />} */}
|
||||
{theme === "dark" ? (
|
||||
<iframe
|
||||
key={`${panelId}-dark-${theme}`}
|
||||
src={getGrafanaUrl(panelId, "dark")}
|
||||
className="w-full h-full"
|
||||
style={{ display: loaded ? 'block' : 'none' }}
|
||||
onLoad={() => setLoaded(true)}
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
key={`${panelId}-light-${theme}`}
|
||||
src={getGrafanaUrl(panelId, "light")}
|
||||
className="w-full h-full"
|
||||
style={{ display: loaded ? 'block' : 'none' }}
|
||||
onLoad={() => setLoaded(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="page-container">
|
||||
<main className="page-container" ref={sectionRef}>
|
||||
<SectionHeader
|
||||
title="Monitoring"
|
||||
description="Real-time Kubernetes cluster monitoring with Grafana dashboards"
|
||||
/>
|
||||
|
||||
{/* 섹션 선택 버튼 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
<div className="flex gap-2 flex-wrap justify-center mb-4">
|
||||
{sections.map((section) => (
|
||||
<Button
|
||||
key={section.id}
|
||||
onClick={() => setActiveSection(section.id)}
|
||||
variant={activeSection === section.id ? "default" : "outline"}
|
||||
className="transition-all"
|
||||
className="transition-all text-xs sm:text-sm"
|
||||
>
|
||||
{section.label}
|
||||
<span className="ml-2 text-sm opacity-60">
|
||||
({section.panels} panels)
|
||||
<span className="ml-1 sm:ml-2 text-xs opacity-60">
|
||||
({section.panels})
|
||||
</span>
|
||||
</Button>
|
||||
))}
|
||||
@@ -42,106 +89,90 @@ export default function MonitoringPage() {
|
||||
|
||||
{/* Overview 섹션 */}
|
||||
{activeSection === "overview" && (
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
<div className="flex flex-col lg:flex-row gap-2 justify-center w-full h-full">
|
||||
{/* 왼쪽 2개 열 (CPU, RAM) */}
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col sm:flex-row gap-2 w-full lg:flex-1">
|
||||
{/* Global CPU Usage 열 */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-2 w-full sm:flex-1">
|
||||
{/* Global CPU Usage (id:77) - w:6, h:8 */}
|
||||
<div className="w-[400px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=77 */}
|
||||
<div className="w-full aspect-5/4 theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={77} />
|
||||
</div>
|
||||
{/* CPU Usage (id:37) - w:6, h:4 */}
|
||||
<div className="w-[400px] h-[160px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=37 */}
|
||||
<div className="w-full aspect-5/2 theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={37} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Global RAM Usage 열 */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-2 w-full sm:flex-1">
|
||||
{/* Global RAM Usage (id:78) - w:6, h:8 */}
|
||||
<div className="w-[400px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=78 */}
|
||||
<div className="w-full aspect-5/4 theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={78} />
|
||||
</div>
|
||||
{/* RAM Usage (id:39) - w:6, h:4 */}
|
||||
<div className="w-[400px] h-[160px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=39 */}
|
||||
<div className="w-full aspect-5/2 theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={39} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 가운데 좁은 열 (Nodes, Namespace, Running Pods) - w:2 */}
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Nodes (id:63) - w:2, h:4 */}
|
||||
<div className="w-[133px] h-[160px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=63 */}
|
||||
</div>
|
||||
{/* Namespaces (id:59) - w:2, h:4 */}
|
||||
<div className="w-[133px] h-[160px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=59 */}
|
||||
</div>
|
||||
{/* Running Pods (id:62) - w:2, h:4 */}
|
||||
<div className="w-[133px] h-[160px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=62 */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 오른쪽 넓은 열 (Kubernetes Resource Count) */}
|
||||
{/* Kubernetes Resource Count (id:52) - w:10, h:12 */}
|
||||
<div className="w-[667px] h-[488px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=52 */}
|
||||
<div className="w-full lg:flex-1 lg:max-w-[40%] theme-background rounded-lg overflow-hidden flex items-stretch">
|
||||
<GrafanaPanel panelId={52} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Resources 섹션 */}
|
||||
{activeSection === "resources" && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
{/* Row 1 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
<div className="flex flex-col lg:flex-row gap-2 justify-center w-full">
|
||||
{/* Cluster CPU Utilization (id:72) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=72 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={72} />
|
||||
</div>
|
||||
{/* Cluster Memory Utilization (id:55) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=55 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={55} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 2 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
<div className="flex flex-col lg:flex-row gap-2 justify-center w-full">
|
||||
{/* CPU Utilization by namespace (id:46) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=46 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={46} />
|
||||
</div>
|
||||
{/* Memory Utilization by namespace (id:50) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=50 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={50} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 3 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
<div className="flex flex-col lg:flex-row gap-2 justify-center w-full">
|
||||
{/* CPU Utilization by instance (id:54) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=54 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={54} />
|
||||
</div>
|
||||
{/* Memory Utilization by instance (id:73) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=73 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={73} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 4 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
<div className="flex flex-col lg:flex-row gap-2 justify-center w-full">
|
||||
{/* CPU Throttled seconds by namespace (id:82) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=82 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={82} />
|
||||
</div>
|
||||
{/* CPU Core Throttled by instance (id:83) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=83 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={83} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -149,28 +180,28 @@ export default function MonitoringPage() {
|
||||
|
||||
{/* Kubernetes 섹션 */}
|
||||
{activeSection === "kubernetes" && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
{/* Row 1 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
<div className="flex flex-col lg:flex-row gap-2 justify-center w-full">
|
||||
{/* Kubernetes Pods QoS classes (id:84) - w:12, h:9 */}
|
||||
<div className="w-[800px] h-[360px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=84 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={84} />
|
||||
</div>
|
||||
{/* Kubernetes Pods Status Reason (id:85) - w:12, h:9 */}
|
||||
<div className="w-[800px] h-[360px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=85 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={85} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 2 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
<div className="flex flex-col lg:flex-row gap-2 justify-center w-full">
|
||||
{/* OOM Events by namespace (id:87) - w:12, h:9 */}
|
||||
<div className="w-[800px] h-[360px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=87 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={87} />
|
||||
</div>
|
||||
{/* Container Restarts by namespace (id:88) - w:12, h:9 */}
|
||||
<div className="w-[800px] h-[360px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=88 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={88} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -178,40 +209,40 @@ export default function MonitoringPage() {
|
||||
|
||||
{/* Network 섹션 */}
|
||||
{activeSection === "network" && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
{/* Row 1 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
<div className="flex flex-col lg:flex-row gap-2 justify-center w-full">
|
||||
{/* Global Network Utilization by device (id:44) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=44 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={44} />
|
||||
</div>
|
||||
{/* Network Saturation - Packets dropped (id:53) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=53 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={53} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 2 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
<div className="flex flex-col lg:flex-row gap-2 justify-center w-full">
|
||||
{/* Network Received by namespace (id:79) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=79 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={79} />
|
||||
</div>
|
||||
{/* Total Network Received by instance (id:80) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=80 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={80} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 3 */}
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
<div className="flex flex-col lg:flex-row gap-2 justify-center w-full">
|
||||
{/* Network Received (without loopback) by instance (id:56) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=56 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={56} />
|
||||
</div>
|
||||
{/* Network Received (loopback only) by instance (id:81) - w:12, h:8 */}
|
||||
<div className="w-[800px] h-[320px] theme-background rounded-lg overflow-hidden">
|
||||
{/* iframe: panelId=81 */}
|
||||
<div className="w-full lg:flex-1 aspect-video theme-background rounded-lg overflow-hidden">
|
||||
<GrafanaPanel panelId={81} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import Particles from '@/components/ui/Particles';
|
||||
|
||||
export default function Hero() {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Github, Linkedin, Mail } from 'lucide-react';
|
||||
import { Github, Mail } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
const FOOTER_MENU_ITEMS = [
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Menu, X, Languages } from 'lucide-react';
|
||||
import { Menu, X } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { ModeToggle } from '@/components/ui/mode-toggle';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const HEADER_MENU_ITEMS = [
|
||||
{ name: 'About', path: '/#about', isScroll: true },
|
||||
{ name: 'Skills', path: '/#skills', isScroll: true },
|
||||
{ name: 'Projects', path: '/#projects', isScroll: true },
|
||||
{ name: 'Grafana', path: '/#monitoring', isScroll: true },
|
||||
{ name: 'Contact', path: '/#contact', isScroll: true },
|
||||
{ name: 'Grafana', path: '/monitoring', isScroll: false },
|
||||
];
|
||||
|
||||
interface HeaderProfileProps {
|
||||
|
||||
Reference in New Issue
Block a user