merge with master
This commit is contained in:
commit
00698f8394
28
docs/user.md
28
docs/user.md
|
|
@ -30,7 +30,7 @@ spec:
|
||||||
databases:
|
databases:
|
||||||
foo: zalando
|
foo: zalando
|
||||||
postgresql:
|
postgresql:
|
||||||
version: "10"
|
version: "11"
|
||||||
```
|
```
|
||||||
|
|
||||||
Once you cloned the Postgres Operator [repository](https://github.com/zalando/postgres-operator)
|
Once you cloned the Postgres Operator [repository](https://github.com/zalando/postgres-operator)
|
||||||
|
|
@ -42,6 +42,8 @@ kubectl create -f manifests/minimal-postgres-manifest.yaml
|
||||||
|
|
||||||
Make sure, the `spec` section of the manifest contains at least a `teamId`, the
|
Make sure, the `spec` section of the manifest contains at least a `teamId`, the
|
||||||
`numberOfInstances` and the `postgresql` object with the `version` specified.
|
`numberOfInstances` and the `postgresql` object with the `version` specified.
|
||||||
|
The minimum volume size to run the `postgresql` resource on Elastic Block
|
||||||
|
Storage (EBS) is `1Gi`.
|
||||||
|
|
||||||
Note, that the name of the cluster must start with the `teamId` and `-`. At
|
Note, that the name of the cluster must start with the `teamId` and `-`. At
|
||||||
Zalando we use team IDs (nicknames) to lower the chance of duplicate cluster
|
Zalando we use team IDs (nicknames) to lower the chance of duplicate cluster
|
||||||
|
|
@ -214,6 +216,28 @@ to choose superusers, group roles, [PAM configuration](https://github.com/CyberD
|
||||||
etc. An OAuth2 token can be passed to the Teams API via a secret. The name for
|
etc. An OAuth2 token can be passed to the Teams API via a secret. The name for
|
||||||
this secret is configurable with the `oauth_token_secret_name` parameter.
|
this secret is configurable with the `oauth_token_secret_name` parameter.
|
||||||
|
|
||||||
|
## Resource definition
|
||||||
|
|
||||||
|
The compute resources to be used for the Postgres containers in the pods can be
|
||||||
|
specified in the postgresql cluster manifest.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spec:
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 100Mi
|
||||||
|
limits:
|
||||||
|
cpu: 300m
|
||||||
|
memory: 300Mi
|
||||||
|
```
|
||||||
|
|
||||||
|
The minimum limit to properly run the `postgresql` resource is `256m` for `cpu`
|
||||||
|
and `256Mi` for `memory`. If a lower value is set in the manifest the operator
|
||||||
|
will cancel ADD or UPDATE events on this resource with an error. If no
|
||||||
|
resources are defined in the manifest the operator will obtain the configured
|
||||||
|
[default requests](reference/operator_parameters.md#kubernetes-resource-requests).
|
||||||
|
|
||||||
## Use taints and tolerations for dedicated PostgreSQL nodes
|
## Use taints and tolerations for dedicated PostgreSQL nodes
|
||||||
|
|
||||||
To ensure Postgres pods are running on nodes without any other application pods,
|
To ensure Postgres pods are running on nodes without any other application pods,
|
||||||
|
|
@ -385,7 +409,7 @@ specified but globally disabled in the configuration. The
|
||||||
|
|
||||||
## Increase volume size
|
## Increase volume size
|
||||||
|
|
||||||
PostgreSQL operator supports statefulset volume resize if you're using the
|
Postgres operator supports statefulset volume resize if you're using the
|
||||||
operator on top of AWS. For that you need to change the size field of the
|
operator on top of AWS. For that you need to change the size field of the
|
||||||
volume description in the cluster manifest and apply the change:
|
volume description in the cluster manifest and apply the change:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ spec:
|
||||||
size: 1Gi
|
size: 1Gi
|
||||||
numberOfInstances: 1
|
numberOfInstances: 1
|
||||||
postgresql:
|
postgresql:
|
||||||
version: "10"
|
version: "11"
|
||||||
# Make this a standby cluster and provide the s3 bucket path of source cluster for continuous streaming.
|
# Make this a standby cluster and provide the s3 bucket path of source cluster for continuous streaming.
|
||||||
standby:
|
standby:
|
||||||
s3_wal_path: "s3://path/to/bucket/containing/wal/of/source/cluster/"
|
s3_wal_path: "s3://path/to/bucket/containing/wal/of/source/cluster/"
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,10 @@ func (c *Cluster) Create() error {
|
||||||
|
|
||||||
c.setStatus(acidv1.ClusterStatusCreating)
|
c.setStatus(acidv1.ClusterStatusCreating)
|
||||||
|
|
||||||
|
if err = c.validateResources(&c.Spec); err != nil {
|
||||||
|
return fmt.Errorf("insufficient resource limits specified: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
for _, role := range []PostgresRole{Master, Replica} {
|
for _, role := range []PostgresRole{Master, Replica} {
|
||||||
|
|
||||||
if c.Endpoints[role] != nil {
|
if c.Endpoints[role] != nil {
|
||||||
|
|
@ -491,6 +495,44 @@ func compareResourcesAssumeFirstNotNil(a *v1.ResourceRequirements, b *v1.Resourc
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) validateResources(spec *acidv1.PostgresSpec) error {
|
||||||
|
|
||||||
|
// setting limits too low can cause unnecessary evictions / OOM kills
|
||||||
|
const (
|
||||||
|
cpuMinLimit = "256m"
|
||||||
|
memoryMinLimit = "256Mi"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
isSmaller bool
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
cpuLimit := spec.Resources.ResourceLimits.CPU
|
||||||
|
if cpuLimit != "" {
|
||||||
|
isSmaller, err = util.IsSmallerQuantity(cpuLimit, cpuMinLimit)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error validating CPU limit: %v", err)
|
||||||
|
}
|
||||||
|
if isSmaller {
|
||||||
|
return fmt.Errorf("defined CPU limit %s is below required minimum %s to properly run postgresql resource", cpuLimit, cpuMinLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryLimit := spec.Resources.ResourceLimits.Memory
|
||||||
|
if memoryLimit != "" {
|
||||||
|
isSmaller, err = util.IsSmallerQuantity(memoryLimit, memoryMinLimit)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error validating memory limit: %v", err)
|
||||||
|
}
|
||||||
|
if isSmaller {
|
||||||
|
return fmt.Errorf("defined memory limit %s is below required minimum %s to properly run postgresql resource", memoryLimit, memoryMinLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Update changes Kubernetes objects according to the new specification. Unlike the sync case, the missing object
|
// Update changes Kubernetes objects according to the new specification. Unlike the sync case, the missing object
|
||||||
// (i.e. service) is treated as an error
|
// (i.e. service) is treated as an error
|
||||||
// logical backup cron jobs are an exception: a user-initiated Update can enable a logical backup job
|
// logical backup cron jobs are an exception: a user-initiated Update can enable a logical backup job
|
||||||
|
|
@ -501,6 +543,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
oldStatus := c.Status
|
||||||
c.setStatus(acidv1.ClusterStatusUpdating)
|
c.setStatus(acidv1.ClusterStatusUpdating)
|
||||||
c.setSpec(newSpec)
|
c.setSpec(newSpec)
|
||||||
|
|
||||||
|
|
@ -512,6 +555,22 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if err := c.validateResources(&newSpec.Spec); err != nil {
|
||||||
|
err = fmt.Errorf("insufficient resource limits specified: %v", err)
|
||||||
|
|
||||||
|
// cancel update only when (already too low) pod resources were edited
|
||||||
|
// if cluster was successfully running before the update, continue but log a warning
|
||||||
|
isCPULimitSmaller, err2 := util.IsSmallerQuantity(newSpec.Spec.Resources.ResourceLimits.CPU, oldSpec.Spec.Resources.ResourceLimits.CPU)
|
||||||
|
isMemoryLimitSmaller, err3 := util.IsSmallerQuantity(newSpec.Spec.Resources.ResourceLimits.Memory, oldSpec.Spec.Resources.ResourceLimits.Memory)
|
||||||
|
|
||||||
|
if oldStatus.Running() && !isCPULimitSmaller && !isMemoryLimitSmaller && err2 == nil && err3 == nil {
|
||||||
|
c.logger.Warning(err)
|
||||||
|
} else {
|
||||||
|
updateFailed = true
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if oldSpec.Spec.PgVersion != newSpec.Spec.PgVersion { // PG versions comparison
|
if oldSpec.Spec.PgVersion != newSpec.Spec.PgVersion { // PG versions comparison
|
||||||
c.logger.Warningf("postgresql version change(%q -> %q) has no effect", oldSpec.Spec.PgVersion, newSpec.Spec.PgVersion)
|
c.logger.Warningf("postgresql version change(%q -> %q) has no effect", oldSpec.Spec.PgVersion, newSpec.Spec.PgVersion)
|
||||||
//we need that hack to generate statefulset with the old version
|
//we need that hack to generate statefulset with the old version
|
||||||
|
|
|
||||||
|
|
@ -741,7 +741,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
|
||||||
limit = c.OpConfig.DefaultMemoryLimit
|
limit = c.OpConfig.DefaultMemoryLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
isSmaller, err := util.RequestIsSmallerThanLimit(request, limit)
|
isSmaller, err := util.IsSmallerQuantity(request, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -768,7 +768,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
|
||||||
limit = c.OpConfig.DefaultMemoryLimit
|
limit = c.OpConfig.DefaultMemoryLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
isSmaller, err := util.RequestIsSmallerThanLimit(sidecarRequest, sidecarLimit)
|
isSmaller, err := util.IsSmallerQuantity(sidecarRequest, sidecarLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
oldStatus := c.Status
|
||||||
c.setSpec(newSpec)
|
c.setSpec(newSpec)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -34,6 +35,16 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if err = c.validateResources(&c.Spec); err != nil {
|
||||||
|
err = fmt.Errorf("insufficient resource limits specified: %v", err)
|
||||||
|
if oldStatus.Running() {
|
||||||
|
c.logger.Warning(err)
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err = c.initUsers(); err != nil {
|
if err = c.initUsers(); err != nil {
|
||||||
err = fmt.Errorf("could not init users: %v", err)
|
err = fmt.Errorf("could not init users: %v", err)
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ func (c *Controller) initOperatorConfig() {
|
||||||
|
|
||||||
if c.opConfig.SetMemoryRequestToLimit {
|
if c.opConfig.SetMemoryRequestToLimit {
|
||||||
|
|
||||||
isSmaller, err := util.RequestIsSmallerThanLimit(c.opConfig.DefaultMemoryRequest, c.opConfig.DefaultMemoryLimit)
|
isSmaller, err := util.IsSmallerQuantity(c.opConfig.DefaultMemoryRequest, c.opConfig.DefaultMemoryLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +120,7 @@ func (c *Controller) initOperatorConfig() {
|
||||||
c.opConfig.DefaultMemoryRequest = c.opConfig.DefaultMemoryLimit
|
c.opConfig.DefaultMemoryRequest = c.opConfig.DefaultMemoryLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
isSmaller, err = util.RequestIsSmallerThanLimit(c.opConfig.ScalyrMemoryRequest, c.opConfig.ScalyrMemoryLimit)
|
isSmaller, err = util.IsSmallerQuantity(c.opConfig.ScalyrMemoryRequest, c.opConfig.ScalyrMemoryLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,17 +141,17 @@ func Coalesce(val, defaultVal string) string {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestIsSmallerThanLimit : ...
|
// IsSmallerQuantity : checks if first resource is of a smaller quantity than the second
|
||||||
func RequestIsSmallerThanLimit(requestStr, limitStr string) (bool, error) {
|
func IsSmallerQuantity(requestStr, limitStr string) (bool, error) {
|
||||||
|
|
||||||
request, err := resource.ParseQuantity(requestStr)
|
request, err := resource.ParseQuantity(requestStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("could not parse memory request %v : %v", requestStr, err)
|
return false, fmt.Errorf("could not parse request %v : %v", requestStr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
limit, err2 := resource.ParseQuantity(limitStr)
|
limit, err2 := resource.ParseQuantity(limitStr)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return false, fmt.Errorf("could not parse memory limit %v : %v", limitStr, err2)
|
return false, fmt.Errorf("could not parse limit %v : %v", limitStr, err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return request.Cmp(limit) == -1, nil
|
return request.Cmp(limit) == -1, nil
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ var substringMatch = []struct {
|
||||||
{regexp.MustCompile(`aaaa (\d+) bbbb`), "aaaa 123 bbbb", nil},
|
{regexp.MustCompile(`aaaa (\d+) bbbb`), "aaaa 123 bbbb", nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestIsSmallerThanLimitTests = []struct {
|
var requestIsSmallerQuantityTests = []struct {
|
||||||
request string
|
request string
|
||||||
limit string
|
limit string
|
||||||
out bool
|
out bool
|
||||||
|
|
@ -155,14 +155,14 @@ func TestMapContains(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRequestIsSmallerThanLimit(t *testing.T) {
|
func TestIsSmallerQuantity(t *testing.T) {
|
||||||
for _, tt := range requestIsSmallerThanLimitTests {
|
for _, tt := range requestIsSmallerQuantityTests {
|
||||||
res, err := RequestIsSmallerThanLimit(tt.request, tt.limit)
|
res, err := IsSmallerQuantity(tt.request, tt.limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("RequestIsSmallerThanLimit returned unexpected error: %#v", err)
|
t.Errorf("IsSmallerQuantity returned unexpected error: %#v", err)
|
||||||
}
|
}
|
||||||
if res != tt.out {
|
if res != tt.out {
|
||||||
t.Errorf("RequestIsSmallerThanLimit expected: %#v, got: %#v", tt.out, res)
|
t.Errorf("IsSmallerQuantity expected: %#v, got: %#v", tt.out, res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue