Forráskód Böngészése

Implement hpa metrics

jnfrati 4 éve
szülő
commit
02ccf65326

+ 246 - 215
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/AreaChart.tsx

@@ -1,5 +1,5 @@
 import React, { useMemo, useCallback } from "react";
-import { AreaClosed, Line, Bar } from "@visx/shape";
+import { AreaClosed, Line, Bar, LinePath } from "@visx/shape";
 import { curveMonotoneX } from "@visx/curve";
 import { scaleTime, scaleLinear } from "@visx/scale";
 import { AxisLeft, AxisBottom } from "@visx/axis";
@@ -9,6 +9,7 @@ import {
   Tooltip,
   TooltipWithBounds,
   defaultStyles,
+  useTooltip,
 } from "@visx/tooltip";
 
 import { GridRows, GridColumns } from "@visx/grid";
@@ -67,235 +68,265 @@ const bisectDate = bisector<MetricsData, Date>((d) => new Date(d.date * 1000))
 
 export type AreaProps = {
   data: MetricsData[];
+  hpaData: MetricsData[];
   resolution: string;
   width: number;
   height: number;
   margin?: { top: number; right: number; bottom: number; left: number };
 };
 
-export default withTooltip<AreaProps, TooltipData>(
-  ({
-    data,
-    resolution,
-    width,
-    height,
-    margin = { top: 0, right: 0, bottom: 0, left: 0 },
+// export default withTooltip<AreaProps, TooltipData>();
+
+const AreaChart: React.FunctionComponent<AreaProps> = ({
+  data,
+  hpaData,
+  resolution,
+  width,
+  height,
+  margin = { top: 0, right: 0, bottom: 0, left: 0 },
+}) => {
+  globalData = data;
+
+  const {
     showTooltip,
     hideTooltip,
     tooltipData,
-    tooltipTop = 0,
-    tooltipLeft = 0,
-  }: AreaProps & WithTooltipProvidedProps<TooltipData>) => {
-    globalData = data;
+    tooltipTop,
+    tooltipLeft,
+  } = useTooltip<MetricsData>();
+
+  // bounds
+  const innerWidth = width - margin.left - margin.right - 40;
+  const innerHeight = height - margin.top - margin.bottom - 20;
 
-    if (width == 0 || height == 0 || width < 10) {
-      return null;
-    }
+  // scales
+  const dateScale = useMemo(
+    () =>
+      scaleTime({
+        range: [margin.left, innerWidth + margin.left],
+        domain: extent([...globalData, ...hpaData], getDate) as [Date, Date],
+      }),
+    [innerWidth, margin.left, width, height, data, hpaData]
+  );
+  const valueScale = useMemo(
+    () =>
+      scaleLinear({
+        range: [innerHeight + margin.top, margin.top],
+        domain: [0, 1.25 * max([...globalData, ...hpaData], getValue)],
+        nice: true,
+      }),
+    [margin.top, innerHeight, width, height, data, hpaData]
+  );
 
-    // bounds
-    const innerWidth = width - margin.left - margin.right - 40;
-    const innerHeight = height - margin.top - margin.bottom - 20;
+  const xScale = useMemo(
+    () =>
+      scaleTime({
+        domain: extent(hpaData, getDate) as number[],
+        range: [0, width],
+      }),
+    [width, hpaData]
+  );
+  const yScale = useMemo(() => {
+    return scaleLinear({
+      domain: extent(hpaData, getValue) as number[],
+      range: [innerHeight + margin.top, margin.top],
+    });
+  }, [margin.top, innerHeight, width, height, hpaData]);
 
-    // scales
-    const dateScale = useMemo(
-      () =>
-        scaleTime({
-          range: [margin.left, innerWidth + margin.left],
-          domain: extent(globalData, getDate) as [Date, Date],
-        }),
-      [innerWidth, margin.left, width, height, data]
-    );
-    const valueScale = useMemo(
-      () =>
-        scaleLinear({
-          range: [innerHeight + margin.top, margin.top],
-          domain: [0, 1.25 * max(globalData, getValue)],
-          nice: true,
-        }),
-      [margin.top, innerHeight, width, height, data]
-    );
+  // tooltip handler
+  const handleTooltip = useCallback(
+    (
+      event: React.TouchEvent<SVGRectElement> | React.MouseEvent<SVGRectElement>
+    ) => {
+      const { x } = localPoint(event) || { x: 0 };
+      const x0 = dateScale.invert(x);
+      const index = bisectDate(globalData, x0, 1);
+      const d0 = globalData[index - 1];
+      const d1 = globalData[index];
+      let d = d0;
 
-    // tooltip handler
-    const handleTooltip = useCallback(
-      (
-        event:
-          | React.TouchEvent<SVGRectElement>
-          | React.MouseEvent<SVGRectElement>
-      ) => {
-        const { x } = localPoint(event) || { x: 0 };
-        const x0 = dateScale.invert(x);
-        const index = bisectDate(globalData, x0, 1);
-        const d0 = globalData[index - 1];
-        const d1 = globalData[index];
-        let d = d0;
+      if (d1 && getDate(d1)) {
+        d =
+          x0.valueOf() - getDate(d0).valueOf() >
+          getDate(d1).valueOf() - x0.valueOf()
+            ? d1
+            : d0;
+      }
 
-        if (d1 && getDate(d1)) {
-          d =
-            x0.valueOf() - getDate(d0).valueOf() >
-            getDate(d1).valueOf() - x0.valueOf()
-              ? d1
-              : d0;
-        }
+      showTooltip({
+        tooltipData: d,
+        tooltipLeft: x || 0,
+        tooltipTop: valueScale(getValue(d)) || 0,
+      });
+    },
+    [showTooltip, valueScale, dateScale, width, height, data]
+  );
+
+  if (width == 0 || height == 0 || width < 10) {
+    return null;
+  }
 
-        showTooltip({
-          tooltipData: d,
-          tooltipLeft: x || 0,
-          tooltipTop: valueScale(getValue(d)) || 0,
-        });
-      },
-      [showTooltip, valueScale, dateScale, width, height, data]
-    );
+  return (
+    <div>
+      <svg width={width} height={height}>
+        <rect
+          x={0}
+          y={0}
+          width={width}
+          height={height}
+          fill="url(#area-background-gradient)"
+          rx={14}
+        />
 
-    return (
-      <div>
-        <svg width={width} height={height}>
-          <rect
-            x={0}
-            y={0}
-            width={width}
-            height={height}
-            fill="url(#area-background-gradient)"
-            rx={14}
-          />
-          <LinearGradient
-            id="area-background-gradient"
-            from={background}
-            to={background2}
-          />
-          <LinearGradient
-            id="area-gradient"
-            from={accentColor}
-            to={accentColor}
-            toOpacity={0}
-          />
-          <GridRows
-            left={margin.left}
-            scale={valueScale}
-            width={innerWidth}
-            strokeDasharray="1,3"
-            stroke="white"
-            strokeOpacity={0.2}
-            pointerEvents="none"
-          />
-          <GridColumns
-            top={margin.top}
-            scale={dateScale}
-            height={innerHeight}
-            strokeDasharray="1,3"
-            stroke="white"
-            strokeOpacity={0.2}
-            pointerEvents="none"
-          />
-          <AreaClosed<MetricsData>
-            data={data}
-            x={(d) => dateScale(getDate(d)) ?? 0}
-            y={(d) => valueScale(getValue(d)) ?? 0}
-            height={innerHeight}
-            yScale={valueScale}
-            strokeWidth={1}
-            stroke="url(#area-gradient)"
-            fill="url(#area-gradient)"
-            curve={curveMonotoneX}
-          />
-          <AxisLeft
-            left={10}
-            scale={valueScale}
-            hideAxisLine={true}
-            hideTicks={true}
-            tickLabelProps={() => ({
-              fill: "white",
-              fontSize: 11,
-              textAnchor: "start",
-              fillOpacity: 0.4,
-              dy: 0,
-            })}
-          />
-          <AxisBottom
-            top={height - 20}
-            scale={dateScale}
-            tickFormat={formats[resolution]}
-            hideAxisLine={true}
-            hideTicks={true}
-            tickLabelProps={() => ({
-              fill: "white",
-              fontSize: 11,
-              textAnchor: "middle",
-              fillOpacity: 0.4,
-            })}
-          />
-          <Bar
-            x={margin.left}
-            y={margin.top}
-            width={innerWidth}
-            height={innerHeight}
-            fill="transparent"
-            rx={14}
-            onTouchStart={handleTooltip}
-            onTouchMove={handleTooltip}
-            onMouseMove={handleTooltip}
-            onMouseLeave={() => hideTooltip()}
-          />
-          {tooltipData && (
-            <g>
-              <Line
-                from={{ x: tooltipLeft, y: margin.top }}
-                to={{ x: tooltipLeft, y: innerHeight + margin.top }}
-                stroke={accentColorDark}
-                strokeWidth={2}
-                pointerEvents="none"
-                strokeDasharray="5,2"
-              />
-              <circle
-                cx={tooltipLeft}
-                cy={tooltipTop + 1}
-                r={4}
-                fill="black"
-                fillOpacity={0.1}
-                stroke="black"
-                strokeOpacity={0.1}
-                strokeWidth={2}
-                pointerEvents="none"
-              />
-              <circle
-                cx={tooltipLeft}
-                cy={tooltipTop}
-                r={4}
-                fill={accentColorDark}
-                stroke="white"
-                strokeWidth={2}
-                pointerEvents="none"
-              />
-            </g>
-          )}
-        </svg>
+        <LinearGradient
+          id="area-background-gradient"
+          from={background}
+          to={background2}
+        />
+        <LinearGradient
+          id="area-gradient"
+          from={accentColor}
+          to={accentColor}
+          toOpacity={0}
+        />
+        <GridRows
+          left={margin.left}
+          scale={valueScale}
+          width={innerWidth}
+          strokeDasharray="1,3"
+          stroke="white"
+          strokeOpacity={0.2}
+          pointerEvents="none"
+        />
+        <GridColumns
+          top={margin.top}
+          scale={dateScale}
+          height={innerHeight}
+          strokeDasharray="1,3"
+          stroke="white"
+          strokeOpacity={0.2}
+          pointerEvents="none"
+        />
+        <AreaClosed<MetricsData>
+          data={data}
+          x={(d) => dateScale(getDate(d)) ?? 0}
+          y={(d) => valueScale(getValue(d)) ?? 0}
+          height={innerHeight}
+          yScale={valueScale}
+          strokeWidth={1}
+          stroke="url(#area-gradient)"
+          fill="url(#area-gradient)"
+          curve={curveMonotoneX}
+        />
+        <LinePath<MetricsData>
+          stroke="#fd0101"
+          strokeWidth={2}
+          data={hpaData}
+          x={(d) => dateScale(getDate(d)) ?? 0}
+          y={(d) => valueScale(getValue(d)) ?? 0}
+        />
+        <AxisLeft
+          left={10}
+          scale={valueScale}
+          hideAxisLine={true}
+          hideTicks={true}
+          tickLabelProps={() => ({
+            fill: "white",
+            fontSize: 11,
+            textAnchor: "start",
+            fillOpacity: 0.4,
+            dy: 0,
+          })}
+        />
+        <AxisBottom
+          top={height - 20}
+          scale={dateScale}
+          tickFormat={formats[resolution]}
+          hideAxisLine={true}
+          hideTicks={true}
+          tickLabelProps={() => ({
+            fill: "white",
+            fontSize: 11,
+            textAnchor: "middle",
+            fillOpacity: 0.4,
+          })}
+        />
+        <Bar
+          x={margin.left}
+          y={margin.top}
+          width={innerWidth}
+          height={innerHeight}
+          fill="transparent"
+          rx={14}
+          onTouchStart={handleTooltip}
+          onTouchMove={handleTooltip}
+          onMouseMove={handleTooltip}
+          onMouseLeave={() => hideTooltip()}
+        />
         {tooltipData && (
-          <div>
-            <TooltipWithBounds
-              key={Math.random()}
-              top={tooltipTop - 12}
-              left={tooltipLeft + 12}
-              style={tooltipStyles}
-            >
-              {getValue(tooltipData)}
-            </TooltipWithBounds>
-            <Tooltip
-              top={-10}
-              left={tooltipLeft}
-              style={{
-                ...defaultStyles,
-                background: "#26272f",
-                color: "#aaaabb",
-                width: 100,
-                paddingTop: 35,
-                textAlign: "center",
-                transform: "translateX(-60px)",
-              }}
-            >
-              {formatDate(getDate(tooltipData))}
-            </Tooltip>
-          </div>
+          <g>
+            <Line
+              from={{ x: tooltipLeft, y: margin.top }}
+              to={{ x: tooltipLeft, y: innerHeight + margin.top }}
+              stroke={accentColorDark}
+              strokeWidth={2}
+              pointerEvents="none"
+              strokeDasharray="5,2"
+            />
+            <circle
+              cx={tooltipLeft}
+              cy={tooltipTop + 1}
+              r={4}
+              fill="black"
+              fillOpacity={0.1}
+              stroke="black"
+              strokeOpacity={0.1}
+              strokeWidth={2}
+              pointerEvents="none"
+            />
+            <circle
+              cx={tooltipLeft}
+              cy={tooltipTop}
+              r={4}
+              fill={accentColorDark}
+              stroke="white"
+              strokeWidth={2}
+              pointerEvents="none"
+            />
+          </g>
         )}
-      </div>
-    );
-  }
-);
+      </svg>
+      {tooltipData && (
+        <div>
+          <TooltipWithBounds
+            key={Math.random()}
+            top={tooltipTop - 12}
+            left={tooltipLeft + 12}
+            style={tooltipStyles}
+          >
+            {getValue(tooltipData)}
+          </TooltipWithBounds>
+          <Tooltip
+            top={-10}
+            left={tooltipLeft}
+            style={{
+              ...defaultStyles,
+              background: "#26272f",
+              color: "#aaaabb",
+              width: 100,
+              paddingTop: 35,
+              textAlign: "center",
+              transform: "translateX(-60px)",
+            }}
+          >
+            {formatDate(getDate(tooltipData))}
+          </Tooltip>
+        </div>
+      )}
+    </div>
+  );
+};
+
+const TooltipHelper = () => {};
+
+export default AreaChart;

