Merge pull request #2797 from pikasTech/fix/2793-k3s-systemd-reclaim
为 NC01 k3s 增加 systemd cgroup 精确回收
This commit is contained in:
@@ -250,7 +250,7 @@ gc:
|
||||
processSwapLimit: 4
|
||||
commandTimeoutSeconds: 20
|
||||
kubeconfigPath: /etc/rancher/k3s/k3s.yaml
|
||||
targetMemAvailableBytes: 5368709120
|
||||
targetMemAvailableBytes: 3221225472
|
||||
objectCountResources:
|
||||
- pods
|
||||
- pipelineruns.tekton.dev
|
||||
@@ -288,9 +288,16 @@ gc:
|
||||
- /headless_shell
|
||||
cgroupReclaim:
|
||||
enabled: true
|
||||
targetMemAvailableBytes: 5368709120
|
||||
targetMemAvailableBytes: 3221225472
|
||||
minimumSwapFreeBytesAfterReclaim: 34359738368
|
||||
targets:
|
||||
- id: k3s-service-inactive-anon
|
||||
systemdUnitSelector:
|
||||
unit: k3s.service
|
||||
reclaimBytes: 805306368
|
||||
swappiness: 200
|
||||
minimumInactiveAnonBytes: 805306368
|
||||
priority: 1
|
||||
- id: kafka-broker-inactive-anon
|
||||
workloadSelector:
|
||||
namespace: platform-infra
|
||||
|
||||
@@ -249,8 +249,8 @@ YAML运维状态输出应区分三类依赖:必须保留的 host bootstrap 面
|
||||
| --- | --- | --- | --- |
|
||||
| OPS-YAML-REQ-012 | 精确工作负载回收 | PJ2026-01060315 精确工作负载回收 | [平台运维](PJ2026-0106-platform-ops.md)、[NC01 CI资源治理](PJ2026-01060107-nc01-ci-resource-governance.md) |
|
||||
|
||||
平台 GC 应允许 owning YAML 使用稳定工作负载选择器声明精确 cgroup 回收目标。选择器至少包含 namespace、非空 labels 和最大匹配 Pod 数;目标可用内存、单 Pod 回收上限、swappiness、inactive anonymous memory 下限和 swap 保留线继续由 YAML 声明。YAML 不得保存 Pod UID、容器 ID、当前 cgroup 路径或从运行面反解出的可调数值。
|
||||
平台 GC 应允许 owning YAML 使用稳定工作负载选择器声明精确 cgroup 回收目标。Kubernetes workload selector 至少包含 namespace、非空 labels 和最大匹配 Pod 数;host service 使用仅包含稳定 unit 名的 `systemdUnitSelector`。目标可用内存、单目标回收上限、swappiness、inactive anonymous memory 下限、swap 保留线和优先级继续由 YAML 声明。YAML 不得保存 Pod UID、容器 ID、MainPID、进程 startTicks、当前 cgroup 路径、cgroup inode 或从运行面反解出的可调数值。
|
||||
|
||||
受控 plan 只选择 Running Pod,并披露 selector、解析出的 namespace、Pod、UID、cgroup、当前内存、inactive anonymous memory 和保护原因。零匹配、超过最大匹配数、API 不可读、cgroup 缺失、symlink、内存下限不足或 swap 保留线不足时必须保护,禁止回退到父 cgroup、按名称猜测或扩大 selector。
|
||||
受控 plan 对 Kubernetes selector 只选择 Running Pod,并披露 selector、解析出的 namespace、Pod、UID、cgroup、当前内存、inactive anonymous memory 和保护原因。对 `systemdUnitSelector` 必须通过 `systemctl show` 读取 `LoadState`、`ActiveState`、`SubState`、`ControlGroup` 和 `MainPID`,只接受 loaded、active、running 的 service;ControlGroup 必须唯一、绝对、位于 `/system.slice`,映射到 `/sys/fs/cgroup/system.slice` 下的真实非 symlink 目录,且 MainPID 当前 cgroup 必须与 ControlGroup 一致。plan 同时记录 MainPID 的 `/proc/<pid>/stat` startTicks,禁止从 unit 名猜测路径或回退到父 cgroup。
|
||||
|
||||
执行前必须使用同一 YAML selector 重新解析当前 Running Pod,并逐项核对 Pod UID、cgroup 路径和全部回收参数。身份变化、目标已满足或候选不再合格时跳过且结构化披露;只有身份闭合的当前 cgroup 才能写入有界 `memory.reclaim`。执行不得停止、重启、驱逐或迁移 workload,也不得把内核请求上限宣称为实际收益;终态继续分别披露 cgroup 下降、宿主 `MemAvailable` 变化、目标缺口和 swap 排除状态。
|
||||
执行前必须使用同一 YAML selector 重新解析当前目标。Kubernetes workload 逐项核对 Pod UID 与 cgroup 路径;systemd service 逐项核对 unit、`LoadState`、`ActiveState`、`SubState`、`ControlGroup`、MainPID 与 startTicks;两类目标都必须重新核对全部回收参数。身份变化、目标已满足或候选不再合格时跳过且结构化披露;只有身份闭合的当前 cgroup 才能写入有界 `memory.reclaim`。执行不得停止、重启、驱逐或迁移 workload,也不得把内核请求上限宣称为实际收益;终态继续分别披露执行前后 cgroup current、宿主 `MemAvailable` 变化、真实收益、目标缺口和 swap 排除状态。
|
||||
|
||||
@@ -123,11 +123,14 @@ test("memory-pressure async runner uses a parent-child barrier and locked worker
|
||||
assert.match(runnerSource, /worker-identity-unavailable|worker-exited-without-terminal-state/u);
|
||||
});
|
||||
|
||||
test("workload-selected cgroup reclaim re-resolves the Pod UID and path before execution", () => {
|
||||
test("cgroup reclaim re-resolves workload and systemd identities before execution", () => {
|
||||
assert.match(webObserveSource, /_CGROUP_RECLAIM_RUNTIME_INVENTORY = None/u);
|
||||
assert.match(webObserveSource, /cgroup reclaim candidate identity changed before execution/u);
|
||||
assert.match(webObserveSource, /item\.get\("path"\) == path/u);
|
||||
assert.match(webObserveSource, /item\.get\("pod"\).*get\("uid"\).*resolved_uid/u);
|
||||
assert.match(webObserveSource, /item\.get\("systemdUnit"\) == resolved_systemd_unit/u);
|
||||
assert.match(webObserveSource, /systemd-main-pid-cgroup-mismatch/u);
|
||||
assert.match(webObserveSource, /systemd-main-pid-start-ticks-unavailable/u);
|
||||
});
|
||||
|
||||
test("memory-pressure plan includes only YAML allowlisted cgroup reclaim stages with swap reserve", () => {
|
||||
@@ -143,10 +146,11 @@ MEMORY_CONFIG = {
|
||||
"enabled": True, "targetMemAvailableBytes": 4294967296,
|
||||
"minimumSwapFreeBytesAfterReclaim": 402653184,
|
||||
"targets": [
|
||||
{"id": "file-pages", "path": "/sys/fs/cgroup/system.slice/k3s.service", "reclaimBytes": 402653184, "swappiness": 0, "priority": 10},
|
||||
{"id": "inactive-anon", "path": "/sys/fs/cgroup/system.slice/k3s.service", "reclaimBytes": 1342177280, "swappiness": 100, "minimumInactiveAnonBytes": 1342177280, "priority": 20},
|
||||
{"id": "file-pages", "systemdUnitSelector": {"unit": "k3s.service"}, "reclaimBytes": 402653184, "swappiness": 0, "priority": 10},
|
||||
{"id": "inactive-anon", "systemdUnitSelector": {"unit": "k3s.service"}, "reclaimBytes": 1342177280, "swappiness": 100, "minimumInactiveAnonBytes": 1342177280, "priority": 20},
|
||||
],
|
||||
},
|
||||
"attribution": {"commandTimeoutSeconds": 20},
|
||||
}
|
||||
read_proc_process_table = lambda: {1: {"pid": 1, "ppid": 0, "sid": 1, "startTicks": 1, "ageSeconds": 1, "rssBytes": 1, "commandLine": "init", "commandPreview": "init"}}
|
||||
observer_run_records = lambda: []
|
||||
@@ -156,13 +160,41 @@ os.path.isdir = lambda path: True
|
||||
os.path.islink = lambda path: False
|
||||
os.path.exists = lambda path: True
|
||||
os.access = lambda path, mode: True
|
||||
command = lambda argv, timeout: {"exitCode": 0, "stdout": "LoadState=loaded\nActiveState=active\nSubState=running\nControlGroup=/system.slice/k3s.service\nMainPID=321\n", "stderr": ""}
|
||||
cgroup_path_for_pid = lambda pid: "/system.slice/k3s.service"
|
||||
process_start_ticks = lambda pid: 987654
|
||||
read_cgroup_integer = lambda path, name: 6 * 1024 * 1024 * 1024
|
||||
read_cgroup_memory_stat = lambda path: {"inactive_anon": 2 * 1024 * 1024 * 1024}
|
||||
candidates = collect_memory_reclaim_candidates()
|
||||
print(json.dumps({"ids": [item["id"] for item in candidates], "swappiness": [item["swappiness"] for item in candidates], "diagnostics": memory_reclaim_scan_diagnostics()}))
|
||||
memory_reclaim_write_attempts = []
|
||||
original_open = open
|
||||
def tracked_open(path, *args, **kwargs):
|
||||
if str(path).endswith("/memory.reclaim"):
|
||||
memory_reclaim_write_attempts.append(path)
|
||||
return original_open(path, *args, **kwargs)
|
||||
open = tracked_open
|
||||
MEMORY_CONFIG["cgroupReclaim"]["targets"][0]["priority"] = 11
|
||||
try:
|
||||
execute_cgroup_reclaim_candidate(candidates[0])
|
||||
priority_revalidation_error = None
|
||||
except RuntimeError as error:
|
||||
priority_revalidation_error = str(error)
|
||||
MEMORY_CONFIG["cgroupReclaim"]["targets"][0]["priority"] = 10
|
||||
process_start_ticks = lambda pid: 987655
|
||||
try:
|
||||
execute_cgroup_reclaim_candidate(candidates[0])
|
||||
revalidation_error = None
|
||||
except RuntimeError as error:
|
||||
revalidation_error = str(error)
|
||||
print(json.dumps({"ids": [item["id"] for item in candidates], "swappiness": [item["swappiness"] for item in candidates], "systemd": [item["resolvedSystemdUnit"] for item in candidates], "priorityRevalidationError": priority_revalidation_error, "memoryReclaimWriteAttempts": memory_reclaim_write_attempts, "revalidationError": revalidation_error, "diagnostics": memory_reclaim_scan_diagnostics()}))
|
||||
`);
|
||||
assert.deepEqual(result.ids, ["cgroup-memory-reclaim:file-pages", "cgroup-memory-reclaim:inactive-anon"]);
|
||||
assert.deepEqual(result.swappiness, [0, 100]);
|
||||
assert.deepEqual((result.systemd as Array<Record<string, unknown>>).map((item) => item.unit), ["k3s.service", "k3s.service"]);
|
||||
assert.deepEqual((result.systemd as Array<Record<string, unknown>>).map((item) => item.startTicks), [987654, 987654]);
|
||||
assert.equal(result.priorityRevalidationError, "cgroup reclaim candidate no longer matches YAML: priority");
|
||||
assert.deepEqual(result.memoryReclaimWriteAttempts, []);
|
||||
assert.equal(result.revalidationError, "cgroup reclaim candidate identity changed before execution");
|
||||
assert.equal(((result.diagnostics as Record<string, unknown>).cgroupReclaim as Record<string, unknown>).eligibleCount, 2);
|
||||
assert.match(webObserveSource, /minimum SwapFree reserve/u);
|
||||
assert.match(webObserveSource, /memory\.reclaim/u);
|
||||
@@ -199,6 +231,16 @@ test("default memory-pressure plan projects an actionable YAML-bounded preview b
|
||||
risk: "medium",
|
||||
reason: "host-memavailable-below-yaml-target",
|
||||
path: "/sys/fs/cgroup/system.slice/k3s.service",
|
||||
systemdUnitSelector: { unit: "k3s.service" },
|
||||
resolvedSystemdUnit: {
|
||||
unit: "k3s.service",
|
||||
loadState: "loaded",
|
||||
activeState: "active",
|
||||
subState: "running",
|
||||
controlGroup: "/system.slice/k3s.service",
|
||||
mainPid: 321,
|
||||
startTicks: 987654,
|
||||
},
|
||||
reclaimBytes: 256 * 1024 * 1024,
|
||||
swappiness: 0,
|
||||
targetMemAvailableBytes: 4 * 1024 * 1024 * 1024,
|
||||
@@ -207,7 +249,7 @@ test("default memory-pressure plan projects an actionable YAML-bounded preview b
|
||||
memorySwapCurrentBytes: 0,
|
||||
expectedMemoryRssBytes: 256 * 1024 * 1024,
|
||||
expectedMemoryRss: "256 MiB",
|
||||
action: { op: "cgroup-v2-memory-reclaim", allowlist: "yaml-exact-cgroup-path" },
|
||||
action: { op: "cgroup-v2-memory-reclaim", allowlist: "yaml-stable-cgroup-selector" },
|
||||
});
|
||||
const candidates = [cgroupCandidate(1), cgroupCandidate(2), ...Array.from({ length: 4 }, (_, index) => processCandidate(index))];
|
||||
const payload = {
|
||||
|
||||
@@ -360,18 +360,26 @@ def configured_cgroup_reclaim_targets():
|
||||
target_id = item.get("id")
|
||||
path = item.get("path")
|
||||
workload_selector = item.get("workloadSelector")
|
||||
systemd_unit_selector = item.get("systemdUnitSelector")
|
||||
reclaim_bytes = item.get("reclaimBytes")
|
||||
swappiness = item.get("swappiness")
|
||||
minimum_inactive_anon = item.get("minimumInactiveAnonBytes", 0)
|
||||
if not isinstance(target_id, str) or not re.match(r"^[A-Za-z0-9._-]{1,80}$", target_id) or target_id in seen_ids:
|
||||
_MEMORY_RECLAIM_CONFIG_ERROR = "yaml-cgroup-reclaim-target-id-invalid:%s" % index
|
||||
return []
|
||||
if (path is None) == (workload_selector is None):
|
||||
if path is not None:
|
||||
_MEMORY_RECLAIM_CONFIG_ERROR = "yaml-cgroup-reclaim-path-selector-unsupported:%s" % target_id
|
||||
return []
|
||||
if (workload_selector is None) == (systemd_unit_selector is None):
|
||||
_MEMORY_RECLAIM_CONFIG_ERROR = "yaml-cgroup-reclaim-target-identity-invalid:%s" % target_id
|
||||
return []
|
||||
if path is not None:
|
||||
if not isinstance(path, str) or (path != "/sys/fs/cgroup" and not path.startswith("/sys/fs/cgroup/")) or os.path.normpath(path) != path:
|
||||
_MEMORY_RECLAIM_CONFIG_ERROR = "yaml-cgroup-reclaim-target-path-invalid:%s" % target_id
|
||||
if systemd_unit_selector is not None:
|
||||
if not isinstance(systemd_unit_selector, dict) or set(systemd_unit_selector.keys()) != set(["unit"]):
|
||||
_MEMORY_RECLAIM_CONFIG_ERROR = "yaml-cgroup-reclaim-systemd-selector-invalid:%s" % target_id
|
||||
return []
|
||||
unit = systemd_unit_selector.get("unit")
|
||||
if not isinstance(unit, str) or not re.match(r"^[A-Za-z0-9_.@-]{1,120}\.service$", unit):
|
||||
_MEMORY_RECLAIM_CONFIG_ERROR = "yaml-cgroup-reclaim-systemd-unit-invalid:%s" % target_id
|
||||
return []
|
||||
if workload_selector is not None:
|
||||
if not isinstance(workload_selector, dict):
|
||||
@@ -412,8 +420,8 @@ def configured_cgroup_reclaim_targets():
|
||||
seen_ids.add(target_id)
|
||||
validated.append({
|
||||
"id": target_id,
|
||||
"path": path,
|
||||
"workloadSelector": workload_selector,
|
||||
"systemdUnitSelector": systemd_unit_selector,
|
||||
"reclaimBytes": reclaim_bytes,
|
||||
"swappiness": swappiness,
|
||||
"minimumInactiveAnonBytes": minimum_inactive_anon,
|
||||
@@ -479,9 +487,48 @@ def cgroup_reclaim_runtime_inventory():
|
||||
return _CGROUP_RECLAIM_RUNTIME_INVENTORY
|
||||
|
||||
def resolve_cgroup_reclaim_target(item):
|
||||
path = item.get("path")
|
||||
if isinstance(path, str):
|
||||
return {"ok": True, "matches": [{"path": path, "pod": None}]}
|
||||
systemd_selector = item.get("systemdUnitSelector")
|
||||
if isinstance(systemd_selector, dict):
|
||||
unit = systemd_selector.get("unit")
|
||||
timeout = safe_int((MEMORY_CONFIG.get("attribution") or {}).get("commandTimeoutSeconds"))
|
||||
result = command([
|
||||
"systemctl", "show", unit, "--no-pager",
|
||||
"--property=LoadState", "--property=ActiveState", "--property=SubState",
|
||||
"--property=ControlGroup", "--property=MainPID",
|
||||
], timeout)
|
||||
if result.get("exitCode") != 0:
|
||||
return {"ok": False, "reason": "systemd-unit-show-failed", "command": bounded(result), "matches": []}
|
||||
fields = {}
|
||||
for line in str(result.get("stdout") or "").splitlines():
|
||||
key, separator, value = line.partition("=")
|
||||
if separator:
|
||||
fields[key] = value
|
||||
if fields.get("LoadState") != "loaded" or fields.get("ActiveState") != "active" or fields.get("SubState") != "running":
|
||||
return {"ok": False, "reason": "systemd-unit-not-active-running", "systemdState": fields, "matches": []}
|
||||
control_group = fields.get("ControlGroup")
|
||||
main_pid = safe_int(fields.get("MainPID"), None)
|
||||
if not isinstance(control_group, str) or not control_group.startswith("/system.slice/") or os.path.normpath(control_group) != control_group:
|
||||
return {"ok": False, "reason": "systemd-control-group-invalid", "matches": []}
|
||||
if main_pid is None or main_pid <= 0:
|
||||
return {"ok": False, "reason": "systemd-main-pid-invalid", "matches": []}
|
||||
if cgroup_path_for_pid(main_pid) != control_group:
|
||||
return {"ok": False, "reason": "systemd-main-pid-cgroup-mismatch", "matches": []}
|
||||
start_ticks = process_start_ticks(main_pid)
|
||||
if start_ticks is None or safe_int(start_ticks) <= 0:
|
||||
return {"ok": False, "reason": "systemd-main-pid-start-ticks-unavailable", "matches": []}
|
||||
path = os.path.join("/sys/fs/cgroup", control_group.lstrip("/"))
|
||||
if os.path.realpath(path) != path or not os.path.isdir(path) or os.path.islink(path):
|
||||
return {"ok": False, "reason": "systemd-cgroup-path-unavailable-or-redirected", "matches": []}
|
||||
identity = {
|
||||
"unit": unit,
|
||||
"loadState": fields.get("LoadState"),
|
||||
"activeState": fields.get("ActiveState"),
|
||||
"subState": fields.get("SubState"),
|
||||
"controlGroup": control_group,
|
||||
"mainPid": main_pid,
|
||||
"startTicks": safe_int(start_ticks),
|
||||
}
|
||||
return {"ok": True, "matches": [{"path": path, "pod": None, "systemdUnit": identity}]}
|
||||
selector = item.get("workloadSelector") or {}
|
||||
labels = selector.get("matchLabels") or {}
|
||||
label_selector = ",".join("%s=%s" % pair for pair in sorted(labels.items()))
|
||||
@@ -506,6 +553,7 @@ def resolve_cgroup_reclaim_target(item):
|
||||
matches.append({
|
||||
"path": resolved_path,
|
||||
"pod": {"namespace": selector.get("namespace"), "name": name, "uid": uid, "labelSelector": label_selector},
|
||||
"systemdUnit": None,
|
||||
})
|
||||
return {"ok": True, "matches": matches}
|
||||
|
||||
@@ -529,6 +577,7 @@ def collect_cgroup_reclaim_candidates():
|
||||
diagnostics["protected"].append({
|
||||
"id": item.get("id"),
|
||||
"workloadSelector": item.get("workloadSelector"),
|
||||
"systemdUnitSelector": item.get("systemdUnitSelector"),
|
||||
"reason": resolution.get("reason"),
|
||||
"matchedPodCount": resolution.get("matchedPodCount"),
|
||||
})
|
||||
@@ -536,6 +585,7 @@ def collect_cgroup_reclaim_candidates():
|
||||
for resolved in resolution.get("matches") or []:
|
||||
path = resolved.get("path")
|
||||
pod = resolved.get("pod")
|
||||
systemd_unit = resolved.get("systemdUnit")
|
||||
reclaim_path = os.path.join(path, "memory.reclaim")
|
||||
protected_reason = None
|
||||
if os.path.realpath(path) != path or not os.path.isdir(path) or os.path.islink(path):
|
||||
@@ -556,7 +606,9 @@ def collect_cgroup_reclaim_candidates():
|
||||
"id": item.get("id"),
|
||||
"path": path,
|
||||
"workloadSelector": item.get("workloadSelector"),
|
||||
"systemdUnitSelector": item.get("systemdUnitSelector"),
|
||||
"resolvedPod": pod,
|
||||
"resolvedSystemdUnit": systemd_unit,
|
||||
"reason": protected_reason,
|
||||
"memoryCurrentBytes": current,
|
||||
"inactiveAnonBytes": inactive_anon,
|
||||
@@ -572,11 +624,13 @@ def collect_cgroup_reclaim_candidates():
|
||||
"configId": item.get("id"),
|
||||
"kind": "cgroup-memory-reclaim",
|
||||
"risk": "medium",
|
||||
"description": "Request bounded cgroup v2 memory reclaim from a YAML-resolved workload",
|
||||
"description": "Request bounded cgroup v2 memory reclaim from a YAML-resolved target",
|
||||
"reason": "host-memavailable-below-yaml-target",
|
||||
"path": path,
|
||||
"workloadSelector": item.get("workloadSelector"),
|
||||
"systemdUnitSelector": item.get("systemdUnitSelector"),
|
||||
"resolvedPod": pod,
|
||||
"resolvedSystemdUnit": systemd_unit,
|
||||
"priority": safe_int(item.get("priority")),
|
||||
"reclaimBytes": reclaim_bytes,
|
||||
"swappiness": safe_int(item.get("swappiness")),
|
||||
@@ -593,7 +647,7 @@ def collect_cgroup_reclaim_candidates():
|
||||
"expectedMemoryRss": fmt_bytes(reclaim_bytes),
|
||||
"estimatedDiskBytes": 0,
|
||||
"estimatedReclaimBytes": 0,
|
||||
"action": {"op": "cgroup-v2-memory-reclaim", "allowlist": "yaml-workload-selector-or-exact-path"},
|
||||
"action": {"op": "cgroup-v2-memory-reclaim", "allowlist": "yaml-stable-cgroup-selector"},
|
||||
})
|
||||
diagnostics["eligibleCount"] = len(candidates)
|
||||
return candidates
|
||||
@@ -815,16 +869,21 @@ def execute_cgroup_reclaim_candidate(candidate):
|
||||
configured = next((item for item in configured_cgroup_reclaim_targets() if item.get("id") == config_id), None)
|
||||
if configured is None:
|
||||
raise RuntimeError("cgroup reclaim candidate is no longer present in YAML allowlist")
|
||||
for key in ["workloadSelector", "reclaimBytes", "swappiness", "minimumInactiveAnonBytes", "targetMemAvailableBytes", "minimumSwapFreeBytesAfterReclaim"]:
|
||||
for key in ["workloadSelector", "systemdUnitSelector", "reclaimBytes", "swappiness", "minimumInactiveAnonBytes", "targetMemAvailableBytes", "minimumSwapFreeBytesAfterReclaim", "priority"]:
|
||||
if configured.get(key) != candidate.get(key):
|
||||
raise RuntimeError("cgroup reclaim candidate no longer matches YAML: %s" % key)
|
||||
_CGROUP_RECLAIM_RUNTIME_INVENTORY = None
|
||||
resolution = resolve_cgroup_reclaim_target(configured)
|
||||
if resolution.get("ok") is not True:
|
||||
raise RuntimeError("cgroup reclaim workload selector no longer resolves: %s" % resolution.get("reason"))
|
||||
raise RuntimeError("cgroup reclaim stable selector no longer resolves: %s" % resolution.get("reason"))
|
||||
path = candidate.get("path")
|
||||
resolved_uid = ((candidate.get("resolvedPod") or {}).get("uid"))
|
||||
current_match = next((item for item in resolution.get("matches") or [] if item.get("path") == path and ((item.get("pod") or {}).get("uid")) == resolved_uid), None)
|
||||
resolved_systemd_unit = candidate.get("resolvedSystemdUnit")
|
||||
current_match = next((item for item in resolution.get("matches") or []
|
||||
if item.get("path") == path
|
||||
and ((item.get("pod") or {}).get("uid")) == resolved_uid
|
||||
and item.get("systemdUnit") == resolved_systemd_unit
|
||||
), None)
|
||||
if current_match is None:
|
||||
raise RuntimeError("cgroup reclaim candidate identity changed before execution")
|
||||
reclaim_path = os.path.join(path, "memory.reclaim")
|
||||
|
||||
@@ -428,6 +428,31 @@ function compactResolvedPod(value: unknown): Record<string, unknown> | undefined
|
||||
return { name: `${pod.namespace}/${pod.name}`, uid: pod.uid };
|
||||
}
|
||||
|
||||
function compactSystemdUnitSelector(value: unknown): string | undefined {
|
||||
const selector = recordOrEmpty(value);
|
||||
return typeof selector.unit === "string" ? selector.unit : undefined;
|
||||
}
|
||||
|
||||
function compactResolvedSystemdUnit(value: unknown): Record<string, unknown> | undefined {
|
||||
const unit = recordOrEmpty(value);
|
||||
if (typeof unit.unit !== "string"
|
||||
|| typeof unit.loadState !== "string"
|
||||
|| typeof unit.activeState !== "string"
|
||||
|| typeof unit.subState !== "string"
|
||||
|| typeof unit.controlGroup !== "string"
|
||||
|| positiveInteger(unit.mainPid) === null
|
||||
|| positiveInteger(unit.startTicks) === null) return undefined;
|
||||
return {
|
||||
unit: unit.unit,
|
||||
loadState: unit.loadState,
|
||||
activeState: unit.activeState,
|
||||
subState: unit.subState,
|
||||
controlGroup: unit.controlGroup,
|
||||
mainPid: unit.mainPid,
|
||||
startTicks: unit.startTicks,
|
||||
};
|
||||
}
|
||||
|
||||
function compactMemoryPressureCandidate(value: unknown): Record<string, unknown> {
|
||||
const candidate = recordOrEmpty(value);
|
||||
const action = recordOrEmpty(candidate.action);
|
||||
@@ -449,6 +474,8 @@ function compactMemoryPressureCandidate(value: unknown): Record<string, unknown>
|
||||
path: candidate.path,
|
||||
workloadSelector: compactWorkloadSelector(candidate.workloadSelector),
|
||||
resolvedPod: compactResolvedPod(candidate.resolvedPod),
|
||||
systemdUnitSelector: compactSystemdUnitSelector(candidate.systemdUnitSelector),
|
||||
resolvedSystemdUnit: compactResolvedSystemdUnit(candidate.resolvedSystemdUnit),
|
||||
requestedMemoryReclaimBytes: candidate.requestedMemoryReclaimBytes,
|
||||
requestedMemoryReclaim: candidate.requestedMemoryReclaim,
|
||||
estimateKind: candidate.estimateKind,
|
||||
@@ -493,16 +520,29 @@ function memoryPressureCandidateAllowed(value: unknown): boolean {
|
||||
const swappiness = typeof candidate.swappiness === "number" && Number.isInteger(candidate.swappiness)
|
||||
? candidate.swappiness
|
||||
: null;
|
||||
const workloadSelector = recordOrEmpty(candidate.workloadSelector);
|
||||
const resolvedPod = recordOrEmpty(candidate.resolvedPod);
|
||||
const systemdUnitSelector = recordOrEmpty(candidate.systemdUnitSelector);
|
||||
const resolvedSystemdUnit = recordOrEmpty(candidate.resolvedSystemdUnit);
|
||||
const workloadIdentityClosed = typeof workloadSelector.namespace === "string" && typeof resolvedPod.uid === "string";
|
||||
const systemdIdentityClosed = typeof systemdUnitSelector.unit === "string"
|
||||
&& resolvedSystemdUnit.unit === systemdUnitSelector.unit
|
||||
&& resolvedSystemdUnit.loadState === "loaded"
|
||||
&& resolvedSystemdUnit.activeState === "active"
|
||||
&& resolvedSystemdUnit.subState === "running"
|
||||
&& typeof resolvedSystemdUnit.controlGroup === "string"
|
||||
&& resolvedSystemdUnit.controlGroup.startsWith("/system.slice/")
|
||||
&& candidate.path === `/sys/fs/cgroup${resolvedSystemdUnit.controlGroup}`
|
||||
&& positiveInteger(resolvedSystemdUnit.mainPid) !== null
|
||||
&& positiveInteger(resolvedSystemdUnit.startTicks) !== null;
|
||||
return typeof candidate.configId === "string" && candidate.configId.length > 0
|
||||
&& typeof candidate.path === "string" && candidate.path.startsWith("/")
|
||||
&& positiveInteger(candidate.reclaimBytes) !== null
|
||||
&& swappiness !== null && swappiness >= 0 && swappiness <= 200
|
||||
&& (swappiness === 0 || positiveInteger(candidate.minimumInactiveAnonBytes) !== null)
|
||||
&& action.op === "cgroup-v2-memory-reclaim"
|
||||
&& (action.allowlist === "yaml-exact-cgroup-path"
|
||||
|| (action.allowlist === "yaml-workload-selector-or-exact-path"
|
||||
&& typeof recordOrEmpty(candidate.workloadSelector).namespace === "string"
|
||||
&& typeof recordOrEmpty(candidate.resolvedPod).uid === "string"));
|
||||
&& action.allowlist === "yaml-stable-cgroup-selector"
|
||||
&& workloadIdentityClosed !== systemdIdentityClosed;
|
||||
}
|
||||
if (candidate.kind !== "browser-orphan-process-tree" && candidate.kind !== "stale-web-observer-process-tree") return false;
|
||||
const compact = compactMemoryPressureCandidate(candidate);
|
||||
@@ -558,6 +598,8 @@ function compactMemoryPressureCandidateScan(value: unknown): Record<string, unkn
|
||||
inactiveAnonBytes: protectedItem.inactiveAnonBytes,
|
||||
workloadSelector: compactWorkloadSelector(protectedItem.workloadSelector),
|
||||
resolvedPod: compactResolvedPod(protectedItem.resolvedPod),
|
||||
systemdUnitSelector: compactSystemdUnitSelector(protectedItem.systemdUnitSelector),
|
||||
resolvedSystemdUnit: compactResolvedSystemdUnit(protectedItem.resolvedSystemdUnit),
|
||||
};
|
||||
}),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user