From 9e5eb84236865069396adc29d3e5855d5d42a70a Mon Sep 17 00:00:00 2001 From: "Alex Ellis (OpenFaaS Ltd)" Date: Fri, 18 Sep 2020 12:52:14 +0100 Subject: [PATCH] Add memory limit support Memory limits now work and a function will be killed with OOM however, it will remain in a stopped state and will not restart automatically. Signed-off-by: Alex Ellis (OpenFaaS Ltd) --- README.md | 15 ++++++++++++++ pkg/provider/handlers/deploy.go | 35 ++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 48fc2f0..cb42c95 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,21 @@ You could also perform this task over SSH, or use a configuration management too > Note: if you are using Caddy or Let's Encrypt for free SSL certificates, that you may hit rate-limits for generating new certificates if you do this too often within a given week. +### Memory limits for functions + +Memory limits for functions are supported. When the limit is exceeded the function will be killed. + +Example: + +```yaml +functions: + figlet: + skip_build: true + image: functions/figlet:latest + limits: + memory: 20Mi +``` + ## What does faasd deploy? * faasd - itself, and its [faas-provider](https://github.com/openfaas/faas-provider) for containerd - CRUD for functions and services, implements the OpenFaaS REST API diff --git a/pkg/provider/handlers/deploy.go b/pkg/provider/handlers/deploy.go index 098031e..9776850 100644 --- a/pkg/provider/handlers/deploy.go +++ b/pkg/provider/handlers/deploy.go @@ -12,6 +12,7 @@ import ( "github.com/containerd/containerd" "github.com/containerd/containerd/cio" + "github.com/containerd/containerd/containers" "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/oci" gocni "github.com/containerd/go-cni" @@ -22,6 +23,7 @@ import ( cninetwork "github.com/openfaas/faasd/pkg/cninetwork" "github.com/openfaas/faasd/pkg/service" "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/api/resource" ) func MakeDeployHandler(client *containerd.Client, cni gocni.CNI, secretMountPath string, alwaysPull bool) func(w http.ResponseWriter, r *http.Request) { @@ -104,6 +106,18 @@ func deploy(ctx context.Context, req types.FunctionDeployment, client *container labels = *req.Labels } + var memory *specs.LinuxMemory + if req.Limits != nil && len(req.Limits.Memory) > 0 { + memory = &specs.LinuxMemory{} + + qty, err := resource.ParseQuantity(req.Limits.Memory) + if err != nil { + log.Printf("error parsing (%q) as quantity: %s", req.Limits.Memory, err.Error()) + } + v := qty.Value() + memory.Limit = &v + } + container, err := client.NewContainer( ctx, name, @@ -113,7 +127,8 @@ func deploy(ctx context.Context, req types.FunctionDeployment, client *container containerd.WithNewSpec(oci.WithImageConfig(image), oci.WithCapabilities([]string{"CAP_NET_RAW"}), oci.WithMounts(mounts), - oci.WithEnv(envs)), + oci.WithEnv(envs), + withMemory(memory)), containerd.WithContainerLabels(labels), ) @@ -210,3 +225,21 @@ func validateSecrets(secretMountPath string, secrets []string) error { } return nil } + +func withMemory(mem *specs.LinuxMemory) oci.SpecOpts { + return func(ctx context.Context, _ oci.Client, c *containers.Container, s *oci.Spec) error { + if mem != nil { + if s.Linux == nil { + s.Linux = &specs.Linux{} + } + if s.Linux.Resources == nil { + s.Linux.Resources = &specs.LinuxResources{} + } + if s.Linux.Resources.Memory == nil { + s.Linux.Resources.Memory = &specs.LinuxMemory{} + } + s.Linux.Resources.Memory.Limit = mem.Limit + } + return nil + } +}