+ 72 - 31
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/MetricsSection.tsx

@@ -117,6 +117,7 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
     { value: "network", label: "Network Received Bytes (Ki)" },
   ]);
   const [isLoading, setIsLoading] = useState(0);
+  const [hpaData, setHpaData] = useState([]);
 
   const { currentCluster, currentProject, setCurrentError } = useContext(
     Context
@@ -253,6 +254,51 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
       });
   };
 
+  const getAutoscalingThreshold = (
+    kind: string,
+    shouldsum: boolean,
+    namespace: string,
+    start: number,
+    end: number
+  ) => {
+    return api.getMetrics(
+      "<token>",
+      {
+        cluster_id: currentCluster.id,
+        metric: kind,
+        shouldsum: shouldsum,
+        kind: selectedController?.kind,
+        name: selectedController?.metadata.name,
+        namespace: namespace,
+        startrange: start,
+        endrange: end,
+        resolution: resolutions[selectedRange],
+        pods: [],
+      },
+      {
+        id: currentProject.id,
+      }
+    );
+  };
+
+  const parseCPUMetrics = (arr: Array<{ date: number; cpu: string }>) => {
+    return arr.map((d) => {
+      return {
+        date: d.date,
+        value: parseFloat(d.cpu),
+      };
+    });
+  };
+
+  const parseMemoryMetrics = (arr: Array<{ date: number; memory: string }>) => {
+    return arr.map((d) => {
+      return {
+        date: d.date,
+        value: parseFloat(d.memory) / (1024 * 1024), // put units in Mi
+      };
+    });
+  };
+
   const getMetrics = () => {
     if (pods?.length == 0) {
       return;
@@ -304,45 +350,39 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
         if (!Array.isArray(res.data) || !res.data[0]?.results) {
           return;
         }
+
         // transform the metrics to expected form
         if (kind == "cpu") {
           let data = res.data as MetricsCPUDataResponse;
 
           // if summed, just look at the first data
-          let tData = data[0].results.map(
-            (
-              d: {
-                date: number;
-                cpu: string;
-              },
-              i: number
-            ) => {
-              return {
-                date: d.date,
-                value: parseFloat(d.cpu),
-              };
-            }
-          );
-
-          setData(tData);
+          let tData = parseCPUMetrics(data[0].results);
+          getAutoscalingThreshold(
+            "cpu_hpa_threshold",
+            shouldsum,
+            namespace,
+            start,
+            end
+          ).then((res) => {
+            const data = res.data as MetricsCPUDataResponse;
+            setHpaData(parseCPUMetrics(data[0].results));
+            setData(tData);
+          });
         } else if (kind == "memory") {
           let data = res.data as MetricsMemoryDataResponse;
 
-          let tData = data[0].results.map(
-            (
-              d: {
-                date: number;
-                memory: string;
-              },
-              i: number
-            ) => {
-              return {
-                date: d.date,
-                value: parseFloat(d.memory) / (1024 * 1024), // put units in Mi
-              };
-            }
-          );
-
+          let tData = parseMemoryMetrics(data[0].results);
+          getAutoscalingThreshold(
+            "memory_hpa_threshold",
+            shouldsum,
+            namespace,
+            start,
+            end
+          ).then((res) => {
+            const data = res.data as MetricsMemoryDataResponse;
+            setHpaData(parseMemoryMetrics(data[0].results));
+            setData(tData);
+          });
           setData(tData);
         } else if (kind == "network") {
           let data = res.data as MetricsNetworkDataResponse;
@@ -538,6 +578,7 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
           {({ width, height }) => (
             <AreaChart
               data={data}
+              hpaData={hpaData}
               width={width}
               height={height - 10}
               resolution={selectedRange}

+ 2 - 2
dashboard/src/shared/api.tsx

@@ -558,8 +558,8 @@ const getMetrics = baseApi<
     cluster_id: number;
     metric: string;
     shouldsum: boolean;
-    pods: string[];
-    kind: string, // the controller kind
+    pods?: string[];
+    kind?: string; // the controller kind
     name: string;
     namespace: string;
     startrange: number;

+ 6 - 4
internal/kubernetes/prometheus/metrics.go

@@ -117,6 +117,8 @@ func QueryPrometheus(
 		query = fmt.Sprintf("sum(%s)", query)
 	}
 
+	fmt.Println(query)
+
 	queryParams := map[string]string{
 		"query": query,
 		"start": fmt.Sprintf("%d", opts.StartRange),
@@ -231,14 +233,14 @@ func getPodSelectionRegex(kind, name string) (string, error) {
 
 func createHPAAbsoluteCPUThresholdQuery(podSelectionRegex, hpaName, namespace string) string {
 	requestCPU := fmt.Sprintf(
-		`sum by (hpa) (label_replace(kube_pod_container_resource_requests_cpu_cores{pod=~"%s",namespace="%s",container!="POD",container!=""},"hpa", "%s", "", ""))`,
+		`sum by (hpa) (label_replace(kube_pod_container_resource_requests_cpu_cores{pod=~"%s",namespace="%s",container!="POD",container!="",app_kubernetes_io_instance="prometheus"},"hpa", "%s", "", ""))`,
 		podSelectionRegex,
 		namespace,
 		hpaName,
 	)
 
 	targetCPUUtilThreshold := fmt.Sprintf(
-		`kube_hpa_spec_target_metric{hpa="%s",namespace="%s",metric_name="cpu",metric_target_type="utilization"} / 100`,
+		`kube_hpa_spec_target_metric{hpa="%s",namespace="%s",metric_name="cpu",metric_target_type="utilization",app_kubernetes_io_instance="prometheus"} / 100`,
 		hpaName,
 		namespace,
 	)
@@ -248,14 +250,14 @@ func createHPAAbsoluteCPUThresholdQuery(podSelectionRegex, hpaName, namespace st
 
 func createHPAAbsoluteMemoryThresholdQuery(podSelectionRegex, hpaName, namespace string) string {
 	requestMem := fmt.Sprintf(
-		`sum by (hpa) (label_replace(kube_pod_container_resource_requests_memory_bytes{pod=~"%s",namespace="%s",container!="POD",container!=""},"hpa", "%s", "", ""))`,
+		`sum by (hpa) (label_replace(kube_pod_container_resource_requests_memory_bytes{pod=~"%s",namespace="%s",container!="POD",container!="",app_kubernetes_io_instance="prometheus"},"hpa", "%s", "", ""))`,
 		podSelectionRegex,
 		namespace,
 		hpaName,
 	)
 
 	targetMemUtilThreshold := fmt.Sprintf(
-		`kube_hpa_spec_target_metric{hpa="%s",namespace="%s",metric_name="memory",metric_target_type="utilization"} / 100`,
+		`kube_hpa_spec_target_metric{hpa="%s",namespace="%s",metric_name="memory",metric_target_type="utilization",app_kubernetes_io_instance="prometheus"} / 100`,
 		hpaName,
 		namespace,
 	)