From 1892b270b1012f2353baa0d72e77e1f40a3adb57 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 17 Jan 2022 20:20:30 -0700 Subject: [PATCH] environment: allow overriding memory overhead; closes pterodactyl/panel#3728 (#111) --- config/config_docker.go | 74 ++++++++++++++++++++++++++++++++++++++--- environment/settings.go | 8 +---- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/config/config_docker.go b/config/config_docker.go index 33e40a4..7fd2034 100644 --- a/config/config_docker.go +++ b/config/config_docker.go @@ -3,6 +3,7 @@ package config import ( "encoding/base64" "encoding/json" + "sort" "github.com/docker/docker/api/types" ) @@ -51,9 +52,9 @@ type DockerConfiguration struct { // Registries . Registries map[string]RegistryConfiguration `json:"registries" yaml:"registries"` - // The size of the /tmp directory when mounted into a container. Please be aware that Docker - // utilizes host memory for this value, and that we do not keep track of the space used here - // so avoid allocating too much to a server. + // TmpfsSize specifies the size for the /tmp directory mounted into containers. Please be + // aware that Docker utilizes the host's system memory for this value, and that we do not + // keep track of the space used there, so avoid allocating too much to a server. TmpfsSize uint `default:"100" json:"tmpfs_size" yaml:"tmpfs_size"` // ContainerPidLimit sets the total number of processes that can be active in a container @@ -62,14 +63,18 @@ type DockerConfiguration struct { // available pids and crash. ContainerPidLimit int64 `default:"512" json:"container_pid_limit" yaml:"container_pid_limit"` - // InstallLimits defines the limits on the installer containers that prevents a server's + // InstallerLimits defines the limits on the installer containers that prevents a server's // installation process from unintentionally consuming more resources than expected. This // is used in conjunction with the server's defined limits. Whichever value is higher will - // take precedence in the install containers. + // take precedence in the installer containers. InstallerLimits struct { Memory int64 `default:"1024" json:"memory" yaml:"memory"` Cpu int64 `default:"100" json:"cpu" yaml:"cpu"` } `json:"installer_limits" yaml:"installer_limits"` + + // Overhead controls the memory overhead given to all containers to circumvent certain + // software such as the JVM not staying below the maximum memory limit. + Overhead Overhead `json:"overhead" yaml:"overhead"` } // RegistryConfiguration defines the authentication credentials for a given @@ -91,3 +96,62 @@ func (c RegistryConfiguration) Base64() (string, error) { } return base64.URLEncoding.EncodeToString(b), nil } + +// Overhead controls the memory overhead given to all containers to circumvent certain +// software such as the JVM not staying below the maximum memory limit. +type Overhead struct { + // Override controls if the overhead limits should be overridden by the values in the config file. + Override bool `default:"false" json:"override" yaml:"override"` + + // DefaultMultiplier sets the default multiplier for if no Multipliers are able to be applied. + DefaultMultiplier float64 `default:"1.05" json:"default_multiplier" yaml:"default_multiplier"` + + // Multipliers allows overriding DefaultMultiplier depending on the amount of memory + // configured for a server. + // + // Default values (used if Override is `false`) + // - Less than 2048 MB of memory, multiplier of 1.15 (15%) + // - Less than 4096 MB of memory, multiplier of 1.10 (10%) + // - Otherwise, multiplier of 1.05 (5%) - specified in DefaultMultiplier + // + // If the defaults were specified in the config they would look like: + // ```yaml + // multipliers: + // 2048: 1.15 + // 4096: 1.10 + // ``` + Multipliers map[int]float64 `json:"multipliers" yaml:"multipliers"` +} + +func (o Overhead) GetMultiplier(memoryLimit int64) float64 { + // Default multiplier values. + if !o.Override { + if memoryLimit <= 2048 { + return 1.15 + } else if memoryLimit <= 4096 { + return 1.10 + } + return 1.05 + } + + // This plucks the keys of the Multipliers map, so they can be sorted from + // smallest to largest in order to correctly apply the proper multiplier. + i := 0 + multipliers := make([]int, len(o.Multipliers)) + for k := range o.Multipliers { + multipliers[i] = k + i++ + } + sort.Ints(multipliers) + + // Loop through the memory values in order (smallest to largest) + for _, m := range multipliers { + // If the server's memory limit exceeds the modifier's limit, don't apply it. + if memoryLimit > int64(m) { + continue + } + return o.Multipliers[m] + } + + return o.DefaultMultiplier +} diff --git a/environment/settings.go b/environment/settings.go index 0371757..ae853cf 100644 --- a/environment/settings.go +++ b/environment/settings.go @@ -75,13 +75,7 @@ func (l Limits) ConvertedCpuLimit() int64 { // server is < 4G, use 10%, if less than 2G use 15%. This avoids unexpected // crashes from processes like Java which run over the limit. func (l Limits) MemoryOverheadMultiplier() float64 { - if l.MemoryLimit <= 2048 { - return 1.15 - } else if l.MemoryLimit <= 4096 { - return 1.10 - } - - return 1.05 + return config.Get().Docker.Overhead.GetMultiplier(l.MemoryLimit) } func (l Limits) BoundedMemoryLimit() int64 {