betelgeusebytes/k8s/postgres/postgres-ha.yaml

276 lines
8.4 KiB
YAML

---
apiVersion: v1
kind: Namespace
metadata:
name: db
---
# Password secret (replace with your own or generate one)
apiVersion: v1
kind: Secret
metadata:
name: pg18-secret
namespace: db
type: Opaque
stringData:
POSTGRES_PASSWORD: "pa$$word"
---
# Init SQL: keeps your original name and keeps enabling PostGIS + vector
apiVersion: v1
kind: ConfigMap
metadata:
name: pg-init-sql
namespace: db
data:
00_extensions.sql: |
-- enable common extensions in the default DB and template1 so future DBs inherit them
\connect gitea
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS vector;
CREATE COLLATION IF NOT EXISTS arabic (provider = icu, locale = 'ar', deterministic = false);
CREATE EXTENSION IF NOT EXISTS tablefunc;
-- postpone pg_stat_statements CREATE to postStart (needs preload)
CREATE EXTENSION IF NOT EXISTS postgis_topology;
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS citext;
CREATE EXTENSION IF NOT EXISTS unaccent;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- PL/Python (available in your image)
DO $$ BEGIN
CREATE EXTENSION IF NOT EXISTS plpython3u;
EXCEPTION WHEN undefined_file THEN
RAISE NOTICE 'plpython3u not available in this image';
END $$;
-- Also on template1 for new DBs (heavier, but intentional)
\connect template1
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS citext;
CREATE EXTENSION IF NOT EXISTS unaccent;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Arabic-friendly ICU collation, non-deterministic for case/diacritics
DO $$
BEGIN
PERFORM 1 FROM pg_collation WHERE collname='arabic';
IF NOT FOUND THEN
CREATE COLLATION arabic (provider = icu, locale = 'ar', deterministic = false);
END IF;
END$$;
01_tune.sql: |
-- Enable pg_stat_statements on next server start
DO $$
DECLARE
cur text := current_setting('shared_preload_libraries', true);
BEGIN
IF cur IS NULL OR position('pg_stat_statements' in cur) = 0 THEN
PERFORM pg_catalog.pg_reload_conf(); -- harmless even if no changes yet
EXECUTE $$ALTER SYSTEM SET shared_preload_libraries =
$$ || quote_literal(coalesce(NULLIF(cur,'' ) || ',pg_stat_statements', 'pg_stat_statements'));
END IF;
END$$;
-- Optional tuning (adjust to your limits)
ALTER SYSTEM SET shared_buffers = '1GB';
ALTER SYSTEM SET work_mem = '32MB';
ALTER SYSTEM SET maintenance_work_mem = '512MB';
ALTER SYSTEM SET max_connections = 200;
-- Reload applies some settings immediately; others need restart (OK after init completes)
SELECT pg_reload_conf();
ALTER SYSTEM SET pg_stat_statements.max = 10000;
ALTER SYSTEM SET pg_stat_statements.track = 'all';
ALTER SYSTEM SET pg_stat_statements.save = on;
pg_hba.conf: |
# Allow loopback
local all all trust
host all all 127.0.0.1/32 trust
host all all ::1/128 trust
# Allow TLS connections from your IP(s) only
hostssl all all YOUR_PUBLIC_IP/32 md5
# (Optional) Add more CIDRs or a private network range here:
# hostssl all all 10.0.0.0/8 md5
---
# Headless service required by StatefulSet for stable network IDs
apiVersion: v1
kind: Service
metadata:
name: postgres-hl
namespace: db
spec:
clusterIP: None
selector:
app: postgres
ports:
- name: postgres
port: 5432
targetPort: 5432
---
# Regular ClusterIP service for clients (keeps your original name)
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: db
spec:
selector:
app: postgres
ports:
- name: postgres
port: 5432
targetPort: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: db
spec:
serviceName: postgres-hl
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
securityContext:
runAsUser: 999
runAsGroup: 999
fsGroup: 999
fsGroupChangePolicy: "Always"
initContainers:
# Copy cert-manager certs to a writable path with correct perms for Postgres
- name: install-certs
image: busybox:1.36
command:
- sh
- -c
- |
cp /in/tls.crt /out/server.crt
cp /in/tls.key /out/server.key
cp /in/ca.crt /out/ca.crt || true
chown 999:999 /out/* || true
chmod 600 /out/server.key
securityContext:
runAsUser: 0
volumeMounts:
- { name: pg-tls, mountPath: /in, readOnly: true }
- { name: pg-certs, mountPath: /out }
containers:
- name: postgres
image: axxs/postgres:18-postgis-vector
imagePullPolicy: IfNotPresent
args:
- -c
- ssl=on
- -c
- ssl_cert_file=/certs/server.crt
- -c
- ssl_key_file=/certs/server.key
- -c
- ssl_ca_file=/certs/ca.crt
- -c
- hba_file=/etc/postgresql-custom/pg_hba.conf
lifecycle:
postStart:
exec:
command:
- /bin/sh
- -c
- |
set -e
# Wait until server accepts connections
for i in $(seq 1 30); do
pg_isready -h 127.0.0.1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" && break
sleep 1
done
psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"
env:
- name: POSTGRES_USER
value: "app"
- name: POSTGRES_DB
value: "gitea" # matches your \connect gitea
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: pg18-secret
key: POSTGRES_PASSWORD
- name: TZ
value: "Europe/Paris"
ports:
- name: postgres
containerPort: 5432
volumeMounts:
# ✅ PG 18 requires this parent path; it will create /var/lib/postgresql/18/main
- name: data
mountPath: /var/lib/postgresql
# your init scripts ConfigMap
- name: init
mountPath: /docker-entrypoint-initdb.d
readOnly: true
- name: pg-certs
mountPath: /certs
# pg_hba.conf
- name: pg-conf
mountPath: /etc/postgresql-custom
readinessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB" -h 127.0.0.1
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
livenessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB" -h 127.0.0.1
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1"
memory: "2Gi"
volumes:
- name: init
configMap:
name: pg-init-sql
defaultMode: 0444
- name: pg-tls
secret:
secretName: pg-tls
- name: pg-certs
emptyDir: {}
- name: pg-conf
configMap:
name: pg-conf
defaultMode: 0444
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
# storageClassName: <your-storageclass> # optionally pin this