diff --git a/postgresql/.gitignore b/postgresql/.gitignore new file mode 100644 index 0000000..2ed1a52 --- /dev/null +++ b/postgresql/.gitignore @@ -0,0 +1 @@ +pod.secrets diff --git a/postgresql/api-service.yaml b/postgresql/api-service.yaml new file mode 100644 index 0000000..6164481 --- /dev/null +++ b/postgresql/api-service.yaml @@ -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 diff --git a/postgresql/default-cluster.yaml b/postgresql/default-cluster.yaml new file mode 100644 index 0000000..652b41f --- /dev/null +++ b/postgresql/default-cluster.yaml @@ -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 diff --git a/postgresql/kustomization.yaml b/postgresql/kustomization.yaml new file mode 100644 index 0000000..505fc03 --- /dev/null +++ b/postgresql/kustomization.yaml @@ -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 diff --git a/postgresql/namespace.yaml b/postgresql/namespace.yaml new file mode 100644 index 0000000..a6bb64a --- /dev/null +++ b/postgresql/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: postgresql diff --git a/postgresql/operator-service-account-rbac.yaml b/postgresql/operator-service-account-rbac.yaml new file mode 100644 index 0000000..c10dc5f --- /dev/null +++ b/postgresql/operator-service-account-rbac.yaml @@ -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 diff --git a/postgresql/operatorconfiguration.crd.yaml b/postgresql/operatorconfiguration.crd.yaml new file mode 100644 index 0000000..f60d842 --- /dev/null +++ b/postgresql/operatorconfiguration.crd.yaml @@ -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 diff --git a/postgresql/pod.env b/postgresql/pod.env new file mode 100644 index 0000000..8c48719 --- /dev/null +++ b/postgresql/pod.env @@ -0,0 +1,2 @@ +WAL_S3_BUCKET=pgbackup +AWS_ENDPOINT=https://burp.pyrocufflink.blue:9000 diff --git a/postgresql/postgres-operator.yaml b/postgresql/postgres-operator.yaml new file mode 100644 index 0000000..6c323d5 --- /dev/null +++ b/postgresql/postgres-operator.yaml @@ -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" diff --git a/postgresql/postgresql-operator-configuration.yaml b/postgresql/postgresql-operator-configuration.yaml new file mode 100644 index 0000000..92d3809 --- /dev/null +++ b/postgresql/postgresql-operator-configuration.yaml @@ -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 diff --git a/postgresql/postgresteam.crd.yaml b/postgresql/postgresteam.crd.yaml new file mode 100644 index 0000000..2588e53 --- /dev/null +++ b/postgresql/postgresteam.crd.yaml @@ -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 diff --git a/postgresql/ui/kustomization.yaml b/postgresql/ui/kustomization.yaml new file mode 100644 index 0000000..dd17eb6 --- /dev/null +++ b/postgresql/ui/kustomization.yaml @@ -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