Merge branch 'master' into delete-volume-on-scale-down

This commit is contained in:
Sergey Dudoladov 2020-04-08 07:18:49 +02:00
commit a562a6ed59
20 changed files with 104 additions and 30 deletions

View File

@ -68,6 +68,8 @@ spec:
type: boolean
etcd_host:
type: string
kubernetes_use_configmaps:
type: boolean
max_instances:
type: integer
minimum: -1 # -1 = disabled

View File

@ -218,6 +218,10 @@ spec:
type: integer
retry_timeout:
type: integer
synchronous_mode:
type: boolean
synchronous_mode_strict:
type: boolean
maximum_lag_on_failover:
type: integer
podAnnotations:

View File

@ -25,6 +25,8 @@ configGeneral:
enable_unused_pvc_deletion: false
# etcd connection string for Patroni. Empty uses K8s-native DCS.
etcd_host: ""
# Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s)
# kubernetes_use_configmaps: false
# Spilo docker image
docker_image: registry.opensource.zalan.do/acid/spilo-12:1.6-p2
# max number of instances in Postgres cluster. -1 = no limit

View File

@ -25,6 +25,8 @@ configGeneral:
enable_unused_pvc_deletion: "false"
# etcd connection string for Patroni. Empty uses K8s-native DCS.
etcd_host: ""
# Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s)
# kubernetes_use_configmaps: "false"
# Spilo docker image
docker_image: registry.opensource.zalan.do/acid/spilo-12:1.6-p2
# max number of instances in Postgres cluster. -1 = no limit

View File

@ -217,6 +217,12 @@ explanation of `ttl` and `loop_wait` parameters.
automatically created by Patroni for cluster members and permanent replication
slots. Optional.
* **synchronous_mode**
Patroni `synchronous_mode` parameter value. The default is set to `false`. Optional.
* **synchronous_mode_strict**
Patroni `synchronous_mode_strict` parameter value. Can be used in addition to `synchronous_mode`. The default is set to `false`. Optional.
## Postgres container resources
Those parameters define [CPU and memory requests and limits](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/)
@ -376,10 +382,11 @@ present.
How many instances of connection pooler to create.
* **schema**
Schema to create for credentials lookup function.
Database schema to create for credentials lookup function.
* **user**
User to create for connection pooler to be able to connect to a database.
You can also choose a role from the `users` section or a system user role.
* **dockerImage**
Which docker image to use for connection pooler deployment.

View File

@ -80,6 +80,12 @@ Those are top-level keys, containing both leaf keys and groups.
Patroni native Kubernetes support is used. The default is empty (use
Kubernetes-native DCS).
* **kubernetes_use_configmaps**
Select if setup uses endpoints (default), or configmaps to manage leader when
DCS is kubernetes (not etcd or similar). In OpenShift it is not possible to
use endpoints option, and configmaps is required. By default,
`kubernetes_use_configmaps: false`, meaning endpoints will be used.
* **docker_image**
Spilo Docker image for Postgres instances. For production, don't rely on the
default image, as it might be not the most up-to-date one. Instead, build
@ -613,11 +619,14 @@ operator being able to provide some reasonable defaults.
the required minimum.
* **connection_pooler_schema**
Schema to create for credentials lookup function. Default is `pooler`.
Database schema to create for credentials lookup function to be used by the
connection pooler. Is is created in every database of the Postgres cluster.
You can also choose an existing schema. Default schema is `pooler`.
* **connection_pooler_user**
User to create for connection pooler to be able to connect to a database.
Default is `pooler`.
You can also choose an existing role, but make sure it has the `LOGIN`
privilege. Default role is `pooler`.
* **connection_pooler_image**
Docker image to use for connection pooler deployment.

View File

@ -527,7 +527,7 @@ spec:
This will tell the operator to create a connection pooler with default
configuration, through which one can access the master via a separate service
`{cluster-name}-pooler`. In most of the cases the
[default configuration](reference/operator_parameters.md#connection-pool-configuration)
[default configuration](reference/operator_parameters.md#connection-pooler-configuration)
should be good enough. To configure a new connection pooler individually for
each Postgres cluster, specify:
@ -540,7 +540,8 @@ spec:
# in which mode to run, session or transaction
mode: "transaction"
# schema, which operator will create to install credentials lookup function
# schema, which operator will create in each database
# to install credentials lookup function for connection pooler
schema: "pooler"
# user, which operator will create for connection pooler
@ -560,11 +561,11 @@ The `enableConnectionPooler` flag is not required when the `connectionPooler`
section is present in the manifest. But, it can be used to disable/remove the
pooler while keeping its configuration.
By default, `pgbouncer` is used as connection pooler. To find out about pooler
modes read the `pgbouncer` [docs](https://www.pgbouncer.org/config.html#pooler_mode)
By default, [`PgBouncer`](https://www.pgbouncer.org/) is used as connection pooler.
To find out about pool modes read the `PgBouncer` [docs](https://www.pgbouncer.org/config.html#pooler_mode)
(but it should be the general approach between different implementation).
Note, that using `pgbouncer` a meaningful resource CPU limit should be 1 core
Note, that using `PgBouncer` a meaningful resource CPU limit should be 1 core
or less (there is a way to utilize more than one, but in K8s it's easier just to
spin up more instances).

View File

@ -67,6 +67,8 @@ spec:
ttl: 30
loop_wait: &loop_wait 10
retry_timeout: 10
synchronous_mode: false
synchronous_mode_strict: false
maximum_lag_on_failover: 33554432
# restore a Postgres DB with point-in-time-recovery

View File

@ -15,7 +15,7 @@ data:
# connection_pooler_default_cpu_request: "500m"
# connection_pooler_default_memory_limit: 100Mi
# connection_pooler_default_memory_request: 100Mi
connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-5"
connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-6"
# connection_pooler_max_db_connections: 60
# connection_pooler_mode: "transaction"
# connection_pooler_number_of_instances: 2
@ -43,6 +43,7 @@ data:
# enable_team_superuser: "false"
enable_teams_api: "false"
# etcd_host: ""
# kubernetes_use_configmaps: "false"
# infrastructure_roles_secret_name: postgresql-infrastructure-roles
# inherited_labels: application,environment
# kube_iam_role: ""

View File

@ -44,6 +44,8 @@ spec:
type: boolean
etcd_host:
type: string
kubernetes_use_configmaps:
type: boolean
max_instances:
type: integer
minimum: -1 # -1 = disabled

View File

@ -5,6 +5,7 @@ metadata:
configuration:
# enable_crd_validation: true
etcd_host: ""
# kubernetes_use_configmaps: false
docker_image: registry.opensource.zalan.do/acid/spilo-12:1.6-p2
# enable_shm_volume: true
# enable_unused_pvc_deletion: false
@ -127,7 +128,7 @@ configuration:
connection_pooler_default_cpu_request: "500m"
connection_pooler_default_memory_limit: 100Mi
connection_pooler_default_memory_request: 100Mi
connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-5"
connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-6"
# connection_pooler_max_db_connections: 60
connection_pooler_mode: "transaction"
connection_pooler_number_of_instances: 2

View File

@ -184,6 +184,10 @@ spec:
type: integer
maximum_lag_on_failover:
type: integer
synchronous_mode:
type: boolean
synchronous_mode_strict:
type: boolean
podAnnotations:
type: object
additionalProperties:

View File

@ -358,6 +358,12 @@ var PostgresCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
"maximum_lag_on_failover": {
Type: "integer",
},
"synchronous_mode": {
Type: "boolean",
},
"synchronous_mode_strict": {
Type: "boolean",
},
},
},
"podAnnotations": {
@ -727,6 +733,9 @@ var OperatorConfigCRDResourceValidation = apiextv1beta1.CustomResourceValidation
"etcd_host": {
Type: "string",
},
"kubernetes_use_configmaps": {
Type: "boolean",
},
"max_instances": {
Type: "integer",
Description: "-1 = disabled",

View File

@ -183,6 +183,7 @@ type OperatorLogicalBackupConfiguration struct {
type OperatorConfigurationData struct {
EnableCRDValidation *bool `json:"enable_crd_validation,omitempty"`
EtcdHost string `json:"etcd_host,omitempty"`
KubernetesUseConfigMaps bool `json:"kubernetes_use_configmaps,omitempty"`
DockerImage string `json:"docker_image,omitempty"`
Workers uint32 `json:"workers,omitempty"`
MinInstances int32 `json:"min_instances,omitempty"`

View File

@ -118,13 +118,15 @@ type Resources struct {
// Patroni contains Patroni-specific configuration
type Patroni struct {
InitDB map[string]string `json:"initdb"`
PgHba []string `json:"pg_hba"`
TTL uint32 `json:"ttl"`
LoopWait uint32 `json:"loop_wait"`
RetryTimeout uint32 `json:"retry_timeout"`
MaximumLagOnFailover float32 `json:"maximum_lag_on_failover"` // float32 because https://github.com/kubernetes/kubernetes/issues/30213
Slots map[string]map[string]string `json:"slots"`
InitDB map[string]string `json:"initdb"`
PgHba []string `json:"pg_hba"`
TTL uint32 `json:"ttl"`
LoopWait uint32 `json:"loop_wait"`
RetryTimeout uint32 `json:"retry_timeout"`
MaximumLagOnFailover float32 `json:"maximum_lag_on_failover"` // float32 because https://github.com/kubernetes/kubernetes/issues/30213
Slots map[string]map[string]string `json:"slots"`
SynchronousMode bool `json:"synchronous_mode"`
SynchronousModeStrict bool `json:"synchronous_mode_strict"`
}
//StandbyCluster

View File

@ -49,6 +49,8 @@ type patroniDCS struct {
LoopWait uint32 `json:"loop_wait,omitempty"`
RetryTimeout uint32 `json:"retry_timeout,omitempty"`
MaximumLagOnFailover float32 `json:"maximum_lag_on_failover,omitempty"`
SynchronousMode bool `json:"synchronous_mode,omitempty"`
SynchronousModeStrict bool `json:"synchronous_mode_strict,omitempty"`
PGBootstrapConfiguration map[string]interface{} `json:"postgresql,omitempty"`
Slots map[string]map[string]string `json:"slots,omitempty"`
}
@ -283,6 +285,12 @@ PatroniInitDBParams:
if patroni.Slots != nil {
config.Bootstrap.DCS.Slots = patroni.Slots
}
if patroni.SynchronousMode {
config.Bootstrap.DCS.SynchronousMode = patroni.SynchronousMode
}
if patroni.SynchronousModeStrict != false {
config.Bootstrap.DCS.SynchronousModeStrict = patroni.SynchronousModeStrict
}
config.PgLocalConfiguration = make(map[string]interface{})
config.PgLocalConfiguration[patroniPGBinariesParameterName] = fmt.Sprintf(pgBinariesLocationTemplate, pg.PgVersion)
@ -672,6 +680,10 @@ func (c *Cluster) generateSpiloPodEnvVars(uid types.UID, spiloConfiguration stri
envVars = append(envVars, v1.EnvVar{Name: "ETCD_HOST", Value: c.OpConfig.EtcdHost})
}
if c.patroniKubernetesUseConfigMaps() {
envVars = append(envVars, v1.EnvVar{Name: "KUBERNETES_USE_CONFIGMAPS", Value: "true"})
}
if cloneDescription.ClusterName != "" {
envVars = append(envVars, c.generateCloneEnvironment(cloneDescription)...)
}
@ -1406,7 +1418,7 @@ func (c *Cluster) generateService(role PostgresRole, spec *acidv1.PostgresSpec)
Type: v1.ServiceTypeClusterIP,
}
if role == Replica {
if role == Replica || c.patroniKubernetesUseConfigMaps() {
serviceSpec.Selector = c.roleLabelsSet(false, role)
}

View File

@ -65,16 +65,18 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) {
"locale": "en_US.UTF-8",
"data-checksums": "true",
},
PgHba: []string{"hostssl all all 0.0.0.0/0 md5", "host all all 0.0.0.0/0 md5"},
TTL: 30,
LoopWait: 10,
RetryTimeout: 10,
MaximumLagOnFailover: 33554432,
Slots: map[string]map[string]string{"permanent_logical_1": {"type": "logical", "database": "foo", "plugin": "pgoutput"}},
PgHba: []string{"hostssl all all 0.0.0.0/0 md5", "host all all 0.0.0.0/0 md5"},
TTL: 30,
LoopWait: 10,
RetryTimeout: 10,
MaximumLagOnFailover: 33554432,
SynchronousMode: true,
SynchronousModeStrict: true,
Slots: map[string]map[string]string{"permanent_logical_1": {"type": "logical", "database": "foo", "plugin": "pgoutput"}},
},
role: "zalandos",
opConfig: config.Config{},
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/11/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}}}}`,
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/11/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}}}}`,
},
}
for _, tt := range tests {

View File

@ -510,6 +510,15 @@ func (c *Cluster) patroniUsesKubernetes() bool {
return c.OpConfig.EtcdHost == ""
}
func (c *Cluster) patroniKubernetesUseConfigMaps() bool {
if !c.patroniUsesKubernetes() {
return false
}
// otherwise, follow the operator configuration
return c.OpConfig.KubernetesUseConfigMaps
}
func (c *Cluster) needConnectionPoolerWorker(spec *acidv1.PostgresSpec) bool {
if spec.EnableConnectionPooler == nil {
return spec.ConnectionPooler != nil

View File

@ -35,6 +35,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
// general config
result.EnableCRDValidation = fromCRD.EnableCRDValidation
result.EtcdHost = fromCRD.EtcdHost
result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps
result.DockerImage = fromCRD.DockerImage
result.Workers = fromCRD.Workers
result.MinInstances = fromCRD.MinInstances

View File

@ -107,11 +107,12 @@ type Config struct {
LogicalBackup
ConnectionPooler
WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-12:1.6-p2"`
Sidecars map[string]string `name:"sidecar_docker_images"`
PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"`
WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"`
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-12:1.6-p2"`
Sidecars map[string]string `name:"sidecar_docker_images"`
PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"`
// value of this string must be valid JSON or YAML; see initPodServiceAccount
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""`