diff --git a/README.md b/README.md index 8d5abb9..7829c88 100644 --- a/README.md +++ b/README.md @@ -17,4 +17,5 @@ helm repo add ckotzbauer https://ckotzbauer.github.io/helm-charts - [cadvisor](https://github.com/ckotzbauer/helm-charts/tree/main/charts/cadvisor) - [chekr](https://github.com/ckotzbauer/helm-charts/tree/main/charts/chekr) - [nfs-client-provisioner](https://github.com/ckotzbauer/helm-charts/tree/main/charts/nfs-client-provisioner) +- [postgres-operator](https://github.com/ckotzbauer/helm-charts/tree/main/charts/postgres-operator) - [prometheus-blackbox-exporter](https://github.com/ckotzbauer/helm-charts/tree/main/charts/prometheus-blackbox-exporter) diff --git a/charts/postgres-operator/.helmignore b/charts/postgres-operator/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/charts/postgres-operator/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/postgres-operator/Chart.yaml b/charts/postgres-operator/Chart.yaml new file mode 100644 index 0000000..160a39c --- /dev/null +++ b/charts/postgres-operator/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: postgres-operator +description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes +version: 1.6.3 +appVersion: 1.6.3 +home: https://github.com/zalando/postgres-operator +sources: + - https://github.com/zalando/postgres-operator + - https://github.com/ckotzbauer/helm-charts +keywords: + - postgres + - operator + - cloud-native + - patroni + - spilo +maintainers: + - name: ckotzbauer + email: christian.kotzbauer@gmail.com diff --git a/charts/postgres-operator/README.md b/charts/postgres-operator/README.md new file mode 100644 index 0000000..fd41c03 --- /dev/null +++ b/charts/postgres-operator/README.md @@ -0,0 +1,164 @@ +# Postgres-operator + +Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes + +Learn more: [https://github.com/zalando/postgres-operator](https://github.com/zalando/postgres-operator) + +## TL;DR; + +```bash +$ helm install ckotzbauer/postgres-operator +``` + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```bash +$ helm install my-release ckotzbauer/postgres-operator +``` + +The command deploys the operator on the Kubernetes cluster using the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. + +## Uninstalling the Chart + +To uninstall/delete the `my-release` installation: + +```bash +$ helm delete my-release +``` +The command removes all the Kubernetes components associated with the chart and deletes the release. + + +## Configuration + +The following table lists the configurable parameters of the Postgres-operator chart and their default values. + +| Parameter | Description | Default | +| ------------------------ | ----------------------- | -------------- | +| `image.registry` | | `"registry.opensource.zalan.do"` | +| `image.repository` | | `"acid/postgres-operator"` | +| `image.tag` | | `"v1.6.3"` | +| `image.pullPolicy` | | `"IfNotPresent"` | +| `podAnnotations` | | `{}` | +| `podLabels` | | `{}` | +| `enableJsonLogging` | | `false` | +| `configGeneral.enable_crd_validation` | | `true` | +| `configGeneral.enable_lazy_spilo_upgrade` | | `false` | +| `configGeneral.enable_pgversion_env_var` | | `true` | +| `configGeneral.enable_shm_volume` | | `true` | +| `configGeneral.enable_spilo_wal_path_compat` | | `false` | +| `configGeneral.etcd_host` | | `""` | +| `configGeneral.docker_image` | | `"registry.opensource.zalan.do/acid/spilo-13:2.0-p7"` | +| `configGeneral.min_instances` | | `-1` | +| `configGeneral.max_instances` | | `-1` | +| `configGeneral.repair_period` | | `"5m"` | +| `configGeneral.resync_period` | | `"30m"` | +| `configGeneral.workers` | | `8` | +| `configUsers.replication_username` | | `"standby"` | +| `configUsers.super_username` | | `"postgres"` | +| `configMajorVersionUpgrade.major_version_upgrade_mode` | | `"off"` | +| `configMajorVersionUpgrade.minimal_major_version` | | `"9.5"` | +| `configMajorVersionUpgrade.target_major_version` | | `"13"` | +| `configKubernetes.cluster_domain` | | `"cluster.local"` | +| `configKubernetes.cluster_labels.application` | | `"spilo"` | +| `configKubernetes.cluster_name_label` | | `"cluster-name"` | +| `configKubernetes.enable_init_containers` | | `true` | +| `configKubernetes.enable_pod_antiaffinity` | | `false` | +| `configKubernetes.enable_pod_disruption_budget` | | `true` | +| `configKubernetes.enable_sidecars` | | `true` | +| `configKubernetes.pdb_name_format` | | `"postgres-{cluster}-pdb"` | +| `configKubernetes.pod_antiaffinity_topology_key` | | `"kubernetes.io/hostname"` | +| `configKubernetes.pod_management_policy` | | `"ordered_ready"` | +| `configKubernetes.pod_role_label` | | `"spilo-role"` | +| `configKubernetes.pod_terminate_grace_period` | | `"5m"` | +| `configKubernetes.secret_name_template` | | `"{username}.{cluster}.credentials.{tprkind}.{tprgroup}"` | +| `configKubernetes.spilo_privileged` | | `false` | +| `configKubernetes.spilo_allow_privilege_escalation` | | `true` | +| `configKubernetes.storage_resize_mode` | | `"pvc"` | +| `configKubernetes.watched_namespace` | listen to all namespaces | `"*"` | +| `configPostgresPodResources.default_cpu_limit` | | `"1"` | +| `configPostgresPodResources.default_cpu_request` | | `"100m"` | +| `configPostgresPodResources.default_memory_limit` | | `"500Mi"` | +| `configPostgresPodResources.default_memory_request` | | `"100Mi"` | +| `configPostgresPodResources.min_cpu_limit` | | `"250m"` | +| `configPostgresPodResources.min_memory_limit` | | `"250Mi"` | +| `configTimeouts.pod_deletion_wait_timeout` | | `"10m"` | +| `configTimeouts.pod_label_wait_timeout` | | `"10m"` | +| `configTimeouts.ready_wait_interval` | | `"3s"` | +| `configTimeouts.ready_wait_timeout` | | `"30s"` | +| `configTimeouts.resource_check_interval` | | `"3s"` | +| `configTimeouts.resource_check_timeout` | | `"10m"` | +| `configLoadBalancer.db_hosted_zone` | | `"db.example.com"` | +| `configLoadBalancer.enable_master_load_balancer` | | `false` | +| `configLoadBalancer.enable_replica_load_balancer` | | `false` | +| `configLoadBalancer.external_traffic_policy` | | `"Cluster"` | +| `configLoadBalancer.master_dns_name_format` | | `"{cluster}.{team}.{hostedzone}"` | +| `configLoadBalancer.replica_dns_name_format` | | `"{cluster}-repl.{team}.{hostedzone}"` | +| `configDebug.debug_logging` | | `true` | +| `configDebug.enable_database_access` | | `true` | +| `configLoggingRestApi.api_port` | | `8080` | +| `configLoggingRestApi.cluster_history_entries` | | `1000` | +| `configLoggingRestApi.ring_log_lines` | | `100` | +| `configAwsOrGcp.aws_region` | | `"eu-central-1"` | +| `configAwsOrGcp.enable_ebs_gp3_migration` | | `false` | +| `configLogicalBackup.logical_backup_docker_image` | | `"registry.opensource.zalan.do/acid/logical-backup:v1.6.3"` | +| `configLogicalBackup.logical_backup_job_prefix` | | `"logical-backup-"` | +| `configLogicalBackup.logical_backup_provider` | | `"s3"` | +| `configLogicalBackup.logical_backup_s3_access_key_id` | | `""` | +| `configLogicalBackup.logical_backup_s3_bucket` | | `"my-bucket-url"` | +| `configLogicalBackup.logical_backup_s3_region` | | `""` | +| `configLogicalBackup.logical_backup_s3_endpoint` | | `""` | +| `configLogicalBackup.logical_backup_s3_secret_access_key` | | `""` | +| `configLogicalBackup.logical_backup_s3_sse` | | `"AES256"` | +| `configLogicalBackup.logical_backup_schedule` | | `"30 00 * * *"` | +| `configTeamsApi.enable_admin_role_for_users` | | `true` | +| `configTeamsApi.enable_postgres_team_crd` | | `false` | +| `configTeamsApi.enable_postgres_team_crd_superusers` | | `false` | +| `configTeamsApi.enable_team_member_deprecation` | | `false` | +| `configTeamsApi.enable_team_superuser` | | `false` | +| `configTeamsApi.enable_teams_api` | | `false` | +| `configTeamsApi.pam_role_name` | | `"zalandos"` | +| `configTeamsApi.postgres_superuser_teams` | | `["postgres_superusers"]` | +| `configTeamsApi.protected_role_names` | | `["admin"]` | +| `configTeamsApi.role_deletion_suffix` | | `"_deleted"` | +| `configTeamsApi.team_admin_role` | | `"admin"` | +| `configTeamsApi.team_api_role_configuration.log_statement` | | `"all"` | +| `configConnectionPooler.connection_pooler_schema` | | `"pooler"` | +| `configConnectionPooler.connection_pooler_user` | | `"pooler"` | +| `configConnectionPooler.connection_pooler_image` | | `"registry.opensource.zalan.do/acid/pgbouncer:master-16"` | +| `configConnectionPooler.connection_pooler_max_db_connections` | | `60` | +| `configConnectionPooler.connection_pooler_mode` | | `"transaction"` | +| `configConnectionPooler.connection_pooler_number_of_instances` | | `2` | +| `configConnectionPooler.connection_pooler_default_cpu_request` | | `"500m"` | +| `configConnectionPooler.connection_pooler_default_memory_request` | | `"100Mi"` | +| `configConnectionPooler.connection_pooler_default_cpu_limit` | | `"1"` | +| `configConnectionPooler.connection_pooler_default_memory_limit` | | `"100Mi"` | +| `rbac.create` | | `true` | +| `crd.create` | | `true` | +| `serviceAccount.create` | | `true` | +| `serviceAccount.name` | | `null` | +| `podServiceAccount.name` | | `"postgres-pod"` | +| `priorityClassName` | | `""` | +| `podPriorityClassName` | | `""` | +| `resources.limits.cpu` | | `"500m"` | +| `resources.limits.memory` | | `"500Mi"` | +| `resources.requests.cpu` | | `"100m"` | +| `resources.requests.memory` | | `"250Mi"` | +| `securityContext.runAsUser` | | `1000` | +| `securityContext.runAsNonRoot` | | `true` | +| `securityContext.readOnlyRootFilesystem` | | `true` | +| `securityContext.allowPrivilegeEscalation` | | `false` | +| `affinity` | | `{}` | +| `nodeSelector` | | `{}` | +| `tolerations` | | `[]` | +| `controllerID.create` | | `false` | +| `controllerID.name` | | `null` | +| `pod_environment_config` | | `{}` | + + +## Changes compared to the [official chart](https://github.com/zalando/postgres-operator/tree/master/charts/postgres-operator) + +- No `ConfigMap` config-target. +- `pod_environment_configmap` is created automatically. Contents can be configured via `pod_environment_config`. + diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml new file mode 100644 index 0000000..82a737a --- /dev/null +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -0,0 +1,568 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: operatorconfigurations.acid.zalan.do + labels: + app.kubernetes.io/name: postgres-operator + annotations: + "helm.sh/hook": crd-install +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: + docker_image: + type: string + default: "registry.opensource.zalan.do/acid/spilo-13:2.0-p7" + enable_crd_validation: + type: boolean + 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 + etcd_host: + type: string + default: "" + kubernetes_use_configmaps: + type: boolean + default: false + max_instances: + type: integer + minimum: -1 # -1 = disabled + default: -1 + min_instances: + type: integer + minimum: -1 # -1 = disabled + 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: + 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" + minimal_major_version: + type: string + default: "9.5" + target_major_version: + type: string + default: "13" + 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_init_containers: + type: boolean + default: true + enable_pod_antiaffinity: + type: boolean + default: false + enable_pod_disruption_budget: + type: boolean + default: true + enable_sidecars: + type: boolean + default: true + 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 + oauth_token_secret_name: + type: string + default: "postgresql-operator" + pdb_name_format: + type: string + default: "postgres-{cluster}-pdb" + 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}" + 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" + - "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" + 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: + 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_replica_load_balancer: + type: boolean + default: false + external_traffic_policy: + type: string + enum: + - "Cluster" + - "Local" + default: "Cluster" + master_dns_name_format: + type: string + default: "{cluster}.{team}.{hostedzone}" + replica_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_gs_bucket: + type: string + wal_s3_bucket: + type: string + logical_backup: + type: object + properties: + logical_backup_docker_image: + type: string + default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" + logical_backup_google_application_credentials: + type: string + logical_backup_job_prefix: + type: string + default: "logical-backup-" + logical_backup_provider: + type: string + 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_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 + 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-16" + 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" + status: + type: object + additionalProperties: + type: string diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml new file mode 100644 index 0000000..aead7fe --- /dev/null +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -0,0 +1,575 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: postgresqls.acid.zalan.do + labels: + app.kubernetes.io/name: postgres-operator + annotations: + "helm.sh/hook": crd-install +spec: + group: acid.zalan.do + names: + kind: postgresql + listKind: postgresqlList + plural: postgresqls + singular: postgresql + shortNames: + - pg + categories: + - all + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: Team + type: string + description: Team responsible for Postgres cluster + jsonPath: .spec.teamId + - name: Version + type: string + description: PostgreSQL version + jsonPath: .spec.postgresql.version + - name: Pods + type: integer + description: Number of Pods per Postgres cluster + jsonPath: .spec.numberOfInstances + - name: Volume + type: string + description: Size of the bound volume + jsonPath: .spec.volume.size + - name: CPU-Request + type: string + description: Requested CPU for Postgres containers + jsonPath: .spec.resources.requests.cpu + - name: Memory-Request + type: string + description: Requested memory for Postgres containers + jsonPath: .spec.resources.requests.memory + - name: Age + type: date + jsonPath: .metadata.creationTimestamp + - name: Status + type: string + description: Current sync status of postgresql resource + jsonPath: .status.PostgresClusterStatus + schema: + openAPIV3Schema: + type: object + required: + - kind + - apiVersion + - spec + properties: + kind: + type: string + enum: + - postgresql + apiVersion: + type: string + enum: + - acid.zalan.do/v1 + spec: + type: object + required: + - numberOfInstances + - teamId + - postgresql + - volume + properties: + additionalVolumes: + type: array + items: + type: object + required: + - name + - mountPath + - volumeSource + properties: + name: + type: string + mountPath: + type: string + targetContainers: + type: array + nullable: true + items: + type: string + volumeSource: + type: object + x-kubernetes-preserve-unknown-fields: true + subPath: + type: string + allowedSourceRanges: + type: array + nullable: true + items: + type: string + pattern: '^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$' + clone: + type: object + required: + - cluster + properties: + cluster: + type: string + s3_endpoint: + type: string + s3_access_key_id: + type: string + s3_secret_access_key: + type: string + s3_force_path_style: + type: boolean + s3_wal_path: + type: string + timestamp: + type: string + pattern: '^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$' + # The regexp matches the date-time format (RFC 3339 Section 5.6) that specifies a timezone as an offset relative to UTC + # Example: 1996-12-19T16:39:57-08:00 + # Note: this field requires a timezone + uid: + format: uuid + type: string + connectionPooler: + type: object + properties: + dockerImage: + type: string + maxDBConnections: + type: integer + mode: + type: string + enum: + - "session" + - "transaction" + numberOfInstances: + type: integer + minimum: 2 + resources: + type: object + required: + - requests + - limits + properties: + limits: + type: object + required: + - cpu + - memory + properties: + cpu: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + memory: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + requests: + type: object + required: + - cpu + - memory + properties: + cpu: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + memory: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + schema: + type: string + user: + type: string + databases: + type: object + additionalProperties: + type: string + # Note: usernames specified here as database owners must be declared in the users key of the spec key. + dockerImage: + type: string + enableConnectionPooler: + type: boolean + enableReplicaConnectionPooler: + type: boolean + enableLogicalBackup: + type: boolean + enableMasterLoadBalancer: + type: boolean + enableReplicaLoadBalancer: + type: boolean + enableShmVolume: + type: boolean + init_containers: # deprecated + type: array + nullable: true + items: + type: object + x-kubernetes-preserve-unknown-fields: true + initContainers: + type: array + nullable: true + items: + type: object + x-kubernetes-preserve-unknown-fields: true + logicalBackupSchedule: + type: string + pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' + maintenanceWindows: + type: array + items: + type: string + pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$' + numberOfInstances: + type: integer + minimum: 0 + patroni: + type: object + properties: + initdb: + type: object + additionalProperties: + type: string + loop_wait: + type: integer + maximum_lag_on_failover: + type: integer + pg_hba: + type: array + items: + type: string + retry_timeout: + type: integer + slots: + type: object + additionalProperties: + type: object + additionalProperties: + type: string + synchronous_mode: + type: boolean + synchronous_mode_strict: + type: boolean + ttl: + type: integer + podAnnotations: + type: object + additionalProperties: + type: string + pod_priority_class_name: # deprecated + type: string + podPriorityClassName: + type: string + postgresql: + type: object + required: + - version + properties: + version: + type: string + enum: + - "9.3" + - "9.4" + - "9.5" + - "9.6" + - "10" + - "11" + - "12" + - "13" + parameters: + type: object + additionalProperties: + type: string + preparedDatabases: + type: object + additionalProperties: + type: object + properties: + defaultUsers: + type: boolean + extensions: + type: object + additionalProperties: + type: string + schemas: + type: object + additionalProperties: + type: object + properties: + defaultUsers: + type: boolean + defaultRoles: + type: boolean + replicaLoadBalancer: # deprecated + type: boolean + resources: + type: object + required: + - requests + - limits + properties: + limits: + type: object + required: + - cpu + - memory + properties: + cpu: + type: string + # Decimal natural followed by m, or decimal natural followed by + # dot followed by up to three decimal digits. + # + # This is because the Kubernetes CPU resource has millis as the + # maximum precision. The actual values are checked in code + # because the regular expression would be huge and horrible and + # not very helpful in validation error messages; this one checks + # only the format of the given number. + # + # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + # Note: the value specified here must not be zero or be lower + # than the corresponding request. + memory: + type: string + # You can express memory as a plain integer or as a fixed-point + # integer using one of these suffixes: E, P, T, G, M, k. You can + # also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + # + # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + # Note: the value specified here must not be zero or be higher + # than the corresponding limit. + requests: + type: object + required: + - cpu + - memory + properties: + cpu: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + memory: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + schedulerName: + type: string + serviceAnnotations: + type: object + additionalProperties: + type: string + sidecars: + type: array + nullable: true + items: + type: object + x-kubernetes-preserve-unknown-fields: true + spiloRunAsUser: + type: integer + spiloRunAsGroup: + type: integer + spiloFSGroup: + type: integer + standby: + type: object + required: + - s3_wal_path + properties: + s3_wal_path: + type: string + teamId: + type: string + tls: + type: object + required: + - secretName + properties: + secretName: + type: string + certificateFile: + type: string + privateKeyFile: + type: string + caFile: + type: string + caSecretName: + type: string + nodeAffinity: + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + type: array + items: + type: object + required: + - weight + - preference + properties: + preference: + type: object + properties: + matchExpressions: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + values: + type: array + items: + type: string + matchFields: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + values: + type: array + items: + type: string + weight: + format: int32 + type: integer + requiredDuringSchedulingIgnoredDuringExecution: + type: object + required: + - nodeSelectorTerms + properties: + nodeSelectorTerms: + type: array + items: + type: object + properties: + matchExpressions: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + values: + type: array + items: + type: string + matchFields: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + values: + type: array + items: + type: string + tolerations: + type: array + items: + type: object + required: + - key + - operator + - effect + properties: + key: + type: string + operator: + type: string + enum: + - Equal + - Exists + value: + type: string + effect: + type: string + enum: + - NoExecute + - NoSchedule + - PreferNoSchedule + tolerationSeconds: + type: integer + useLoadBalancer: # deprecated + type: boolean + users: + type: object + additionalProperties: + type: array + nullable: true + description: "Role flags specified here must not contradict each other" + items: + type: string + enum: + - bypassrls + - BYPASSRLS + - nobypassrls + - NOBYPASSRLS + - createdb + - CREATEDB + - nocreatedb + - NOCREATEDB + - createrole + - CREATEROLE + - nocreaterole + - NOCREATEROLE + - inherit + - INHERIT + - noinherit + - NOINHERIT + - login + - LOGIN + - nologin + - NOLOGIN + - replication + - REPLICATION + - noreplication + - NOREPLICATION + - superuser + - SUPERUSER + - nosuperuser + - NOSUPERUSER + volume: + type: object + required: + - size + properties: + iops: + type: integer + size: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + # Note: the value specified here must not be zero. + storageClass: + type: string + subPath: + type: string + throughput: + type: integer + status: + type: object + additionalProperties: + type: string diff --git a/charts/postgres-operator/crds/postgresteams.yaml b/charts/postgres-operator/crds/postgresteams.yaml new file mode 100644 index 0000000..fbf873b --- /dev/null +++ b/charts/postgres-operator/crds/postgresteams.yaml @@ -0,0 +1,72 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: postgresteams.acid.zalan.do + labels: + app.kubernetes.io/name: postgres-operator + annotations: + "helm.sh/hook": crd-install +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/charts/postgres-operator/templates/NOTES.txt b/charts/postgres-operator/templates/NOTES.txt new file mode 100644 index 0000000..f1fe050 --- /dev/null +++ b/charts/postgres-operator/templates/NOTES.txt @@ -0,0 +1,3 @@ +To verify that postgres-operator has started, run: + + kubectl --namespace={{ .Release.Namespace }} get pods -l "app.kubernetes.io/name={{ template "postgres-operator.name" . }}" diff --git a/charts/postgres-operator/templates/_helpers.tpl b/charts/postgres-operator/templates/_helpers.tpl new file mode 100644 index 0000000..e496707 --- /dev/null +++ b/charts/postgres-operator/templates/_helpers.tpl @@ -0,0 +1,53 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "postgres-operator.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "postgres-operator.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create a service account name. +*/}} +{{- define "postgres-operator.serviceAccountName" -}} +{{ default (include "postgres-operator.fullname" .) .Values.serviceAccount.name }} +{{- end -}} + +{{/* +Create a pod service account name. +*/}} +{{- define "postgres-pod.serviceAccountName" -}} +{{ default (printf "%s-%v" (include "postgres-operator.fullname" .) "pod") .Values.podServiceAccount.name }} +{{- end -}} + +{{/* +Create a controller ID. +*/}} +{{- define "postgres-operator.controllerID" -}} +{{ default (include "postgres-operator.fullname" .) .Values.controllerID.name }} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "postgres-operator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/charts/postgres-operator/templates/clusterrole-postgres-pod.yaml b/charts/postgres-operator/templates/clusterrole-postgres-pod.yaml new file mode 100644 index 0000000..33c4382 --- /dev/null +++ b/charts/postgres-operator/templates/clusterrole-postgres-pod.yaml @@ -0,0 +1,77 @@ +{{ if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "postgres-pod.serviceAccountName" . }} + labels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + helm.sh/chart: {{ template "postgres-operator.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} +rules: +# Patroni needs to watch and manage endpoints +{{- if toString .Values.configGeneral.kubernetes_use_configmaps | eq "true" }} +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - endpoints + verbs: + - get +{{- else }} +- apiGroups: + - "" + resources: + - endpoints + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +{{- end }} +# 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 +{{- if toString .Values.configKubernetes.spilo_privileged | eq "true" }} +# to run privileged pods +- apiGroups: + - extensions + resources: + - podsecuritypolicies + resourceNames: + - privileged + verbs: + - use +{{- end }} +{{ end }} diff --git a/charts/postgres-operator/templates/clusterrole.yaml b/charts/postgres-operator/templates/clusterrole.yaml new file mode 100644 index 0000000..885bad3 --- /dev/null +++ b/charts/postgres-operator/templates/clusterrole.yaml @@ -0,0 +1,242 @@ +{{ if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "postgres-operator.serviceAccountName" . }} + labels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + helm.sh/chart: {{ template "postgres-operator.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} +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 +# to create or get/update CRDs when starting up +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - create + - get + - patch + - update +# to send events to the CRs +- apiGroups: + - "" + resources: + - events + verbs: + - create + - get + - list + - patch + - update + - watch +# to manage endpoints/configmaps which are also used by Patroni +{{- if toString .Values.configGeneral.kubernetes_use_configmaps | eq "true" }} +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - endpoints + verbs: + - get +{{- else }} +# to read configuration from ConfigMaps +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get +- apiGroups: + - "" + resources: + - endpoints + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +{{- end }} +# 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 +{{- if toString .Values.configKubernetes.storage_resize_mode | eq "pvc" }} + - patch + - update +{{- end }} + # to read existing PVs. Creation should be done via dynamic provisioning +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list +{{- if toString .Values.configKubernetes.storage_resize_mode | eq "ebs" }} + - update # only for resizing AWS volumes +{{- end }} +# 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 +{{- if toString .Values.configKubernetes.spilo_privileged | eq "true" }} +# to run privileged pods +- apiGroups: + - extensions + resources: + - podsecuritypolicies + resourceNames: + - privileged + verbs: + - use +{{- end }} +{{ end }} diff --git a/charts/postgres-operator/templates/clusterrolebinding.yaml b/charts/postgres-operator/templates/clusterrolebinding.yaml new file mode 100644 index 0000000..dbf65d0 --- /dev/null +++ b/charts/postgres-operator/templates/clusterrolebinding.yaml @@ -0,0 +1,19 @@ +{{ if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "postgres-operator.serviceAccountName" . }} + labels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + helm.sh/chart: {{ template "postgres-operator.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "postgres-operator.serviceAccountName" . }} +subjects: +- kind: ServiceAccount + name: {{ include "postgres-operator.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{ end }} diff --git a/charts/postgres-operator/templates/crds.yaml b/charts/postgres-operator/templates/crds.yaml new file mode 100644 index 0000000..7338300 --- /dev/null +++ b/charts/postgres-operator/templates/crds.yaml @@ -0,0 +1,6 @@ +{{ if .Values.crd.create }} +{{- range $path, $bytes := .Files.Glob "crds/*.yaml" }} +{{ $.Files.Get $path }} +--- +{{- end }} +{{- end }} diff --git a/charts/postgres-operator/templates/deployment.yaml b/charts/postgres-operator/templates/deployment.yaml new file mode 100644 index 0000000..c84e0dd --- /dev/null +++ b/charts/postgres-operator/templates/deployment.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + helm.sh/chart: {{ template "postgres-operator.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + name: {{ template "postgres-operator.fullname" . }} +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/operatorconfiguration.yaml") . | sha256sum }} + {{- if .Values.podAnnotations }} +{{ toYaml .Values.podAnnotations | indent 8 }} + {{- end }} + labels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- if .Values.podLabels }} +{{ toYaml .Values.podLabels | indent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "postgres-operator.serviceAccountName" . }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + {{- if .Values.enableJsonLogging }} + - name: ENABLE_JSON_LOGGING + value: "true" + {{- end }} + - name: POSTGRES_OPERATOR_CONFIGURATION_OBJECT + value: {{ template "postgres-operator.fullname" . }} + {{- if .Values.controllerID.create }} + - name: CONTROLLER_ID + value: {{ template "postgres-operator.controllerID" . }} + {{- end }} + resources: +{{ toYaml .Values.resources | indent 10 }} + securityContext: +{{ toYaml .Values.securityContext | indent 10 }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml .Values.imagePullSecrets | indent 8 }} + {{- end }} + affinity: +{{ toYaml .Values.affinity | indent 8 }} + nodeSelector: +{{ toYaml .Values.nodeSelector | indent 8 }} + tolerations: +{{ toYaml .Values.tolerations | indent 8 }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} diff --git a/charts/postgres-operator/templates/operatorconfiguration.yaml b/charts/postgres-operator/templates/operatorconfiguration.yaml new file mode 100644 index 0000000..c160691 --- /dev/null +++ b/charts/postgres-operator/templates/operatorconfiguration.yaml @@ -0,0 +1,41 @@ +apiVersion: acid.zalan.do/v1 +kind: OperatorConfiguration +metadata: + name: {{ template "postgres-operator.fullname" . }} + labels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + helm.sh/chart: {{ template "postgres-operator.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} +configuration: +{{ toYaml .Values.configGeneral | indent 2 }} + users: +{{ toYaml .Values.configUsers | indent 4 }} + major_version_upgrade: +{{ toYaml .Values.configMajorVersionUpgrade | indent 4 }} + kubernetes: + {{- if .Values.podPriorityClassName }} + pod_priority_class_name: {{ .Values.podPriorityClassName }} + {{- end }} + pod_service_account_name: {{ include "postgres-pod.serviceAccountName" . }} + oauth_token_secret_name: {{ template "postgres-operator.fullname" . }} + pod_environment_configmap: "{{ .Release.Namespace }}/{{ template "postgres-operator.fullname" . }}-env" +{{ toYaml .Values.configKubernetes | indent 4 }} + postgres_pod_resources: +{{ toYaml .Values.configPostgresPodResources | indent 4 }} + timeouts: +{{ toYaml .Values.configTimeouts | indent 4 }} + load_balancer: +{{ toYaml .Values.configLoadBalancer | indent 4 }} + aws_or_gcp: +{{ toYaml .Values.configAwsOrGcp | indent 4 }} + logical_backup: +{{ toYaml .Values.configLogicalBackup | indent 4 }} + debug: +{{ toYaml .Values.configDebug | indent 4 }} + teams_api: +{{ toYaml .Values.configTeamsApi | indent 4 }} + logging_rest_api: +{{ toYaml .Values.configLoggingRestApi | indent 4 }} + connection_pooler: +{{ toYaml .Values.configConnectionPooler | indent 4 }} diff --git a/charts/postgres-operator/templates/pod-env-configmap.yaml b/charts/postgres-operator/templates/pod-env-configmap.yaml new file mode 100644 index 0000000..fbd0ec2 --- /dev/null +++ b/charts/postgres-operator/templates/pod-env-configmap.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + helm.sh/chart: {{ template "postgres-operator.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + name: {{ template "postgres-operator.fullname" . }}-env +data: +{{ toYaml .Values.pod_environment_config | indent 2 }} diff --git a/charts/postgres-operator/templates/postgres-pod-priority-class.yaml b/charts/postgres-operator/templates/postgres-pod-priority-class.yaml new file mode 100644 index 0000000..7ee0f2e --- /dev/null +++ b/charts/postgres-operator/templates/postgres-pod-priority-class.yaml @@ -0,0 +1,15 @@ +{{- if .Values.podPriorityClassName }} +apiVersion: scheduling.k8s.io/v1 +description: 'Use only for databases controlled by Postgres operator' +kind: PriorityClass +metadata: + labels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + helm.sh/chart: {{ template "postgres-operator.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + name: {{ .Values.podPriorityClassName }} +preemptionPolicy: PreemptLowerPriority +globalDefault: false +value: 1000000 +{{- end }} diff --git a/charts/postgres-operator/templates/service.yaml b/charts/postgres-operator/templates/service.yaml new file mode 100644 index 0000000..38ea9a0 --- /dev/null +++ b/charts/postgres-operator/templates/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + helm.sh/chart: {{ template "postgres-operator.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + name: {{ template "postgres-operator.fullname" . }} +spec: + type: ClusterIP + ports: + - port: 8080 + protocol: TCP + targetPort: 8080 + selector: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} diff --git a/charts/postgres-operator/templates/serviceaccount.yaml b/charts/postgres-operator/templates/serviceaccount.yaml new file mode 100644 index 0000000..e04e8ad --- /dev/null +++ b/charts/postgres-operator/templates/serviceaccount.yaml @@ -0,0 +1,11 @@ +{{ if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "postgres-operator.serviceAccountName" . }} + labels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + helm.sh/chart: {{ template "postgres-operator.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} +{{ end }} diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml new file mode 100644 index 0000000..bc4caf2 --- /dev/null +++ b/charts/postgres-operator/values.yaml @@ -0,0 +1,406 @@ +image: + registry: registry.opensource.zalan.do + repository: acid/postgres-operator + tag: v1.6.3 + pullPolicy: "IfNotPresent" + + # Optionally specify an array of imagePullSecrets. + # Secrets must be manually created in the namespace. + # ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + # imagePullSecrets: + # - name: myRegistryKeySecretName + +podAnnotations: {} +podLabels: {} + +# JSON logging format +enableJsonLogging: false + +# general configuration parameters +configGeneral: + # choose if deployment creates/updates CRDs with OpenAPIV3Validation + enable_crd_validation: true + # update only the statefulsets without immediately doing the rolling update + enable_lazy_spilo_upgrade: false + # set the PGVERSION env var instead of providing the version via postgresql.bin_dir in SPILO_CONFIGURATION + enable_pgversion_env_var: true + # start any new database pod without limitations on shm memory + enable_shm_volume: true + # enables backwards compatible path between Spilo 12 and Spilo 13 images + enable_spilo_wal_path_compat: 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-13:2.0-p7 + # min number of instances in Postgres cluster. -1 = no limit + min_instances: -1 + # max number of instances in Postgres cluster. -1 = no limit + max_instances: -1 + # period between consecutive repair requests + repair_period: 5m + # period between consecutive sync requests + resync_period: 30m + # can prevent certain cases of memory overcommitment + # set_memory_request_to_limit: false + + # map of sidecar names to docker images + # sidecar_docker_images: + # example: "exampleimage:exampletag" + + # number of routines the operator spawns to process requests concurrently + workers: 8 + +# parameters describing Postgres users +configUsers: + # postgres username used for replication between instances + replication_username: standby + # postgres superuser name to be created by initdb + super_username: postgres + +configMajorVersionUpgrade: + # "off": no upgrade, "manual": manifest triggers action, "full": minimal version violation triggers too + major_version_upgrade_mode: "off" + # minimal Postgres major version that will not automatically be upgraded + minimal_major_version: "9.5" + # target Postgres major version when upgrading clusters automatically + target_major_version: "13" + +configKubernetes: + # list of additional capabilities for postgres container + # additional_pod_capabilities: + # - "SYS_NICE" + + # default DNS domain of K8s cluster where operator is running + cluster_domain: cluster.local + # additional labels assigned to the cluster objects + cluster_labels: + application: spilo + # label assigned to Kubernetes objects created by the operator + cluster_name_label: cluster-name + # additional annotations to add to every database pod + # custom_pod_annotations: + # keya: valuea + # keyb: valueb + + # key name for annotation that compares manifest value with current date + # delete_annotation_date_key: "delete-date" + + # key name for annotation that compares manifest value with cluster name + # delete_annotation_name_key: "delete-clustername" + + # list of annotations propagated from cluster manifest to statefulset and deployment + # downscaler_annotations: + # - deployment-time + # - downscaler/* + + # enables initContainers to run actions before Spilo is started + enable_init_containers: true + # toggles pod anti affinity on the Postgres pods + enable_pod_antiaffinity: false + # toggles PDB to set to MinAvailabe 0 or 1 + enable_pod_disruption_budget: true + # enables sidecar containers to run alongside Spilo in the same pod + enable_sidecars: true + # namespaced name of the secret containing infrastructure roles names and passwords + # infrastructure_roles_secret_name: postgresql-infrastructure-roles + + # list of annotation keys that can be inherited from the cluster manifest + # inherited_annotations: + # - owned-by + + # list of label keys that can be inherited from the cluster manifest + # inherited_labels: + # - application + # - environment + + # timeout for successful migration of master pods from unschedulable node + # master_pod_move_timeout: 20m + + # set of labels that a running and active node should possess to be considered ready + # node_readiness_label: + # status: ready + + # namespaced name of the secret containing the OAuth2 token to pass to the teams API + # oauth_token_secret_name: postgresql-operator + + # defines the template for PDB (Pod Disruption Budget) names + pdb_name_format: "postgres-{cluster}-pdb" + # override topology key for pod anti affinity + pod_antiaffinity_topology_key: "kubernetes.io/hostname" + # name of the Secret (in cluster namespace) with environment variables to populate on every pod + # pod_environment_secret: "my-custom-secret" + + # specify the pod management policy of stateful sets of Postgres clusters + pod_management_policy: "ordered_ready" + # label assigned to the Postgres pods (and services/endpoints) + pod_role_label: spilo-role + # service account definition as JSON/YAML string to be used by postgres cluster pods + # pod_service_account_definition: "" + + # role binding definition as JSON/YAML string to be used by pod service account + # pod_service_account_role_binding_definition: "" + + # Postgres pods are terminated forcefully after this timeout + pod_terminate_grace_period: 5m + # template for database user secrets generated by the operator + secret_name_template: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}" + # set user and group for the spilo container (required to run Spilo as non-root process) + # spilo_runasuser: 101 + # spilo_runasgroup: 103 + # group ID with write-access to volumes (required to run Spilo as non-root process) + # spilo_fsgroup: 103 + + # whether the Spilo container should run in privileged mode + spilo_privileged: false + # whether the Spilo container should run with additional permissions other than parent. + # required by cron which needs setuid + spilo_allow_privilege_escalation: true + # storage resize strategy, available options are: ebs, pvc, off + storage_resize_mode: pvc + # operator watches for postgres objects in the given namespace + watched_namespace: "*" # listen to all namespaces + +# configure resource requests for the Postgres pods +configPostgresPodResources: + # CPU limits for the postgres containers + default_cpu_limit: "1" + # CPU request value for the postgres containers + default_cpu_request: 100m + # memory limits for the postgres containers + default_memory_limit: 500Mi + # memory request value for the postgres containers + default_memory_request: 100Mi + # hard CPU minimum required to properly run a Postgres cluster + min_cpu_limit: 250m + # hard memory minimum required to properly run a Postgres cluster + min_memory_limit: 250Mi + +# timeouts related to some operator actions +configTimeouts: + # timeout when waiting for the Postgres pods to be deleted + pod_deletion_wait_timeout: 10m + # timeout when waiting for pod role and cluster labels + pod_label_wait_timeout: 10m + # interval between consecutive attempts waiting for postgresql CRD to be created + ready_wait_interval: 3s + # timeout for the complete postgres CRD creation + ready_wait_timeout: 30s + # interval to wait between consecutive attempts to check for some K8s resources + resource_check_interval: 3s + # timeout when waiting for the presence of a certain K8s resource (e.g. Sts, PDB) + resource_check_timeout: 10m + +# configure behavior of load balancers +configLoadBalancer: + # DNS zone for cluster DNS name when load balancer is configured for cluster + db_hosted_zone: db.example.com + # annotations to apply to service when load balancing is enabled + # custom_service_annotations: + # keyx: valuez + # keya: valuea + + # toggles service type load balancer pointing to the master pod of the cluster + enable_master_load_balancer: false + # toggles service type load balancer pointing to the replica pod of the cluster + enable_replica_load_balancer: false + # define external traffic policy for the load balancer + external_traffic_policy: "Cluster" + # defines the DNS name string template for the master load balancer cluster + master_dns_name_format: "{cluster}.{team}.{hostedzone}" + # defines the DNS name string template for the replica load balancer cluster + replica_dns_name_format: "{cluster}-repl.{team}.{hostedzone}" + +# options to aid debugging of the operator itself +configDebug: + # toggles verbose debug logs from the operator + debug_logging: true + # toggles operator functionality that require access to the postgres database + enable_database_access: true + +# parameters affecting logging and REST API listener +configLoggingRestApi: + # REST API listener listens to this port + api_port: 8080 + # number of entries in the cluster history ring buffer + cluster_history_entries: 1000 + # number of lines in the ring buffer used to store cluster logs + ring_log_lines: 100 + +# configure interaction with non-Kubernetes objects from AWS or GCP +configAwsOrGcp: + # Additional Secret (aws or gcp credentials) to mount in the pod + # additional_secret_mount: "some-secret-name" + + # Path to mount the above Secret in the filesystem of the container(s) + # additional_secret_mount_path: "/some/dir" + + # AWS region used to store ESB volumes + aws_region: eu-central-1 + + # enable automatic migration on AWS from gp2 to gp3 volumes + enable_ebs_gp3_migration: false + # defines maximum volume size in GB until which auto migration happens + # enable_ebs_gp3_migration_max_size: 1000 + + # GCP credentials that will be used by the operator / pods + # gcp_credentials: "" + + # AWS IAM role to supply in the iam.amazonaws.com/role annotation of Postgres pods + # kube_iam_role: "" + + # S3 bucket to use for shipping postgres daily logs + # log_s3_bucket: "" + + # S3 bucket to use for shipping WAL segments with WAL-E + # wal_s3_bucket: "" + + # GCS bucket to use for shipping WAL segments with WAL-E + # wal_gs_bucket: "" + +# configure K8s cron job managed by the operator +configLogicalBackup: + # image for pods of the logical backup job (example runs pg_dumpall) + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" + # path of google cloud service account json file + # logical_backup_google_application_credentials: "" + + # prefix for the backup job name + logical_backup_job_prefix: "logical-backup-" + # storage provider - either "s3" or "gcs" + logical_backup_provider: "s3" + # S3 Access Key ID + logical_backup_s3_access_key_id: "" + # S3 bucket to store backup results + logical_backup_s3_bucket: "my-bucket-url" + # S3 region of bucket + logical_backup_s3_region: "" + # S3 endpoint url when not using AWS + logical_backup_s3_endpoint: "" + # S3 Secret Access Key + logical_backup_s3_secret_access_key: "" + # S3 server side encryption + logical_backup_s3_sse: "AES256" + # backup schedule in the cron format + logical_backup_schedule: "30 00 * * *" + +# automate creation of human users with teams API service +configTeamsApi: + # team_admin_role will have the rights to grant roles coming from PG manifests + enable_admin_role_for_users: true + # operator watches for PostgresTeam CRs to assign additional teams and members to clusters + enable_postgres_team_crd: false + # toogle to create additional superuser teams from PostgresTeam CRs + enable_postgres_team_crd_superusers: false + # toggle to automatically rename roles of former team members and deny LOGIN + enable_team_member_deprecation: false + # toggle to grant superuser to team members created from the Teams API + enable_team_superuser: false + # toggles usage of the Teams API by the operator + enable_teams_api: false + # should contain a URL to use for authentication (username and token) + # pam_configuration: https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees + + # operator will add all team member roles to this group and add a pg_hba line + pam_role_name: zalandos + # List of teams which members need the superuser role in each Postgres cluster + postgres_superuser_teams: + - postgres_superusers + # List of roles that cannot be overwritten by an application, team or infrastructure role + protected_role_names: + - admin + # Suffix to add if members are removed from TeamsAPI or PostgresTeam CRD + role_deletion_suffix: "_deleted" + # role name to grant to team members created from the Teams API + team_admin_role: admin + # postgres config parameters to apply to each team member role + team_api_role_configuration: + log_statement: all + # URL of the Teams API service + # teams_api_url: http://fake-teams-api.default.svc.cluster.local + +# configure connection pooler deployment created by the operator +configConnectionPooler: + # db schema to install lookup function into + connection_pooler_schema: "pooler" + # db user for pooler to use + connection_pooler_user: "pooler" + # docker image + connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-16" + # max db connections the pooler should hold + connection_pooler_max_db_connections: 60 + # default pooling mode + connection_pooler_mode: "transaction" + # number of pooler instances + connection_pooler_number_of_instances: 2 + # default resources + connection_pooler_default_cpu_request: 500m + connection_pooler_default_memory_request: 100Mi + connection_pooler_default_cpu_limit: "1" + connection_pooler_default_memory_limit: 100Mi + +rbac: + # Specifies whether RBAC resources should be created + create: true + +crd: + # Specifies whether custom resource definitions should be created + # When using helm3, this is ignored; instead use "--skip-crds" to skip. + create: true + +serviceAccount: + # Specifies whether a ServiceAccount should be created + create: true + # The name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the fullname template + name: + +podServiceAccount: + # The name of the ServiceAccount to be used by postgres cluster pods + # If not set a name is generated using the fullname template and "-pod" suffix + name: "postgres-pod" + +# priority class for operator pod +priorityClassName: "" + +# priority class for database pods +podPriorityClassName: "" + +resources: + limits: + cpu: 500m + memory: 500Mi + requests: + cpu: 100m + memory: 250Mi + +securityContext: + runAsUser: 1000 + runAsNonRoot: true + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + +# Affinity for pod assignment +# Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +affinity: {} + +# Node labels for pod assignment +# Ref: https://kubernetes.io/docs/user-guide/node-selection/ +nodeSelector: {} + +# Tolerations for pod assignment +# Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +tolerations: [] + +controllerID: + # Specifies whether a controller ID should be defined for the operator + # Note, all postgres manifest must then contain the following annotation to be found by this operator + # "acid.zalan.do/controller": + create: false + # The name of the controller ID to use. + # If not set and create is true, a name is generated using the fullname template + name: + + +pod_environment_config: {}