From 246e1f7379d849ce3160a2f46a7be9d99927555b Mon Sep 17 00:00:00 2001 From: Antti Kervinen Date: Mon, 18 Sep 2023 23:24:41 +0300 Subject: [PATCH] Memory QoS example plugin Signed-off-by: Antti Kervinen --- Makefile | 3 +- cmd/plugins/memory-qos/Dockerfile | 23 ++ cmd/plugins/memory-qos/main.go | 303 ++++++++++++++++++ .../nri-memory-qos-deployment.yaml.in | 62 ++++ deployment/overlays/memory-qos/daemonset.yaml | 46 +++ .../overlays/memory-qos/kustomization.yaml | 13 + .../overlays/memory-qos/sample-configmap.yaml | 15 + docs/index.md | 1 + docs/memory/index.md | 9 + docs/memory/memory-qos.md | 155 +++++++++ go.mod | 2 +- sample-configs/nri-memory-qos.yaml | 8 + .../files/nri-memory-qos-deployment.yaml.in | 62 ++++ test/e2e/files/nri-memory-qos-test-pod.yaml | 55 ++++ 14 files changed, 755 insertions(+), 2 deletions(-) create mode 100644 cmd/plugins/memory-qos/Dockerfile create mode 100644 cmd/plugins/memory-qos/main.go create mode 100644 cmd/plugins/memory-qos/nri-memory-qos-deployment.yaml.in create mode 100644 deployment/overlays/memory-qos/daemonset.yaml create mode 100644 deployment/overlays/memory-qos/kustomization.yaml create mode 100644 deployment/overlays/memory-qos/sample-configmap.yaml create mode 100644 docs/memory/index.md create mode 100644 docs/memory/memory-qos.md create mode 100644 sample-configs/nri-memory-qos.yaml create mode 100644 test/e2e/files/nri-memory-qos-deployment.yaml.in create mode 100644 test/e2e/files/nri-memory-qos-test-pod.yaml diff --git a/Makefile b/Makefile index b77240bd5..4102feb69 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,8 @@ DOCKER_BUILD := $(DOCKER) build PLUGINS ?= \ nri-resource-policy-topology-aware \ nri-resource-policy-balloons \ - nri-resource-policy-template + nri-resource-policy-template \ + nri-memory-qos BINARIES ?= \ config-manager diff --git a/cmd/plugins/memory-qos/Dockerfile b/cmd/plugins/memory-qos/Dockerfile new file mode 100644 index 000000000..8e0b36c57 --- /dev/null +++ b/cmd/plugins/memory-qos/Dockerfile @@ -0,0 +1,23 @@ +ARG GO_VERSION=1.20 + +FROM golang:${GO_VERSION}-bullseye as builder + +WORKDIR /go/builder + +# Fetch go dependencies in a separate layer for caching +COPY go.mod go.sum ./ +COPY pkg/topology/ pkg/topology/ +RUN go mod download + +# Build nri-resmgr +COPY . . + +RUN make clean +RUN make PLUGINS=nri-memory-qos build-plugins-static + +FROM gcr.io/distroless/static + +COPY --from=builder /go/builder/build/bin/nri-memory-qos /bin/nri-memory-qos +COPY --from=builder /go/builder/cmd/plugins/memory-qos/sample-config.yaml /etc/nri/memory-qos/config.yaml + +ENTRYPOINT ["/bin/nri-memory-qos", "-idx", "40", "-config", "/etc/nri/memory-qos/config.yaml"] diff --git a/cmd/plugins/memory-qos/main.go b/cmd/plugins/memory-qos/main.go new file mode 100644 index 000000000..98c06a901 --- /dev/null +++ b/cmd/plugins/memory-qos/main.go @@ -0,0 +1,303 @@ +// Copyright 2023 Inter Corporation. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "flag" + "fmt" + "os" + "strconv" + "strings" + + "sigs.k8s.io/yaml" + + "github.com/sirupsen/logrus" + + "github.com/containerd/nri/pkg/api" + "github.com/containerd/nri/pkg/stub" +) + +type plugin struct { + stub stub.Stub + mask stub.EventMask + config *pluginConfig +} + +type pluginConfig struct { + // UnifiedAnnotations lists keys whose values are written + // directly from annotations to the OCI Linux unified + // object. Example: + // UnifiedAnnotations: ["memory.high", "memory.swap.max"] + // allows using pod annotation + // memory.swap.max.memory-qos.nri.io: max + // that will add unified["memory.swap.max"] = "max" + UnifiedAnnotations []string + + // Classes define how memory of all workloads in each QoS + // class should be managed. + Classes []QoSClass +} + +type QoSClass struct { + // Name of the QoS class, matches to annotations in + // pods. Examples: + // Set the default class for containers in the pod: + // class.memory-qos.nri.io: "swap" + // Override the default class of CONTAINERNAME: + // class.memory-qos.nri.io/CONTAINERNAME: "noswap" + Name string + + // SwapLimitRatio sets memory.high based on memory limit. + // 1.0 means no throttling before getting OOM-killed. + // 0.75 throttle (reclaim pages) when usage reaches 75 % of memory limit. + SwapLimitRatio float32 +} + +const ( + annotationSuffix = ".memory-qos.nri.io" +) + +var ( + log *logrus.Logger +) + +// Configure handles connecting to container runtime's NRI server. +func (p *plugin) Configure(ctx context.Context, config, runtime, version string) (stub.EventMask, error) { + log.Infof("Connected to %s %s...", runtime, version) + if config != "" { + log.Debugf("loading configuration from NRI server") + if err := p.setConfig([]byte(config)); err != nil { + return 0, err + } + return 0, nil + } + return 0, nil +} + +// onClose handles losing connection to container runtime. +func (p *plugin) onClose() { + log.Infof("Connection to the runtime lost, exiting...") + os.Exit(0) +} + +// setConfig applies new plugin configuration. +func (p *plugin) setConfig(config []byte) error { + log.Tracef("setConfig: parsing\n---8<---\n%s\n--->8---", config) + cfg := pluginConfig{} + err := yaml.Unmarshal(config, &cfg) + if err != nil { + errWithContext := fmt.Errorf("setConfig: cannot parse configuration: %w", err) + log.Debugf("%s", errWithContext) + return errWithContext + } + p.config = &cfg + log.Tracef("new configuration has %d classes:", len(p.config.Classes)) + for _, cls := range p.config.Classes { + log.Tracef("- %s", cls.Name) + } + return nil +} + +// pprintCtr() returns unique human readable container name. +func pprintCtr(pod *api.PodSandbox, ctr *api.Container) string { + return fmt.Sprintf("%s/%s:%s", pod.GetNamespace(), pod.GetName(), ctr.GetName()) +} + +// applyQosClass applies QoS class to a container, updates unified values. +func (p *plugin) applyQosClass(pod *api.PodSandbox, ctr *api.Container, cls string, unified map[string]string) error { + if p.config == nil { + return fmt.Errorf("missing plugin configuration") + } + for _, class := range p.config.Classes { + log.Tracef("comparing configuration class %q to annotation %q", class.Name, cls) + if class.Name == cls { + log.Tracef("applying SwapLimitRatio=%.2f on unified=%v", class.SwapLimitRatio, unified) + if class.SwapLimitRatio > 0 { + memLimitp := ctr.Linux.Resources.Memory.Limit + if memLimitp == nil { + return fmt.Errorf("missing container memory limit") + } + // memory.high and memory.swap.max + // values defined by the QoS class do + // not override these values if set by + // specifically with unified annotations. + associate(unified, "memory.high", strconv.FormatInt(int64(float32(memLimitp.Value)*(1.0-class.SwapLimitRatio)), 10), false) + associate(unified, "memory.swap.max", "max", false) + } + log.Tracef("resulted unified=%v", unified) + return nil + } + } + return fmt.Errorf("class not found in plugin configuration") +} + +// associate adds new key-value pair to a map, or updates existing +// pair if called with override. Returns true if added/updated. +func associate(m map[string]string, key, value string, override bool) bool { + if _, exists := m[key]; override || !exists { + m[key] = value + return true + } + return false +} + +// sliceContains returns true if and only if haystack contains +// needle. Note: go 1.21+ will enable using slices.Contains(). +func sliceContains(haystack []string, needle string) bool { + for _, hay := range haystack { + if hay == needle { + return true + } + } + return false +} + +// effectiveAnnotations returns map of annotation key prefixes and +// values that are effective for a container. +// Example: a container-specific pod annotation +// +// memory.high.memory-qos.nri.io/CTRNAME: 10000000 +// +// shows up as +// +// effAnn["memory.high"] = "10000000" +func effectiveAnnotations(pod *api.PodSandbox, ctr *api.Container) map[string]string { + effAnn := map[string]string{} + for key, value := range pod.GetAnnotations() { + annPrefix, hasSuffix := strings.CutSuffix(key, annotationSuffix+"/"+ctr.Name) + if hasSuffix { + // Override possibly already found pod-level annotation. + log.Tracef("- found container-specific annotation %q", key) + associate(effAnn, annPrefix, value, true) + effAnn[annPrefix] = value + continue + } + annPrefix, hasSuffix = strings.CutSuffix(key, annotationSuffix) + if hasSuffix { + // Do not override if there already is a + // container-level annotation. + if associate(effAnn, annPrefix, value, false) { + log.Tracef("- found pod-level annotation %q", key) + } else { + log.Tracef("- ignoring pod-level annotation %q due to a container-level annotation", key) + } + continue + } + log.Tracef("- ignoring annotation %q", key) + } + return effAnn +} + +// CreateContainer modifies container when it is being created. +func (p *plugin) CreateContainer(ctx context.Context, pod *api.PodSandbox, ctr *api.Container) (*api.ContainerAdjustment, []*api.ContainerUpdate, error) { + ppName := pprintCtr(pod, ctr) + log.Tracef("CreateContainer %s", ppName) + unified := map[string]string{} + class := "" + for annPrefix, value := range effectiveAnnotations(pod, ctr) { + switch { + case annPrefix == "class": + if err := p.applyQosClass(pod, ctr, value, unified); err != nil { + errWithContext := fmt.Errorf("cannot apply memory QoS class %q: %w", value, err) + log.Errorf("CreateContainer %s: %s", ppName, errWithContext) + return nil, nil, errWithContext + } + class = value + case sliceContains(p.config.UnifiedAnnotations, annPrefix) == true: + unified[annPrefix] = value + log.Tracef("applying unified annotation %q resulted in unified=%v", annPrefix, unified) + default: + err := fmt.Errorf("CreateContainer %s: invalid annotation: %q", ppName, annPrefix) + log.Errorf("%s", err) + return nil, nil, err + } + } + if len(unified) == 0 { + log.Debugf("CreateContainer %s: no adjustments", ppName) + return nil, nil, nil + } + ca := api.ContainerAdjustment{ + Linux: &api.LinuxContainerAdjustment{ + Resources: &api.LinuxResources{ + Unified: unified, + }, + }, + } + log.Debugf("CreateContainer %s: class %q, LinuxResources.Unified=%v", ppName, class, ca.Linux.Resources.Unified) + return &ca, nil, nil +} + +func main() { + var ( + pluginName string + pluginIdx string + configFile string + err error + verbose bool + veryVerbose bool + ) + + log = logrus.StandardLogger() + log.SetFormatter(&logrus.TextFormatter{ + PadLevelText: true, + }) + + flag.StringVar(&pluginName, "name", "", "plugin name to register to NRI") + flag.StringVar(&pluginIdx, "idx", "", "plugin index to register to NRI") + flag.StringVar(&configFile, "config", "", "configuration file name") + flag.BoolVar(&verbose, "v", false, "verbose output") + flag.BoolVar(&veryVerbose, "vv", false, "very verbose output") + flag.Parse() + + if verbose { + log.SetLevel(logrus.DebugLevel) + } + if veryVerbose { + log.SetLevel(logrus.TraceLevel) + } + + p := &plugin{} + + if configFile != "" { + log.Debugf("read configuration from %q", configFile) + config, err := os.ReadFile(configFile) + if err != nil { + log.Fatalf("error reading configuration file %q: %s", configFile, err) + } + if err = p.setConfig(config); err != nil { + log.Fatalf("error applying configuration from file %q: %s", configFile, err) + } + } + + opts := []stub.Option{ + stub.WithOnClose(p.onClose), + } + if pluginName != "" { + opts = append(opts, stub.WithPluginName(pluginName)) + } + if pluginIdx != "" { + opts = append(opts, stub.WithPluginIdx(pluginIdx)) + } + + if p.stub, err = stub.New(p, opts...); err != nil { + log.Fatalf("failed to create plugin stub: %v", err) + } + + if err = p.stub.Run(context.Background()); err != nil { + log.Errorf("plugin exited (%v)", err) + os.Exit(1) + } +} diff --git a/cmd/plugins/memory-qos/nri-memory-qos-deployment.yaml.in b/cmd/plugins/memory-qos/nri-memory-qos-deployment.yaml.in new file mode 100644 index 000000000..0342c52b9 --- /dev/null +++ b/cmd/plugins/memory-qos/nri-memory-qos-deployment.yaml.in @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + labels: + app: nri-memory-qos + name: nri-memory-qos + namespace: kube-system +spec: + selector: + matchLabels: + app: nri-memory-qos + template: + metadata: + labels: + app: nri-memory-qos + spec: + nodeSelector: + kubernetes.io/os: "linux" + containers: + - name: nri-memory-qos + command: + - nri-memory-qos + - --idx + - "40" + - --config + - /etc/nri/memory-qos/config.yaml + - -v + image: IMAGE_PLACEHOLDER + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 10m + memory: 100Mi + volumeMounts: + - name: memory-qos-config-vol + mountPath: /etc/nri/memory-qos + - name: nri-sockets-vol + mountPath: /var/run/nri + volumes: + - name: memory-qos-config-vol + configMap: + name: nri-memory-qos-config.default + - name: nri-sockets-vol + hostPath: + path: /var/run/nri + type: Directory +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: nri-memory-qos-config.default + namespace: kube-system +data: + config.yaml: | + classes: + - name: bronze + swaplimitratio: 0.5 + - name: silver + swaplimitratio: 0.2 + unifiedannotations: + - memory.swap.max + - memory.high diff --git a/deployment/overlays/memory-qos/daemonset.yaml b/deployment/overlays/memory-qos/daemonset.yaml new file mode 100644 index 000000000..be60b4a7a --- /dev/null +++ b/deployment/overlays/memory-qos/daemonset.yaml @@ -0,0 +1,46 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + labels: + app: nri-memory-qos + name: nri-memory-qos + namespace: kube-system +spec: + selector: + matchLabels: + app: nri-memory-qos + template: + metadata: + labels: + app: nri-memory-qos + spec: + nodeSelector: + kubernetes.io/os: "linux" + containers: + - name: nri-memory-qos + command: + - nri-memory-qos + - --idx + - "40" + - --config + - /etc/nri/memory-qos/config.yaml + - -v + image: IMAGE_PLACEHOLDER + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 10m + memory: 100Mi + volumeMounts: + - name: memory-qos-config-vol + mountPath: /etc/nri/memory-qos + - name: nri-sockets-vol + mountPath: /var/run/nri + volumes: + - name: memory-qos-config-vol + configMap: + name: nri-memory-qos-config.default + - name: nri-sockets-vol + hostPath: + path: /var/run/nri + type: Directory diff --git a/deployment/overlays/memory-qos/kustomization.yaml b/deployment/overlays/memory-qos/kustomization.yaml new file mode 100644 index 000000000..017af116f --- /dev/null +++ b/deployment/overlays/memory-qos/kustomization.yaml @@ -0,0 +1,13 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: kube-system + +images: +- name: '*' + newName: ghcr.io/containers/nri-plugins/nri-memory-qos + newTag: unstable + +resources: +- daemonset.yaml +- sample-configmap.yaml diff --git a/deployment/overlays/memory-qos/sample-configmap.yaml b/deployment/overlays/memory-qos/sample-configmap.yaml new file mode 100644 index 000000000..11bc635ea --- /dev/null +++ b/deployment/overlays/memory-qos/sample-configmap.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: nri-memory-qos-config.default + namespace: kube-system +data: + config.yaml: | + classes: + - name: bronze + swaplimitratio: 0.5 + - name: silver + swaplimitratio: 0.2 + unifiedannotations: + - memory.swap.max + - memory.high diff --git a/docs/index.md b/docs/index.md index 628226361..1aa33fb9a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,6 +7,7 @@ caption: Contents --- introduction.md resource-policy/index.rst +memory/index.md contributing.md Project GitHub repository ``` diff --git a/docs/memory/index.md b/docs/memory/index.md new file mode 100644 index 000000000..5f1d68b37 --- /dev/null +++ b/docs/memory/index.md @@ -0,0 +1,9 @@ +# Memory plugins + +```{toctree} +--- +maxdepth: 2 +caption: Contents +--- +memory-qos.md +``` diff --git a/docs/memory/memory-qos.md b/docs/memory/memory-qos.md new file mode 100644 index 000000000..da13faf6e --- /dev/null +++ b/docs/memory/memory-qos.md @@ -0,0 +1,155 @@ +# Memory QoS NRI plugin + +This NRI plugin adds two methods for controlling cgroups v2 `memory.*` +parameters: memory QoS classes and direct memory annotations. + +## Workload configuration + +There are two configuration methods: +1. Memory QoS classes: memory parameters are calculated in the same + way for all workloads that belong to the same class. +2. Direct workload-specific memory parameters. + +Memory QoS class of a pod or a container is defined using annotations +in pod yaml: + +```yaml + annotations: + # Set the default memory QoS class for all containers in this pod. + class.memory-qos.nri.io: silver + + # Override the default class for the c0 container. + class.memory-qos.nri.io/c0: bronze + + # Remove the default class from the c1 container. + class.memory-qos.nri.io/c1: "" +``` + +Cgroups v2 memory parameters are given pod annotations. Following +example affects `memory.swap.max`, `memory.high` and +`memory.oom.group`: + +```yaml + annotations: + # Never swap memory of the noswap container in this pod. + memory.swap.max.memory-qos.nri.io/noswap: "0" + memory.high.memory-qos.nri.io/noswap: max + + # For all containers: if a process gets OOM killed, + # do not group-kill the whole cgroup. + memory.oom.group.memory-qos.nri.io: "0" +``` + +## Plugin configuration + +### Classes + +Plugin configuration lists memory QoS classes and their parameters +that affect calculating actual memory parameters. + +`classes:` is followed by list of maps with following keys and values: +- `name` (string): name of the memory QoS class, matches + `class.memory-qos.nri.io` annotation values. +- `swaplimitratio` (from 0.0 to 1.0): minimum ratio of container's + memory on swap and resources.limits.memory when container's memory + consumption reaches the limit. Adjusts `memory.high` watermark to + `resources.limits.memory * (1.0 - swaplimitratio)`. + +### Unified annotations + +`unifiedannotations:` (list of strings): OCI Linux unified fields +(cgroups v2 file names) whose values are allowed to be set using +direct annotations. If annotations define these values, they override +values implied by container's memory QoS class. + +### Example + +```yaml +classes: +- name: bronze + swaplimitratio: 0.5 +- name: silver + swaplimitratio: 0.2 +unifiedannotations: +- memory.swap.max +- memory.high +``` + +This configuration defines the following. + +- If a container belogs to the memory QoS class `bronze` has allocated + half of the memory of its `resources.limits.memory`, next + allocations will cause kernel to swap out corresponding amount of + container's memory. In other words, when container's memory usage is + close to the limit, at most half of its data is stored in RAM. +- Containers in `silver` class are allowed to keep up to 80 % of their + data in RAM when reaching memory limit. +- Memory annotations are allowed to modify `memory.swap.max` and + `memory.high` values directly but, for instance, modifying + `memory.oom.group` is not enabled by this configuration. + +## Developer's guide + +### Prerequisites + +- Containerd v1.7+ +- Enable NRI in /etc/containerd/config.toml: + ``` + [plugins."io.containerd.nri.v1.nri"] + disable = false + disable_connections = false + plugin_config_path = "/etc/nri/conf.d" + plugin_path = "/opt/nri/plugins" + plugin_registration_timeout = "5s" + plugin_request_timeout = "2s" + socket_path = "/var/run/nri/nri.sock" + ``` + +### Build + +```bash +cd cmd/plugins/memory-qos && go build . +``` + +### Run + +```bash +cmd/plugins/memory-qos/memory-qos -config sample-configs/nri-memory-qos.yaml -idx 40 -vv +``` + +### Manual test + +```bash +kubectl create -f test/e2e/files/nri-memory-qos-test-pod.yaml +``` + +See swap status of dd processes, each allocating the same amount of +memory: + +```bash +for pid in $(pidof dd); do + grep VmSwap /proc/$pid/status +done +``` + +### Debug + +```bash +go install github.com/go-delve/delve/cmd/dlv@latest +dlv exec cmd/plugins/memory-qos/memory-qos -- -config sample-configs/nri-memory-qos.yaml -idx 40 +(dlv) break plugin.CreateContainer +(dlv) continue +``` + +### Deploy + +Build an image, import it on the node, and deploy the plugin by +running the following in `nri-plugins`: + +```bash +rm -rf build +make clean +make PLUGINS=nri-memory-qos IMAGE_VERSION=devel images +ctr -n k8s.io images import build/images/nri-memory-qos-image-*.tar +kubectl create -f build/images/nri-memory-qos-deployment.yaml +``` diff --git a/go.mod b/go.mod index 87ac5abc0..73b64f605 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/prometheus/client_golang v1.16.0 github.com/prometheus/client_model v0.3.0 github.com/prometheus/common v0.42.0 + github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.8.4 go.opencensus.io v0.24.0 go.opentelemetry.io/otel v1.15.1 @@ -71,7 +72,6 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/prometheus/statsd_exporter v0.22.8 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.1 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.1 // indirect diff --git a/sample-configs/nri-memory-qos.yaml b/sample-configs/nri-memory-qos.yaml new file mode 100644 index 000000000..611a66bdd --- /dev/null +++ b/sample-configs/nri-memory-qos.yaml @@ -0,0 +1,8 @@ +classes: +- name: bronze + swaplimitratio: 0.5 +- name: silver + swaplimitratio: 0.2 +unifiedannotations: +- memory.swap.max +- memory.high diff --git a/test/e2e/files/nri-memory-qos-deployment.yaml.in b/test/e2e/files/nri-memory-qos-deployment.yaml.in new file mode 100644 index 000000000..ff3a50936 --- /dev/null +++ b/test/e2e/files/nri-memory-qos-deployment.yaml.in @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + labels: + app: nri-memory-qos + name: nri-memory-qos + namespace: kube-system +spec: + selector: + matchLabels: + app: nri-memory-qos + template: + metadata: + labels: + app: nri-memory-qos + spec: + nodeSelector: + kubernetes.io/os: "linux" + containers: + - name: nri-memory-qos + command: + - nri-memory-qos + - --idx + - "40" + - --config + - /etc/nri/memory-qos/config.yaml + - -vv + image: IMAGE_PLACEHOLDER + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 10m + memory: 100Mi + volumeMounts: + - name: memory-qos-config-vol + mountPath: /etc/nri/memory-qos + - name: nri-sockets-vol + mountPath: /var/run/nri + volumes: + - name: memory-qos-config-vol + configMap: + name: nri-memory-qos-config.default + - name: nri-sockets-vol + hostPath: + path: /var/run/nri + type: Directory +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: nri-memory-qos-config.default + namespace: kube-system +data: + config.yaml: | + classes: + - name: bronze + swaplimitratio: 0.5 + - name: silver + swaplimitratio: 0.2 + unifiedannotations: + - memory.swap.max + - memory.high diff --git a/test/e2e/files/nri-memory-qos-test-pod.yaml b/test/e2e/files/nri-memory-qos-test-pod.yaml new file mode 100644 index 000000000..af4be5b1c --- /dev/null +++ b/test/e2e/files/nri-memory-qos-test-pod.yaml @@ -0,0 +1,55 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nri-memory-qos-test-pod + labels: + app: nri-memory-qos-test-pod + annotations: + # Set the default QoS class for all containers in this pod. + class.memory-qos.nri.io: silver + + # Override the default class for the c0-lowprio container. + class.memory-qos.nri.io/c0-lowprio: bronze + + # Never swap memory of the c2-noswap container. + memory.swap.max.memory-qos.nri.io/c2-noswap: "0" + memory.high.memory-qos.nri.io/c2-noswap: max +spec: + containers: + - name: c0-lowprio + image: busybox + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - dd count=1 bs=80M if=/dev/zero | sleep inf + resources: + requests: + memory: 64M + limits: + memory: 100M + - name: c1-normal + image: busybox + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - dd count=1 bs=80M if=/dev/zero | sleep inf + resources: + requests: + memory: 64M + limits: + memory: 100M + - name: c2-noswap + image: busybox + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - dd count=1 bs=80M if=/dev/zero | sleep inf + resources: + requests: + memory: 64M + limits: + memory: 100M + terminationGracePeriodSeconds: 1