diff --git a/docs/user.md b/docs/user.md index 828badfe8791135b7a877063573205a2951dadb9..c0343a18f528b36cb17cda4ee5604783a3b532eb 100644 --- a/docs/user.md +++ b/docs/user.md @@ -690,6 +690,27 @@ manifest the operator will raise the limits to the configured minimum values. If no resources are defined in the manifest they will be obtained from the configured [default requests](reference/operator_parameters.md#kubernetes-resource-requests). +### HugePages support + +The operator supports [HugePages](https://www.postgresql.org/docs/15/kernel-resources.html#LINUX-HUGEPAGES). +To enable HugePages, set the matching resource requests and/or limits in the manifest: + +```yaml +spec: + resources: + requests: + hugepages-2Mi: 250Mi + hugepages-1Gi: 1Gi + limits: + hugepages-2Mi: 500Mi + hugepages-1Gi: 2Gi +``` + +There are no minimums or maximums and the default is 0 for both HugePage sizes, +but Kubernetes will not spin up the pod if the requested HugePages cannot be allocated. +For more information on HugePages in Kubernetes, see also +[https://kubernetes.io/docs/tasks/manage-hugepages/scheduling-hugepages/](https://kubernetes.io/docs/tasks/manage-hugepages/scheduling-hugepages/) + ## Use taints, tolerations and node affinity for dedicated PostgreSQL nodes To ensure Postgres pods are running on nodes without any other application pods, diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 5f5b6ff093f0776ee765a1861aa87ba6aea86014..f0174c80f2097c78d32cb81c6295d895cfb24ff1 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -439,6 +439,12 @@ spec: pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' # Note: the value specified here must not be zero or be higher # than the corresponding limit. + hugepages-2Mi: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + hugepages-1Gi: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' requests: type: object properties: @@ -448,6 +454,12 @@ spec: memory: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + hugepages-2Mi: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + hugepages-1Gi: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' schedulerName: type: string serviceAnnotations: diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 3d9f4f08d1e18ce1a4408e63e2bc51f21c1e8022..a766c79e2e2d4967868b8fe06ed29feffdd9a4d3 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -3,10 +3,11 @@ package v1 import ( "fmt" - acidzalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do" - "github.com/zalando/postgres-operator/pkg/util" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + acidzalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do" + "github.com/zalando/postgres-operator/pkg/util" ) // CRDResource* define names necesssary for the k8s CRD API @@ -684,6 +685,14 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "string", Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", }, + "hugepages-2Mi": { + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + }, + "hugepages-1Gi": { + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + }, }, }, "requests": { @@ -697,6 +706,14 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "string", Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", }, + "hugepages-2Mi": { + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + }, + "hugepages-1Gi": { + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + }, }, }, }, diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index 3504d615d8c7e2b7d50c50bd0807d4bcf220d60a..a07dacef4c0c93b4208f24127afd8ce755dce9d4 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -153,8 +153,10 @@ type PostgresqlParam struct { // ResourceDescription describes CPU and memory resources defined for a cluster. type ResourceDescription struct { - CPU string `json:"cpu"` - Memory string `json:"memory"` + CPU string `json:"cpu"` + Memory string `json:"memory"` + HugePages2Mi string `json:"hugepages-2Mi"` + HugePages1Gi string `json:"hugepages-1Gi"` } // Resources describes requests and limits for the cluster resouces. diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 502886854666efd5ec0b6768ec5c78838f413717..0edf6ebca16488180b3c9746bddcfa2e3a4046a5 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -20,6 +20,10 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" + "golang.org/x/exp/maps" + batchv1 "k8s.io/api/batch/v1" + "k8s.io/apimachinery/pkg/labels" + acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando/postgres-operator/pkg/spec" "github.com/zalando/postgres-operator/pkg/util" @@ -28,9 +32,6 @@ import ( "github.com/zalando/postgres-operator/pkg/util/k8sutil" "github.com/zalando/postgres-operator/pkg/util/patroni" "github.com/zalando/postgres-operator/pkg/util/retryutil" - "golang.org/x/exp/maps" - batchv1 "k8s.io/api/batch/v1" - "k8s.io/apimachinery/pkg/labels" ) const ( @@ -267,6 +268,19 @@ func fillResourceList(spec acidv1.ResourceDescription, defaults acidv1.ResourceD } } + if spec.HugePages2Mi != "" { + requests[v1.ResourceHugePagesPrefix+"2Mi"], err = resource.ParseQuantity(spec.HugePages2Mi) + if err != nil { + return nil, fmt.Errorf("could not parse hugepages-2Mi quantity: %v", err) + } + } + if spec.HugePages1Gi != "" { + requests[v1.ResourceHugePagesPrefix+"1Gi"], err = resource.ParseQuantity(spec.HugePages1Gi) + if err != nil { + return nil, fmt.Errorf("could not parse hugepages-1Gi quantity: %v", err) + } + } + return requests, nil } diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index a88320debe8d44035a58800a4033f249473c5df8..026634068ab61f73303ccb766f585df571da9d47 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -5,9 +5,8 @@ import ( "fmt" "reflect" "sort" - "time" - "testing" + "time" "github.com/stretchr/testify/assert" @@ -2979,6 +2978,131 @@ func TestGenerateResourceRequirements(t *testing.T) { ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "2Gi"}, }, }, + { + subTest: "test HugePages are not set on container when not requested in manifest", + config: config.Config{ + Resources: configResources, + PodManagementPolicy: "ordered_ready", + }, + pgSpec: acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{}, + ResourceLimits: acidv1.ResourceDescription{}, + }, + TeamID: "acid", + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + }, + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{ + CPU: "100m", + Memory: "100Mi", + }, + ResourceLimits: acidv1.ResourceDescription{ + CPU: "1", + Memory: "500Mi", + }, + }, + }, + { + subTest: "test HugePages are passed through to the postgres container", + config: config.Config{ + Resources: configResources, + PodManagementPolicy: "ordered_ready", + }, + pgSpec: acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{ + HugePages2Mi: "128Mi", + HugePages1Gi: "1Gi", + }, + ResourceLimits: acidv1.ResourceDescription{ + HugePages2Mi: "256Mi", + HugePages1Gi: "2Gi", + }, + }, + TeamID: "acid", + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + }, + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{ + CPU: "100m", + Memory: "100Mi", + HugePages2Mi: "128Mi", + HugePages1Gi: "1Gi", + }, + ResourceLimits: acidv1.ResourceDescription{ + CPU: "1", + Memory: "500Mi", + HugePages2Mi: "256Mi", + HugePages1Gi: "2Gi", + }, + }, + }, + { + subTest: "test HugePages are passed through on sidecars", + config: config.Config{ + Resources: configResources, + PodManagementPolicy: "ordered_ready", + }, + pgSpec: acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + Sidecars: []acidv1.Sidecar{ + { + Name: "test-sidecar", + DockerImage: "test-image", + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{ + HugePages2Mi: "128Mi", + HugePages1Gi: "1Gi", + }, + ResourceLimits: acidv1.ResourceDescription{ + HugePages2Mi: "256Mi", + HugePages1Gi: "2Gi", + }, + }, + }, + }, + TeamID: "acid", + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + }, + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{ + CPU: "100m", + Memory: "100Mi", + HugePages2Mi: "128Mi", + HugePages1Gi: "1Gi", + }, + ResourceLimits: acidv1.ResourceDescription{ + CPU: "1", + Memory: "500Mi", + HugePages2Mi: "256Mi", + HugePages1Gi: "2Gi", + }, + }, + }, } for _, tt := range tests {