Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize network metrics collection #3302

Merged
merged 1 commit into from
Jun 8, 2023

Conversation

kolyshkin
Copy link
Contributor

Apparently, we collect network stats for all containers, and then discarding some or most of it:

  • for docker, we collect and discard stats for containers which share netns with another container (which is rare I guess);

  • for both crio and containerd, we collect and discard stats for containers that are not infra (sandbox, pod, pause) containers (which is very common).

Instead of reading and parsing a bunch of files in /proc/PID/net and when removing those, let's set things up in a way so we don't collect useless stats in the first place.

This should improve performance, memory usage, and ease the load on garbage collection.

@k8s-ci-robot
Copy link
Collaborator

Hi @kolyshkin. Thanks for your PR.

I'm waiting for a google member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@mrunalp
Copy link
Collaborator

mrunalp commented May 4, 2023

/ok-to-test

@kolyshkin
Copy link
Contributor Author

@bobbypage PTAL

Also, is there anything I can do to skip waiting for ok-to-test? The bot says I need to join kubernetes org but I am already a member.

@bobbypage
Copy link
Collaborator

bobbypage commented May 4, 2023

Thanks @kolyshkin for the investigation here and perf fixes. Will take a closer look!

Also, is there anything I can do to skip waiting for ok-to-test? The bot says I need to join kubernetes org but I am already a member.

Hmm, I'm not sure, I was under the assumption that all k8s members could skip the ok-to-test, but there may be some differences for non k8s repos...

Comment on lines 188 to 191
if !metrics.HasAny(container.AllNetworkMetrics) {
return metrics
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason we need to first check if the network metrics are included and only if true, then we call metrics.Difference? Shouldn't it be safe to always call metric.Difference (if the network metrics are not included, it should be a no-op)?

Or is this check more a performance optimization to avoid creating a new MetricSet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is a performance optimization -- why bothering with copying if we don't have to? The downside is code looks a bit less compact than it could be -- but still pretty readable.

This comment was marked as off-topic.

@@ -217,7 +216,6 @@ func newDockerContainerHandler(
Namespace: DockerNamespace,
}
handler.image = ctnr.Config.Image
handler.networkMode = ctnr.HostConfig.NetworkMode
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because the only user of h.networkMode was func (h *dockerContainerHandler) needNet() bool.

That method is now gone, and so the field is unused.

@bobbypage
Copy link
Collaborator

bobbypage commented May 4, 2023

A couple small questions but changes largely LGTM. Has this been tested locally? It would be nice to test this end to end, by integrating into k8s and ensuring that the network metrics are still present.

We have an e2e test in k8s, summary_test that verifies network metrics (xref: https://github.com/kubernetes/kubernetes/blob/master/test/e2e_node/summary_test.go#L208-L218).

One idea, can you maybe open a WIP PR to k/k that vendors this copy of cAdvisor (with the changes in the PR) and then use that WIP PR to run pull job in k/k to verify network metrics are still working correctly?

You should be able to vendor this into k/k PR via:

$ hack/pin-dependency.sh github.com/google/cadvisor=github.com/kolyshkin/cadvisor optimize-net-metrics
$ hack/update-vendor.sh

@kolyshkin
Copy link
Contributor Author

Sure; kubernetes/kubernetes#117833

@BenTheElder
Copy link

Also, is there anything I can do to skip waiting for ok-to-test? The bot #3302 (comment) but I am already a member.

It's actually checking if you you're a member of the organization the PR is opened to (so github.com/google), but the join link is mis-configured to Kubernetes's.

The first part of the bot message is correct:

I'm waiting for a google member to verify that this patch is reasonable to test.

But the join link is wrongly configured: https://github.com/kubernetes/test-infra/blob/62ef55338ead7466542e0c20d226c695f97ac3f4/config/prow/plugins.yaml#L21

Prow had support for setting a trusted organization but that's been long deprecated in favor of "is a member of the organization that owns the repo || is a collaborator on the repo" (which can be restricted to only org members by config).

cAdvisor as a Google project should stop using Kubernetes's instance and use the Google OSS instance anyhow (#3116), now that we have distinct instances and dedicated funding for Kubernetes (well, for years now ... it's one of the few projects lagging on switching).

@iwankgb
Copy link
Collaborator

iwankgb commented May 7, 2023

Could you provide tests for maybeRemoveNet() to avoid weird regression in future, please?

@kolyshkin kolyshkin marked this pull request as draft May 8, 2023 21:30
@kolyshkin kolyshkin marked this pull request as ready for review May 8, 2023 21:43
@kolyshkin
Copy link
Contributor Author

OK, I did a rewrite to avoid code repetition, added a unit tests for RemoveNetMetrics (as requested by @iwankgb), improved some comments and hopefully made the whole thing more readable.

kubernetes/kubernetes#117833 is also updated.

@iwankgb
Copy link
Collaborator

iwankgb commented May 9, 2023

@bobbypage if you don't have more comments then I'm going to merge it tomorrow.

Apparently, we collect network stats for *all* containers, and then
discard most (or some) of these statistics:

 - for both crio and containerd, we collect and discard stats for non-infra
   containers containers (i.e. most of containers);

 - for docker, we collect and discard stats for containers which share
   netns with another container (which is rare I guess);

Instead of reading and parsing a bunch of files in /proc/PID/net and
when discarding the just-gathered stats, let's set things up in a way so
we don't collect useless stats in the first place.

This should improve performance, memory usage, and ease the load on
garbage collection.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
@kolyshkin
Copy link
Contributor Author

Whoops, fixed the logic (that was inverted for containerd and crio). 😊

diff --git a/container/containerd/handler.go b/container/containerd/handler.go
index 626d4fd3..57a2d82c 100644
--- a/container/containerd/handler.go
+++ b/container/containerd/handler.go
@@ -131,7 +131,7 @@ func newContainerdContainerHandler(
        // infrastructure container -- does not need their stats to be
        // reported. This stops metrics being reported multiple times for each
        // container in a pod.
-       metrics := common.RemoveNetMetrics(includedMetrics, cntr.Labels["io.cri-containerd.kind"] == "sandbox")
+       metrics := common.RemoveNetMetrics(includedMetrics, cntr.Labels["io.cri-containerd.kind"] != "sandbox")
 
        libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), metrics)
 
