From ddcfd9603b39d834b396b74face542a007cbf350 Mon Sep 17 00:00:00 2001 From: OG T Date: Tue, 5 May 2026 14:58:11 +0800 Subject: [PATCH] fix(ops): cap momo runtime startup load --- docker-compose.yml | 8 ++++++++ gunicorn.conf.py | 15 ++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 44d284b..b530041 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,6 +43,8 @@ services: image: ${MOMO_IMAGE:-registry.wooo.work/wooo/momo-pro-system}:${VERSION:-stable} container_name: momo-pro-system restart: unless-stopped + cpus: "2.0" + mem_limit: 2g labels: - "com.centurylinklabs.watchtower.enable=true" ports: @@ -188,6 +190,8 @@ services: image: ${MOMO_IMAGE:-registry.wooo.work/wooo/momo-pro-system}:${VERSION:-stable} container_name: momo-scheduler restart: unless-stopped + cpus: "2.0" + mem_limit: 2g labels: - "com.centurylinklabs.watchtower.enable=true" init: true # 使用 tini 作為 init 進程,自動回收僵屍進程 @@ -245,6 +249,8 @@ services: image: ${MOMO_IMAGE:-registry.wooo.work/wooo/momo-pro-system}:${VERSION:-stable} container_name: momo-telegram-bot restart: unless-stopped + cpus: "0.5" + mem_limit: 512m labels: - "com.centurylinklabs.watchtower.enable=true" init: true @@ -665,6 +671,8 @@ services: image: postgres:15-alpine container_name: momo-postgres restart: unless-stopped + cpus: "2.0" + mem_limit: 4g # ADR-011: 生產主機使用獨立的 momo-db(手動 docker run,非 compose 管理) # 此 service 僅供本地開發 / 一次性 migration 使用,預設不啟動。 # 啟用方式: docker compose --profile bundled-db up -d postgres diff --git a/gunicorn.conf.py b/gunicorn.conf.py index 8a8d5a5..ef99279 100644 --- a/gunicorn.conf.py +++ b/gunicorn.conf.py @@ -7,6 +7,7 @@ restart workers but keep the preloaded app object from the old master process. import os import sys +import fcntl import threading from sqlalchemy.engine import Engine @@ -93,15 +94,23 @@ def post_fork(server, worker): def post_worker_init(worker): - """Warm the expensive dashboard cache after each worker is ready.""" + """Warm the expensive dashboard cache once per container start.""" enabled = os.getenv("DASHBOARD_PREWARM_ON_WORKER_INIT", "1").lower() if enabled in {"0", "false", "no"}: return def _warm_dashboard_cache(): + lock_path = os.getenv("DASHBOARD_PREWARM_LOCK_PATH", "/tmp/momo-dashboard-prewarm.lock") try: - from routes.dashboard_routes import warm_full_dashboard_cache - warm_full_dashboard_cache(reason=f"gunicorn-worker-{worker.pid}") + with open(lock_path, "w", encoding="utf-8") as lock_file: + try: + fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError: + worker.log.info("Dashboard cache prewarm already running; worker %s skips", worker.pid) + return + + from routes.dashboard_routes import warm_full_dashboard_cache + warm_full_dashboard_cache(reason=f"gunicorn-worker-{worker.pid}") except Exception as exc: worker.log.warning("Dashboard cache prewarm failed in worker %s: %s", worker.pid, exc)