Improving metric queries (#3424)

This commit is contained in:
Alex Andreev authored and GitHub committed 2021-07-22 09:57:14 -04:00
1 parent 84c767a3d2
commit c6a4d55d86
33 files changed
+452 -357

No files matched your search

@@ -27,36 +27,46 @@ import kebabCase from "lodash/kebabCase";
import { disposeOnUnmount, observer } from "mobx-react";
import { DrawerItem, DrawerItemLabels } from "../drawer";
import { Badge } from "../badge";
import { nodesStore } from "./nodes.store";
import { ResourceMetrics } from "../resource-metrics";
import { podsStore } from "../+workloads-pods/pods.store";
import type { KubeObjectDetailsProps } from "../kube-object";
import type { Node } from "../../api/endpoints";
import { getMetricsByNodeNames, IClusterMetrics, Node } from "../../api/endpoints";
import { NodeCharts } from "./node-charts";
import { reaction } from "mobx";
import { makeObservable, observable, reaction } from "mobx";
import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
import { ClusterMetricsResourceType } from "../../../main/cluster";
import { NodeDetailsResources } from "./node-details-resources";
import { DrawerTitle } from "../drawer/drawer-title";
import { boundMethod } from "../../utils";
interface Props extends KubeObjectDetailsProps<Node> {
}
@observer
export class NodeDetails extends React.Component<Props> {
@observable metrics: Partial<IClusterMetrics>;
constructor(props: Props) {
super(props);
makeObservable(this);
}
@disposeOnUnmount
clean = reaction(() => this.props.object.getName(), () => {
nodesStore.nodeMetrics = null;
this.metrics = null;
});
async componentDidMount() {
podsStore.reloadAll();
}
componentWillUnmount() {
nodesStore.nodeMetrics = null;
@boundMethod
async loadMetrics() {
const { object: node } = this.props;
this.metrics = await getMetricsByNodeNames([node.getName()]);
}
render() {
@@ -68,7 +78,7 @@ export class NodeDetails extends React.Component<Props> {
const conditions = node.getActiveConditions();
const taints = node.getTaints();
const childPods = podsStore.getPodsByNode(node.getName());
const metrics = nodesStore.nodeMetrics;
const { metrics } = this;
const metricTabs = [
"CPU",
"Memory",
@@ -81,7 +91,7 @@ export class NodeDetails extends React.Component<Props> {
<div className="NodeDetails">
{!isMetricHidden && podsStore.isLoaded && (
<ResourceMetrics
loader={() => nodesStore.loadMetrics(node.getName())}
loader={this.loadMetrics}
tabs={metricTabs} object={node} params={{ metrics }}
>
<NodeCharts/>
+5 -59
View File
@@ -18,22 +18,17 @@
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { sum } from "lodash";
import { action, computed, observable, makeObservable } from "mobx";
import { clusterApi, IClusterMetrics, INodeMetrics, Node, nodesApi } from "../../api/endpoints";
import { autoBind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { computed, makeObservable } from "mobx";
import { apiManager } from "../../api/api-manager";
import { Node, nodesApi } from "../../api/endpoints";
import { KubeObjectStore } from "../../kube-object.store";
import { autoBind } from "../../utils";
export class NodesStore extends KubeObjectStore<Node> {
api = nodesApi;
@observable metrics: Partial<INodeMetrics> = {};
@observable nodeMetrics: Partial<IClusterMetrics> = null;
@observable metricsLoading = false;
@observable metricsLoaded = false;
constructor() {
super();
@@ -41,23 +36,6 @@ export class NodesStore extends KubeObjectStore<Node> {
autoBind(this);
}
@action
async loadUsageMetrics() {
this.metricsLoading = true;
try {
this.metrics = await nodesApi.getMetrics();
this.metricsLoaded = true;
} finally {
this.metricsLoading = false;
}
}
@action
async loadMetrics(nodeName: string) {
this.nodeMetrics = await clusterApi.getMetrics([nodeName]);
}
@computed get masterNodes() {
return this.items.filter(node => node.getRoleLabels().includes("master"));
}
@@ -66,41 +44,9 @@ export class NodesStore extends KubeObjectStore<Node> {
return this.items.filter(node => !node.getRoleLabels().includes("master"));
}
getLastMetricValues(node: Node, metricNames: string[]): number[] {
if (!this.metricsLoaded) {
return [];
}
const nodeName = node.getName();
return metricNames.map(metricName => {
try {
const metric = this.metrics[metricName];
const result = metric.data.result.find(result => {
return [
result.metric.node,
result.metric.instance,
result.metric.kubernetes_node,
].includes(nodeName);
});
return result ? parseFloat(result.values.slice(-1)[0][1]) : 0;
} catch (e) {
return 0;
}
});
}
getWarningsCount(): number {
return sum(this.items.map((node: Node) => node.getWarningConditions().length));
}
reset() {
super.reset();
this.metrics = {};
this.nodeMetrics = null;
this.metricsLoading = false;
this.metricsLoaded = false;
}
}
export const nodesStore = new NodesStore();
+35 -8
View File
@@ -28,7 +28,7 @@ import { TabLayout } from "../layout/tab-layout";
import { nodesStore } from "./nodes.store";
import { podsStore } from "../+workloads-pods/pods.store";
import { KubeObjectListLayout } from "../kube-object";
import type { Node } from "../../api/endpoints/nodes.api";
import { getMetricsForAllNodes, INodeMetrics, Node } from "../../api/endpoints/nodes.api";
import { LineProgress } from "../line-progress";
import { bytesToUnits } from "../../utils/convertMemory";
import { Tooltip, TooltipPosition } from "../tooltip";
@@ -39,6 +39,8 @@ import { Badge } from "../badge/badge";
import { kubeWatchApi } from "../../api/kube-watch-api";
import { eventStore } from "../+events/event.store";
import type { NodesRouteParams } from "../../../common/routes";
import { observable } from "mobx";
import isEmpty from "lodash/isEmpty";
enum columnId {
name = "name",
@@ -58,7 +60,8 @@ interface Props extends RouteComponentProps<NodesRouteParams> {
@observer
export class Nodes extends React.Component<Props> {
private metricsWatcher = interval(30, () => nodesStore.loadUsageMetrics());
@observable metrics: Partial<INodeMetrics> = {};
private metricsWatcher = interval(30, async () => this.metrics = await getMetricsForAllNodes());
componentDidMount() {
this.metricsWatcher.start(true);
@@ -73,8 +76,32 @@ export class Nodes extends React.Component<Props> {
this.metricsWatcher.stop();
}
getLastMetricValues(node: Node, metricNames: string[]): number[] {
if (isEmpty(this.metrics)) {
return [];
}
const nodeName = node.getName();
return metricNames.map(metricName => {
try {
const metric = this.metrics[metricName];
const result = metric.data.result.find(result => {
return [
result.metric.node,
result.metric.instance,
result.metric.kubernetes_node,
].includes(nodeName);
});
return result ? parseFloat(result.values.slice(-1)[0][1]) : 0;
} catch (e) {
return 0;
}
});
}
renderCpuUsage(node: Node) {
const metrics = nodesStore.getLastMetricValues(node, ["cpuUsage", "cpuCapacity"]);
const metrics = this.getLastMetricValues(node, ["cpuUsage", "cpuCapacity"]);
if (!metrics || !metrics[1]) return <LineProgress value={0}/>;
const usage = metrics[0];
@@ -97,7 +124,7 @@ export class Nodes extends React.Component<Props> {
}
renderMemoryUsage(node: Node) {
const metrics = nodesStore.getLastMetricValues(node, ["workloadMemoryUsage", "memoryAllocatableCapacity"]);
const metrics = this.getLastMetricValues(node, ["workloadMemoryUsage", "memoryAllocatableCapacity"]);
if (!metrics || !metrics[1]) return <LineProgress value={0}/>;
const usage = metrics[0];
@@ -116,7 +143,7 @@ export class Nodes extends React.Component<Props> {
}
renderDiskUsage(node: Node): any {
const metrics = nodesStore.getLastMetricValues(node, ["fsUsage", "fsSize"]);
const metrics = this.getLastMetricValues(node, ["fsUsage", "fsSize"]);
if (!metrics || !metrics[1]) return <LineProgress value={0}/>;
const usage = metrics[0];
@@ -172,9 +199,9 @@ export class Nodes extends React.Component<Props> {
isSelectable={false}
sortingCallbacks={{
[columnId.name]: (node: Node) => node.getName(),
[columnId.cpu]: (node: Node) => nodesStore.getLastMetricValues(node, ["cpuUsage"]),
[columnId.memory]: (node: Node) => nodesStore.getLastMetricValues(node, ["memoryUsage"]),
[columnId.disk]: (node: Node) => nodesStore.getLastMetricValues(node, ["fsUsage"]),
[columnId.cpu]: (node: Node) => this.getLastMetricValues(node, ["cpuUsage"]),
[columnId.memory]: (node: Node) => this.getLastMetricValues(node, ["memoryUsage"]),
[columnId.disk]: (node: Node) => this.getLastMetricValues(node, ["fsUsage"]),
[columnId.conditions]: (node: Node) => node.getNodeConditionText(),
[columnId.taints]: (node: Node) => node.getTaints().length,
[columnId.roles]: (node: Node) => node.getRoleLabels(),