diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index c9fd30f87..e304aa340 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -441,6 +441,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: @@ -450,6 +456,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/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index f7d0eebc6..0f4f0bab6 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -359,6 +359,14 @@ CPU and memory requests for the Postgres container. memory requests for the Postgres container. Optional, overrides the `default_memory_request` operator configuration parameter. +* **hugepages-2Mi** + hugepages-2Mi requests for the sidecar container. + Optional, defaults to not set. + +* **hugepages-1Gi** + 1Gi hugepages requests for the sidecar container. + Optional, defaults to not set. + ### Limits CPU and memory limits for the Postgres container. @@ -371,6 +379,14 @@ CPU and memory limits for the Postgres container. memory limits for the Postgres container. Optional, overrides the `default_memory_limits` operator configuration parameter. +* **hugepages-2Mi** + hugepages-2Mi requests for the sidecar container. + Optional, defaults to not set. + +* **hugepages-1Gi** + 1Gi hugepages requests for the sidecar container. + Optional, defaults to not set. + ## Parameters defining how to clone the cluster from another one Those parameters are applied when the cluster should be a clone of another one @@ -500,6 +516,14 @@ CPU and memory requests for the sidecar container. memory requests for the sidecar container. Optional, overrides the `default_memory_request` operator configuration parameter. Optional. +* **hugepages-2Mi** + hugepages-2Mi requests for the sidecar container. + Optional, defaults to not set. + +* **hugepages-1Gi** + 1Gi hugepages requests for the sidecar container. + Optional, defaults to not set. + ### Limits CPU and memory limits for the sidecar container. @@ -512,6 +536,14 @@ CPU and memory limits for the sidecar container. memory limits for the sidecar container. Optional, overrides the `default_memory_limits` operator configuration parameter. Optional. +* **hugepages-2Mi** + hugepages-2Mi requests for the sidecar container. + Optional, defaults to not set. + +* **hugepages-1Gi** + 1Gi hugepages requests for the sidecar container. + Optional, defaults to not set. + ## Connection pooler Parameters are grouped under the `connectionPooler` top-level key and specify diff --git a/docs/user.md b/docs/user.md index d7d3bb3d5..59b0729e0 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/complete-postgres-manifest.yaml b/manifests/complete-postgres-manifest.yaml index aa94dab23..5d23b028d 100644 --- a/manifests/complete-postgres-manifest.yaml +++ b/manifests/complete-postgres-manifest.yaml @@ -107,9 +107,13 @@ spec: requests: cpu: 10m memory: 100Mi +# hugepages-2Mi: 128Mi +# hugepages-1Gi: 1Gi limits: cpu: 500m memory: 500Mi +# hugepages-2Mi: 128Mi +# hugepages-1Gi: 1Gi patroni: failsafe_mode: false initdb: diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 5f5b6ff09..f0174c80f 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 4dd30fc84..0c66b4b12 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 3504d615d..a07dacef4 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 56f9c0314..53317ebe9 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 ( @@ -266,6 +267,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 bf394fe86..143f50f25 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" @@ -3081,6 +3080,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 {