1
0
Fork 0

postgresql: Deploy Postgres Operator

While I was preparing to deploy PostgreSQL for Firefly III, I was
thinking it would be a neat idea to write an operator that uses
custom resources to manage PostgreSQL roles and databases.  Then I
though, surely something like that must exist already.  As it turns out,
the [Postgres Operator][0] does exactly that, and a whole lot more.

The *Postgres Operator* handles deploying PostgreSQL server instances,
including primary/standby replication with load balancers.  It uses
custom resources to manage the databases and users (roles) in an
instance, and stores role passwords in Secret resources.  It supports
backing up instances using `pg_basebackup` and WAL archives (i.e.
physical backups) via [WAL-E][1]/[WAL-G][2].  While various backup
storage targets are supported, *Postgres Operator* really only works
well with the cloud storage services like S3, Azure, and Google Cloud
Platform.  Fortunately, S3-compatible on-premises solutions like MinIO
are just fine.

I think for my use cases, a single PostgreSQL cluster with multiple
databases will be sufficient.  I know *Firefly III* will need a
PostgreSQL database, and I will likely want to migrate *Paperless-ngx*
to PostgreSQL eventually too.  Having a single instance will save on
memory resources, at the cost of per-application point-in-time recovery.
For now, just one server in the cluster is probably sufficient, but
luckily adding standby servers appears to be really easy should the need
arise.

[0]: https://postgres-operator.readthedocs.io/en/latest/
[1]: https://github.com/wal-e/wal-e
[2]: https://github.com/wal-g/wal-g
dch-webhooks-secrets
Dustin 2023-04-30 22:08:02 -05:00
parent d8aadb01af
commit ffffe9d3c8
12 changed files with 1401 additions and 0 deletions

1
postgresql/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
pod.secrets

View File

@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: postgres-operator
spec:
type: ClusterIP
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
name: postgres-operator

View File

@ -0,0 +1,18 @@
apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
name: default
namespace: postgresql
spec:
teamId: acid
volume:
size: 10Gi
numberOfInstances: 1
postgresql:
version: '15'
users:
dustin:
- superuser
- createdb
databases:
dustin: dustin

View File

@ -0,0 +1,33 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: postgresql
resources:
- namespace.yaml
- operatorconfiguration.crd.yaml
- postgresteam.crd.yaml
- postgresql-operator-configuration.yaml
- operator-service-account-rbac.yaml
- postgres-operator.yaml
- api-service.yaml
- default-cluster.yaml
secretGenerator:
- name: ssh-auth
files:
- ssh-backup.key
options:
disableNameSuffixHash: true
- name: pod-secrets
envs:
- pod.secrets
options:
disableNameSuffixHash: true
configMapGenerator:
- name: pod-env
envs:
- pod.env
options:
disableNameSuffixHash: true

View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: postgresql

View File

@ -0,0 +1,290 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: postgres-operator
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: postgres-operator
rules:
# all verbs allowed for custom operator resources
- apiGroups:
- acid.zalan.do
resources:
- postgresqls
- postgresqls/status
- operatorconfigurations
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
# operator only reads PostgresTeams
- apiGroups:
- acid.zalan.do
resources:
- postgresteams
verbs:
- get
- list
- watch
# all verbs allowed for event streams (Zalando-internal feature)
# - apiGroups:
# - zalando.org
# resources:
# - fabriceventstreams
# verbs:
# - create
# - delete
# - deletecollection
# - get
# - list
# - patch
# - update
# - watch
# to create or get/update CRDs when starting up
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- create
- get
- patch
- update
# to read configuration from ConfigMaps
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
# to send events to the CRs
- apiGroups:
- ""
resources:
- events
verbs:
- create
- get
- list
- patch
- update
- watch
# to manage endpoints which are also used by Patroni
- apiGroups:
- ""
resources:
- endpoints
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
# to CRUD secrets for database access
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
- delete
- get
- update
# to check nodes for node readiness label
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- list
- watch
# to read or delete existing PVCs. Creation via StatefulSet
- apiGroups:
- ""
resources:
- persistentvolumeclaims
verbs:
- delete
- get
- list
- patch
- update
# to read existing PVs. Creation should be done via dynamic provisioning
- apiGroups:
- ""
resources:
- persistentvolumes
verbs:
- get
- list
- update # only for resizing AWS volumes
# to watch Spilo pods and do rolling updates. Creation via StatefulSet
- apiGroups:
- ""
resources:
- pods
verbs:
- delete
- get
- list
- patch
- update
- watch
# to resize the filesystem in Spilo pods when increasing volume size
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- create
# to CRUD services to point to Postgres cluster instances
- apiGroups:
- ""
resources:
- services
verbs:
- create
- delete
- get
- patch
- update
# to CRUD the StatefulSet which controls the Postgres cluster instances
- apiGroups:
- apps
resources:
- statefulsets
- deployments
verbs:
- create
- delete
- get
- list
- patch
# to CRUD cron jobs for logical backups
- apiGroups:
- batch
resources:
- cronjobs
verbs:
- create
- delete
- get
- list
- patch
- update
# to get namespaces operator resources can run in
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
# to define PDBs. Update happens via delete/create
- apiGroups:
- policy
resources:
- poddisruptionbudgets
verbs:
- create
- delete
- get
# to create ServiceAccounts in each namespace the operator watches
- apiGroups:
- ""
resources:
- serviceaccounts
verbs:
- get
- create
# to create role bindings to the postgres-pod service account
- apiGroups:
- rbac.authorization.k8s.io
resources:
- rolebindings
verbs:
- get
- create
# to grant privilege to run privileged pods (not needed by default)
#- apiGroups:
# - extensions
# resources:
# - podsecuritypolicies
# resourceNames:
# - privileged
# verbs:
# - use
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: postgres-operator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: postgres-operator
subjects:
- kind: ServiceAccount
name: postgres-operator
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: postgres-pod
rules:
# Patroni needs to watch and manage endpoints
- apiGroups:
- ""
resources:
- endpoints
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
# Patroni needs to watch pods
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- patch
- update
- watch
# to let Patroni create a headless service
- apiGroups:
- ""
resources:
- services
verbs:
- create
# to grant privilege to run privileged pods (not needed by default)
#- apiGroups:
# - extensions
# resources:
# - podsecuritypolicies
# resourceNames:
# - privileged
# verbs:
# - use

View File

@ -0,0 +1,677 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: operatorconfigurations.acid.zalan.do
spec:
group: acid.zalan.do
names:
kind: OperatorConfiguration
listKind: OperatorConfigurationList
plural: operatorconfigurations
singular: operatorconfiguration
shortNames:
- opconfig
categories:
- all
scope: Namespaced
versions:
- name: v1
served: true
storage: true
subresources:
status: {}
additionalPrinterColumns:
- name: Image
type: string
description: Spilo image to be used for Pods
jsonPath: .configuration.docker_image
- name: Cluster-Label
type: string
description: Label for K8s resources created by operator
jsonPath: .configuration.kubernetes.cluster_name_label
- name: Service-Account
type: string
description: Name of service account to be used
jsonPath: .configuration.kubernetes.pod_service_account_name
- name: Min-Instances
type: integer
description: Minimum number of instances per Postgres cluster
jsonPath: .configuration.min_instances
- name: Age
type: date
jsonPath: .metadata.creationTimestamp
schema:
openAPIV3Schema:
type: object
required:
- kind
- apiVersion
- configuration
properties:
kind:
type: string
enum:
- OperatorConfiguration
apiVersion:
type: string
enum:
- acid.zalan.do/v1
configuration:
type: object
properties:
crd_categories:
type: array
nullable: true
items:
type: string
docker_image:
type: string
default: "ghcr.io/zalando/spilo-15:3.0-p1"
enable_crd_registration:
type: boolean
default: true
enable_crd_validation:
type: boolean
description: deprecated
default: true
enable_lazy_spilo_upgrade:
type: boolean
default: false
enable_pgversion_env_var:
type: boolean
default: true
enable_shm_volume:
type: boolean
default: true
enable_spilo_wal_path_compat:
type: boolean
default: false
enable_team_id_clustername_prefix:
type: boolean
default: false
etcd_host:
type: string
default: ""
ignore_instance_limits_annotation_key:
type: string
kubernetes_use_configmaps:
type: boolean
default: false
max_instances:
type: integer
description: "-1 = disabled"
minimum: -1
default: -1
min_instances:
type: integer
description: "-1 = disabled"
minimum: -1
default: -1
resync_period:
type: string
default: "30m"
repair_period:
type: string
default: "5m"
set_memory_request_to_limit:
type: boolean
default: false
sidecar_docker_images:
type: object
additionalProperties:
type: string
sidecars:
type: array
nullable: true
items:
type: object
x-kubernetes-preserve-unknown-fields: true
workers:
type: integer
minimum: 1
default: 8
users:
type: object
properties:
additional_owner_roles:
type: array
nullable: true
items:
type: string
enable_password_rotation:
type: boolean
default: false
password_rotation_interval:
type: integer
default: 90
password_rotation_user_retention:
type: integer
default: 180
replication_username:
type: string
default: standby
super_username:
type: string
default: postgres
major_version_upgrade:
type: object
properties:
major_version_upgrade_mode:
type: string
default: "off"
major_version_upgrade_team_allow_list:
type: array
items:
type: string
minimal_major_version:
type: string
default: "11"
target_major_version:
type: string
default: "15"
kubernetes:
type: object
properties:
additional_pod_capabilities:
type: array
items:
type: string
cluster_domain:
type: string
default: "cluster.local"
cluster_labels:
type: object
additionalProperties:
type: string
default:
application: spilo
cluster_name_label:
type: string
default: "cluster-name"
custom_pod_annotations:
type: object
additionalProperties:
type: string
delete_annotation_date_key:
type: string
delete_annotation_name_key:
type: string
downscaler_annotations:
type: array
items:
type: string
enable_cross_namespace_secret:
type: boolean
default: false
enable_init_containers:
type: boolean
default: true
enable_pod_antiaffinity:
type: boolean
default: false
enable_pod_disruption_budget:
type: boolean
default: true
enable_readiness_probe:
type: boolean
default: false
enable_sidecars:
type: boolean
default: true
ignored_annotations:
type: array
items:
type: string
infrastructure_roles_secret_name:
type: string
infrastructure_roles_secrets:
type: array
nullable: true
items:
type: object
required:
- secretname
- userkey
- passwordkey
properties:
secretname:
type: string
userkey:
type: string
passwordkey:
type: string
rolekey:
type: string
defaultuservalue:
type: string
defaultrolevalue:
type: string
details:
type: string
template:
type: boolean
inherited_annotations:
type: array
items:
type: string
inherited_labels:
type: array
items:
type: string
master_pod_move_timeout:
type: string
default: "20m"
node_readiness_label:
type: object
additionalProperties:
type: string
node_readiness_label_merge:
type: string
enum:
- "AND"
- "OR"
oauth_token_secret_name:
type: string
default: "postgresql-operator"
pdb_name_format:
type: string
default: "postgres-{cluster}-pdb"
pod_antiaffinity_preferred_during_scheduling:
type: boolean
default: false
pod_antiaffinity_topology_key:
type: string
default: "kubernetes.io/hostname"
pod_environment_configmap:
type: string
pod_environment_secret:
type: string
pod_management_policy:
type: string
enum:
- "ordered_ready"
- "parallel"
default: "ordered_ready"
pod_priority_class_name:
type: string
pod_role_label:
type: string
default: "spilo-role"
pod_service_account_definition:
type: string
default: ""
pod_service_account_name:
type: string
default: "postgres-pod"
pod_service_account_role_binding_definition:
type: string
default: ""
pod_terminate_grace_period:
type: string
default: "5m"
secret_name_template:
type: string
default: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}"
share_pgsocket_with_sidecars:
type: boolean
default: false
spilo_allow_privilege_escalation:
type: boolean
default: true
spilo_runasuser:
type: integer
spilo_runasgroup:
type: integer
spilo_fsgroup:
type: integer
spilo_privileged:
type: boolean
default: false
storage_resize_mode:
type: string
enum:
- "ebs"
- "mixed"
- "pvc"
- "off"
default: "pvc"
toleration:
type: object
additionalProperties:
type: string
watched_namespace:
type: string
postgres_pod_resources:
type: object
properties:
default_cpu_limit:
type: string
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
default: "1"
default_cpu_request:
type: string
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
default: "100m"
default_memory_limit:
type: string
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
default: "500Mi"
default_memory_request:
type: string
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
default: "100Mi"
max_cpu_request:
type: string
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
max_memory_request:
type: string
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
min_cpu_limit:
type: string
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
default: "250m"
min_memory_limit:
type: string
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
default: "250Mi"
timeouts:
type: object
properties:
patroni_api_check_interval:
type: string
default: "1s"
patroni_api_check_timeout:
type: string
default: "5s"
pod_label_wait_timeout:
type: string
default: "10m"
pod_deletion_wait_timeout:
type: string
default: "10m"
ready_wait_interval:
type: string
default: "4s"
ready_wait_timeout:
type: string
default: "30s"
resource_check_interval:
type: string
default: "3s"
resource_check_timeout:
type: string
default: "10m"
load_balancer:
type: object
properties:
custom_service_annotations:
type: object
additionalProperties:
type: string
db_hosted_zone:
type: string
default: "db.example.com"
enable_master_load_balancer:
type: boolean
default: true
enable_master_pooler_load_balancer:
type: boolean
default: false
enable_replica_load_balancer:
type: boolean
default: false
enable_replica_pooler_load_balancer:
type: boolean
default: false
external_traffic_policy:
type: string
enum:
- "Cluster"
- "Local"
default: "Cluster"
master_dns_name_format:
type: string
default: "{cluster}.{namespace}.{hostedzone}"
master_legacy_dns_name_format:
type: string
default: "{cluster}.{team}.{hostedzone}"
replica_dns_name_format:
type: string
default: "{cluster}-repl.{namespace}.{hostedzone}"
replica_legacy_dns_name_format:
type: string
default: "{cluster}-repl.{team}.{hostedzone}"
aws_or_gcp:
type: object
properties:
additional_secret_mount:
type: string
additional_secret_mount_path:
type: string
default: "/meta/credentials"
aws_region:
type: string
default: "eu-central-1"
enable_ebs_gp3_migration:
type: boolean
default: false
enable_ebs_gp3_migration_max_size:
type: integer
default: 1000
gcp_credentials:
type: string
kube_iam_role:
type: string
log_s3_bucket:
type: string
wal_az_storage_account:
type: string
wal_gs_bucket:
type: string
wal_s3_bucket:
type: string
logical_backup:
type: object
properties:
logical_backup_azure_storage_account_name:
type: string
logical_backup_azure_storage_container:
type: string
logical_backup_azure_storage_account_key:
type: string
logical_backup_cpu_limit:
type: string
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
logical_backup_cpu_request:
type: string
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
logical_backup_docker_image:
type: string
default: "registry.opensource.zalan.do/acid/logical-backup:v1.10.0"
logical_backup_google_application_credentials:
type: string
logical_backup_job_prefix:
type: string
default: "logical-backup-"
logical_backup_memory_limit:
type: string
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
logical_backup_memory_request:
type: string
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
logical_backup_provider:
type: string
enum:
- "az"
- "gcs"
- "s3"
default: "s3"
logical_backup_s3_access_key_id:
type: string
logical_backup_s3_bucket:
type: string
logical_backup_s3_endpoint:
type: string
logical_backup_s3_region:
type: string
logical_backup_s3_secret_access_key:
type: string
logical_backup_s3_sse:
type: string
logical_backup_s3_retention_time:
type: string
logical_backup_schedule:
type: string
pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
default: "30 00 * * *"
debug:
type: object
properties:
debug_logging:
type: boolean
default: true
enable_database_access:
type: boolean
default: true
teams_api:
type: object
properties:
enable_admin_role_for_users:
type: boolean
default: true
enable_postgres_team_crd:
type: boolean
default: true
enable_postgres_team_crd_superusers:
type: boolean
default: false
enable_team_member_deprecation:
type: boolean
default: false
enable_team_superuser:
type: boolean
default: false
enable_teams_api:
type: boolean
default: true
pam_configuration:
type: string
default: "https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees"
pam_role_name:
type: string
default: "zalandos"
postgres_superuser_teams:
type: array
items:
type: string
protected_role_names:
type: array
items:
type: string
default:
- admin
- cron_admin
role_deletion_suffix:
type: string
default: "_deleted"
team_admin_role:
type: string
default: "admin"
team_api_role_configuration:
type: object
additionalProperties:
type: string
default:
log_statement: all
teams_api_url:
type: string
default: "https://teams.example.com/api/"
logging_rest_api:
type: object
properties:
api_port:
type: integer
default: 8080
cluster_history_entries:
type: integer
default: 1000
ring_log_lines:
type: integer
default: 100
scalyr: # deprecated
type: object
properties:
scalyr_api_key:
type: string
scalyr_cpu_limit:
type: string
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
default: "1"
scalyr_cpu_request:
type: string
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
default: "100m"
scalyr_image:
type: string
scalyr_memory_limit:
type: string
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
default: "500Mi"
scalyr_memory_request:
type: string
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
default: "50Mi"
scalyr_server_url:
type: string
default: "https://upload.eu.scalyr.com"
connection_pooler:
type: object
properties:
connection_pooler_schema:
type: string
default: "pooler"
connection_pooler_user:
type: string
default: "pooler"
connection_pooler_image:
type: string
default: "registry.opensource.zalan.do/acid/pgbouncer:master-27"
connection_pooler_max_db_connections:
type: integer
default: 60
connection_pooler_mode:
type: string
enum:
- "session"
- "transaction"
default: "transaction"
connection_pooler_number_of_instances:
type: integer
minimum: 1
default: 2
connection_pooler_default_cpu_limit:
type: string
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
default: "1"
connection_pooler_default_cpu_request:
type: string
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
default: "500m"
connection_pooler_default_memory_limit:
type: string
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
default: "100Mi"
connection_pooler_default_memory_request:
type: string
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
default: "100Mi"
patroni:
type: object
properties:
enable_patroni_failsafe_mode:
type: boolean
default: false
status:
type: object
additionalProperties:
type: string

2
postgresql/pod.env Normal file
View File

@ -0,0 +1,2 @@
WAL_S3_BUCKET=pgbackup
AWS_ENDPOINT=https://burp.pyrocufflink.blue:9000

View File

@ -0,0 +1,45 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-operator
labels:
application: postgres-operator
spec:
replicas: 1
strategy:
type: "Recreate"
selector:
matchLabels:
name: postgres-operator
template:
metadata:
labels:
name: postgres-operator
spec:
serviceAccountName: postgres-operator
containers:
- name: postgres-operator
image: registry.opensource.zalan.do/acid/postgres-operator:v1.10.0
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 100m
memory: 250Mi
limits:
cpu: 500m
memory: 500Mi
securityContext:
runAsUser: 1000
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
env:
# provided additional ENV vars can overwrite individual config map entries
#- name: CONFIG_MAP_NAME
# value: "postgres-operator"
# In order to use the CRD OperatorConfiguration instead, uncomment these lines and comment out the two lines above
- name: POSTGRES_OPERATOR_CONFIGURATION_OBJECT
value: postgresql-operator-configuration
# Define an ID to isolate controllers from each other
# - name: CONTROLLER_ID
# value: "second-operator"

View File

@ -0,0 +1,213 @@
apiVersion: "acid.zalan.do/v1"
kind: OperatorConfiguration
metadata:
name: postgresql-operator-configuration
configuration:
docker_image: ghcr.io/zalando/spilo-15:3.0-p1
# enable_crd_registration: true
# crd_categories:
# - all
# enable_lazy_spilo_upgrade: false
enable_pgversion_env_var: true
# enable_shm_volume: true
enable_spilo_wal_path_compat: false
enable_team_id_clustername_prefix: false
etcd_host: ""
# ignore_instance_limits_annotation_key: ""
# kubernetes_use_configmaps: false
max_instances: -1
min_instances: -1
resync_period: 30m
repair_period: 5m
# set_memory_request_to_limit: false
# sidecars:
# - image: image:123
# name: global-sidecar-1
# ports:
# - containerPort: 80
# protocol: TCP
workers: 2
users:
# additional_owner_roles:
# - cron_admin
enable_password_rotation: false
password_rotation_interval: 90
password_rotation_user_retention: 180
replication_username: standby
super_username: postgres
major_version_upgrade:
major_version_upgrade_mode: "off"
# major_version_upgrade_team_allow_list:
# - acid
minimal_major_version: "11"
target_major_version: "15"
kubernetes:
# additional_pod_capabilities:
# - "SYS_NICE"
cluster_domain: cluster.local
cluster_labels:
application: spilo
cluster_name_label: cluster-name
# custom_pod_annotations:
# keya: valuea
# keyb: valueb
# delete_annotation_date_key: delete-date
# delete_annotation_name_key: delete-clustername
# downscaler_annotations:
# - deployment-time
# - downscaler/*
enable_cross_namespace_secret: true
enable_init_containers: true
enable_pod_antiaffinity: false
enable_pod_disruption_budget: true
enable_readiness_probe: false
enable_sidecars: true
# ignored_annotations:
# - k8s.v1.cni.cncf.io/network-status
# infrastructure_roles_secret_name: "postgresql-infrastructure-roles"
# infrastructure_roles_secrets:
# - secretname: "monitoring-roles"
# userkey: "user"
# passwordkey: "password"
# rolekey: "inrole"
# - secretname: "other-infrastructure-role"
# userkey: "other-user-key"
# passwordkey: "other-password-key"
# inherited_annotations:
# - owned-by
# inherited_labels:
# - application
# - environment
master_pod_move_timeout: 20m
# node_readiness_label:
# status: ready
# node_readiness_label_merge: "OR"
oauth_token_secret_name: postgresql-operator
pdb_name_format: "postgres-{cluster}-pdb"
pod_antiaffinity_preferred_during_scheduling: false
pod_antiaffinity_topology_key: "kubernetes.io/hostname"
pod_environment_configmap: postgresql/pod-env
pod_environment_secret: pod-secrets
pod_management_policy: "ordered_ready"
# pod_priority_class_name: "postgres-pod-priority"
pod_role_label: spilo-role
# pod_service_account_definition: ""
pod_service_account_name: postgres-pod
# pod_service_account_role_binding_definition: ""
pod_terminate_grace_period: 5m
secret_name_template: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}"
share_pgsocket_with_sidecars: false
spilo_allow_privilege_escalation: true
# spilo_runasuser: 101
# spilo_runasgroup: 103
# spilo_fsgroup: 103
spilo_privileged: false
storage_resize_mode: pvc
# toleration:
# key: db-only
# operator: Exists
# effect: NoSchedule
# watched_namespace: ""
postgres_pod_resources:
default_cpu_limit: "1"
default_cpu_request: 100m
default_memory_limit: 500Mi
default_memory_request: 100Mi
# max_cpu_request: "1"
# max_memory_request: 4Gi
# min_cpu_limit: 250m
# min_memory_limit: 250Mi
timeouts:
patroni_api_check_interval: 1s
patroni_api_check_timeout: 5s
pod_label_wait_timeout: 10m
pod_deletion_wait_timeout: 10m
ready_wait_interval: 4s
ready_wait_timeout: 30s
resource_check_interval: 3s
resource_check_timeout: 10m
load_balancer:
# custom_service_annotations:
# keyx: valuex
# keyy: valuey
# db_hosted_zone: ""
enable_master_load_balancer: false
enable_master_pooler_load_balancer: false
enable_replica_load_balancer: false
enable_replica_pooler_load_balancer: false
external_traffic_policy: "Cluster"
master_dns_name_format: "{cluster}.{namespace}.{hostedzone}"
# master_legacy_dns_name_format: "{cluster}.{team}.{hostedzone}"
replica_dns_name_format: "{cluster}-repl.{namespace}.{hostedzone}"
# replica_dns_old_name_format: "{cluster}-repl.{team}.{hostedzone}"
aws_or_gcp:
additional_secret_mount: ssh-auth
additional_secret_mount_path: /run/secrets/ssh-auth
aws_region: eu-central-1
enable_ebs_gp3_migration: false
# enable_ebs_gp3_migration_max_size: 1000
# gcp_credentials: ""
# kube_iam_role: ""
# log_s3_bucket: ""
# wal_az_storage_account: ""
# wal_gs_bucket: ""
# wal_s3_bucket: ""
logical_backup:
# logical_backup_azure_storage_account_name: ""
# logical_backup_azure_storage_container: ""
# logical_backup_azure_storage_account_key: ""
# logical_backup_cpu_limit: ""
# logical_backup_cpu_request: ""
# logical_backup_memory_limit: ""
# logical_backup_memory_request: ""
logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.10.0"
# logical_backup_google_application_credentials: ""
logical_backup_job_prefix: "logical-backup-"
logical_backup_provider: "s3"
# logical_backup_s3_access_key_id: ""
logical_backup_s3_bucket: "my-bucket-url"
# logical_backup_s3_endpoint: ""
# logical_backup_s3_region: ""
# logical_backup_s3_secret_access_key: ""
logical_backup_s3_sse: "AES256"
# logical_backup_s3_retention_time: ""
logical_backup_schedule: "30 00 * * *"
debug:
debug_logging: true
enable_database_access: true
teams_api:
# enable_admin_role_for_users: true
# enable_postgres_team_crd: false
# enable_postgres_team_crd_superusers: false
enable_team_member_deprecation: false
enable_team_superuser: false
enable_teams_api: false
# pam_configuration: ""
pam_role_name: zalandos
# postgres_superuser_teams:
# - postgres_superusers
protected_role_names:
- admin
- cron_admin
role_deletion_suffix: "_deleted"
team_admin_role: admin
team_api_role_configuration:
log_statement: all
# teams_api_url: ""
logging_rest_api:
api_port: 8080
cluster_history_entries: 1000
ring_log_lines: 100
connection_pooler:
connection_pooler_default_cpu_limit: "1"
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-27"
# connection_pooler_max_db_connections: 60
connection_pooler_mode: "transaction"
connection_pooler_number_of_instances: 2
# connection_pooler_schema: "pooler"
# connection_pooler_user: "pooler"
patroni:
enable_patroni_failsafe_mode: false

View File

@ -0,0 +1,68 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: postgresteams.acid.zalan.do
spec:
group: acid.zalan.do
names:
kind: PostgresTeam
listKind: PostgresTeamList
plural: postgresteams
singular: postgresteam
shortNames:
- pgteam
categories:
- all
scope: Namespaced
versions:
- name: v1
served: true
storage: true
subresources:
status: {}
schema:
openAPIV3Schema:
type: object
required:
- kind
- apiVersion
- spec
properties:
kind:
type: string
enum:
- PostgresTeam
apiVersion:
type: string
enum:
- acid.zalan.do/v1
spec:
type: object
properties:
additionalSuperuserTeams:
type: object
description: "Map for teamId and associated additional superuser teams"
additionalProperties:
type: array
nullable: true
description: "List of teams to become Postgres superusers"
items:
type: string
additionalTeams:
type: object
description: "Map for teamId and associated additional teams"
additionalProperties:
type: array
nullable: true
description: "List of teams whose members will also be added to the Postgres cluster"
items:
type: string
additionalMembers:
type: object
description: "Map for teamId and associated additional users"
additionalProperties:
type: array
nullable: true
description: "List of users who will also be added to the Postgres cluster"
items:
type: string

View File

@ -0,0 +1,38 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: postgresql
resources:
- github.com/zalando/postgres-operator/ui/manifests?ref=v1.9.0
patches:
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-operator-ui
spec:
template:
spec:
containers:
- name: service
env:
- name: TARGET_NAMESPACE
value: '*'
- target:
kind: Ingress
labelSelector: application=postgres-operator-ui
patch: |-
- op: replace
path: /spec/rules/0/host
value: psqlui.pyrocufflink.blue
- patch: |-
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: postgres-operator-ui
annotations:
nginx.ingress.kubernetes.io/auth-method: GET
nginx.ingress.kubernetes.io/auth-url: http://authelia.authelia.svc.cluster.local:9091/api/verify
nginx.ingress.kubernetes.io/auth-signin: https://auth.pyrocufflink.blue/?rm=$request_method