Compare commits
3 Commits
c1ef717d17
...
1c19bac8bc
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c19bac8bc | |||
| cec7fa25e3 | |||
| 774b907da8 |
@@ -14,6 +14,7 @@ JWT_SECRET=geo-web-Aa123456-change-me-in-production
|
||||
OPS_JWT_SECRET=geo-ops-Aa123456-change-me-in-production
|
||||
OPS_DEFAULT_ADMIN_PASSWORD=Aa123456
|
||||
SCHEDULER_INTERNAL_METRICS_TOKEN=geo-scheduler-Aa123456-change-me-in-production
|
||||
POSTGRES_PASSWORD=geo_dev
|
||||
|
||||
# ─── Optional: MinIO credentials ─────────────────────────────────────────────
|
||||
MINIO_ROOT_USER=admin
|
||||
|
||||
@@ -20,6 +20,8 @@ bash deploy.sh
|
||||
docker compose -f docker-compose.yaml -f docker-compose.offline.yaml --env-file .env up -d
|
||||
```
|
||||
|
||||
Runtime services connect to Postgres through PgBouncer in `session` pooling mode. The one-shot `migrate` job still connects directly to `postgres` and `monitoring-postgres` so DDL and seed work do not go through the pooler.
|
||||
|
||||
On a k3s host:
|
||||
|
||||
```bash
|
||||
|
||||
+8
-8
@@ -3,24 +3,24 @@ server:
|
||||
mode: release
|
||||
|
||||
database:
|
||||
host: postgres
|
||||
host: pgbouncer
|
||||
port: 5432
|
||||
user: geo
|
||||
password: geo_dev
|
||||
password: ${POSTGRES_PASSWORD:geo_dev}
|
||||
dbname: geo
|
||||
sslmode: disable
|
||||
max_open_conns: 25
|
||||
max_idle_conns: 5
|
||||
max_open_conns: 12
|
||||
max_idle_conns: 3
|
||||
|
||||
monitoring_database:
|
||||
host: monitoring-postgres
|
||||
host: monitoring-pgbouncer
|
||||
port: 5432
|
||||
user: geo
|
||||
password: geo_dev
|
||||
password: ${POSTGRES_PASSWORD:geo_dev}
|
||||
dbname: geo_monitoring
|
||||
sslmode: disable
|
||||
max_open_conns: 25
|
||||
max_idle_conns: 5
|
||||
max_open_conns: 8
|
||||
max_idle_conns: 2
|
||||
|
||||
rabbitmq:
|
||||
url: amqp://geo:geo_dev@rabbitmq:5672/geo
|
||||
|
||||
@@ -54,6 +54,12 @@ services:
|
||||
monitoring-postgres:
|
||||
pull_policy: never
|
||||
|
||||
pgbouncer:
|
||||
pull_policy: never
|
||||
|
||||
monitoring-pgbouncer:
|
||||
pull_policy: never
|
||||
|
||||
rabbitmq:
|
||||
pull_policy: never
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ name: geo-rankly
|
||||
|
||||
x-app-env: &app-env
|
||||
CONFIG_PATH: /app/configs/config.yaml
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-geo_dev}
|
||||
JWT_SECRET: ${JWT_SECRET:-geo-Aa123456-change-me-in-production}
|
||||
JWT_ACCESS_TTL: ${JWT_ACCESS_TTL:-15m}
|
||||
JWT_REFRESH_TTL: ${JWT_REFRESH_TTL:-720h}
|
||||
@@ -23,9 +24,9 @@ x-app-volumes: &app-volumes
|
||||
- ./prompts.yml:/app/configs/prompts.yml:ro
|
||||
|
||||
x-app-depends: &app-depends
|
||||
postgres:
|
||||
pgbouncer:
|
||||
condition: service_healthy
|
||||
monitoring-postgres:
|
||||
monitoring-pgbouncer:
|
||||
condition: service_healthy
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
@@ -49,7 +50,7 @@ services:
|
||||
environment:
|
||||
POSTGRES_DB: geo
|
||||
POSTGRES_USER: geo
|
||||
POSTGRES_PASSWORD: geo_dev
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-geo_dev}
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
@@ -65,7 +66,7 @@ services:
|
||||
environment:
|
||||
POSTGRES_DB: geo_monitoring
|
||||
POSTGRES_USER: geo
|
||||
POSTGRES_PASSWORD: geo_dev
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-geo_dev}
|
||||
volumes:
|
||||
- monitoring_pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
@@ -74,6 +75,72 @@ services:
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
pgbouncer:
|
||||
image: edoburu/pgbouncer:v1.24.1-p1
|
||||
container_name: geo-pgbouncer
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_USER: geo
|
||||
DB_PASSWORD: ${POSTGRES_PASSWORD:-geo_dev}
|
||||
DB_NAME: geo
|
||||
AUTH_TYPE: scram-sha-256
|
||||
POOL_MODE: session
|
||||
MAX_CLIENT_CONN: 200
|
||||
DEFAULT_POOL_SIZE: 30
|
||||
MIN_POOL_SIZE: 2
|
||||
RESERVE_POOL_SIZE: 5
|
||||
RESERVE_POOL_TIMEOUT: 5
|
||||
SERVER_IDLE_TIMEOUT: 60
|
||||
SERVER_LIFETIME: 1800
|
||||
SERVER_CONNECT_TIMEOUT: 15
|
||||
CLIENT_LOGIN_TIMEOUT: 15
|
||||
IGNORE_STARTUP_PARAMETERS: extra_float_digits
|
||||
ADMIN_USERS: geo
|
||||
STATS_USERS: geo
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "PGPASSWORD=\"$${DB_PASSWORD}\" psql -h 127.0.0.1 -p 5432 -U geo -d geo -c 'SELECT 1' >/dev/null"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
monitoring-pgbouncer:
|
||||
image: edoburu/pgbouncer:v1.24.1-p1
|
||||
container_name: geo-monitoring-pgbouncer
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
monitoring-postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DB_HOST: monitoring-postgres
|
||||
DB_PORT: 5432
|
||||
DB_USER: geo
|
||||
DB_PASSWORD: ${POSTGRES_PASSWORD:-geo_dev}
|
||||
DB_NAME: geo_monitoring
|
||||
AUTH_TYPE: scram-sha-256
|
||||
POOL_MODE: session
|
||||
MAX_CLIENT_CONN: 200
|
||||
DEFAULT_POOL_SIZE: 30
|
||||
MIN_POOL_SIZE: 2
|
||||
RESERVE_POOL_SIZE: 5
|
||||
RESERVE_POOL_TIMEOUT: 5
|
||||
SERVER_IDLE_TIMEOUT: 60
|
||||
SERVER_LIFETIME: 1800
|
||||
SERVER_CONNECT_TIMEOUT: 15
|
||||
CLIENT_LOGIN_TIMEOUT: 15
|
||||
IGNORE_STARTUP_PARAMETERS: extra_float_digits
|
||||
ADMIN_USERS: geo
|
||||
STATS_USERS: geo
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "PGPASSWORD=\"$${DB_PASSWORD}\" psql -h 127.0.0.1 -p 5432 -U geo -d geo_monitoring -c 'SELECT 1' >/dev/null"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.13-management-alpine
|
||||
container_name: geo-rabbitmq
|
||||
@@ -161,9 +228,10 @@ services:
|
||||
- ./prompts.yml:/app/configs/prompts.yml:ro
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
/usr/local/bin/migrate -path /migrations -database 'postgres://geo:geo_dev@postgres:5432/geo?sslmode=disable' up &&
|
||||
/usr/local/bin/migrate -path /migrations_monitoring -database 'postgres://geo:geo_dev@monitoring-postgres:5432/geo_monitoring?sslmode=disable' up &&
|
||||
/usr/local/bin/migrate -path /migrations_ops -database 'postgres://geo:geo_dev@postgres:5432/geo?sslmode=disable&x-migrations-table=schema_migrations_ops' up &&
|
||||
export PGPASSWORD='$${POSTGRES_PASSWORD:-geo_dev}' &&
|
||||
/usr/local/bin/migrate -path /migrations -database 'postgres://geo@postgres:5432/geo?sslmode=disable' up &&
|
||||
/usr/local/bin/migrate -path /migrations_monitoring -database 'postgres://geo@monitoring-postgres:5432/geo_monitoring?sslmode=disable' up &&
|
||||
/usr/local/bin/migrate -path /migrations_ops -database 'postgres://geo@postgres:5432/geo?sslmode=disable&x-migrations-table=schema_migrations_ops' up &&
|
||||
/usr/local/bin/seed-platform-templates
|
||||
"
|
||||
restart: "no"
|
||||
@@ -250,6 +318,8 @@ services:
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
CONFIG_PATH: /app/configs/ops-config.yaml
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-geo_dev}
|
||||
OPS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-geo_dev}
|
||||
OPS_JWT_SECRET: ${OPS_JWT_SECRET:-change-me-in-production}
|
||||
OPS_DEFAULT_ADMIN_USERNAME: ${OPS_DEFAULT_ADMIN_USERNAME:-admin}
|
||||
OPS_DEFAULT_ADMIN_PASSWORD: ${OPS_DEFAULT_ADMIN_PASSWORD:-}
|
||||
@@ -258,9 +328,9 @@ services:
|
||||
volumes:
|
||||
- ./ops-config.yaml:/app/configs/ops-config.yaml:ro
|
||||
depends_on:
|
||||
postgres:
|
||||
pgbouncer:
|
||||
condition: service_healthy
|
||||
monitoring-postgres:
|
||||
monitoring-pgbouncer:
|
||||
condition: service_healthy
|
||||
migrate:
|
||||
condition: service_completed_successfully
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
这套清单用于把当前 Docker Compose 拓扑搬到单节点或小集群 k3s 中。默认包含:
|
||||
|
||||
- infra: `postgres`, `monitoring-postgres`, `rabbitmq`, `redis`, `qdrant`, `minio`
|
||||
- infra: `postgres`, `monitoring-postgres`, `pgbouncer`, `monitoring-pgbouncer`, `rabbitmq`, `redis`, `qdrant`, `minio`
|
||||
- jobs: `migrate`, `minio-init`
|
||||
- apps: `tenant-api`, `worker-generate`, `kol-assist-worker`, `scheduler`, `ops-api`, `frontend`, `ops-web`
|
||||
- ingress: `geo-rankly.local` -> `frontend`, `ops.geo-rankly.local` -> `ops-web`
|
||||
@@ -38,7 +38,7 @@ sudo k3s ctr images import images.tar
|
||||
- `config/config.yaml` 里的 LLM / SiliconFlow API key
|
||||
- `config.local.yaml` 里对应的数据库、RabbitMQ、MinIO、JWT 配置
|
||||
|
||||
注意:`ops-api` 目前从 `deploy/k3s/config/ops-config.yaml` 读取数据库连接信息;如果改了 Postgres 密码,也同步改这个文件。
|
||||
注意:应用运行时通过 `pgbouncer` / `monitoring-pgbouncer` 访问数据库;`migrate` 任务仍然直连 `postgres` / `monitoring-postgres`。数据库密码统一来自 `POSTGRES_PASSWORD` secret,应用配置和 PgBouncer 都会跟随这个 secret。
|
||||
|
||||
## 3. 部署
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ spec:
|
||||
env:
|
||||
- name: CONFIG_PATH
|
||||
value: /app/configs/config.yaml
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: geo-rankly-app-secret
|
||||
key: POSTGRES_PASSWORD
|
||||
- name: TZ
|
||||
value: Asia/Shanghai
|
||||
- name: JWT_SECRET
|
||||
@@ -125,6 +130,11 @@ spec:
|
||||
env:
|
||||
- name: CONFIG_PATH
|
||||
value: /app/configs/config.yaml
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: geo-rankly-app-secret
|
||||
key: POSTGRES_PASSWORD
|
||||
- name: TZ
|
||||
value: Asia/Shanghai
|
||||
- name: JWT_SECRET
|
||||
@@ -193,6 +203,11 @@ spec:
|
||||
env:
|
||||
- name: CONFIG_PATH
|
||||
value: /app/configs/config.yaml
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: geo-rankly-app-secret
|
||||
key: POSTGRES_PASSWORD
|
||||
- name: TZ
|
||||
value: Asia/Shanghai
|
||||
- name: JWT_SECRET
|
||||
@@ -278,6 +293,11 @@ spec:
|
||||
env:
|
||||
- name: CONFIG_PATH
|
||||
value: /app/configs/config.yaml
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: geo-rankly-app-secret
|
||||
key: POSTGRES_PASSWORD
|
||||
- name: TZ
|
||||
value: Asia/Shanghai
|
||||
- name: SCHEDULER_HTTP_HOST
|
||||
@@ -384,6 +404,16 @@ spec:
|
||||
env:
|
||||
- name: CONFIG_PATH
|
||||
value: /app/configs/ops-config.yaml
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: geo-rankly-app-secret
|
||||
key: POSTGRES_PASSWORD
|
||||
- name: OPS_POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: geo-rankly-app-secret
|
||||
key: POSTGRES_PASSWORD
|
||||
- name: TZ
|
||||
value: Asia/Shanghai
|
||||
- name: OPS_JWT_SECRET
|
||||
|
||||
@@ -3,24 +3,24 @@ server:
|
||||
mode: release
|
||||
|
||||
database:
|
||||
host: postgres
|
||||
host: pgbouncer
|
||||
port: 5432
|
||||
user: geo
|
||||
password: geo_dev
|
||||
password: ${POSTGRES_PASSWORD:geo_dev}
|
||||
dbname: geo
|
||||
sslmode: disable
|
||||
max_open_conns: 25
|
||||
max_idle_conns: 5
|
||||
max_open_conns: 12
|
||||
max_idle_conns: 3
|
||||
|
||||
monitoring_database:
|
||||
host: monitoring-postgres
|
||||
host: monitoring-pgbouncer
|
||||
port: 5432
|
||||
user: geo
|
||||
password: geo_dev
|
||||
password: ${POSTGRES_PASSWORD:geo_dev}
|
||||
dbname: geo_monitoring
|
||||
sslmode: disable
|
||||
max_open_conns: 25
|
||||
max_idle_conns: 5
|
||||
max_open_conns: 8
|
||||
max_idle_conns: 2
|
||||
|
||||
rabbitmq:
|
||||
url: amqp://geo:geo_dev@rabbitmq:5672/geo
|
||||
|
||||
@@ -3,23 +3,23 @@ server:
|
||||
mode: release
|
||||
|
||||
database:
|
||||
host: postgres
|
||||
host: pgbouncer
|
||||
port: 5432
|
||||
user: geo
|
||||
password: geo_dev
|
||||
password: ${OPS_POSTGRES_PASSWORD:geo_dev}
|
||||
dbname: geo
|
||||
sslmode: disable
|
||||
max_open_conns: 10
|
||||
max_open_conns: 5
|
||||
max_idle_conns: 2
|
||||
|
||||
monitoring_database:
|
||||
host: monitoring-postgres
|
||||
host: monitoring-pgbouncer
|
||||
port: 5432
|
||||
user: geo
|
||||
password: geo_dev
|
||||
password: ${OPS_POSTGRES_PASSWORD:geo_dev}
|
||||
dbname: geo_monitoring
|
||||
sslmode: disable
|
||||
max_open_conns: 10
|
||||
max_open_conns: 5
|
||||
max_idle_conns: 2
|
||||
|
||||
redis:
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: pgbouncer
|
||||
labels:
|
||||
app.kubernetes.io/name: pgbouncer
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: pgbouncer
|
||||
ports:
|
||||
- name: postgres
|
||||
port: 5432
|
||||
targetPort: postgres
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pgbouncer
|
||||
labels:
|
||||
app.kubernetes.io/name: pgbouncer
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: pgbouncer
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: pgbouncer
|
||||
spec:
|
||||
containers:
|
||||
- name: pgbouncer
|
||||
image: edoburu/pgbouncer:v1.24.1-p1
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: postgres
|
||||
containerPort: 5432
|
||||
env:
|
||||
- name: DB_HOST
|
||||
value: postgres
|
||||
- name: DB_PORT
|
||||
value: "5432"
|
||||
- name: DB_USER
|
||||
value: geo
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: geo-rankly-app-secret
|
||||
key: POSTGRES_PASSWORD
|
||||
- name: DB_NAME
|
||||
value: geo
|
||||
- name: AUTH_TYPE
|
||||
value: scram-sha-256
|
||||
- name: POOL_MODE
|
||||
value: session
|
||||
- name: MAX_CLIENT_CONN
|
||||
value: "200"
|
||||
- name: DEFAULT_POOL_SIZE
|
||||
value: "30"
|
||||
- name: MIN_POOL_SIZE
|
||||
value: "2"
|
||||
- name: RESERVE_POOL_SIZE
|
||||
value: "5"
|
||||
- name: RESERVE_POOL_TIMEOUT
|
||||
value: "5"
|
||||
- name: SERVER_IDLE_TIMEOUT
|
||||
value: "60"
|
||||
- name: SERVER_LIFETIME
|
||||
value: "1800"
|
||||
- name: SERVER_CONNECT_TIMEOUT
|
||||
value: "15"
|
||||
- name: CLIENT_LOGIN_TIMEOUT
|
||||
value: "15"
|
||||
- name: IGNORE_STARTUP_PARAMETERS
|
||||
value: extra_float_digits
|
||||
- name: ADMIN_USERS
|
||||
value: geo
|
||||
- name: STATS_USERS
|
||||
value: geo
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
- PGPASSWORD="${DB_PASSWORD}" psql -h 127.0.0.1 -p 5432 -U geo -d geo -c 'SELECT 1' >/dev/null
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 5
|
||||
livenessProbe:
|
||||
exec:
|
||||
command: ["pg_isready", "-h", "127.0.0.1", "-p", "5432", "-U", "geo", "-d", "geo"]
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: monitoring-pgbouncer
|
||||
labels:
|
||||
app.kubernetes.io/name: monitoring-pgbouncer
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: monitoring-pgbouncer
|
||||
ports:
|
||||
- name: postgres
|
||||
port: 5432
|
||||
targetPort: postgres
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: monitoring-pgbouncer
|
||||
labels:
|
||||
app.kubernetes.io/name: monitoring-pgbouncer
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: monitoring-pgbouncer
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: monitoring-pgbouncer
|
||||
spec:
|
||||
containers:
|
||||
- name: pgbouncer
|
||||
image: edoburu/pgbouncer:v1.24.1-p1
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: postgres
|
||||
containerPort: 5432
|
||||
env:
|
||||
- name: DB_HOST
|
||||
value: monitoring-postgres
|
||||
- name: DB_PORT
|
||||
value: "5432"
|
||||
- name: DB_USER
|
||||
value: geo
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: geo-rankly-app-secret
|
||||
key: POSTGRES_PASSWORD
|
||||
- name: DB_NAME
|
||||
value: geo_monitoring
|
||||
- name: AUTH_TYPE
|
||||
value: scram-sha-256
|
||||
- name: POOL_MODE
|
||||
value: session
|
||||
- name: MAX_CLIENT_CONN
|
||||
value: "200"
|
||||
- name: DEFAULT_POOL_SIZE
|
||||
value: "30"
|
||||
- name: MIN_POOL_SIZE
|
||||
value: "2"
|
||||
- name: RESERVE_POOL_SIZE
|
||||
value: "5"
|
||||
- name: RESERVE_POOL_TIMEOUT
|
||||
value: "5"
|
||||
- name: SERVER_IDLE_TIMEOUT
|
||||
value: "60"
|
||||
- name: SERVER_LIFETIME
|
||||
value: "1800"
|
||||
- name: SERVER_CONNECT_TIMEOUT
|
||||
value: "15"
|
||||
- name: CLIENT_LOGIN_TIMEOUT
|
||||
value: "15"
|
||||
- name: IGNORE_STARTUP_PARAMETERS
|
||||
value: extra_float_digits
|
||||
- name: ADMIN_USERS
|
||||
value: geo
|
||||
- name: STATS_USERS
|
||||
value: geo
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
- PGPASSWORD="${DB_PASSWORD}" psql -h 127.0.0.1 -p 5432 -U geo -d geo_monitoring -c 'SELECT 1' >/dev/null
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 5
|
||||
livenessProbe:
|
||||
exec:
|
||||
command: ["pg_isready", "-h", "127.0.0.1", "-p", "5432", "-U", "geo", "-d", "geo_monitoring"]
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
@@ -70,9 +70,10 @@ spec:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
- |
|
||||
/usr/local/bin/migrate -path /migrations -database "postgres://geo:${POSTGRES_PASSWORD}@postgres:5432/geo?sslmode=disable" up
|
||||
/usr/local/bin/migrate -path /migrations_monitoring -database "postgres://geo:${POSTGRES_PASSWORD}@monitoring-postgres:5432/geo_monitoring?sslmode=disable" up
|
||||
/usr/local/bin/migrate -path /migrations_ops -database "postgres://geo:${POSTGRES_PASSWORD}@postgres:5432/geo?sslmode=disable&x-migrations-table=schema_migrations_ops" up
|
||||
export PGPASSWORD="${POSTGRES_PASSWORD}"
|
||||
/usr/local/bin/migrate -path /migrations -database "postgres://geo@postgres:5432/geo?sslmode=disable" up
|
||||
/usr/local/bin/migrate -path /migrations_monitoring -database "postgres://geo@monitoring-postgres:5432/geo_monitoring?sslmode=disable" up
|
||||
/usr/local/bin/migrate -path /migrations_ops -database "postgres://geo@postgres:5432/geo?sslmode=disable&x-migrations-table=schema_migrations_ops" up
|
||||
/usr/local/bin/seed-platform-templates
|
||||
volumes:
|
||||
- name: app-config
|
||||
|
||||
@@ -8,6 +8,7 @@ resources:
|
||||
- secrets.yaml
|
||||
- infra-postgres.yaml
|
||||
- infra-monitoring-postgres.yaml
|
||||
- infra-pgbouncer.yaml
|
||||
- infra-rabbitmq.yaml
|
||||
- infra-redis.yaml
|
||||
- infra-qdrant-minio.yaml
|
||||
|
||||
@@ -14,10 +14,10 @@ stringData:
|
||||
SCHEDULER_INTERNAL_METRICS_TOKEN: geo-metrics-token
|
||||
config.local.yaml: |
|
||||
database:
|
||||
password: geo_dev
|
||||
password: ${POSTGRES_PASSWORD:geo_dev}
|
||||
|
||||
monitoring_database:
|
||||
password: geo_dev
|
||||
password: ${POSTGRES_PASSWORD:geo_dev}
|
||||
|
||||
rabbitmq:
|
||||
url: amqp://geo:geo_dev@rabbitmq:5672/geo
|
||||
|
||||
@@ -3,23 +3,23 @@ server:
|
||||
mode: release
|
||||
|
||||
database:
|
||||
host: postgres
|
||||
host: pgbouncer
|
||||
port: 5432
|
||||
user: geo
|
||||
password: geo_dev
|
||||
password: ${OPS_POSTGRES_PASSWORD:geo_dev}
|
||||
dbname: geo
|
||||
sslmode: disable
|
||||
max_open_conns: 10
|
||||
max_open_conns: 5
|
||||
max_idle_conns: 2
|
||||
|
||||
monitoring_database:
|
||||
host: monitoring-postgres
|
||||
host: monitoring-pgbouncer
|
||||
port: 5432
|
||||
user: geo
|
||||
password: geo_dev
|
||||
password: ${OPS_POSTGRES_PASSWORD:geo_dev}
|
||||
dbname: geo_monitoring
|
||||
sslmode: disable
|
||||
max_open_conns: 10
|
||||
max_open_conns: 5
|
||||
max_idle_conns: 2
|
||||
|
||||
redis:
|
||||
|
||||
@@ -108,6 +108,7 @@ fi
|
||||
echo "==> [2/5] 拉取基础设施镜像 (platform: ${PLATFORM})"
|
||||
INFRA_IMAGES=(
|
||||
"postgres:16-alpine"
|
||||
"edoburu/pgbouncer:v1.24.1-p1"
|
||||
"rabbitmq:3.13-management-alpine"
|
||||
"redis:7-alpine"
|
||||
"qdrant/qdrant:v1.13.2"
|
||||
@@ -134,6 +135,7 @@ ALL_IMAGES=(
|
||||
"${LOCAL_PREFIX}/frontend:${IMAGE_TAG}"
|
||||
"${LOCAL_PREFIX}/ops-web:${IMAGE_TAG}"
|
||||
"postgres:16-alpine"
|
||||
"edoburu/pgbouncer:v1.24.1-p1"
|
||||
"rabbitmq:3.13-management-alpine"
|
||||
"redis:7-alpine"
|
||||
"qdrant/qdrant:v1.13.2"
|
||||
|
||||
@@ -236,3 +236,12 @@
|
||||
- Gitea Registry secret name is `REGISTRY_PASSWORD`; Gitea rejects secret names beginning with `GITEA_`.
|
||||
- CD workflows verify required commit8 image tags in Gitea Registry before pulling or deploying. Missing images are treated as a failed prerequisite, not as a signal to build during CD.
|
||||
- Offline packaging pulls previously published Gitea Registry images into the package; it does not rebuild business images when `SOURCE_IMAGE_REGISTRY` is set by the workflow.
|
||||
|
||||
## Stable PgBouncer Deployment - 2026-05-03
|
||||
- Before this change the deployment had no PgBouncer/pooler service. Runtime services used `pgxpool` and connected directly to `postgres` / `monitoring-postgres`.
|
||||
- Stable-first decision: add PgBouncer in `session` pooling mode, not transaction pooling, so current `pgx` behavior and any session-level assumptions remain compatible.
|
||||
- Runtime services now target `pgbouncer` and `monitoring-pgbouncer`; migration and seed jobs continue to connect directly to the underlying Postgres services.
|
||||
- Application-side pools are intentionally smaller: tenant runtime config uses 12 main DB connections and 8 monitoring DB connections per process; ops runtime uses 5 for each DB.
|
||||
- Offline packages must include `edoburu/pgbouncer:v1.24.1-p1` alongside the existing infra images.
|
||||
- Postgres passwords are passed through environment placeholders instead of hard-coded in deploy configs. The Go DSN builder now URL-escapes credentials so strong passwords containing `@`, `:`, `/`, or `?` remain valid.
|
||||
- Local smoke validation used the existing `server/docker-compose.yaml` Postgres containers on `server_default`; PgBouncer successfully returned `SELECT 1` through the pooler and the temporary test container was removed.
|
||||
|
||||
+17
@@ -706,3 +706,20 @@
|
||||
- Added commit8 image verification to compose/k3s/offline deployment paths so CD fails if CI has not produced the required image.
|
||||
- Changed offline package defaults to use commit8 instead of tag names and to pull business images from the Gitea Registry.
|
||||
- Verification passed: workflow YAML parse, deploy script `bash -n`, and `sh -n` over all workflow run blocks.
|
||||
|
||||
## 2026-05-03T03:03:09Z - Stable PgBouncer Deployment
|
||||
|
||||
- Confirmed the existing repository topology uses application-side `pgxpool` and direct Postgres service names; no PgBouncer service/config existed before this change.
|
||||
- Started Phase 40 to add stable PgBouncer deployment with session pooling, app runtime traffic through PgBouncer, and migration jobs remaining direct to Postgres.
|
||||
- Added `pgbouncer` and `monitoring-pgbouncer` to production Docker Compose and k3s using `edoburu/pgbouncer:v1.24.1-p1`, `POOL_MODE=session`, and `AUTH_TYPE=scram-sha-256`.
|
||||
- Changed runtime app configs to target PgBouncer while keeping Docker/k3s migration jobs direct to `postgres` / `monitoring-postgres`.
|
||||
- Lowered application-side Postgres pools: tenant runtime uses 12 main / 8 monitoring connections per process; ops runtime uses 5 / 5.
|
||||
- Made deployment passwords flow from `POSTGRES_PASSWORD` / `OPS_POSTGRES_PASSWORD` placeholders and changed `DatabaseConfig.DSN()` to URL-escape credentials for stronger production passwords.
|
||||
- Included PgBouncer in offline packaging and docs.
|
||||
- Verification passed:
|
||||
- `docker compose -f deploy/docker-compose.yaml --env-file deploy/.env.example config`
|
||||
- `kubectl kustomize deploy/k3s` plus local YAML parse
|
||||
- `bash -n deploy/scripts/package.sh`
|
||||
- `git diff --check`
|
||||
- `go test ./internal/shared/config ./internal/shared/repository/postgres ./internal/ops/config`
|
||||
- Temporary PgBouncer container attached to existing `server_default` network returned `SELECT 1` through PgBouncer, then was removed.
|
||||
|
||||
@@ -11,6 +11,9 @@ database:
|
||||
user: geo
|
||||
password: geo_dev
|
||||
dbname: geo
|
||||
sslmode: disable
|
||||
max_open_conns: 12
|
||||
max_idle_conns: 3
|
||||
|
||||
monitoring_database:
|
||||
host: localhost
|
||||
@@ -19,8 +22,8 @@ monitoring_database:
|
||||
password: geo_dev
|
||||
dbname: geo_monitoring
|
||||
sslmode: disable
|
||||
max_open_conns: 25
|
||||
max_idle_conns: 5
|
||||
max_open_conns: 8
|
||||
max_idle_conns: 2
|
||||
|
||||
rabbitmq:
|
||||
url: amqp://geo:geo_dev@localhost:5672/geo
|
||||
|
||||
@@ -9,8 +9,8 @@ database:
|
||||
password: geo_dev
|
||||
dbname: geo
|
||||
sslmode: disable
|
||||
max_open_conns: 25
|
||||
max_idle_conns: 5
|
||||
max_open_conns: 12
|
||||
max_idle_conns: 3
|
||||
|
||||
monitoring_database:
|
||||
host: localhost
|
||||
@@ -19,8 +19,8 @@ monitoring_database:
|
||||
password: geo_dev
|
||||
dbname: geo_monitoring
|
||||
sslmode: disable
|
||||
max_open_conns: 25
|
||||
max_idle_conns: 5
|
||||
max_open_conns: 8
|
||||
max_idle_conns: 2
|
||||
|
||||
rabbitmq:
|
||||
url: amqp://geo:geo_dev@localhost:5672/geo
|
||||
|
||||
@@ -9,7 +9,7 @@ database:
|
||||
password: geo_dev
|
||||
dbname: geo
|
||||
sslmode: disable
|
||||
max_open_conns: 10
|
||||
max_open_conns: 5
|
||||
max_idle_conns: 2
|
||||
|
||||
monitoring_database:
|
||||
@@ -19,7 +19,7 @@ monitoring_database:
|
||||
password: geo_dev
|
||||
dbname: geo_monitoring
|
||||
sslmode: disable
|
||||
max_open_conns: 10
|
||||
max_open_conns: 5
|
||||
max_idle_conns: 2
|
||||
|
||||
redis:
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -53,8 +55,16 @@ type DatabaseConfig struct {
|
||||
}
|
||||
|
||||
func (d DatabaseConfig) DSN() string {
|
||||
return fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s",
|
||||
d.User, d.Password, d.Host, d.Port, d.DBName, d.SSLMode)
|
||||
u := url.URL{
|
||||
Scheme: "postgres",
|
||||
User: url.UserPassword(d.User, d.Password),
|
||||
Host: net.JoinHostPort(d.Host, fmt.Sprintf("%d", d.Port)),
|
||||
Path: "/" + d.DBName,
|
||||
}
|
||||
q := u.Query()
|
||||
q.Set("sslmode", d.SSLMode)
|
||||
u.RawQuery = q.Encode()
|
||||
return u.String()
|
||||
}
|
||||
|
||||
type RedisConfig struct {
|
||||
|
||||
@@ -27,6 +27,23 @@ llm:
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabaseConfigDSNEscapesCredentials(t *testing.T) {
|
||||
cfg := DatabaseConfig{
|
||||
Host: "db.internal",
|
||||
Port: 5432,
|
||||
User: "geo",
|
||||
Password: "p@ss:word/with?symbols",
|
||||
DBName: "geo",
|
||||
SSLMode: "disable",
|
||||
}
|
||||
|
||||
got := cfg.DSN()
|
||||
want := "postgres://geo:p%40ss%3Aword%2Fwith%3Fsymbols@db.internal:5432/geo?sslmode=disable"
|
||||
if got != want {
|
||||
t.Fatalf("DSN() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFallsBackToConfigLLMAPIKey(t *testing.T) {
|
||||
t.Setenv("LLM_API_KEY", "")
|
||||
|
||||
|
||||
+10
-1
@@ -4,7 +4,7 @@
|
||||
Continue the desktop AI monitoring implementation by first adding a client-side local scheduler with durable queueing and adaptive execution policy, then adding a reusable hidden Playwright CDP execution layer that attaches to Electron Chromium and reuses existing account partitions.
|
||||
|
||||
## Current Phase
|
||||
Phase 37
|
||||
Phase 40
|
||||
|
||||
## Phases
|
||||
### Phase 1: Progress Verification
|
||||
@@ -276,6 +276,14 @@ Phase 37
|
||||
- [x] Run workflow YAML and shell syntax checks
|
||||
- **Status:** complete
|
||||
|
||||
### Phase 40: Stable PgBouncer Deployment
|
||||
- [x] Add PgBouncer infrastructure to Docker Compose and k3s
|
||||
- [x] Route runtime services through PgBouncer while keeping migrations direct to Postgres
|
||||
- [x] Lower application-side connection pools for stability
|
||||
- [x] Include PgBouncer in offline packaging and deployment docs
|
||||
- [x] Validate rendered deployment/config files
|
||||
- **Status:** complete
|
||||
|
||||
## Key Questions
|
||||
1. How should the desktop client defer and locally optimize monitor-task execution without violating one-task-per-platform serialism?
|
||||
2. What is the smallest durable local cache that allows same-day resume while safely dropping stale next-day monitor tasks?
|
||||
@@ -343,6 +351,7 @@ Phase 37
|
||||
| `connectOverCDP()` is not wired yet even though `playwright-core` is installed | 1 | Add a dedicated hidden-browser manager instead of pushing CDP code directly into adapters or the runtime controller |
|
||||
| `pnpm --filter @geo/desktop-client typecheck` fails in `electron.vite.config.ts` due to a pre-existing Vite 5/8 plugin type mismatch | 1 | Verified this change with `pnpm --filter @geo/desktop-client build` and `pnpm --filter @geo/desktop-client test`; leave config dependency alignment as a separate fix |
|
||||
| `kubectl apply --dry-run=client` still attempted to contact the local kube-apiserver, which is not running at `127.0.0.1:26443` | 1 | Verified with `kubectl kustomize`, parsed the rendered YAML locally, and recorded that live API validation should run on the target k3s cluster |
|
||||
| Production compose PgBouncer smoke test could not start bundled Postgres containers because existing `server/docker-compose.yaml` containers already own `geo-postgres` / `geo-monitoring-postgres` names | 1 | Did not stop user/local DB containers; verified PgBouncer with a temporary test container attached to the existing `server_default` network and removed it after `SELECT 1` passed |
|
||||
|
||||
## Notes
|
||||
- Re-check task_plan.md before major implementation decisions.
|
||||
|
||||
Reference in New Issue
Block a user