262 lines
6.0 KiB
Markdown
262 lines
6.0 KiB
Markdown
# ⚠️ PostgreSQL 不适合使用 KEDA 自动扩缩容
|
||
|
||
## 问题说明
|
||
|
||
对于传统的 PostgreSQL 架构,直接通过 KEDA 增加副本数会导致:
|
||
|
||
### 1. 存储冲突
|
||
- 多个 Pod 尝试挂载同一个 PVC
|
||
- ReadWriteOnce 存储只能被一个 Pod 使用
|
||
- 会导致 Pod 启动失败
|
||
|
||
### 2. 数据损坏风险
|
||
- 如果使用 ReadWriteMany 存储,多个实例同时写入会导致数据损坏
|
||
- PostgreSQL 不支持多主写入
|
||
- 没有锁机制保护数据一致性
|
||
|
||
### 3. 缺少主从复制
|
||
- 需要配置 PostgreSQL 流复制(Streaming Replication)
|
||
- 需要配置主从切换机制
|
||
- 需要使用专门的 PostgreSQL Operator
|
||
|
||
## 正确的 PostgreSQL 扩展方案
|
||
|
||
### 方案 1: 使用 PostgreSQL Operator
|
||
|
||
推荐使用专业的 PostgreSQL Operator:
|
||
|
||
#### Zalando PostgreSQL Operator
|
||
```bash
|
||
# 添加 Helm 仓库
|
||
helm repo add postgres-operator-charts https://opensource.zalando.com/postgres-operator/charts/postgres-operator
|
||
|
||
# 安装 Operator
|
||
helm install postgres-operator postgres-operator-charts/postgres-operator
|
||
|
||
# 创建 PostgreSQL 集群
|
||
apiVersion: "acid.zalan.do/v1"
|
||
kind: postgresql
|
||
metadata:
|
||
name: acid-minimal-cluster
|
||
spec:
|
||
teamId: "acid"
|
||
volume:
|
||
size: 10Gi
|
||
storageClass: longhorn
|
||
numberOfInstances: 3 # 1 主 + 2 从
|
||
users:
|
||
zalando:
|
||
- superuser
|
||
- createdb
|
||
databases:
|
||
foo: zalando
|
||
postgresql:
|
||
version: "16"
|
||
```
|
||
|
||
#### CloudNativePG Operator
|
||
```bash
|
||
# 安装 CloudNativePG
|
||
kubectl apply -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.22/releases/cnpg-1.22.0.yaml
|
||
|
||
# 创建集群
|
||
apiVersion: postgresql.cnpg.io/v1
|
||
kind: Cluster
|
||
metadata:
|
||
name: cluster-example
|
||
spec:
|
||
instances: 3
|
||
storage:
|
||
storageClass: longhorn
|
||
size: 10Gi
|
||
```
|
||
|
||
### 方案 2: 读写分离 + KEDA
|
||
|
||
如果需要使用 KEDA,正确的架构是:
|
||
|
||
```
|
||
┌─────────────────┐
|
||
│ 主库 (Master) │ ← 固定 1 个副本,处理写入
|
||
│ StatefulSet │
|
||
└─────────────────┘
|
||
│
|
||
│ 流复制
|
||
↓
|
||
┌─────────────────┐
|
||
│ 从库 (Replica) │ ← KEDA 管理,处理只读查询
|
||
│ Deployment │ 可以 0-N 个副本
|
||
└─────────────────┘
|
||
```
|
||
|
||
**配置示例:**
|
||
|
||
```yaml
|
||
# 主库 - 固定副本
|
||
apiVersion: apps/v1
|
||
kind: StatefulSet
|
||
metadata:
|
||
name: postgresql-master
|
||
spec:
|
||
replicas: 1 # 固定 1 个
|
||
# ... 配置主库
|
||
|
||
---
|
||
# 从库 - KEDA 管理
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
name: postgresql-replica
|
||
spec:
|
||
# replicas 由 KEDA 管理
|
||
# ... 配置从库(只读)
|
||
|
||
---
|
||
# KEDA ScaledObject - 只扩展从库
|
||
apiVersion: keda.sh/v1alpha1
|
||
kind: ScaledObject
|
||
metadata:
|
||
name: postgresql-replica-scaler
|
||
spec:
|
||
scaleTargetRef:
|
||
name: postgresql-replica # 只针对从库
|
||
minReplicaCount: 0
|
||
maxReplicaCount: 5
|
||
triggers:
|
||
- type: postgresql
|
||
metadata:
|
||
connectionString: postgresql://user:pass@postgresql-master:5432/db
|
||
query: "SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active' AND query NOT LIKE '%pg_stat_activity%'"
|
||
targetQueryValue: "10"
|
||
```
|
||
|
||
### 方案 3: 垂直扩展(推荐用于单实例)
|
||
|
||
对于单实例 PostgreSQL,使用 VPA (Vertical Pod Autoscaler) 更合适:
|
||
|
||
```yaml
|
||
apiVersion: autoscaling.k8s.io/v1
|
||
kind: VerticalPodAutoscaler
|
||
metadata:
|
||
name: postgresql-vpa
|
||
spec:
|
||
targetRef:
|
||
apiVersion: "apps/v1"
|
||
kind: StatefulSet
|
||
name: postgresql
|
||
updatePolicy:
|
||
updateMode: "Auto"
|
||
resourcePolicy:
|
||
containerPolicies:
|
||
- containerName: postgresql
|
||
minAllowed:
|
||
cpu: 250m
|
||
memory: 512Mi
|
||
maxAllowed:
|
||
cpu: 2000m
|
||
memory: 4Gi
|
||
```
|
||
|
||
## 当前部署建议
|
||
|
||
对于您当前的 PostgreSQL 部署(`/home/fei/k3s/010-中间件/002-postgresql/`):
|
||
|
||
### ❌ 不要使用 KEDA 水平扩展
|
||
- 当前是单实例 StatefulSet
|
||
- 没有配置主从复制
|
||
- 直接扩展会导致数据问题
|
||
|
||
### ✅ 推荐的优化方案
|
||
|
||
1. **保持单实例运行**
|
||
```yaml
|
||
replicas: 1 # 固定不变
|
||
```
|
||
|
||
2. **优化资源配置**
|
||
```yaml
|
||
resources:
|
||
requests:
|
||
cpu: 500m
|
||
memory: 1Gi
|
||
limits:
|
||
cpu: 2000m
|
||
memory: 4Gi
|
||
```
|
||
|
||
3. **配置连接池**
|
||
- 使用 PgBouncer 作为连接池
|
||
- PgBouncer 可以使用 KEDA 扩展
|
||
|
||
4. **定期备份**
|
||
- 使用 Longhorn 快照
|
||
- 备份到 S3
|
||
|
||
## PgBouncer + KEDA 方案
|
||
|
||
这是最实用的方案:PostgreSQL 保持单实例,PgBouncer 使用 KEDA 扩展。
|
||
|
||
```yaml
|
||
# PostgreSQL - 固定单实例
|
||
apiVersion: apps/v1
|
||
kind: StatefulSet
|
||
metadata:
|
||
name: postgresql
|
||
spec:
|
||
replicas: 1 # 固定
|
||
# ...
|
||
|
||
---
|
||
# PgBouncer - 连接池
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
name: pgbouncer
|
||
spec:
|
||
# replicas 由 KEDA 管理
|
||
template:
|
||
spec:
|
||
containers:
|
||
- name: pgbouncer
|
||
image: pgbouncer/pgbouncer:latest
|
||
# ...
|
||
|
||
---
|
||
# KEDA ScaledObject - 扩展 PgBouncer
|
||
apiVersion: keda.sh/v1alpha1
|
||
kind: ScaledObject
|
||
metadata:
|
||
name: pgbouncer-scaler
|
||
spec:
|
||
scaleTargetRef:
|
||
name: pgbouncer
|
||
minReplicaCount: 1
|
||
maxReplicaCount: 10
|
||
triggers:
|
||
- type: postgresql
|
||
metadata:
|
||
connectionString: postgresql://postgres:postgres123@postgresql:5432/postgres
|
||
query: "SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active'"
|
||
targetQueryValue: "20"
|
||
```
|
||
|
||
## 总结
|
||
|
||
| 方案 | 适用场景 | 复杂度 | 推荐度 |
|
||
|------|---------|--------|--------|
|
||
| PostgreSQL Operator | 生产环境,需要高可用 | 高 | ⭐⭐⭐⭐⭐ |
|
||
| 读写分离 + KEDA | 读多写少场景 | 中 | ⭐⭐⭐⭐ |
|
||
| PgBouncer + KEDA | 连接数波动大 | 低 | ⭐⭐⭐⭐⭐ |
|
||
| VPA 垂直扩展 | 单实例,资源需求变化 | 低 | ⭐⭐⭐ |
|
||
| 直接 KEDA 扩展 | ❌ 不适用 | - | ❌ |
|
||
|
||
**对于当前部署,建议保持 PostgreSQL 单实例运行,不使用 KEDA 扩展。**
|
||
|
||
如果需要扩展能力,优先考虑:
|
||
1. 部署 PgBouncer 连接池 + KEDA
|
||
2. 或者迁移到 PostgreSQL Operator
|
||
|
||
---
|
||
|
||
**重要提醒:有状态服务的扩展需要特殊处理,不能简单地增加副本数!** ⚠️
|