Files
homelab/project-usage/database-postgres.md
T

189 lines
4.8 KiB
Markdown
Raw Normal View History

# CloudNativePG PostgreSQL Database
**Host:** `ddb-cluster-rw.ddb.svc.cluster.local` (read-write)
**Read replica:** `ddb-cluster-ro.ddb.svc.cluster.local` (read-only)
**Port:** `5432`
**Namespace:** `ddb`
## When to Use
- **Multi-replica HA** — 3 replicas, automatic failover
- **pgvector extension** — Vector similarity search (LLM embeddings)
- **Transactional data** — Authentik, Story Crater backend, custom apps
- **Declarative backups** — Automated WAL archiving to MinIO
## Quick Start
**1. Connect from pod:**
```bash
# Inside a pod (inject secret mount)
psql -h ddb-cluster-rw.ddb.svc.cluster.local \
-U story_crater \
-d story_crater \
-W # prompt for password (from Secret)
```
**2. Create database & user (one-time):**
```bash
# Already done by helmfile postsync hook
# But if needed manually:
psql -h ddb-cluster-rw.ddb.svc.cluster.local \
-U postgres \
-c "CREATE DATABASE myapp OWNER postgres;"
psql -h ddb-cluster-rw.ddb.svc.cluster.local \
-U postgres \
-d myapp \
-c "CREATE USER myapp_user WITH PASSWORD 'secret';"
psql -h ddb-cluster-rw.ddb.svc.cluster.local \
-U postgres \
-d myapp \
-c "GRANT ALL PRIVILEGES ON DATABASE myapp TO myapp_user;"
```
**3. Enable pgvector:**
```bash
psql -h ddb-cluster-rw.ddb.svc.cluster.local \
-U postgres \
-d myapp \
-c "CREATE EXTENSION IF NOT EXISTS vector;"
```
**4. Create table with embeddings:**
```sql
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT,
embedding vector(1536), -- OpenAI embeddings
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX ON documents USING IVFFLAT (embedding vector_cosine_ops);
```
## Configuration
| Key | Value |
|-----|-------|
| Host (RW) | `ddb-cluster-rw.ddb.svc.cluster.local` |
| Host (RO) | `ddb-cluster-ro.ddb.svc.cluster.local` |
| Port | 5432 |
| Replicas | 3 (automatic failover) |
| Extensions | pgvector (LLM embeddings), uuid-ossp |
| Backups | WAL archiving to MinIO (continuous) |
| Retention | 30 days |
## Common Patterns
**Connection pooling (from app):**
```go
import "github.com/jackc/pgx/v5/pgxpool"
config, _ := pgxpool.ParseConfig("postgres://user:[email protected]:5432/myapp")
config.MaxConns = 25
config.MinConns = 5
pool, _ := pgxpool.NewWithConfig(ctx, config)
// Use pool
row := pool.QueryRow(ctx, "SELECT COUNT(*) FROM users")
```
**Read from replica (analytics):**
```go
// Offload SELECT queries to read replica
pool.QueryRow(ctx, "SELECT * FROM documents LIMIT 1") // auto-routes to RO if available
// Writes always go to RW
pool.Exec(ctx, "INSERT INTO documents ...")
```
**Vector similarity search:**
```sql
SELECT id, content, embedding <-> $1 AS distance
FROM documents
ORDER BY embedding <-> $1
LIMIT 10;
-- $1 = query embedding (e.g., from OpenAI API)
```
**Backup & restore:**
```bash
# Backups are automatic (WAL to MinIO)
# To restore from backup:
# 1. Check MinIO s3://postgresql-backups/
# 2. Use PostgreSQL PITR (point-in-time recovery)
# 3. Contact SRE for restore procedure
```
## Monitoring
**Grafana dashboard:** `svc-postgresql` (auto-configured)
**Key metrics:**
- `pg_stat_activity_connections` — active connections
- `pg_stat_database_blks_read` — disk I/O
- `pg_replication_lag_seconds` — replica lag (goal: < 1s)
**CLI health check:**
```bash
# Check replication status
kubectl exec -n ddb pod/ddb-cluster-1 -- \
psql -U postgres -c "SELECT slot_name, restart_lsn FROM pg_replication_slots;"
# Check replica lag
kubectl exec -n ddb pod/ddb-cluster-2 -- \
psql -U postgres -c "SELECT now() - pg_last_xact_replay_timestamp() AS lag;"
```
## Secrets & Credentials
**All user passwords stored in Vault:**
```bash
# Read password
talos get cluster/STORY_CRATER_PG_PASSWORD --key STORY_CRATER_PG_PASSWORD
# Inject into pod (auto via Secret volume)
# Mount: /run/secrets/db-password
```
**Connection string from env:**
```bash
POSTGRES_CONNECTION="postgres://story_crater:${STORY_CRATER_PG_PASSWORD}@ddb-cluster-rw.ddb.svc.cluster.local:5432/story_crater"
```
## Troubleshooting
**Cannot connect (connection refused):**
```bash
# Verify cluster is running
k get pods -n ddb
# Check Service DNS
k exec -it pod/debug-pod -- nslookup ddb-cluster-rw.ddb.svc.cluster.local
# Verify Secret has password
k get secret -n ddb ddb-cluster-superuser -o jsonpath='{.data.password}' | base64 -d
```
**Replica lag is high (> 10s):**
```bash
# Check replica pod CPU/memory
k top pod -n ddb
# Scale down other workloads if cluster is overloaded
# Or scale up database resources (helmfile.yaml.gotmpl)
```
**pgvector queries slow:**
```sql
-- Ensure index exists
SELECT * FROM pg_indexes WHERE tablename = 'documents' AND indexname LIKE '%embedding%';
-- Re-index if missing
CREATE INDEX ON documents USING IVFFLAT (embedding vector_cosine_ops);
```
See `/TROUBLESHOOTING.md` for full incident guide.