-
Notifications
You must be signed in to change notification settings - Fork 428
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
Update cgroup resolver to try all possible base paths #1981
Changes from 2 commits
76a7da6
6593cb2
15a2a33
948cc8f
fc4f5b2
ea2a767
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,12 +27,13 @@ | |
#include "src/common/fs/fs_wrapper.h" | ||
#include "src/shared/metadata/cgroup_path_resolver.h" | ||
|
||
DEFINE_bool(force_cgroup2_mode, true, "Flag to force assume cgroup2 fs for testing purposes"); | ||
DEFINE_bool(force_cgroup2_mode, false, "Flag to force assume cgroup2 fs for testing purposes"); | ||
|
||
namespace px { | ||
namespace md { | ||
|
||
StatusOr<std::string> CGroupBasePath(std::string_view sysfs_path) { | ||
StatusOr<std::vector<std::string>> CGroupBasePaths(std::string_view sysfs_path) { | ||
std::vector<std::string> base_paths; | ||
// Different hosts may mount different cgroup dirs. Try a couple for robustness. | ||
const std::vector<std::string> cgroup_dirs = {"cpu,cpuacct", "cpu", "pids"}; | ||
|
||
|
@@ -43,7 +44,7 @@ StatusOr<std::string> CGroupBasePath(std::string_view sysfs_path) { | |
std::string base_path = absl::StrCat(sysfs_path, "/cgroup/", cgroup_dir); | ||
|
||
if (fs::Exists(base_path)) { | ||
return base_path; | ||
base_paths.push_back(base_path); | ||
} | ||
} | ||
|
||
|
@@ -53,11 +54,17 @@ StatusOr<std::string> CGroupBasePath(std::string_view sysfs_path) { | |
bool cgroupv2 = (fs_status == 0) && (info.f_type == CGROUP2_SUPER_MAGIC); | ||
|
||
if (cgroupv2 || FLAGS_force_cgroup2_mode) { | ||
return cgv2_base_path; | ||
if (FLAGS_force_cgroup2_mode) { | ||
return std::vector{cgv2_base_path}; | ||
} | ||
base_paths.push_back(cgv2_base_path); | ||
} | ||
// (TODO): This check for cgroup2FS is eventually to be moved above the cgroupv1 check. | ||
|
||
return error::NotFound("Could not find CGroup base path"); | ||
if (base_paths.empty()) { | ||
return error::NotFound("Could not find CGroup base path"); | ||
} | ||
return base_paths; | ||
Comment on lines
+65
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. minor: Could consider returning the empty list and letting the caller deal with this, now that this returns a list of candidates. |
||
} | ||
|
||
StatusOr<std::string> FindSelfCGroupProcs(std::string_view base_path) { | ||
|
@@ -137,17 +144,32 @@ StatusOr<CGroupTemplateSpec> CreateCGroupTemplateSpecFromPath(std::string_view p | |
} | ||
|
||
StatusOr<CGroupTemplateSpec> AutoDiscoverCGroupTemplate(std::string_view sysfs_path) { | ||
PX_ASSIGN_OR_RETURN(std::string base_path, CGroupBasePath(sysfs_path)); | ||
LOG(INFO) << "Auto-discovered CGroup base path: " << base_path; | ||
|
||
PX_ASSIGN_OR_RETURN(std::string self_cgroup_procs, FindSelfCGroupProcs(base_path)); | ||
LOG(INFO) << "Auto-discovered example path: " << self_cgroup_procs; | ||
|
||
PX_ASSIGN_OR_RETURN(CGroupTemplateSpec cgroup_path_template, | ||
CreateCGroupTemplateSpecFromPath(self_cgroup_procs)); | ||
LOG(INFO) << "Auto-discovered template: " << cgroup_path_template.templated_path; | ||
PX_ASSIGN_OR_RETURN(std::vector<std::string> base_paths, CGroupBasePaths(sysfs_path)); | ||
for (const auto& base_path : base_paths) { | ||
LOG(INFO) << "Auto-discovered CGroup base path: " << base_path; | ||
|
||
auto self_cgroup_procs_status = FindSelfCGroupProcs(base_path); | ||
if (!self_cgroup_procs_status.ok()) { | ||
LOG(WARNING) << "Could not find self in cgroup procs. Trying next base path."; | ||
continue; | ||
} | ||
auto self_cgroup_procs = self_cgroup_procs_status.ConsumeValueOrDie(); | ||
LOG(INFO) << "Auto-discovered example path: " << self_cgroup_procs; | ||
|
||
auto cgroup_path_template_status = CreateCGroupTemplateSpecFromPath(self_cgroup_procs); | ||
if (!cgroup_path_template_status.ok()) { | ||
LOG(WARNING) << absl::Substitute( | ||
"Failed to create cgroup template spec from path $0. Trying next base path.", | ||
self_cgroup_procs); | ||
continue; | ||
} | ||
auto cgroup_path_template = cgroup_path_template_status.ConsumeValueOrDie(); | ||
LOG(INFO) << "Auto-discovered template: " << cgroup_path_template.templated_path; | ||
|
||
return cgroup_path_template; | ||
return cgroup_path_template; | ||
} | ||
return error::NotFound("Unable to auto discover cgroup template from $0", | ||
absl::StrJoin(base_paths, ", ")); | ||
} | ||
|
||
StatusOr<std::unique_ptr<CGroupPathResolver>> CGroupPathResolver::Create( | ||
|
@@ -222,51 +244,52 @@ Status LegacyCGroupPathResolver::Init(std::string_view sysfs_path) { | |
// $2 = container runtime | ||
// These template parameters are resolved by calls to PodPath. | ||
// Different hosts may mount different cgroup dirs. Try a couple for robustness. | ||
PX_ASSIGN_OR_RETURN(std::string cgroup_dir, CGroupBasePath(sysfs_path)); | ||
|
||
// Attempt assuming naming scheme #1. | ||
std::string cgroup_kubepods_base_path = absl::Substitute("$0/kubepods", cgroup_dir); | ||
if (fs::Exists(cgroup_kubepods_base_path)) { | ||
cgroup_kubepod_guaranteed_path_template_ = | ||
absl::StrCat(cgroup_kubepods_base_path, "/pod$0/$1/cgroup.procs"); | ||
cgroup_kubepod_besteffort_path_template_ = | ||
absl::StrCat(cgroup_kubepods_base_path, "/besteffort/pod$0/$1/cgroup.procs"); | ||
cgroup_kubepod_burstable_path_template_ = | ||
absl::StrCat(cgroup_kubepods_base_path, "/burstable/pod$0/$1/cgroup.procs"); | ||
cgroup_kubepod_convert_dashes_ = false; | ||
return Status::OK(); | ||
} | ||
PX_ASSIGN_OR_RETURN(std::vector<std::string> cgroup_dirs, CGroupBasePaths(sysfs_path)); | ||
|
||
// Attempt assuming naming scheme #3. | ||
// Must be before the scheme below, since there have been systems that have both paths, | ||
// but this must take priority. | ||
cgroup_kubepods_base_path = absl::Substitute("$0/system.slice/containerd.service", cgroup_dir); | ||
if (fs::Exists(cgroup_kubepods_base_path)) { | ||
cgroup_kubepod_guaranteed_path_template_ = | ||
absl::StrCat(cgroup_kubepods_base_path, "/kubepods-pod$0.slice:$2:$1/cgroup.procs"); | ||
cgroup_kubepod_besteffort_path_template_ = absl::StrCat( | ||
cgroup_kubepods_base_path, "/kubepods-besteffort-pod$0.slice:$2:$1/cgroup.procs"); | ||
cgroup_kubepod_burstable_path_template_ = absl::StrCat( | ||
cgroup_kubepods_base_path, "/kubepods-burstable-pod$0.slice:$2:$1/cgroup.procs"); | ||
cgroup_kubepod_convert_dashes_ = true; | ||
return Status::OK(); | ||
} | ||
for (const auto& cgroup_dir : cgroup_dirs) { | ||
// Attempt assuming naming scheme #1. | ||
std::string cgroup_kubepods_base_path = absl::Substitute("$0/kubepods", cgroup_dir); | ||
if (fs::Exists(cgroup_kubepods_base_path)) { | ||
cgroup_kubepod_guaranteed_path_template_ = | ||
absl::StrCat(cgroup_kubepods_base_path, "/pod$0/$1/cgroup.procs"); | ||
cgroup_kubepod_besteffort_path_template_ = | ||
absl::StrCat(cgroup_kubepods_base_path, "/besteffort/pod$0/$1/cgroup.procs"); | ||
cgroup_kubepod_burstable_path_template_ = | ||
absl::StrCat(cgroup_kubepods_base_path, "/burstable/pod$0/$1/cgroup.procs"); | ||
cgroup_kubepod_convert_dashes_ = false; | ||
return Status::OK(); | ||
} | ||
|
||
// Attempt assuming naming scheme #2. | ||
cgroup_kubepods_base_path = absl::Substitute("$0/kubepods.slice", cgroup_dir); | ||
if (fs::Exists(cgroup_kubepods_base_path)) { | ||
cgroup_kubepod_guaranteed_path_template_ = | ||
absl::StrCat(cgroup_kubepods_base_path, "/kubepods-pod$0.slice/$2-$1.scope/cgroup.procs"); | ||
cgroup_kubepod_besteffort_path_template_ = absl::StrCat( | ||
cgroup_kubepods_base_path, | ||
"/kubepods-besteffort.slice/kubepods-besteffort-pod$0.slice/$2-$1.scope/cgroup.procs"); | ||
cgroup_kubepod_burstable_path_template_ = absl::StrCat( | ||
cgroup_kubepods_base_path, | ||
"/kubepods-burstable.slice/kubepods-burstable-pod$0.slice/$2-$1.scope/cgroup.procs"); | ||
cgroup_kubepod_convert_dashes_ = true; | ||
return Status::OK(); | ||
} | ||
// Attempt assuming naming scheme #3. | ||
// Must be before the scheme below, since there have been systems that have both paths, | ||
// but this must take priority. | ||
cgroup_kubepods_base_path = absl::Substitute("$0/system.slice/containerd.service", cgroup_dir); | ||
if (fs::Exists(cgroup_kubepods_base_path)) { | ||
cgroup_kubepod_guaranteed_path_template_ = | ||
absl::StrCat(cgroup_kubepods_base_path, "/kubepods-pod$0.slice:$2:$1/cgroup.procs"); | ||
cgroup_kubepod_besteffort_path_template_ = absl::StrCat( | ||
cgroup_kubepods_base_path, "/kubepods-besteffort-pod$0.slice:$2:$1/cgroup.procs"); | ||
cgroup_kubepod_burstable_path_template_ = absl::StrCat( | ||
cgroup_kubepods_base_path, "/kubepods-burstable-pod$0.slice:$2:$1/cgroup.procs"); | ||
cgroup_kubepod_convert_dashes_ = true; | ||
return Status::OK(); | ||
} | ||
|
||
// Attempt assuming naming scheme #2. | ||
cgroup_kubepods_base_path = absl::Substitute("$0/kubepods.slice", cgroup_dir); | ||
if (fs::Exists(cgroup_kubepods_base_path)) { | ||
cgroup_kubepod_guaranteed_path_template_ = | ||
absl::StrCat(cgroup_kubepods_base_path, "/kubepods-pod$0.slice/$2-$1.scope/cgroup.procs"); | ||
cgroup_kubepod_besteffort_path_template_ = absl::StrCat( | ||
cgroup_kubepods_base_path, | ||
"/kubepods-besteffort.slice/kubepods-besteffort-pod$0.slice/$2-$1.scope/cgroup.procs"); | ||
cgroup_kubepod_burstable_path_template_ = absl::StrCat( | ||
cgroup_kubepods_base_path, | ||
"/kubepods-burstable.slice/kubepods-burstable-pod$0.slice/$2-$1.scope/cgroup.procs"); | ||
cgroup_kubepod_convert_dashes_ = true; | ||
return Status::OK(); | ||
} | ||
} | ||
return error::NotFound("Could not find kubepods slice under sysfs ($0)", sysfs_path); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -317,6 +317,7 @@ TEST(LegacyCGroupPathResolverTest, StandardFormat) { | |
} | ||
|
||
TEST(LeagcyCGroupPathResolverTest, Cgroup2Format) { | ||
PX_SET_FOR_SCOPE(FLAGS_force_cgroup2_mode, true); | ||
ASSERT_OK_AND_ASSIGN( | ||
auto path_resolver, | ||
LegacyCGroupPathResolver::Create(GetSysFsPathFromTestDataFile( | ||
|
@@ -326,7 +327,6 @@ TEST(LeagcyCGroupPathResolverTest, Cgroup2Format) { | |
"cgroup.procs", | ||
"testdata/sysfs3"))); | ||
|
||
FLAGS_force_cgroup2_mode = true; | ||
EXPECT_EQ( | ||
GetPathToTestDataFile( | ||
"testdata/sysfs3/cgroup/kubepods.slice/kubepods-besteffort.slice/" | ||
|
@@ -348,6 +348,46 @@ TEST(LeagcyCGroupPathResolverTest, Cgroup2Format) { | |
ContainerType::kDocker)); | ||
} | ||
|
||
// This tests simulates a system where cgroup v1 and v2 are both present. This has been observed | ||
// in OVH's managed kubernetes service. | ||
// | ||
// The testdata/sysfs4-cgroup-v1andv2 directory was created with the following commands: | ||
// sudo mount -t overlay overlay -o lowerdir=sysfs2/cgroup:sysfs3/cgroup \ | ||
// sysfs4-cgroup-v1andv2/cgroup | ||
// | ||
// sysfsv2 and sysfsv3 model cgroupv1 and cgroupv2 respectively. | ||
TEST(LeagcyCGroupPathResolverTest, Cgroup2With1) { | ||
PX_SET_FOR_SCOPE(FLAGS_force_cgroup2_mode, true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do need this in this case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason for this is that the LegacyCGroupPathResolver doesn't operate on a real cgroupv2 filesystem when under test. The newer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've removed this test case after more deeply thinking about what test cases we would ideally cover. I've added a TODO explaining those cases in its place. We have to keep the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, a different PR for that refactor is best anyways. |
||
ASSERT_OK_AND_ASSIGN( | ||
auto path_resolver, | ||
LegacyCGroupPathResolver::Create(GetSysFsPathFromTestDataFile( | ||
"testdata/sysfs4-cgroup-v1andv2/cgroup/kubepods.slice/kubepods-besteffort.slice/" | ||
"kubepods-besteffort-pod47810e8e_b9cb_4ac6_b12d_9e0577fa8237.slice/" | ||
"docker-28efca84cc7d707bdfbc5646144bba6c4417de2cf63f8583179603ce434d6dfe.scope/" | ||
"cgroup.procs", | ||
"testdata/sysfs4-cgroup-v1andv2"))); | ||
|
||
EXPECT_EQ( | ||
GetPathToTestDataFile( | ||
"testdata/sysfs4-cgroup-v1andv2/cgroup/kubepods.slice/kubepods-besteffort.slice/" | ||
"kubepods-besteffort-pod47810e8e_b9cb_4ac6_b12d_9e0577fa8237.slice/" | ||
"docker-28efca84cc7d707bdfbc5646144bba6c4417de2cf63f8583179603ce434d6dfe.scope/" | ||
"cgroup.procs"), | ||
path_resolver->PodPath(PodQOSClass::kBestEffort, "47810e8e_b9cb_4ac6_b12d_9e0577fa8237", | ||
"28efca84cc7d707bdfbc5646144bba6c4417de2cf63f8583179603ce434d6dfe", | ||
ContainerType::kDocker)); | ||
|
||
EXPECT_EQ( | ||
GetPathToTestDataFile( | ||
"testdata/sysfs4-cgroup-v1andv2/cgroup/kubepods.slice/kubepods-burstable.slice/" | ||
"kubepods-burstable-pod16de73f898f4460d96d28cf19ba8407f.slice/" | ||
"docker-23ac1540f833b029f76af6a513c4861a54bb9b77a6e3648b6f8392b1a09686ba.scope/" | ||
"cgroup.procs"), | ||
path_resolver->PodPath(PodQOSClass::kBurstable, "16de73f898f4460d96d28cf19ba8407f", | ||
"23ac1540f833b029f76af6a513c4861a54bb9b77a6e3648b6f8392b1a09686ba", | ||
ContainerType::kDocker)); | ||
} | ||
|
||
TEST(CGroupPathResolver, Cgroup2Format) { | ||
std::string cgroup_kubepod_path = | ||
"/sys/fs/cgroup/kubepods.slice/" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
123 | ||
456 | ||
789 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add add
test_only
somewhere in the name of this flag.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, now that we have a more robust cgroup resolver, can we get rid of this flag, and just rely on different testdata to make sure we cover {cgroup1 only, cgroup2 only, cgroup1+cgroup2 w/ cgroup1 failing, cgroup1+cgroup2 w/ cgroup1 succeeding}.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are the complications with covering those cases:
testdata
directory.In order to achieve the ideal test coverage, the CGroupPathResolver needs test coverage for AutoDiscoverCGroupTemplate and for the legacy path resolver needs to be able to pass the cgroup v2 filesystem check. The latter is what I was trying to accomplish with an overlayfs (unfortunately that doesn't work).
I'm going to spend more time thinking about how to address those problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After thinking about this more, I don't think there is an easy way to test this without rearchitecting the code quite a bit. My most recent research was around testing the
AutoDiscoverCGroupTemplate
function. I was hoping I could recreate the bug with fake cgroup v1 files and a bind mount to/sys/fs/cgroup
(or some subdirectory) in order to have that function fail. Bazel unfortunately does not like a cgroup filesystem bind mount, so this isn't viable.I'm currently thinking that the test investment right now might not be worth holding up this fix. What do you think about putting a TODO here explaining the test coverage we wish we'd have?