kube icon indicating copy to clipboard operation
kube copied to clipboard

Add support for metrics API

Open deepu105 opened this issue 3 years ago • 17 comments

Is there a way to use the metrics API for Kubernetes? In Go it is a separate package and I believe there is no Rust craete equivalent for that yet

https://kubernetes.io/docs/tasks/debug-application-cluster/resource-metrics-pipeline/

deepu105 avatar Apr 15 '21 16:04 deepu105

kube::Client and kube::Api can work with metrics API (as with any other API). If you are ready to work with untyped JSON, than you can use e.g.:

let gvk = GroupVersionKind::gvk("metrics.k8s.io", "v1beta1" /*IIRC*/, "nodemetrics");
let node_metrics: Api<DynamicObject> = Api::all_with(client.clone(), &gvk);
let metrics_list = node_metrics.list(...).await?;
// now metrics_list is essentially Vec<Json>
...

You may also want typed API (like the one you have for core or apps). But in fact, Kubernetes API typings are provided not by kube itself, but by the k8s-openapi crate.

That's why i think that k8s-openapi repository can be a better place for this issue.

MikailBag avatar Apr 15 '21 19:04 MikailBag

It's also possible that we can simplify how we implement Resource so that you can use Api<MyMetricType> as long as we can impl Resource for MyMetricType where DynamicType = GroupVersionKind in an easy way.

I think we could have a middle ground between completely untyped, and k8s-openapi.

clux avatar Apr 15 '21 21:04 clux

Btw for context. I'm building a Kubernetes terminal dashboard and was trying to use this. Btw if any of you are interested, please consider contributing. I'm quite new to rust and this lib so any help is appreciated

https://github.com/kdash-rs/kdash

deepu105 avatar Apr 16 '21 11:04 deepu105

It's also possible that we can simplify how we implement Resource so that you can use Api<MyMetricType> as long as we can impl Resource for MyMetricType where DynamicType = GroupVersionKind in an easy way.

I think we could have a middle ground between completely untyped, and k8s-openapi.

That would be nice

deepu105 avatar Apr 16 '21 11:04 deepu105

There is a related (closed) issue in k8s-openapi: https://github.com/Arnavion/k8s-openapi/issues/76

Seems like they don't want to support this.

theduke avatar Apr 23 '21 21:04 theduke

Seems like they don't want to support this.

K8s-openapi maintainer has recently merged my PR which adds support for taking spec from arbitrary URL. So ideally you should be able to take spec from a cluster with metrics-server enabled and get typings for metrics.k8s.io group.

The problem is, k8s-openapi seems to make many assumptions about REST operations, so code generation fails for aggregated apis.

As a next step, I'd try adding a parameter to k8s-openapi which completely skips client generation. I hope this will be enough to support non-builtin API groups.

MikailBag avatar Apr 23 '21 21:04 MikailBag

FWIW, here is an implementation for pod metrics that works for me with Api:

#[derive(serde::Deserialize, Clone, Debug)]
pub struct PodMetricsContainer {
    pub name: String,
    pub usage: PodMetricsContainerUsage,
}

#[derive(serde::Deserialize, Clone, Debug)]
pub struct PodMetricsContainerUsage {
    pub cpu: Quantity,
    pub memory: Quantity,
}

#[derive(serde::Deserialize, Clone, Debug)]
pub struct PodMetrics {
    pub metadata: ObjectMeta,
    pub timestamp: String,
    pub window: String,
    pub containers: Vec<PodMetricsContainer>,
}

impl k8s_openapi::Resource for PodMetrics {
    const GROUP: &'static str = "metrics.k8s.io";
    const KIND: &'static str = "pod";
    const VERSION: &'static str = "v1beta1";
    const API_VERSION: &'static str = "metrics.k8s.io/v1beta1";
}

impl k8s_openapi::Metadata for PodMetrics {
    type Ty = ObjectMeta;

    fn metadata(&self) -> &Self::Ty {
        &self.metadata
    }

    fn metadata_mut(&mut self) -> &mut Self::Ty {
        &mut self.metadata
    }
}
Api::<PodMetrics>::namespaces(client.clone(), "default").get("test-pod").await

theduke avatar Apr 24 '21 01:04 theduke

I ended up doing the below for Node metrics

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Usage {
    pub cpu: String,
    pub memory: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeMetrics {
  metadata: kube::api::ObjectMeta,
  usage: Usage,
  timestamp: String,
  window: String,
}

    let request = Request::new("/apis/metrics.k8s.io/v1beta1/nodes");
    client
      .clone()
      .request::<ObjectList<NodeMetrics>>(request.list(&ListParams::default()).unwrap())
      .await

deepu105 avatar Jun 08 '21 08:06 deepu105

May be we can just document the solution from @theduke and close the ticket

deepu105 avatar Jun 08 '21 08:06 deepu105

Yeah, I think there are sufficiently short workarounds at the moment; create structs as above, impl kube::Resource in your source, or use it dynamically with an ApiResource.

Going to mark this as a wontfix for now. I think it would set a difficult precedent for us by trying to do every little peripheral thing in kubernetes ecosystems. I would rather encourage separate packages to help us with code-gen for non-core apis, and have us focus on as much of the generic/apimachinery-esque stuff as possible.

People should be able to find this solution/guidance by searching issues, but I think some kind of document classifying things you can do with kubernetes (in go world) and what the rust-counterparts require, would make sense as a follow-up action here.

clux avatar Jun 21 '21 18:06 clux

Ya documentation sounds like a good solution as the current solutions mentioned here are good enough

On Mon, 21 Jun 2021, 8:24 pm Eirik A, @.***> wrote:

Yeah, I think there are sufficiently short workarounds at the moment; create structs as above, impl kube::Resource in your source, or use it dynamically with an ApiResource.

Going to mark this as a wontfix for now. I think it would set a difficult precedent for us by trying to do every little peripheral thing in kubernetes ecosystems. I would rather encourage separate packages to help us with code-gen for non-core apis, and have us focus on as much of the generic/apimachinery-esque stuff as possible.

People should be able to find this solution/guidance by searching issues, but I think some kind of document classifying things you can do with kubernetes (in go world) and what the rust-counterparts require, would make sense as a follow-up action here.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/clux/kube-rs/issues/492#issuecomment-865249846, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAIOKF5FFZAAQ5L2HZ5POYDTT57VDANCNFSM4274AIQQ .

deepu105 avatar Jun 21 '21 23:06 deepu105

We do actually grab the metrics structs in the new prototype k8s-pb repo. So we will have support for this once we get the protobuf issue #371 resolved. Absolutely no ETA on that though. It's a pretty big project.

clux avatar Nov 03 '21 20:11 clux

Is there swagger.json for metrics? It has // +k8s:openapi-gen=true, but I don't know where the output is.

kazk avatar Nov 03 '21 20:11 kazk

Only reference to it i see was removed from the repo. Following the trail:

  • https://github.com/kubernetes/metrics/commit/3f16cd9d9a9a7fa1fd9bcc3756d9f399e99af870
  • https://github.com/kubernetes/kubernetes/pull/53441
  • https://github.com/kubernetes/kubernetes/issues/44589

Not sure if that's even relevant.

clux avatar Nov 03 '21 21:11 clux

Maybe it's one of those structs that are only ever generated by the metrics-server and so we don't have it on disk. Maybe it needs to be run through https://github.com/kubernetes/kube-openapi ?

I have no idea here. There's very little documentation on this repo. But we might be able to get an answer in #sig-api-machinery or similar channel on the kubernetes slack.

clux avatar Nov 03 '21 21:11 clux

I'm on version 0.15 of the k8s-openapi and there are a few new required properties on the Resource trait, this is what worked for me:

impl k8s_openapi::Resource for PodMetrics {
    const GROUP: &'static str = "metrics.k8s.io";
    const KIND: &'static str = "PodMetrics";
    const VERSION: &'static str = "v1beta1";
    const API_VERSION: &'static str = "metrics.k8s.io/v1beta1";
    const URL_PATH_SEGMENT: &'static str = "pods";

    type Scope = k8s_openapi::NamespaceResourceScope;
}

The rest is just like as mentioned by @theduke above.

goenning avatar Sep 16 '22 12:09 goenning

Fro the sake of reference while this is being sorted out - k8s-metrics crate

imp avatar Jan 16 '23 11:01 imp