刘光辉
昨天 bb638871a7fb692d80f1b7a758f991dc0879002c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 多客户 PG 实例(docs/adr-multi-customer-db-isolation.md)。
#
# 客户1 继续用同目录的 docker-compose.tencent-pg.yml,本文件只服务 c2 及以后的客户
# + 产品母版 tpl——刻意不合并进那个文件:合并会改掉客户1 的 compose 项目名(jnpf-pg),
# 导致 docker compose 认为是新项目并重建它正在跑的容器。
#
# 库名与角色名【全客户一致】(jnpf_init / jnpf_flow / jnpf_xxljob / nacos_config,
# jnpf_app / jnpf_flow_app / nacos_app),客户之间只靠【端口】区分——
# 这是整个方案让 install.sh / create-app-roles.sh / backup.sh / promote-db.sh
# / harden-pg-hba.sh 零改动复用的支点。
#
# 用法(在 /home/ubuntu/jnpf-pg/ 下,.env 提供 JNPF_PG_PASSWORD):
#   CUSTOMER_ID=c2  CUSTOMER_PG_PORT=15433 docker compose -f postgres/tencent/docker-compose.customer-pg.yml up -d
#   CUSTOMER_ID=tpl CUSTOMER_PG_PORT=15439 docker compose -f postgres/tencent/docker-compose.customer-pg.yml up -d
# 一般不直接用,走 ./postgres/tencent/add-customer.sh <客户ID> <端口>。
#
# 端口约定:客户N = 15431+N(客户1=15432 已被既有实例占用),产品母版 = 15439。
name: jnpf-pg-${CUSTOMER_ID:?缺 CUSTOMER_ID,如 customer2 / tpl(新客户直接用拼音;c2 是客户B的历史 ID,卷名沿用)}
services:
  pg:
    # 容器名可与 CUSTOMER_ID 解耦(CUSTOMER_NAME 未设时=CUSTOMER_ID):
    # 客户B历史 CUSTOMER_ID=c2(项目名/卷名 jnpf_pgdata_c2 由它派生,改了会挂空卷、绝不能动),
    # 2026-08-08 起容器名规范为 cx-pg-<客户拼音>,.env.客户B 里设 CUSTOMER_NAME=customer2 即可。
    # 新客户 CUSTOMER_ID 直接用拼音,无需设 CUSTOMER_NAME。
    container_name: cx-pg-${CUSTOMER_NAME:-${CUSTOMER_ID}}
    # 与客户1 同镜像(cx/postgres:18.4,含 pg_bigm/pgvector)。刻意不写 build:
    # CVM 上该镜像已存在,新客户不该触发重新构建。
    image: cx/postgres:18.4
    platform: linux/amd64
    restart: unless-stopped
    shm_size: 256m
    # 客户1 的既有实例是 3g(400 连接 + 512MB shared_buffers);新实例按
    # ADR §5 的 150 连接 + 128MB shared_buffers 预算,1g 足够,6 个实例才 ~3G。
    mem_limit: 1g
    memswap_limit: 1g                 # CVM 无 swap,禁止溢出
    environment:
      TZ: Asia/Shanghai
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: ${JNPF_PG_PASSWORD:?缺 JNPF_PG_PASSWORD,先在 .env 设}
      POSTGRES_INITDB_ARGS: "--locale=C.UTF-8 --encoding=UTF8"
    volumes:
      - pgdata:/var/lib/postgresql
    ports:
      - "${CUSTOMER_PG_PORT:?缺 CUSTOMER_PG_PORT,客户N=15431+N,母版=15439}:5432"
    command:
      - postgres
      - -c
      # 每实例独算,不再抢客户1 那 400 槽的全局池。
      # 实测一套长跑全栈 45 连接、Druid 压到 5/1 后 ~25,150 可容 ~5 套栈。
      - max_connections=150
      - -c
      # 一整套库才 435MB 且实测 active 连接恒为 0(全是池空占),512MB 是浪费。
      # 客户1 的既有实例维持 512MB 不动。
      - shared_buffers=128MB
      - -c
      - shared_preload_libraries=pg_stat_statements,auto_explain
      - -c
      - pg_stat_statements.track=all
      - -c
      - timezone=Asia/Shanghai
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
      interval: 15s
      timeout: 10s
      retries: 10
      start_period: 60s
volumes:
  pgdata:
    # 显式命名,避免 compose 按项目名生成 jnpf-pg-c2_pgdata 这种不好辨认的卷名
    name: jnpf_pgdata_${CUSTOMER_ID}