feat(deploy): cgroup-v2 cpuset drop-ins pin system to core 0, game to rest
Computes NPROC at deploy time. Defaults LEFT4ME_SYSTEM_CPUS=0 and LEFT4ME_GAME_CPUS=1-(NPROC-1). Single-core hosts skip cpuset writes with a stderr warning unless an env var override is set. Spec: docs/superpowers/specs/2026-05-09-l4d2-cpu-isolation-design.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c91c029c38
commit
af3171102a
2 changed files with 61 additions and 0 deletions
|
|
@ -138,6 +138,34 @@ $sudo_cmd cp /opt/left4me/deploy/files/usr/local/lib/systemd/system/left4me-web.
|
|||
$sudo_cmd cp /opt/left4me/deploy/files/usr/local/lib/systemd/system/left4me-server@.service /usr/local/lib/systemd/system/left4me-server@.service
|
||||
$sudo_cmd cp /opt/left4me/deploy/files/usr/local/lib/systemd/system/l4d2-game.slice /usr/local/lib/systemd/system/l4d2-game.slice
|
||||
$sudo_cmd cp /opt/left4me/deploy/files/usr/local/lib/systemd/system/l4d2-build.slice /usr/local/lib/systemd/system/l4d2-build.slice
|
||||
|
||||
# CPU isolation via cgroup-v2 AllowedCPUs= drop-ins. Pin everything that
|
||||
# isn't a live game server to core 0; give game servers cores 1..N-1.
|
||||
# See docs/superpowers/specs/2026-05-09-l4d2-cpu-isolation-design.md.
|
||||
NPROC=$(nproc)
|
||||
SYSTEM_CPUS=${LEFT4ME_SYSTEM_CPUS:-0}
|
||||
if [ "${LEFT4ME_GAME_CPUS+x}" = x ]; then
|
||||
GAME_CPUS=$LEFT4ME_GAME_CPUS
|
||||
else
|
||||
GAME_CPUS="1-$((NPROC - 1))"
|
||||
fi
|
||||
if [ "$NPROC" -lt 2 ] && [ "${LEFT4ME_SYSTEM_CPUS+x}${LEFT4ME_GAME_CPUS+x}" = "" ]; then
|
||||
printf 'left4me deploy: skipping CPU isolation (nproc=%s); cpuset drop-ins not written.\n' "$NPROC" >&2
|
||||
else
|
||||
for slice_drop_in in \
|
||||
/etc/systemd/system/system.slice.d/99-left4me-cpuset.conf \
|
||||
/etc/systemd/system/user.slice.d/99-left4me-cpuset.conf \
|
||||
/etc/systemd/system/l4d2-build.slice.d/99-left4me-cpuset.conf; do
|
||||
$sudo_cmd mkdir -p "$(dirname "$slice_drop_in")"
|
||||
printf '[Slice]\nAllowedCPUs=%s\n' "$SYSTEM_CPUS" \
|
||||
| $sudo_cmd install -m 0644 -o root -g root /dev/stdin "$slice_drop_in"
|
||||
done
|
||||
$sudo_cmd mkdir -p /etc/systemd/system/l4d2-game.slice.d
|
||||
printf '[Slice]\nAllowedCPUs=%s\n' "$GAME_CPUS" \
|
||||
| $sudo_cmd install -m 0644 -o root -g root /dev/stdin \
|
||||
/etc/systemd/system/l4d2-game.slice.d/99-left4me-cpuset.conf
|
||||
fi
|
||||
|
||||
$sudo_cmd cp /opt/left4me/deploy/files/usr/local/libexec/left4me/left4me-systemctl /usr/local/libexec/left4me/left4me-systemctl
|
||||
$sudo_cmd cp /opt/left4me/deploy/files/usr/local/libexec/left4me/left4me-journalctl /usr/local/libexec/left4me/left4me-journalctl
|
||||
$sudo_cmd cp /opt/left4me/deploy/files/usr/local/libexec/left4me/left4me-overlay /usr/local/libexec/left4me/left4me-overlay
|
||||
|
|
|
|||
|
|
@ -170,6 +170,39 @@ def test_deploy_script_installs_perf_artifacts():
|
|||
assert "sysctl --system" in script
|
||||
|
||||
|
||||
def test_deploy_script_writes_cpuset_drop_ins():
|
||||
script = DEPLOY_SCRIPT.read_text()
|
||||
|
||||
# Reads nproc and binds defaults via ${VAR:-...}.
|
||||
assert "nproc" in script
|
||||
assert "LEFT4ME_SYSTEM_CPUS" in script
|
||||
assert "LEFT4ME_GAME_CPUS" in script
|
||||
assert "${LEFT4ME_SYSTEM_CPUS:-0}" in script
|
||||
|
||||
# Default game-core upper bound is computed from nproc; accept either
|
||||
# the NPROC-1 form or LEFT4ME_GAME_CPUS:-1- prefix.
|
||||
assert (
|
||||
"1-$((NPROC - 1))" in script
|
||||
or "1-$((NPROC-1))" in script
|
||||
or "1-$((nproc-1))" in script
|
||||
or "LEFT4ME_GAME_CPUS:-1-" in script
|
||||
)
|
||||
|
||||
# All four drop-in paths.
|
||||
for slice_name in ("system", "user", "l4d2-build", "l4d2-game"):
|
||||
assert (
|
||||
f"/etc/systemd/system/{slice_name}.slice.d/99-left4me-cpuset.conf"
|
||||
in script
|
||||
)
|
||||
|
||||
# Drop-ins use the existing install pattern.
|
||||
assert "install -m 0644 -o root -g root" in script
|
||||
|
||||
# Single-core host: skip with a warning to stderr.
|
||||
assert ("-lt 2" in script) or ("< 2" in script) or ("-ge 2" in script)
|
||||
assert "skipping CPU isolation" in script
|
||||
|
||||
|
||||
def _fake_command(tmp_path, command_name):
|
||||
marker = tmp_path / f"{command_name}.args"
|
||||
command = tmp_path / command_name
|
||||
|
|
|
|||
Loading…
Reference in a new issue