Apply container limits to install containers, defaulting to minimums if the server's resources are set too low

This commit is contained in:
Dane Everitt
2021-06-20 17:21:51 -07:00
parent e0078eee0a
commit 8336f6ff29
4 changed files with 79 additions and 14 deletions

View File

@@ -132,7 +132,7 @@ func (e *Environment) InSituUpdate() error {
//
// @see https://github.com/moby/moby/issues/41946
if _, err := e.client.ContainerUpdate(ctx, e.Id, container.UpdateConfig{
Resources: e.resources(),
Resources: e.Configuration.Limits().AsContainerResources(),
}); err != nil {
return errors.Wrap(err, "environment/docker: could not update container")
}
@@ -203,7 +203,7 @@ func (e *Environment) Create() error {
// Define resource limits for the container based on the data passed through
// from the Panel.
Resources: e.resources(),
Resources: e.Configuration.Limits().AsContainerResources(),
DNS: config.Get().Docker.Network.Dns,

View File

@@ -6,6 +6,7 @@ import (
"strconv"
"github.com/apex/log"
"github.com/docker/docker/api/types/container"
"github.com/pterodactyl/wings/config"
)
@@ -60,49 +61,66 @@ type Limits struct {
// ConvertedCpuLimit converts the CPU limit for a server build into a number
// that can be better understood by the Docker environment. If there is no limit
// set, return -1 which will indicate to Docker that it has unlimited CPU quota.
func (r *Limits) ConvertedCpuLimit() int64 {
if r.CpuLimit == 0 {
func (l Limits) ConvertedCpuLimit() int64 {
if l.CpuLimit == 0 {
return -1
}
return r.CpuLimit * 1000
return l.CpuLimit * 1000
}
// MemoryOverheadMultiplier sets the hard limit for memory usage to be 5% more
// than the amount of memory assigned to the server. If the memory limit for the
// 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 (r *Limits) MemoryOverheadMultiplier() float64 {
if r.MemoryLimit <= 2048 {
func (l Limits) MemoryOverheadMultiplier() float64 {
if l.MemoryLimit <= 2048 {
return 1.15
} else if r.MemoryLimit <= 4096 {
} else if l.MemoryLimit <= 4096 {
return 1.10
}
return 1.05
}
func (r *Limits) BoundedMemoryLimit() int64 {
return int64(math.Round(float64(r.MemoryLimit) * r.MemoryOverheadMultiplier() * 1_000_000))
func (l Limits) BoundedMemoryLimit() int64 {
return int64(math.Round(float64(l.MemoryLimit) * l.MemoryOverheadMultiplier() * 1_000_000))
}
// ConvertedSwap returns the amount of swap available as a total in bytes. This
// is returned as the amount of memory available to the server initially, PLUS
// the amount of additional swap to include which is the format used by Docker.
func (r *Limits) ConvertedSwap() int64 {
if r.Swap < 0 {
func (l Limits) ConvertedSwap() int64 {
if l.Swap < 0 {
return -1
}
return (r.Swap * 1_000_000) + r.BoundedMemoryLimit()
return (l.Swap * 1_000_000) + l.BoundedMemoryLimit()
}
// ProcessLimit returns the process limit for a container. This is currently
// defined at a system level and not on a per-server basis.
func (r *Limits) ProcessLimit() int64 {
func (l Limits) ProcessLimit() int64 {
return config.Get().Docker.ContainerPidLimit
}
func (l Limits) AsContainerResources() container.Resources {
pids := l.ProcessLimit()
return container.Resources{
Memory: l.BoundedMemoryLimit(),
MemoryReservation: l.MemoryLimit * 1_000_000,
MemorySwap: l.ConvertedSwap(),
CPUQuota: l.ConvertedCpuLimit(),
CPUPeriod: 100_000,
CPUShares: 1024,
BlkioWeight: l.IoWeight,
OomKillDisable: &l.OOMDisabled,
CpusetCpus: l.Threads,
PidsLimit: &pids,
}
}
type Variables map[string]interface{}
// Get is an ugly hacky function to handle environment variables that get passed