forked from gethomepage/homepage
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Total CPU and Memory usage for the entire cluster * Total CPU and Memory usage for kubernetes pods * Service discovery via annotations on ingress * No storage stats yet * No network stats yet
- Loading branch information
Showing
18 changed files
with
479 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,3 +41,6 @@ next-env.d.ts | |
|
||
# homepage | ||
/config | ||
|
||
# idea | ||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import useSWR from "swr"; | ||
|
||
export default function KubernetesStatus({ service }) { | ||
const { data, error } = useSWR(`/api/kubernetes/status/${service.namespace}/${service.app}`); | ||
|
||
if (error) { | ||
return <div className="w-3 h-3 bg-rose-300 dark:bg-rose-500 rounded-full" />; | ||
} | ||
|
||
if (data && data.status === "running") { | ||
return <div className="w-3 h-3 bg-emerald-300 dark:bg-emerald-500 rounded-full" />; | ||
} | ||
|
||
if (data && data.status === "not found") { | ||
return <div className="h-2.5 w-2.5 bg-orange-400/50 dark:bg-yellow-200/40 -rotate-45" />; | ||
} | ||
|
||
return <div className="w-3 h-3 bg-black/20 dark:bg-white/40 rounded-full" />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { CoreV1Api, Metrics } from "@kubernetes/client-node"; | ||
|
||
import getKubeConfig from "../../../../utils/config/kubernetes"; | ||
import { parseCpu, parseMemory } from "../../../../utils/kubernetes/kubernetes-utils"; | ||
|
||
export default async function handler(req, res) { | ||
const APP_LABEL = "app.kubernetes.io/name"; | ||
const { service } = req.query; | ||
|
||
const [namespace, appName] = service; | ||
if (!namespace && !appName) { | ||
res.status(400).send({ | ||
error: "kubernetes query parameters are required", | ||
}); | ||
return; | ||
} | ||
const labelSelector = `${APP_LABEL}=${appName}`; | ||
|
||
try { | ||
const kc = getKubeConfig(); | ||
const coreApi = kc.makeApiClient(CoreV1Api); | ||
const metricsApi = new Metrics(kc); | ||
const podsResponse = await coreApi.listNamespacedPod(namespace, null, null, null, null, labelSelector); | ||
const pods = podsResponse.body.items; | ||
|
||
if (pods.length === 0) { | ||
res.status(200).send({ | ||
error: "not found", | ||
}); | ||
return; | ||
} | ||
|
||
let cpuLimit = 0; | ||
let memLimit = 0; | ||
pods.forEach((pod) => { | ||
pod.spec.containers.forEach((container) => { | ||
if (container?.resources?.limits?.cpu) { | ||
cpuLimit += parseCpu(container?.resources?.limits?.cpu); | ||
} | ||
if (container?.resources?.limits?.memory) { | ||
memLimit += parseMemory(container?.resources?.limits?.memory); | ||
} | ||
}); | ||
}); | ||
|
||
const stats = await pods.map(async (pod) => { | ||
let depMem = 0; | ||
let depCpu = 0; | ||
const podMetrics = await metricsApi.getPodMetrics(namespace, pod.metadata.name); | ||
podMetrics.containers.forEach((container) => { | ||
depMem += parseMemory(container.usage.memory); | ||
depCpu += parseCpu(container.usage.cpu); | ||
}); | ||
return { | ||
mem: depMem, | ||
cpu: depCpu | ||
} | ||
}).reduce(async (finalStats, podStatPromise) => { | ||
const podStats = await podStatPromise; | ||
return { | ||
mem: finalStats.mem + podStats.mem, | ||
cpu: finalStats.cpu + podStats.cpu | ||
}; | ||
}); | ||
stats.cpuLimit = cpuLimit; | ||
stats.memLimit = memLimit; | ||
stats.cpuUsage = stats.cpu / cpuLimit; | ||
stats.memUsage = stats.mem / memLimit; | ||
|
||
res.status(200).json({ | ||
stats, | ||
}); | ||
} catch (e) { | ||
console.log("error", e); | ||
res.status(500).send({ | ||
error: "unknown error", | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { CoreV1Api } from "@kubernetes/client-node"; | ||
|
||
import getKubeConfig from "../../../../utils/config/kubernetes"; | ||
|
||
export default async function handler(req, res) { | ||
const APP_LABEL = "app.kubernetes.io/name"; | ||
const { service } = req.query; | ||
|
||
const [namespace, appName] = service; | ||
if (!namespace && !appName) { | ||
res.status(400).send({ | ||
error: "kubernetes query parameters are required", | ||
}); | ||
return; | ||
} | ||
const labelSelector = `${APP_LABEL}=${appName}`; | ||
|
||
try { | ||
const kc = getKubeConfig(); | ||
const coreApi = kc.makeApiClient(CoreV1Api); | ||
const podsResponse = await coreApi.listNamespacedPod(namespace, null, null, null, null, labelSelector); | ||
const pods = podsResponse.body.items; | ||
|
||
if (pods.length === 0) { | ||
res.status(200).send({ | ||
error: "not found", | ||
}); | ||
return; | ||
} | ||
|
||
// at least one pod must be in the "Running" phase, otherwise its "down" | ||
const runningPod = pods.find(pod => pod.status.phase === "Running"); | ||
const status = runningPod ? "running" : "down"; | ||
res.status(200).json({ | ||
status | ||
}); | ||
} catch { | ||
res.status(500).send({ | ||
error: "unknown error", | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { CoreV1Api, Metrics } from "@kubernetes/client-node"; | ||
|
||
import getKubeConfig from "../../../utils/config/kubernetes"; | ||
import { parseCpu, parseMemory } from "../../../utils/kubernetes/kubernetes-utils"; | ||
|
||
export default async function handler(req, res) { | ||
const { type } = req.query; | ||
|
||
const kc = getKubeConfig(); | ||
const coreApi = kc.makeApiClient(CoreV1Api); | ||
const metricsApi = new Metrics(kc); | ||
|
||
const nodes = await coreApi.listNode(); | ||
const nodeCapacity = new Map(); | ||
let cpuTotal = 0; | ||
let cpuUsage = 0; | ||
let memTotal = 0; | ||
let memUsage = 0; | ||
|
||
nodes.body.items.forEach((node) => { | ||
nodeCapacity.set(node.metadata.name, node.status.capacity); | ||
cpuTotal += Number.parseInt(node.status.capacity.cpu, 10); | ||
memTotal += parseMemory(node.status.capacity.memory); | ||
}); | ||
|
||
const nodeMetrics = await metricsApi.getNodeMetrics(); | ||
const nodeUsage = new Map(); | ||
nodeMetrics.items.forEach((metrics) => { | ||
nodeUsage.set(metrics.metadata.name, metrics.usage); | ||
cpuUsage += parseCpu(metrics.usage.cpu); | ||
memUsage += parseMemory(metrics.usage.memory); | ||
}); | ||
|
||
if (type === "cpu") { | ||
return res.status(200).json({ | ||
cpu: { | ||
usage: (cpuUsage / cpuTotal) * 100, | ||
load: cpuUsage | ||
} | ||
}); | ||
} | ||
// Maybe Storage CSI can provide this information | ||
// if (type === "disk") { | ||
// if (!existsSync(target)) { | ||
// return res.status(404).json({ | ||
// error: "Target not found", | ||
// }); | ||
// } | ||
// | ||
// return res.status(200).json({ | ||
// drive: await drive.info(target || "/"), | ||
// }); | ||
// } | ||
// | ||
if (type === "memory") { | ||
const SCALE_MB = 1024 * 1024; | ||
const usedMemMb = memUsage / SCALE_MB; | ||
const totalMemMb = memTotal / SCALE_MB; | ||
const freeMemMb = totalMemMb - usedMemMb; | ||
return res.status(200).json({ | ||
memory: { | ||
usedMemMb, | ||
freeMemMb, | ||
totalMemMb | ||
} | ||
}); | ||
} | ||
|
||
return res.status(400).json({ | ||
error: "invalid type" | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--- | ||
# sample kubernetes config |
Oops, something went wrong.