From 5a88ea7247d9e76a3a94bad9a7b8f492fb115d25 Mon Sep 17 00:00:00 2001 From: ben-toogood Date: Fri, 14 Aug 2020 11:47:28 +0100 Subject: [PATCH] runtime: resource limits (kubernetes implementation) (#1931) * runtime: add resource limit CreateOptions * util/kubernetes/client: implement support for resource limits * runtime/kubernetes: set resource limits for k8s deployments * util/kubernetes: remove template check for ints * util/kubernetes: fix incorrect yaml syntax * runtime/kubernetes: fix incorrect units * runtime: update create options to use Resources struct --- runtime/kubernetes/service.go | 17 +++++++++++++ runtime/options.go | 9 +++++++ runtime/runtime.go | 13 ++++++++++ util/kubernetes/client/templates.go | 37 +++++++++++++++++++++++++++-- util/kubernetes/client/types.go | 28 ++++++++++++++++------ 5 files changed, 95 insertions(+), 9 deletions(-) diff --git a/runtime/kubernetes/service.go b/runtime/kubernetes/service.go index 369a103b..508d55f9 100644 --- a/runtime/kubernetes/service.go +++ b/runtime/kubernetes/service.go @@ -2,6 +2,7 @@ package kubernetes import ( "encoding/json" + "fmt" "strings" "time" @@ -102,6 +103,22 @@ func newService(s *runtime.Service, c runtime.CreateOptions) *service { kdeploy.Spec.Template.PodSpec.Containers[0].Args = c.Args } + // apply resource limits + if c.Resources != nil { + resLimits := &client.ResourceLimits{} + if c.Resources.CPU > 0 { + resLimits.CPU = fmt.Sprintf("%vm", c.Resources.CPU) + } + if c.Resources.Mem > 0 { + resLimits.Memory = fmt.Sprintf("%vMi", c.Resources.Mem) + } + if c.Resources.Disk > 0 { + resLimits.EphemeralStorage = fmt.Sprintf("%vMi", c.Resources.Disk) + } + + kdeploy.Spec.Template.PodSpec.Containers[0].Resources = &client.ResourceRequirements{Limits: resLimits} + } + return &service{ Service: s, kservice: kservice, diff --git a/runtime/options.go b/runtime/options.go index f0bf7e04..dd5cc23d 100644 --- a/runtime/options.go +++ b/runtime/options.go @@ -84,6 +84,8 @@ type CreateOptions struct { Context context.Context // Secrets to use Secrets map[string]string + // Resources to allocate the service + Resources *Resources } // ReadOptions queries runtime services @@ -176,6 +178,13 @@ func WithOutput(out io.Writer) CreateOption { } } +// ResourceLimits sets the resources for the service to use +func ResourceLimits(r *Resources) CreateOption { + return func(o *CreateOptions) { + o.Resources = r + } +} + // ReadService returns services with the given name func ReadService(service string) ReadOption { return func(o *ReadOptions) { diff --git a/runtime/runtime.go b/runtime/runtime.go index d4cae718..ec252d02 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -104,3 +104,16 @@ type Service struct { // Metadata stores metadata Metadata map[string]string } + +// Resources which are allocated to a serivce +type Resources struct { + // CPU is the maximum amount of CPU the service will be allocated (unit millicpu) + // e.g. 0.25CPU would be passed as 250 + CPU int + // Mem is the maximum amount of memory the service will be allocated (unit mebibyte) + // e.g. 128 MiB of memory would be passed as 128 + Mem int + // Disk is the maximum amount of disk space the service will be allocated (unit mebibyte) + // e.g. 128 MiB of memory would be passed as 128 + Disk int +} diff --git a/util/kubernetes/client/templates.go b/util/kubernetes/client/templates.go index 61231119..334cb088 100644 --- a/util/kubernetes/client/templates.go +++ b/util/kubernetes/client/templates.go @@ -90,8 +90,8 @@ spec: {{- range . }} - containerPort: {{ .ContainerPort }} name: {{ .Name }} - {{- end}} - {{- end}} + {{- end }} + {{- end }} {{- if .ReadinessProbe }} {{- with .ReadinessProbe }} readinessProbe: @@ -106,6 +106,39 @@ spec: periodSeconds: {{ .PeriodSeconds }} {{- end }} {{- end }} + {{- if .Resources }} + {{- with .Resources }} + resources: + {{- if .Limits }} + {{- with .Limits }} + limits: + {{- if .Memory }} + memory: {{ .Memory }} + {{- end }} + {{- if .CPU }} + cpu: {{ .CPU }} + {{- end }} + {{- if .EphemeralStorage }} + ephemeral-storage: {{ .EphemeralStorage }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Requests }} + {{- with .Requests }} + requests: + {{- if .Memory }} + memory: {{ .Memory }} + {{- end }} + {{- if .CPU }} + cpu: {{ .CPU }} + {{- end }} + {{- if .EphemeralStorage }} + ephemeral-storage: {{ .EphemeralStorage }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} {{- end }} {{- end }} ` diff --git a/util/kubernetes/client/types.go b/util/kubernetes/client/types.go index 41392733..da78d1dd 100644 --- a/util/kubernetes/client/types.go +++ b/util/kubernetes/client/types.go @@ -35,13 +35,14 @@ type Condition struct { // Container defined container runtime values type Container struct { - Name string `json:"name"` - Image string `json:"image"` - Env []EnvVar `json:"env,omitempty"` - Command []string `json:"command,omitempty"` - Args []string `json:"args,omitempty"` - Ports []ContainerPort `json:"ports,omitempty"` - ReadinessProbe *Probe `json:"readinessProbe,omitempty"` + Name string `json:"name"` + Image string `json:"image"` + Env []EnvVar `json:"env,omitempty"` + Command []string `json:"command,omitempty"` + Args []string `json:"args,omitempty"` + Ports []ContainerPort `json:"ports,omitempty"` + ReadinessProbe *Probe `json:"readinessProbe,omitempty"` + Resources *ResourceRequirements `json:"resources,omitempty"` } // DeploymentSpec defines micro deployment spec @@ -234,3 +235,16 @@ type TCPSocketAction struct { Host string `json:"host,omitempty"` Port int `json:"port,omitempty"` } + +// ResourceRequirements describes the compute resource requirements. +type ResourceRequirements struct { + Limits *ResourceLimits `json:"limits,omitempty"` + Requests *ResourceLimits `json:"requests,omitempty"` +} + +// ResourceLimits describes the limits for a service +type ResourceLimits struct { + Memory string `json:"memory,omitempty"` + CPU string `json:"cpu,omitempty"` + EphemeralStorage string `json:"ephemeral-storage,omitempty"` +}