Skip to content

Commit

Permalink
Merge branch 'master' into revk8s
Browse files Browse the repository at this point in the history
  • Loading branch information
brendandburns committed Oct 21, 2021
2 parents 9bd9fc0 + 723d69c commit 35342b3
Show file tree
Hide file tree
Showing 5 changed files with 594 additions and 11 deletions.
39 changes: 39 additions & 0 deletions examples/top_pods.js
@@ -0,0 +1,39 @@
const k8s = require('../dist/index');

const kc = new k8s.KubeConfig();
kc.loadFromDefault();

const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
const metricsClient = new k8s.Metrics(kc);

k8s.topPods(k8sApi, metricsClient, "kube-system")
.then((pods) => {

const podsColumns = pods.map((pod) => {
return {
"POD": pod.Pod.metadata.name,
"CPU(cores)": pod.CPU.CurrentUsage,
"MEMORY(bytes)": pod.Memory.CurrentUsage,
}
});
console.log("TOP PODS")
console.table(podsColumns)
});

k8s.topPods(k8sApi, metricsClient, "kube-system")
.then((pods) => {

const podsAndContainersColumns = pods.flatMap((pod) => {
return pod.Containers.map(containerUsage => {
return {
"POD": pod.Pod.metadata.name,
"NAME": containerUsage.Container,
"CPU(cores)": containerUsage.CPUUsage.CurrentUsage,
"MEMORY(bytes)": containerUsage.MemoryUsage.CurrentUsage,
};
})
});

console.log("TOP CONTAINERS")
console.table(podsAndContainersColumns)
});
2 changes: 1 addition & 1 deletion settings
Expand Up @@ -15,7 +15,7 @@
# limitations under the License.

# kubernetes-client/gen commit to use for code generation.
export GEN_COMMIT=a3aef4de7a1d5dab72021aa282fffd8bc8a022ca
export GEN_COMMIT=a3aef4d

# GitHub username/organization to clone kubernetes repo from.
export USERNAME=kubernetes
Expand Down
123 changes: 121 additions & 2 deletions src/top.ts
@@ -1,5 +1,15 @@
import { CoreV1Api, V1Node, V1Pod } from './gen/api';
import { add, podsForNode, quantityToScalar, totalCPU, totalMemory } from './util';
import { CoreV1Api, V1Node, V1Pod, V1PodList } from './gen/api';
import { ContainerMetric, Metrics, PodMetric } from './metrics';
import {
add,
podsForNode,
quantityToScalar,
ResourceStatus,
totalCPU,
totalCPUForContainer,
totalMemory,
totalMemoryForContainer,
} from './util';

export class ResourceUsage {
constructor(
Expand All @@ -9,6 +19,14 @@ export class ResourceUsage {
) {}
}

export class CurrentResourceUsage {
constructor(
public readonly CurrentUsage: number | BigInt,
public readonly RequestTotal: number | BigInt,
public readonly LimitTotal: number | BigInt,
) {}
}

export class NodeStatus {
constructor(
public readonly Node: V1Node,
Expand All @@ -17,6 +35,23 @@ export class NodeStatus {
) {}
}

export class ContainerStatus {
constructor(
public readonly Container: string,
public readonly CPUUsage: CurrentResourceUsage,
public readonly MemoryUsage: CurrentResourceUsage,
) {}
}

export class PodStatus {
constructor(
public readonly Pod: V1Pod,
public readonly CPU: CurrentResourceUsage,
public readonly Memory: CurrentResourceUsage,
public readonly Containers: ContainerStatus[],
) {}
}

export async function topNodes(api: CoreV1Api): Promise<NodeStatus[]> {
// TODO: Support metrics APIs in the client and this library
const nodes = await api.listNode();
Expand Down Expand Up @@ -46,3 +81,87 @@ export async function topNodes(api: CoreV1Api): Promise<NodeStatus[]> {
}
return result;
}

// Returns the current pod CPU/Memory usage including the CPU/Memory usage of each container
export async function topPods(api: CoreV1Api, metrics: Metrics, namespace?: string): Promise<PodStatus[]> {
// Figure out which pod list endpoint to call
const getPodList = async (): Promise<V1PodList> => {
if (namespace) {
return (await api.listNamespacedPod(namespace)).body;
}
return (await api.listPodForAllNamespaces()).body;
};

const [podMetrics, podList] = await Promise.all([metrics.getPodMetrics(namespace), getPodList()]);

// Create a map of pod names to their metric usage
// to make it easier to look up when we need it later
const podMetricsMap = podMetrics.items.reduce((accum, next) => {
accum.set(next.metadata.name, next);
return accum;
}, new Map<string, PodMetric>());

const result: PodStatus[] = [];
for (const pod of podList.items) {
const podMetric = podMetricsMap.get(pod.metadata!.name!);

const containerStatuses: ContainerStatus[] = [];
let currentPodCPU: number | bigint = 0;
let currentPodMem: number | bigint = 0;
let podRequestsCPU: number | bigint = 0;
let podLimitsCPU: number | bigint = 0;
let podRequestsMem: number | bigint = 0;
let podLimitsMem: number | bigint = 0;

pod.spec!.containers.forEach((container) => {
// get the the container CPU/Memory container.resources.requests/limits
const containerCpuTotal = totalCPUForContainer(container);
const containerMemTotal = totalMemoryForContainer(container);

// sum each container's CPU/Memory container.resources.requests/limits
// to get the pod's overall requests/limits
podRequestsCPU = add(podRequestsCPU, containerCpuTotal.request);
podLimitsCPU = add(podLimitsCPU, containerCpuTotal.limit);

podRequestsMem = add(podLimitsMem, containerMemTotal.request);
podLimitsMem = add(podLimitsMem, containerMemTotal.limit);

// Find the container metrics by container.name
// if both the pod and container metrics exist
const containerMetrics =
podMetric !== undefined
? podMetric.containers.find((c) => c.name === container.name)
: undefined;

// Store the current usage of each container
// Sum each container to get the overall pod usage
if (containerMetrics !== undefined) {
const currentContainerCPUUsage = quantityToScalar(containerMetrics.usage.cpu);
const currentContainerMemUsage = quantityToScalar(containerMetrics.usage.memory);

currentPodCPU = add(currentPodCPU, currentContainerCPUUsage);
currentPodMem = add(currentPodMem, currentContainerMemUsage);

const containerCpuUsage = new CurrentResourceUsage(
currentContainerCPUUsage,
containerCpuTotal.request,
containerCpuTotal.limit,
);
const containerMemUsage = new CurrentResourceUsage(
currentContainerMemUsage,
containerMemTotal.request,
containerMemTotal.limit,
);

containerStatuses.push(
new ContainerStatus(containerMetrics.name, containerCpuUsage, containerMemUsage),
);
}
});

const podCpuUsage = new CurrentResourceUsage(currentPodCPU, podRequestsCPU, podLimitsCPU);
const podMemUsage = new CurrentResourceUsage(currentPodMem, podRequestsMem, podLimitsMem);
result.push(new PodStatus(pod, podCpuUsage, podMemUsage, containerStatuses));
}
return result;
}

0 comments on commit 35342b3

Please sign in to comment.