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

Add support for CgroupsV2 in beats, migrate away from gosigar #27242

Merged
merged 35 commits into from
Aug 12, 2021

Conversation

fearful-symmetry
Copy link
Contributor

@fearful-symmetry fearful-symmetry commented Aug 4, 2021

What does this PR do?

This PR accomplishes a few different things:

  • Adds support for cgroupsV2
  • Continues our deprecation of elastic/gosigar, by moving the cgroups code into libbeat. A considerable portion of the code here is pre-existing code from gosigar that's been reorganized.
  • Moves the opt library from metricbeat/internal/metrics to libbeat, since we use it here too.
  • Refactors the existing cgroups V1 code to be more in line with the refactor of the system module, removing the MapStr manipulation code in favor of having the data format hard-coded into the structs, and makes the cgroups code overall more "metrics first"

Note that supporting cgroups V1 and V2 involves some compromises, as the two versions structure themselves and report data fairly differently, and as such, most of the metrics APIs require the consumer to differentiate between V1 and V2 if they want to access raw metrics. V1 and V2 can also cooexist on the same system, so this must happen on a pid-by-pid basis.

Also, I'm still testing this on V1 and mixed v1/v2 systems, but the code is otherwise ready for review.

There's also the issue of dashboards. The system Docker dashboard relies on many of the V1 fields that aren't present in V2, and I'm not really sure how to deal with them. Last I tried, we don't really have any mechanism for dashboards to operate with the logic of "use this field if present, otherwise use this other field." We may also just want to re-write the dashboard entirely to just use fields that are present in V1 and V2, at the expense of losing some of the visualizations.

Also keep in mind that most of system/process is going to be aggressively refactored after this, so any issues, unless they're serious, with the code in libbeat/metric/system/process and metricbeat/system/process will almost certainly be fixed as part of 7.16.

What's up with cgroupsV2

Cgroups V2 introduces a few major changes compared to cgroups V1, which necessitated a lot of extra code:

  • A new hierarchy for processes. This "unified" hierarchy requires new logic for code that wants to track processes in a cgroup
  • New controllers. The various resource controllers (cpuacct, memory, etc) have changed dramatically, and in a few cases have merged with other controllers for V2. This requires entirely new metrics code.

File structure

For ease of browsing through this PR (Github honestly isn't great at presenting large PRs), here's the breakdown of what's in here:

libbeat/metric/system/cgroup // The entirety of the cgroup implementation. The files in this base directory are (mostly) unchanged files from gosigar.
├── cgcommon // Files that are shared by cgv1 and cgv2. Mostly struct definitions and helper code
├── cgv1 // Metrics  for cgroups v1. This is almost entirely code from elastic/gosigar that's been refactored.
├── cgv2 // metrics for cgroups v2. This is entirely new code.
└── testdata // data used by by the various *_test.go files

This also includes:

  • libbeat/opt Which is mostly existing code that was moved from metricbeat/internal.
  • Changes to cmd/instance, add_process_metadata and add_docker_metadata to migrate away from gosigar
  • changes to metricbeat/internal to deal with moving the libbeat/opt code.

Why is it important?

Cgroups V2 is coming, and is already default on Fedora. It's supported by most other software at this point.

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have made corresponding change to the default configuration files
  • I have added tests that prove my fix is effective or that my feature works
  • I have added an entry in CHANGELOG.next.asciidoc or CHANGELOG-developer.next.asciidoc.

How to test this PR locally

  • Pull down and build
  • Note what cgroups version your host is using. You can see with grep cgroup /proc/self/mountinfo
  • Test the system/process metricset, as well as add_process_metadata and add_docker_metadata

@fearful-symmetry fearful-symmetry requested a review from a team August 4, 2021 22:30
@fearful-symmetry fearful-symmetry self-assigned this Aug 4, 2021
@elasticmachine
Copy link
Collaborator

Pinging @elastic/integrations (Team:Integrations)

@botelastic botelastic bot added needs_team Indicates that the issue/PR needs a Team:* label and removed needs_team Indicates that the issue/PR needs a Team:* label labels Aug 4, 2021
@elasticmachine
Copy link
Collaborator

elasticmachine commented Aug 4, 2021

💚 Build Succeeded

the below badges are clickable and redirect to their specific view in the CI or DOCS
Pipeline View Test View Changes Artifacts preview preview

Expand to view the summary

Build stats

  • Start Time: 2021-08-11T20:07:48.440+0000

  • Duration: 141 min 16 sec

  • Commit: eeda969

Test stats 🧪

Test Results
Failed 0
Passed 52571
Skipped 5206
Total 57777

Trends 🧪

Image of Build Times

Image of Tests

💚 Flaky test report

Tests succeeded.

Expand to view the summary

Test stats 🧪

Test Results
Failed 0
Passed 52571
Skipped 5206
Total 57777

Copy link
Member

@jsoriano jsoriano left a comment

Choose a reason for hiding this comment

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

Looks quite good. I haven't reviewed the code moved from gosigar, if we want to make any refactor there I would leave it for a separate review. If there are relevant changes there please point to them 🙂

libbeat/cmd/instance/metrics/metrics.go Show resolved Hide resolved
libbeat/cmd/instance/metrics/metrics.go Outdated Show resolved Hide resolved
libbeat/metric/system/process/process.go Outdated Show resolved Hide resolved
@@ -576,19 +531,22 @@ func (procStats *Stats) getSingleProcess(pid int, newProcs ProcsMap) *Process {

err = process.getDetails(procStats.isWhitelistedEnvVar)
if err != nil {
procStats.logger.Debugf("Error getting details for process %s with pid=%d: %v", process.Name, process.Pid, err)
procStats.logger.Errorf("Error getting details for process %s with pid=%d: %v", process.Name, process.Pid, err)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe this was intentionally logged at the debug level? 😬 I can imagine this flooding logs in hosts with many short-lived processes.

Copy link
Member

Choose a reason for hiding this comment

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

Btw, this reminds me to #9111.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yah, just decided to refactor most of this. Even if this library is going away in 7.16, it's still kind of a mess.

libbeat/metric/system/process/process.go Outdated Show resolved Hide resolved
metricbeat/module/system/process/_meta/fields.yml Outdated Show resolved Hide resolved
@fearful-symmetry fearful-symmetry requested review from jsoriano and a team August 11, 2021 04:36
@fearful-symmetry
Copy link
Contributor Author

@jsoriano thanks for the review! addressed most of the issues, and finally tracked down the bug that was breaking CI.

Copy link
Member

@jsoriano jsoriano left a comment

Choose a reason for hiding this comment

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

Minor comments added, nothing blocking, but there still seems to be related failures in CI.

libbeat/metric/system/process/process.go Outdated Show resolved Hide resolved
@@ -202,6 +202,10 @@ func SubsystemMountpoints(rootfsMountpoint string, subsystems map[string]struct{

// V2 option
if mount.filesystemType == "cgroup2" {
if len(mount.mountpoint) < 3 {
// Why is this happening? I don't know.
Copy link
Member

Choose a reason for hiding this comment

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

😃

Where does it happen? How does mountinfo look in these cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Think you're looking at an older version, that's while I was debugging an issue with hybrid cgroups.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, but I was curious about what appeared in mountinfo when this happens 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh! The cgroups line just...isn't there.

libbeat/metric/system/cgroup/util.go Show resolved Hide resolved
@fearful-symmetry fearful-symmetry requested a review from a team August 11, 2021 21:02
result, err := provider.GetCid(1)
assert.NoError(t, err)
assert.Equal(t, "2dcbab615aebfa9313feffc5cfdacd381543cfa04c6be3f39ac656e55ef34805", result)
}
Copy link
Member

Choose a reason for hiding this comment

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

👍 thanks!

@fearful-symmetry fearful-symmetry merged commit d898533 into elastic:master Aug 12, 2021
fearful-symmetry added a commit to fearful-symmetry/beats that referenced this pull request Aug 12, 2021
…c#27242)

* finish first round of cgroupsv2 support

* fix tests

* remove old code, fix PctOpt

* fix imports

* go mod tidy

* try to fix make notice again

* try again

* remove unneeded test files

* fix test paths

* fix crossbuild issues

* fix tests, remove debug statement

* change metadata, fight with mapping defs

* fix v1 test, remove debug line

* somewhat hacky fix for fields issues

* fix v1 fetch, update fields again

* fix omitempty issue

* make update

* remove older test

* fix tests, cgv1 logic

* remove old debug statement

* fix issue with how ubuntu mixes cgroups

* clean up error handling in libbeat

* changes based on feedback, increased error verbosity to try to fix baffling CI errors

* fix fields, add more error messages for weird CI bug

* I give up, add tons of debug statements

* fix issue with docker containers running under hybrid cgroups

* fix hostfs state check

* fix more broken tests

* fix names, log levels

* more changes, docs, test

* still making the mapping checks happy

* fix libbeat code I broke

(cherry picked from commit d898533)
fearful-symmetry added a commit that referenced this pull request Aug 12, 2021
#27346)

* finish first round of cgroupsv2 support

* fix tests

* remove old code, fix PctOpt

* fix imports

* go mod tidy

* try to fix make notice again

* try again

* remove unneeded test files

* fix test paths

* fix crossbuild issues

* fix tests, remove debug statement

* change metadata, fight with mapping defs

* fix v1 test, remove debug line

* somewhat hacky fix for fields issues

* fix v1 fetch, update fields again

* fix omitempty issue

* make update

* remove older test

* fix tests, cgv1 logic

* remove old debug statement

* fix issue with how ubuntu mixes cgroups

* clean up error handling in libbeat

* changes based on feedback, increased error verbosity to try to fix baffling CI errors

* fix fields, add more error messages for weird CI bug

* I give up, add tons of debug statements

* fix issue with docker containers running under hybrid cgroups

* fix hostfs state check

* fix more broken tests

* fix names, log levels

* more changes, docs, test

* still making the mapping checks happy

* fix libbeat code I broke

(cherry picked from commit d898533)
logp.L().Infof(`PID %d contains a cgroups V2 path (%s) but no V2 mountpoint was found.
This may be because metricbeat is running inside a container on a hybrid system.
To monitor cgroups V2 processess in this way, mount the unified (V2) hierarchy inside
the container as /sys/fs/cgroup/unified and start metricbeat with --system.hostfs.`, pid, line)
Copy link
Member

@jsoriano jsoriano Aug 18, 2021

Choose a reason for hiding this comment

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

@fearful-symmetry in an elastic-agent started with elastic-package stack in my laptop, I see continuously (every collection period) this message, I think we should move it to the debug level. In a system where this happens this is going to be continuosly logged.

I use a quite vanilla Ubuntu 18.04.5 LTS, with Linux 4.15, and docker 20.10.7. So maybe this is not so an edge case.

Also, as far as I know, both my host and the docker containers are using cgroups V1 (I don't see any cgroups2 mountpoint or any unified cgroup) so I wonder if this comment about hybrid systems can be misleading in this case.

For reference, this is what I see in logs:

2021-08-18T08:15:09.797Z	INFO	cgroup/util.go:257	PID 10 contains a cgroups V2 path (0::/system.slice/containerd.service) but no V2 mountpoint was found.
This may be because metricbeat is running inside a container on a hybrid system.
To monitor cgroups V2 processess in this way, mount the unified (V2) hierarchy inside
the container as /sys/fs/cgroup/unified and start metricbeat with --system.hostfs.

This is how /proc/10/cgroup looks like in the container:

12:freezer:/docker/b257ef6e9abdac56a8a07bf2e35abc7a064b0638e893bc54ab98d925bba13ecd
11:devices:/docker/b257ef6e9abdac56a8a07bf2e35abc7a064b0638e893bc54ab98d925bba13ecd
10:cpu,cpuacct:/docker/b257ef6e9abdac56a8a07bf2e35abc7a064b0638e893bc54ab98d925bba13ecd
9:pids:/docker/b257ef6e9abdac56a8a07bf2e35abc7a064b0638e893bc54ab98d925bba13ecd
8:hugetlb:/docker/b257ef6e9abdac56a8a07bf2e35abc7a064b0638e893bc54ab98d925bba13ecd
7:blkio:/docker/b257ef6e9abdac56a8a07bf2e35abc7a064b0638e893bc54ab98d925bba13ecd
6:perf_event:/docker/b257ef6e9abdac56a8a07bf2e35abc7a064b0638e893bc54ab98d925bba13ecd
5:cpuset:/docker/b257ef6e9abdac56a8a07bf2e35abc7a064b0638e893bc54ab98d925bba13ecd
4:memory:/docker/b257ef6e9abdac56a8a07bf2e35abc7a064b0638e893bc54ab98d925bba13ecd
3:net_cls,net_prio:/docker/b257ef6e9abdac56a8a07bf2e35abc7a064b0638e893bc54ab98d925bba13ecd
2:rdma:/
1:name=systemd:/docker/b257ef6e9abdac56a8a07bf2e35abc7a064b0638e893bc54ab98d925bba13ecd
0::/system.slice/containerd.service

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jsoriano

0::/system.slice/containerd.service

So, that's the V2 controller there. You said you didn't see any unified mountpoint on the system? Can you get me /proc/self/mountinfo ?

However, I think you're right that we might want some kind of behavior to prevent this message from getting spammy. Unfortunately, from within a docker container it's really hard to get that info without the user mounting sysfs inside the container. Maybe just have it print the message at startup?

Copy link
Member

Choose a reason for hiding this comment

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

Oh, you are right, there is some cgroup2 around, I didn't see it.

$ cat /proc/self/mountinfo  | grep cgroup2
34 33 0:29 / /sys/fs/cgroup/unified rw,nosuid,nodev,noexec,relatime shared:10 - cgroup2 cgroup rw,nsdelegate

So yeah, let's keep the message but trying to make it less verbose.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Team:Integrations Label for the Integrations team v7.15.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants