Skip to content

Commit 7913d33

Browse files
israbbaniedoakes
authored andcommitted
[core] (cgroups 3/n) Creating CgroupManager to setup Ray's cgroup hierarchy and clean it up (ray-project#56186)
Signed-off-by: irabbani <[email protected]> Signed-off-by: Ibrahim Rabbani <[email protected]> Signed-off-by: Ibrahim Rabbani <[email protected]> Co-authored-by: Edward Oakes <[email protected]> Signed-off-by: zac <[email protected]>
1 parent e621adf commit 7913d33

File tree

9 files changed

+822
-0
lines changed

9 files changed

+822
-0
lines changed

src/ray/common/cgroup2/BUILD.bazel

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,40 @@ ray_cc_library(
1414
],
1515
)
1616

17+
ray_cc_library(
18+
name = "cgroup_manager_interface",
19+
hdrs = [
20+
"cgroup_manager_interface.h",
21+
],
22+
target_compatible_with = [
23+
"@platforms//os:linux",
24+
],
25+
deps = [
26+
"//src/ray/common:status",
27+
"//src/ray/common:status_or",
28+
],
29+
)
30+
31+
ray_cc_library(
32+
name = "cgroup_manager",
33+
srcs = ["cgroup_manager.cc"],
34+
hdrs = [
35+
"cgroup_manager.h",
36+
"scoped_cgroup_operation.h",
37+
],
38+
target_compatible_with = [
39+
"@platforms//os:linux",
40+
],
41+
deps = [
42+
":cgroup_driver_interface",
43+
":cgroup_manager_interface",
44+
"//src/ray/common:status",
45+
"//src/ray/common:status_or",
46+
"//src/ray/util:logging",
47+
"@com_google_absl//absl/strings",
48+
],
49+
)
50+
1751
ray_cc_library(
1852
name = "sysfs_cgroup_driver",
1953
srcs = ["sysfs_cgroup_driver.cc"],
@@ -32,6 +66,20 @@ ray_cc_library(
3266
],
3367
)
3468

69+
ray_cc_library(
70+
name = "fake_cgroup_driver",
71+
hdrs = [
72+
"fake_cgroup_driver.h",
73+
],
74+
target_compatible_with = [
75+
"@platforms//os:linux",
76+
],
77+
deps = [
78+
":cgroup_driver_interface",
79+
"//src/ray/common:status",
80+
],
81+
)
82+
3583
ray_cc_library(
3684
name = "cgroup_test_utils",
3785
srcs = ["cgroup_test_utils.cc"],
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
// Copyright 2025 The Ray Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "ray/common/cgroup2/cgroup_manager.h"
16+
17+
#include <filesystem>
18+
#include <memory>
19+
#include <string>
20+
#include <unordered_set>
21+
#include <utility>
22+
23+
#include "absl/strings/str_format.h"
24+
#include "absl/strings/str_join.h"
25+
#include "ray/common/cgroup2/cgroup_driver_interface.h"
26+
#include "ray/common/cgroup2/scoped_cgroup_operation.h"
27+
#include "ray/common/status_or.h"
28+
29+
namespace ray {
30+
31+
CgroupManager::CgroupManager(std::string base_cgroup_path,
32+
const std::string &node_id,
33+
std::unique_ptr<CgroupDriverInterface> cgroup_driver)
34+
: base_cgroup_path_(std::move(base_cgroup_path)),
35+
cgroup_driver_(std::move(cgroup_driver)) {
36+
node_cgroup_path_ = base_cgroup_path_ + std::filesystem::path::preferred_separator +
37+
absl::StrFormat("%s_%s", kNodeCgroupName, node_id);
38+
system_cgroup_path_ =
39+
node_cgroup_path_ + std::filesystem::path::preferred_separator + kSystemCgroupName;
40+
41+
application_cgroup_path_ = node_cgroup_path_ +
42+
std::filesystem::path::preferred_separator +
43+
kApplicationCgroupName;
44+
}
45+
46+
CgroupManager::~CgroupManager() {
47+
while (!cleanup_operations_.empty()) {
48+
cleanup_operations_.pop_back();
49+
}
50+
}
51+
52+
StatusOr<std::unique_ptr<CgroupManager>> CgroupManager::Create(
53+
std::string base_cgroup_path,
54+
const std::string &node_id,
55+
const int64_t system_reserved_cpu_weight,
56+
const int64_t system_reserved_memory_bytes,
57+
std::unique_ptr<CgroupDriverInterface> cgroup_driver) {
58+
// TODO(#54703): Add bounds checking for system_reserved_cpu_weight
59+
// and system_reserved_memory_bytes.
60+
RAY_RETURN_NOT_OK(cgroup_driver->CheckCgroupv2Enabled());
61+
RAY_RETURN_NOT_OK(cgroup_driver->CheckCgroup(base_cgroup_path));
62+
StatusOr<std::unordered_set<std::string>> available_controllers =
63+
cgroup_driver->GetAvailableControllers(base_cgroup_path);
64+
65+
if (!available_controllers.ok()) {
66+
return available_controllers.status();
67+
}
68+
69+
std::string supported_controllers_str =
70+
absl::StrCat("[", absl::StrJoin(supported_controllers_, ", "), "]");
71+
72+
for (const auto &ctrl : supported_controllers_) {
73+
if (available_controllers->find(ctrl) == available_controllers->end()) {
74+
std::string available_controllers_str =
75+
absl::StrCat("[", absl::StrJoin(*available_controllers, ", "), "]");
76+
return Status::Invalid(absl::StrFormat(
77+
"Failed to initialize resource isolation "
78+
"because required controllers are not available in the cgroup %s. "
79+
"To make controllers available in %s, you need to enable controllers for its "
80+
"ancestor cgroups. See "
81+
"https://docs.kernel.org/admin-guide/cgroup-v2.html#controlling-controllers "
82+
"for more details. Available controllers: %s. Required controllers: "
83+
"%s.",
84+
base_cgroup_path,
85+
base_cgroup_path,
86+
available_controllers_str,
87+
supported_controllers_str));
88+
}
89+
}
90+
91+
std::unique_ptr<CgroupManager> cgroup_manager = std::unique_ptr<CgroupManager>(
92+
new CgroupManager(std::move(base_cgroup_path), node_id, std::move(cgroup_driver)));
93+
94+
RAY_RETURN_NOT_OK(cgroup_manager->Initialize(system_reserved_cpu_weight,
95+
system_reserved_memory_bytes));
96+
97+
return cgroup_manager;
98+
}
99+
100+
// TODO(#54703): This is a placeholder for cleanup. This will call
101+
// CgroupDriver::DeleteCgroup.
102+
void CgroupManager::RegisterDeleteCgroup(const std::string &cgroup_path) {
103+
cleanup_operations_.emplace_back([cgroup = cgroup_path]() {
104+
RAY_LOG(INFO) << absl::StrFormat("Deleting all cgroup %s.", cgroup);
105+
});
106+
}
107+
108+
// TODO(#54703): This is a placeholder for cleanup. This will call
109+
// CgroupDriver::MoveAllProcesses.
110+
void CgroupManager::RegisterMoveAllProcesses(const std::string &from,
111+
const std::string &to) {
112+
cleanup_operations_.emplace_back([from_cgroup = from, to_cgroup = to]() {
113+
RAY_LOG(INFO) << absl::StrFormat(
114+
"Moved All Processes from %s to %s.", from_cgroup, to_cgroup);
115+
});
116+
}
117+
118+
// TODO(#54703): This is a placeholder for cleanup. This will call
119+
// CgroupDriver::AddConstraint(cgroup, constraint, default_value).
120+
void CgroupManager::RegisterRemoveConstraint(const std::string &cgroup,
121+
const std::string &constraint) {
122+
cleanup_operations_.emplace_back(
123+
[constrained_cgroup = cgroup, constraint_to_remove = constraint]() {
124+
auto constraint_metadata = supported_constraints_.find(constraint_to_remove);
125+
RAY_CHECK(constraint_metadata != supported_constraints_.end());
126+
RAY_LOG(INFO) << absl::StrFormat(
127+
"Setting constraint %s to default value %lld for cgroup %s",
128+
constraint_to_remove,
129+
constraint_metadata->second.default_value,
130+
constrained_cgroup);
131+
});
132+
}
133+
134+
// TODO(#54703): This is a placeholder for cleanup. This will call
135+
// CgroupDriver::DisableController.
136+
void CgroupManager::RegisterDisableController(const std::string &cgroup,
137+
const std::string &controller) {
138+
cleanup_operations_.emplace_back([cgroup_to_clean = cgroup,
139+
controller_to_disable = controller]() {
140+
RAY_LOG(INFO) << absl::StrFormat(
141+
"Disabling controller %s for cgroup %s.", controller_to_disable, cgroup_to_clean);
142+
});
143+
}
144+
145+
Status CgroupManager::Initialize(int64_t system_reserved_cpu_weight,
146+
int64_t system_reserved_memory_bytes) {
147+
std::string supported_controllers =
148+
absl::StrCat("[", absl::StrJoin(supported_controllers_, ", "), "]");
149+
150+
// The cpu.weight is distributed between the system and application cgroups.
151+
// The application cgroup gets whatever is leftover from the system cgroup.
152+
int64_t max_cpu_weight = supported_constraints_.at(kCPUWeightConstraint).Max();
153+
int64_t application_cgroup_cpu_weight = max_cpu_weight - system_reserved_cpu_weight;
154+
155+
RAY_LOG(INFO) << absl::StrFormat(
156+
"Initializing CgroupManager at base cgroup path at %s. Ray's cgroup "
157+
"hierarchy will under the node cgroup %s. The %s controllers will be "
158+
"enabled. "
159+
"System cgroup %s will have constraints [%s=%lld, %s=%lld]. "
160+
"Application cgroup %s will have constraints [%s=%lld].",
161+
base_cgroup_path_,
162+
node_cgroup_path_,
163+
supported_controllers,
164+
system_cgroup_path_,
165+
kCPUWeightConstraint,
166+
system_reserved_cpu_weight,
167+
kMemoryMinConstraint,
168+
system_reserved_memory_bytes,
169+
application_cgroup_path_,
170+
kCPUWeightConstraint,
171+
application_cgroup_cpu_weight);
172+
173+
// Create the cgroup heirarchy:
174+
// base_cgroup_path (e.g. /sys/fs/cgroup)
175+
// |
176+
// ray_node_<node_id>
177+
// | |
178+
// system application
179+
RAY_RETURN_NOT_OK(cgroup_driver_->CreateCgroup(node_cgroup_path_));
180+
RegisterDeleteCgroup(node_cgroup_path_);
181+
182+
RAY_RETURN_NOT_OK(cgroup_driver_->CreateCgroup(system_cgroup_path_));
183+
RegisterDeleteCgroup(system_cgroup_path_);
184+
185+
RAY_RETURN_NOT_OK(cgroup_driver_->CreateCgroup(application_cgroup_path_));
186+
RegisterDeleteCgroup(application_cgroup_path_);
187+
188+
// Move all processes from the base_cgroup into the system_cgroup to make sure
189+
// that the no internal process constraint is not violated. This is relevant
190+
// when the base_cgroup_path is not a root cgroup for the system. This is likely
191+
// the case if Ray is running inside a container.
192+
RAY_RETURN_NOT_OK(
193+
cgroup_driver_->MoveAllProcesses(base_cgroup_path_, system_cgroup_path_));
194+
RegisterMoveAllProcesses(system_cgroup_path_, base_cgroup_path_);
195+
196+
for (const auto &ctrl : supported_controllers_) {
197+
RAY_RETURN_NOT_OK(cgroup_driver_->EnableController(base_cgroup_path_, ctrl));
198+
RegisterDisableController(base_cgroup_path_, ctrl);
199+
RAY_RETURN_NOT_OK(cgroup_driver_->EnableController(node_cgroup_path_, ctrl));
200+
RegisterDisableController(node_cgroup_path_, ctrl);
201+
RAY_RETURN_NOT_OK(cgroup_driver_->EnableController(system_cgroup_path_, ctrl));
202+
RegisterDisableController(system_cgroup_path_, ctrl);
203+
RAY_RETURN_NOT_OK(cgroup_driver_->EnableController(application_cgroup_path_, ctrl));
204+
RegisterDisableController(application_cgroup_path_, ctrl);
205+
}
206+
207+
RAY_RETURN_NOT_OK(
208+
cgroup_driver_->AddConstraint(system_cgroup_path_,
209+
kMemoryMinConstraint,
210+
std::to_string(system_reserved_memory_bytes)));
211+
RegisterRemoveConstraint(system_cgroup_path_, kMemoryMinConstraint);
212+
213+
RAY_RETURN_NOT_OK(
214+
cgroup_driver_->AddConstraint(system_cgroup_path_,
215+
kCPUWeightConstraint,
216+
std::to_string(system_reserved_cpu_weight)));
217+
RegisterRemoveConstraint(system_cgroup_path_, kCPUWeightConstraint);
218+
219+
RAY_RETURN_NOT_OK(
220+
cgroup_driver_->AddConstraint(application_cgroup_path_,
221+
kCPUWeightConstraint,
222+
std::to_string(application_cgroup_cpu_weight)));
223+
RegisterRemoveConstraint(application_cgroup_path_, kCPUWeightConstraint);
224+
225+
return Status::OK();
226+
}
227+
} // namespace ray

0 commit comments

Comments
 (0)