diff --git a/container/crio/handler.go b/container/crio/handler.go
index e945b790..a6832518 100644
--- a/container/crio/handler.go
+++ b/container/crio/handler.go
@@ -154,7 +154,7 @@ func newCrioContainerHandler(
        // infrastructure container -- does not need their stats to be
        // reported. This stops metrics being reported multiple times for each
        // container in a pod.
-       metrics := common.RemoveNetMetrics(includedMetrics, cInfo.Labels["io.kubernetes.container.name"] == "POD")
+       metrics := common.RemoveNetMetrics(includedMetrics, cInfo.Labels["io.kubernetes.container.name"] != "POD")
 
        libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootFs, cInfo.Pid, metrics)
 

kolyshkin added a commit to kolyshkin/kubernetes that referenced this pull request May 9, 2023
This is just to run e2e tests with cAdvisor from
google/cadvisor#3302

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
@kolyshkin
Copy link
Contributor Author

kolyshkin commented May 9, 2023

Whoops, fixed the logic (that was inverted for containerd and crio).

OTOH this gave a way to check if the test mentioned in #3302 (comment) is working.

Indeed it is! From https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/117833/pull-kubernetes-node-e2e-containerd/1655703979042017280/:

E2eNode Suite: [It] [sig-node] Summary API [NodeConformance] when querying /stats/summary should report resource usage through the stats api expand_less | 3m21s

{ failed [FAILED] Timed out after 180.001s.
Expected
    <string>: Summary
to match fields: {
[.Pods[summary-test-836::stats-busybox-0].Network:
	Expected
	    <*v1alpha1.NetworkStats | 0x0>: nil
	not to be <nil>, .Pods[summary-test-836::stats-busybox-1].Network:
	Expected
	    <*v1alpha1.NetworkStats | 0x0>: nil
	not to be <nil>]
}
In [It] at: test/e2e_node/summary_test.go:332 @ 05/08/23 22:58:22.455
}

@kolyshkin
Copy link
Contributor Author

With the change from #3302 (comment) the e2e tests are now passing in kubernetes/kubernetes#117833 (this time I ran crio-e2e as well).

pull-kubernetes-node-crio-e2e — Job succeeded.
pull-kubernetes-node-e2e-containerd — Job succeeded.  

@bobbypage
Copy link
Collaborator

all updates LGTM!

thank you for testing in k/k and glad to see it was helpful and caught a potential issue! :)

@bobbypage
Copy link
Collaborator

@iwankgb please take a final look and merge if it's all ok from your side. LGTM from me.

@kolyshkin
Copy link
Contributor Author

thank you for testing in k/k and glad to see it was helpful and caught a potential issue! :)

I actually found the bug while re-reviewing this PR first, and then took a look at test results. In retrospect, it's good that we validated that the e2e test works.

@iwankgb iwankgb merged commit 31361f2 into google:master Jun 8, 2023
@haircommander
Copy link
Contributor

here's a follow up to this we can make for cri-o #3592

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants