Avoid "bulk-comparing" pod resources during sync. (#109)
* Avoid "bulk-comparing" pod resources during sync. First attempt to fix bogus restarts due to the reported mismatch of container resources where one of the resources is an empty struct, while the other has all fields set to nil. In addition, add an ability to set limits and requests per pod, as well as the operator-level defaults.
This commit is contained in:
		
							parent
							
								
									9b0d0d487c
								
							
						
					
					
						commit
						1c4bce86df
					
				|  | @ -23,8 +23,12 @@ spec: | ||||||
|       max_connections: "10" |       max_connections: "10" | ||||||
|       log_statement: "all" |       log_statement: "all" | ||||||
|   resources: |   resources: | ||||||
|  |     requests: | ||||||
|       cpu: 10m |       cpu: 10m | ||||||
|       memory: 100Mi |       memory: 100Mi | ||||||
|  |     limits: | ||||||
|  |       cpu: 300m | ||||||
|  |       memory: 3000Mi | ||||||
|   patroni: |   patroni: | ||||||
|     initdb: |     initdb: | ||||||
|       encoding: "UTF8" |       encoding: "UTF8" | ||||||
|  |  | ||||||
|  | @ -278,7 +278,7 @@ func (c Cluster) compareStatefulSetWith(statefulSet *v1beta1.StatefulSet) (match | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !reflect.DeepEqual(container1.Resources, container2.Resources) { | 	if !compareResources(&container1.Resources, &container2.Resources) { | ||||||
| 		match = false | 		match = false | ||||||
| 		needsRollUpdate = true | 		needsRollUpdate = true | ||||||
| 		reason = "new statefulset's container resources don't match the current ones" | 		reason = "new statefulset's container resources don't match the current ones" | ||||||
|  | @ -293,6 +293,35 @@ func (c Cluster) compareStatefulSetWith(statefulSet *v1beta1.StatefulSet) (match | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func compareResources(a *v1.ResourceRequirements, b *v1.ResourceRequirements) (equal bool) { | ||||||
|  | 	equal = true | ||||||
|  | 	if a != nil { | ||||||
|  | 		equal = compareResoucesAssumeFirstNotNil(a, b) | ||||||
|  | 	} | ||||||
|  | 	if equal && (b != nil) { | ||||||
|  | 		equal = compareResoucesAssumeFirstNotNil(b, a) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func compareResoucesAssumeFirstNotNil(a *v1.ResourceRequirements, b *v1.ResourceRequirements) bool { | ||||||
|  | 	if b == nil || (len(b.Requests) == 0) { | ||||||
|  | 		return (len(a.Requests) == 0) | ||||||
|  | 	} | ||||||
|  | 	for k, v := range a.Requests { | ||||||
|  | 		if (&v).Cmp(b.Requests[k]) != 0 { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for k, v := range a.Limits { | ||||||
|  | 		if (&v).Cmp(b.Limits[k]) != 0 { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (c *Cluster) Update(newSpec *spec.Postgresql) error { | func (c *Cluster) Update(newSpec *spec.Postgresql) error { | ||||||
| 	c.logger.Infof("Cluster update from version %s to %s", | 	c.logger.Infof("Cluster update from version %s to %s", | ||||||
| 		c.Metadata.ResourceVersion, newSpec.Metadata.ResourceVersion) | 		c.Metadata.ResourceVersion, newSpec.Metadata.ResourceVersion) | ||||||
|  |  | ||||||
|  | @ -12,20 +12,41 @@ import ( | ||||||
| 	"github.bus.zalan.do/acid/postgres-operator/pkg/util/constants" | 	"github.bus.zalan.do/acid/postgres-operator/pkg/util/constants" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func resourceList(resources spec.Resources) *v1.ResourceList { | func (c *Cluster) resourceRequirements(resources spec.Resources) *v1.ResourceRequirements { | ||||||
| 	resourceList := v1.ResourceList{} | 	specRequests := resources.ResourceRequest | ||||||
| 	if resources.Cpu != "" { | 	specLimits := resources.ResourceLimits | ||||||
| 		resourceList[v1.ResourceCPU] = resource.MustParse(resources.Cpu) | 
 | ||||||
|  | 	config := c.OpConfig | ||||||
|  | 
 | ||||||
|  | 	defaultRequests := spec.ResourceDescription{Cpu: config.DefaultCpuRequest, Memory: config.DefaultMemoryRequest} | ||||||
|  | 	defaultLimits := spec.ResourceDescription{Cpu: config.DefaultCpuLimit, Memory: config.DefaultMemoryLimit} | ||||||
|  | 
 | ||||||
|  | 	result := v1.ResourceRequirements{} | ||||||
|  | 
 | ||||||
|  | 	result.Requests = fillResourceList(specRequests, defaultRequests) | ||||||
|  | 	result.Limits = fillResourceList(specLimits, defaultLimits) | ||||||
|  | 
 | ||||||
|  | 	return &result | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 	if resources.Memory != "" { | func fillResourceList(spec spec.ResourceDescription, defaults spec.ResourceDescription) v1.ResourceList { | ||||||
| 		resourceList[v1.ResourceMemory] = resource.MustParse(resources.Memory) | 	requests := v1.ResourceList{} | ||||||
|  | 
 | ||||||
|  | 	if spec.Cpu != "" { | ||||||
|  | 		requests[v1.ResourceCPU] = resource.MustParse(spec.Cpu) | ||||||
|  | 	} else { | ||||||
|  | 		requests[v1.ResourceCPU] = resource.MustParse(defaults.Cpu) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &resourceList | 	if spec.Memory != "" { | ||||||
|  | 		requests[v1.ResourceMemory] = resource.MustParse(spec.Memory) | ||||||
|  | 	} else { | ||||||
|  | 		requests[v1.ResourceMemory] = resource.MustParse(defaults.Memory) | ||||||
|  | 	} | ||||||
|  | 	return requests | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *Cluster) genPodTemplate(resourceList *v1.ResourceList, pgVersion string) *v1.PodTemplateSpec { | func (c *Cluster) genPodTemplate(resourceRequirements *v1.ResourceRequirements, pgVersion string) *v1.PodTemplateSpec { | ||||||
| 	envVars := []v1.EnvVar{ | 	envVars := []v1.EnvVar{ | ||||||
| 		{ | 		{ | ||||||
| 			Name:  "SCOPE", | 			Name:  "SCOPE", | ||||||
|  | @ -112,9 +133,7 @@ bootstrap: | ||||||
| 		Name:            c.Metadata.Name, | 		Name:            c.Metadata.Name, | ||||||
| 		Image:           c.OpConfig.DockerImage, | 		Image:           c.OpConfig.DockerImage, | ||||||
| 		ImagePullPolicy: v1.PullAlways, | 		ImagePullPolicy: v1.PullAlways, | ||||||
| 		Resources: v1.ResourceRequirements{ | 		Resources:       *resourceRequirements, | ||||||
| 			Requests: *resourceList, |  | ||||||
| 		}, |  | ||||||
| 		Ports: []v1.ContainerPort{ | 		Ports: []v1.ContainerPort{ | ||||||
| 			{ | 			{ | ||||||
| 				ContainerPort: 8008, | 				ContainerPort: 8008, | ||||||
|  | @ -163,8 +182,8 @@ bootstrap: | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *Cluster) genStatefulSet(spec spec.PostgresSpec) *v1beta1.StatefulSet { | func (c *Cluster) genStatefulSet(spec spec.PostgresSpec) *v1beta1.StatefulSet { | ||||||
| 	resourceList := resourceList(spec.Resources) | 	resourceRequirements := c.resourceRequirements(spec.Resources) | ||||||
| 	podTemplate := c.genPodTemplate(resourceList, spec.PgVersion) | 	podTemplate := c.genPodTemplate(resourceRequirements, spec.PgVersion) | ||||||
| 	volumeClaimTemplate := persistentVolumeClaimTemplate(spec.Volume.Size, spec.Volume.StorageClass) | 	volumeClaimTemplate := persistentVolumeClaimTemplate(spec.Volume.Size, spec.Volume.StorageClass) | ||||||
| 
 | 
 | ||||||
| 	statefulSet := &v1beta1.StatefulSet{ | 	statefulSet := &v1beta1.StatefulSet{ | ||||||
|  |  | ||||||
|  | @ -32,11 +32,16 @@ type PostgresqlParam struct { | ||||||
| 	Parameters map[string]string `json:"parameters"` | 	Parameters map[string]string `json:"parameters"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Resources struct { | type ResourceDescription struct { | ||||||
| 	Cpu    string `json:"cpu"` | 	Cpu    string `json:"cpu"` | ||||||
| 	Memory string `json:"memory"` | 	Memory string `json:"memory"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type Resources struct { | ||||||
|  | 	ResourceRequest ResourceDescription `json:"requests,omitempty""` | ||||||
|  | 	ResourceLimits  ResourceDescription `json:"limits,omitempty""` | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type Patroni struct { | type Patroni struct { | ||||||
| 	InitDB               map[string]string `json:"initdb"` | 	InitDB               map[string]string `json:"initdb"` | ||||||
| 	PgHba                []string          `json:"pg_hba"` | 	PgHba                []string          `json:"pg_hba"` | ||||||
|  |  | ||||||
|  | @ -23,6 +23,10 @@ type Resources struct { | ||||||
| 	ClusterLabels          map[string]string `name:"cluster_labels" default:"application:spilo"` | 	ClusterLabels          map[string]string `name:"cluster_labels" default:"application:spilo"` | ||||||
| 	ClusterNameLabel       string            `name:"cluster_name_label" default:"cluster-name"` | 	ClusterNameLabel       string            `name:"cluster_name_label" default:"cluster-name"` | ||||||
| 	PodRoleLabel           string            `name:"pod_role_label" default:"spilo-role"` | 	PodRoleLabel           string            `name:"pod_role_label" default:"spilo-role"` | ||||||
|  | 	DefaultCpuRequest      string            `name:"default_cpu_request" default:"100m"` | ||||||
|  | 	DefaultMemoryRequest   string            `name:"default_memory_request" default:"100Mi"` | ||||||
|  | 	DefaultCpuLimit        string            `name:"default_cpu_limit" default:"3"` | ||||||
|  | 	DefaultMemoryLimit     string            `name:"default_memory_limit" default:"1Gi"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Auth struct { | type Auth struct { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue