This commit is contained in:
MengMengCode
2026-07-16 18:57:15 +08:00
parent 0e3c059236
commit 7307255130
3 changed files with 45 additions and 10 deletions
+5 -6
View File
@@ -452,12 +452,11 @@ func updateResourceLimit(w http.ResponseWriter, r *http.Request, id int) {
config.NormalizeContainerResourceAliases(c) config.NormalizeContainerResourceAliases(c)
config.SaveConfig() config.SaveConfig()
// Re-apply resource limits to running container // Re-apply persisted/runtime limits. LXC also uses this path to migrate
if c.Status == "running" { // old managed config lines such as lxc.prlimit.nproc.
if err := applyLimitsByRuntime(c); err != nil { if err := applyLimitsByRuntime(c); err != nil {
jsonResponse(w, http.StatusInternalServerError, APIResponse{Success: false, Message: err.Error()}) jsonResponse(w, http.StatusInternalServerError, APIResponse{Success: false, Message: err.Error()})
return return
}
} }
msg := "Resource limits updated" msg := "Resource limits updated"
+32 -4
View File
@@ -632,8 +632,7 @@ func (m *Manager) applyResourceLimits(lxcName string, cfg ContainerConfig) error
// Keep sys_admin: unprivileged containers need it to mount tmpfs (/dev/shm, /run, etc.) // Keep sys_admin: unprivileged containers need it to mount tmpfs (/dev/shm, /run, etc.)
// All capabilities are already confined to the container's user namespace. // All capabilities are already confined to the container's user namespace.
newLines = append(newLines, "lxc.cap.drop = mac_admin mac_override sys_module sys_rawio sys_time sys_boot sys_nice sys_resource sys_ptrace sys_pacct mknod audit_control audit_read") newLines = append(newLines, "lxc.cap.drop = mac_admin mac_override sys_module sys_rawio sys_time sys_boot sys_nice sys_resource sys_ptrace sys_pacct mknod audit_control audit_read")
newLines = append(newLines, "lxc.prlimit.nofile = 1024:4096") newLines = append(newLines, managedPrlimitLines()...)
newLines = append(newLines, "lxc.prlimit.nproc = 128:256")
newLines = append(newLines, "", "# clicd managed resource limits (cgroup v2)") newLines = append(newLines, "", "# clicd managed resource limits (cgroup v2)")
if cfg.VCPU > 0 { if cfg.VCPU > 0 {
@@ -663,6 +662,13 @@ func (m *Manager) applyResourceLimits(lxcName string, cfg ContainerConfig) error
return nil return nil
} }
func managedPrlimitLines() []string {
// Do not set lxc.prlimit.nproc for unprivileged containers: RLIMIT_NPROC is
// accounted by the host-mapped UID, so containers sharing a uid_map would
// consume one shared quota and fail to fork/exec during batch starts.
return []string{"lxc.prlimit.nofile = 1024:4096"}
}
func (m *Manager) ioLimitLines(lxcName string, readMBps int, writeMBps int) ([]string, error) { func (m *Manager) ioLimitLines(lxcName string, readMBps int, writeMBps int) ([]string, error) {
if readMBps < 0 { if readMBps < 0 {
readMBps = 0 readMBps = 0
@@ -1523,13 +1529,35 @@ func (m *Manager) waitForLXCStartup(lxcName, logFile, consoleLog string) error {
} }
// applyBandwidthLimit applies tc-based bandwidth limit on container's veth interface // applyBandwidthLimit applies tc-based bandwidth limit on container's veth interface
// ApplyContainerLimits re-applies resource limits (CPU, RAM, IO, BW) to a running container. // ApplyContainerLimits re-applies persisted LXC config limits and, when running,
// runtime cgroup/tc limits.
func (m *Manager) ApplyContainerLimits(c *config.Container) error { func (m *Manager) ApplyContainerLimits(c *config.Container) error {
if c == nil || c.Status != "running" { if c == nil {
return nil return nil
} }
config.NormalizeContainerResourceAliases(c) config.NormalizeContainerResourceAliases(c)
lxcName := c.LxcName() lxcName := c.LxcName()
if err := m.applyResourceLimits(lxcName, ContainerConfig{
Name: c.Name,
TemplateID: c.Template,
VCPU: c.VCPU,
RAMMB: c.RAMMB,
DiskGB: c.DiskGB,
NetworkBWMbps: c.NetworkBWMbps,
NetworkDownMbps: c.NetworkDownMbps,
NetworkUpMbps: c.NetworkUpMbps,
MonthlyTrafficGB: c.MonthlyTrafficGB,
IOSpeedMBps: c.IOSpeedMBps,
IOReadMBps: c.IOReadMBps,
IOWriteMBps: c.IOWriteMBps,
AssignIPv6: c.IPv6 != "" || len(c.IPv6Addresses) > 0,
ExpiresAt: c.ExpiresAt,
}); err != nil {
return err
}
if c.Status != "running" {
return nil
}
// CPU: write cpu.max // CPU: write cpu.max
cpuQuota := int(c.VCPU * 100000) cpuQuota := int(c.VCPU * 100000)
+8
View File
@@ -113,6 +113,14 @@ open_by_handle_at errno 1
} }
} }
func TestManagedPrlimitLinesDoNotSetNproc(t *testing.T) {
for _, line := range managedPrlimitLines() {
if strings.HasPrefix(strings.TrimSpace(line), "lxc.prlimit.nproc") {
t.Fatalf("managed prlimit lines must not set nproc: %q", line)
}
}
}
func TestAppendMissingSeccompRulesAddsFutexMitigationOnce(t *testing.T) { func TestAppendMissingSeccompRulesAddsFutexMitigationOnce(t *testing.T) {
base := "2\ndenylist\n[all]\nopen_by_handle_at errno 1\n" base := "2\ndenylist\n[all]\nopen_by_handle_at errno 1\n"