Files
MemeMeow/docker-compose.yml
2026-09-29 18:58:10 +08:00

274 lines
11 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
services:
postgres:
image: pgvector/pgvector:pg16
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:-mememeow}
POSTGRES_USER: ${POSTGRES_USER:-mememeow}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-mememeow}
ports:
- "127.0.0.1:5434:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 3s
timeout: 3s
retries: 20
volumes:
- mememeow-postgres-data:/var/lib/postgresql/data
db-init:
build:
context: .
depends_on:
postgres:
condition: service_healthy
environment:
MEMEMEOW_DATABASE_URL: ${MEMEMEOW_DATABASE_URL_CONTAINER:-postgresql+psycopg://mememeow:mememeow@postgres:5432/mememeow}
working_dir: /app
command: ["alembic", "upgrade", "head"]
runtime-init:
build:
context: .
user: "0:0"
restart: "no"
network_mode: none
environment:
MEMEMEOW_RUNTIME_UID: ${MEMEMEOW_RUNTIME_UID:?MEMEMEOW_RUNTIME_UID must be a non-root positive integer}
MEMEMEOW_RUNTIME_GID: ${MEMEMEOW_RUNTIME_GID:?MEMEMEOW_RUNTIME_GID must be a non-root positive integer}
working_dir: /app
command: ["python", "-m", "scripts.runtime_init", "--image-root", "/images", "--runtime-root", "/runtime", "--token-root", "/run/agent-executor-secret"]
volumes:
- type: bind
source: ${MEMEMEOW_IMAGE_ROOT_HOST:-./data/images}
target: /images
bind:
create_host_path: true
- mememeow-agent-runtime-data:/runtime
- mememeow-agent-executor-secret:/run/agent-executor-secret
db-viewer:
profiles: ["db-viewer"]
image: ${MEMEMEOW_DB_VIEWER_IMAGE:-adminer:4}
depends_on:
postgres:
condition: service_healthy
environment:
ADMINER_DEFAULT_SERVER: postgres
ports:
- "127.0.0.1:${MEMEMEOW_DB_VIEWER_PORT:-8080}:8080"
mememeow-visual:
build:
context: .
dockerfile: docker/visual/Dockerfile
restart: unless-stopped
# 视觉端口只加入 Compose 网络,不发布到宿主机。
expose:
- "8276"
networks:
- visual-backend
environment:
MEMEMEOW_VISUAL_MODEL: ${MEMEMEOW_VISUAL_MODEL:-dinov2_vitb14}
MEMEMEOW_VISUAL_DIMENSIONS: ${MEMEMEOW_VISUAL_DIMENSIONS:-768}
MEMEMEOW_VISUAL_PREPROCESS_VERSION: ${MEMEMEOW_VISUAL_PREPROCESS_VERSION:-dinov2_vitb14-rgb224-first-frame-v1}
MEMEMEOW_VISUAL_MODEL_REPO: /opt/dinov2
MEMEMEOW_VISUAL_WEIGHTS_PATH: /models/dinov2_vitb14_pretrain.pth
MEMEMEOW_VISUAL_WEIGHTS_SHA256: ${MEMEMEOW_VISUAL_WEIGHTS_SHA256:-}
MEMEMEOW_VISUAL_INTERNAL_TOKEN: ${MEMEMEOW_VISUAL_INTERNAL_TOKEN:-}
MEMEMEOW_VISUAL_CPU_THREADS: ${MEMEMEOW_VISUAL_CPU_THREADS:-4}
MEMEMEOW_VISUAL_CPU_INTEROP_THREADS: ${MEMEMEOW_VISUAL_CPU_INTEROP_THREADS:-1}
MEMEMEOW_VISUAL_MAX_PIXELS: ${MEMEMEOW_VISUAL_MAX_PIXELS:-25000000}
volumes:
- type: bind
source: ${MEMEMEOW_VISUAL_WEIGHTS_DIR:-./data/models}
target: /models
read_only: true
bind:
create_host_path: true
- type: bind
source: ${MEMEMEOW_IMAGE_ROOT_HOST:-./data/images}
target: /images
read_only: true
cpus: ${MEMEMEOW_VISUAL_CPU_QUOTA:-4.0}
mem_limit: ${MEMEMEOW_VISUAL_MEMORY_LIMIT:-8g}
healthcheck:
test: ["CMD-SHELL", "python -c 'import json,urllib.request; payload=json.load(urllib.request.urlopen(\"http://127.0.0.1:8276/health\", timeout=3)); raise SystemExit(0 if payload.get(\"status\") in (\"ok\", \"degraded\") else 1)' "]
interval: 15s
timeout: 5s
retries: 3
mememeow-agent-runtime:
build:
context: .
dockerfile: docker/agent/Dockerfile
depends_on:
runtime-init:
condition: service_completed_successfully
init: true
restart: unless-stopped
user: "${MEMEMEOW_RUNTIME_UID:?MEMEMEOW_RUNTIME_UID must be a non-root positive integer}:${MEMEMEOW_RUNTIME_GID:?MEMEMEOW_RUNTIME_GID must be a non-root positive integer}"
working_dir: /runtime/workspace
expose:
- "8277"
environment:
# token 首次由非 root executor 写入持久 named volume;API 只读同一文件。
MEMEMEOW_AGENT_EXECUTOR_TOKEN_FILE: /run/agent-executor-secret/token
HOME: /runtime/home
MEMEMEOW_AGENT_EXECUTOR_HOST: 0.0.0.0
MEMEMEOW_AGENT_EXECUTOR_PORT: ${MEMEMEOW_AGENT_EXECUTOR_PORT:-8277}
MEMEMEOW_AGENT_EXECUTOR_MAX_TIMEOUT_SECONDS: ${MEMEMEOW_AGENT_EXECUTOR_MAX_TIMEOUT_SECONDS:-1800}
MEMEMEOW_EXECUTOR_WORKSPACE_ROOT: /runtime/workspaces
MEMEMEOW_EXECUTOR_DIAGNOSTICS_ROOT: /runtime/executor-logs
MEMEMEOW_AGENT_DIAGNOSTICS_ROTATION: ${MEMEMEOW_AGENT_DIAGNOSTICS_ROTATION:-200 MB}
MEMEMEOW_WORKSPACE_CAPABILITY_KEY: ${MEMEMEOW_WORKSPACE_CAPABILITY_KEY:-}
MEMEMEOW_OPENCODE_EXECUTABLE: opencode
MEMEMEOW_OPENCODE_MODEL: ${MEMEMEOW_OPENCODE_MODEL:-}
MEMEMEOW_OPENCODE_BASE_URL: ${MEMEMEOW_OPENCODE_BASE_URL:-}
MEMEMEOW_OPENCODE_API_KEY: ${MEMEMEOW_OPENCODE_API_KEY:-}
MEMEMEOW_OPENCODE_CONCURRENCY: ${MEMEMEOW_OPENCODE_CONCURRENCY:-1}
MEMEMEOW_AGENT_RESULT_MAX_BYTES: ${MEMEMEOW_AGENT_RESULT_MAX_BYTES:-1048576}
MEMEMEOW_AGENT_REVERSE_IMAGE_INTERNAL_URL: http://mememeow:8275/internal/reverse-image/search
volumes:
# named volume 的 owner 从镜像初始化,不会被全新 checkout 的 bind mount 覆盖为 root。
- mememeow-agent-runtime-data:/runtime
- mememeow-agent-executor-secret:/run/agent-executor-secret
- type: bind
source: ${MEMEMEOW_IMAGE_ROOT_HOST:-./data/images}
target: /images
read_only: true
- type: bind
source: ./skills/research-meme-context
target: /skills/research-meme-context
read_only: true
- type: bind
source: ./skills/research-meme-context
target: /runtime/workspace/.opencode/skills/research-meme-context
read_only: true
networks:
- default
healthcheck:
test: ["CMD-SHELL", "test \"$$(id -u)\" != 0 && python3 -m executor.probe"]
interval: 5s
timeout: 5s
retries: 12
start_period: 5s
mememeow-auto-rename:
profiles: ["app"]
build:
context: .
depends_on:
postgres:
condition: service_healthy
db-init:
condition: service_completed_successfully
runtime-init:
condition: service_completed_successfully
user: "${MEMEMEOW_RUNTIME_UID:?MEMEMEOW_RUNTIME_UID must be a non-root positive integer}:${MEMEMEOW_RUNTIME_GID:?MEMEMEOW_RUNTIME_GID must be a non-root positive integer}"
init: true
restart: unless-stopped
stop_grace_period: 120s
command: ["python", "-m", "backend.naming_runtime"]
environment:
MEMEMEOW_DATABASE_URL: ${MEMEMEOW_DATABASE_URL_CONTAINER:-postgresql+psycopg://mememeow:mememeow@postgres:5432/mememeow}
MEMEMEOW_DATA_ROOT: /app/data/opencode
MEMEMEOW_IMAGE_ROOT: /app/data/images
EMBEDDING_MODEL: ${EMBEDDING_MODEL:-BAAI/bge-m3}
MEMEMEOW_AUTO_RENAME_BASE_URL: ${MEMEMEOW_AUTO_RENAME_BASE_URL:-}
MEMEMEOW_AUTO_RENAME_API_KEY: ${MEMEMEOW_AUTO_RENAME_API_KEY:-}
MEMEMEOW_AUTO_RENAME_MODEL: ${MEMEMEOW_AUTO_RENAME_MODEL:-mememeow/auto-rename}
MEMEMEOW_AUTO_RENAME_POOL: ${MEMEMEOW_AUTO_RENAME_POOL:-default}
MEMEMEOW_AUTO_RENAME_CONCURRENCY: ${MEMEMEOW_AUTO_RENAME_CONCURRENCY:-10}
MEMEMEOW_AUTO_RENAME_INTERVAL_SECONDS: ${MEMEMEOW_AUTO_RENAME_INTERVAL_SECONDS:-30}
MEMEMEOW_AUTO_RENAME_TIMEOUT_SECONDS: ${MEMEMEOW_AUTO_RENAME_TIMEOUT_SECONDS:-90}
volumes:
- mememeow-agent-runtime-data:/app/data/opencode
- type: bind
source: ${MEMEMEOW_IMAGE_ROOT_HOST:-./data/images}
target: /app/data/images
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
mememeow:
profiles: ["app"]
# API 需要等待 Agent 进程结束和恢复信息保存,依赖服务随后关闭。
stop_grace_period: 120s
depends_on:
postgres:
condition: service_healthy
db-init:
condition: service_completed_successfully
runtime-init:
condition: service_completed_successfully
mememeow-agent-runtime:
condition: service_healthy
mememeow-visual:
condition: service_healthy
build:
context: .
user: "${MEMEMEOW_RUNTIME_UID:?MEMEMEOW_RUNTIME_UID must be a non-root positive integer}:${MEMEMEOW_RUNTIME_GID:?MEMEMEOW_RUNTIME_GID must be a non-root positive integer}"
restart: unless-stopped
command: ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8275"]
ports:
- "127.0.0.1:8275:8275"
# 视觉权重缺失时 API 可以降级提供界面,因此探活只阻断 HTTP 未启动状态。
healthcheck:
test: ["CMD-SHELL", "test \"$$(id -u)\" != 0 && python -c 'import json,urllib.request; payload=json.load(urllib.request.urlopen(\"http://127.0.0.1:8275/health\", timeout=3)); raise SystemExit(0 if payload.get(\"status\") in (\"ok\", \"degraded\") else 1)' "]
interval: 10s
timeout: 5s
retries: 12
start_period: 15s
env_file:
- path: .env
required: false
environment:
MEMEMEOW_DATABASE_URL: ${MEMEMEOW_DATABASE_URL_CONTAINER:-postgresql+psycopg://mememeow:mememeow@postgres:5432/mememeow}
MEMEMEOW_DATA_ROOT: /app/data/opencode
MEMEMEOW_IMAGE_ROOT: /app/data/images
MEMEMEOW_OPENCODE_RUNTIME_ROOT: /app/data/opencode
MEMEMEOW_OPENCODE_WORKSPACE_ROOT: /app/data/opencode/workspaces
MEMEMEOW_OPENCODE_WORKSPACE_CAPABILITY_KEY: ${MEMEMEOW_WORKSPACE_CAPABILITY_KEY:-}
MEMEMEOW_OPENCODE_WORKSPACE_CAPABILITY_TTL_SECONDS: ${MEMEMEOW_OPENCODE_WORKSPACE_CAPABILITY_TTL_SECONDS:-300}
HOME: /app/data/opencode/home
MEMEMEOW_AGENT_RUNTIME_MODE: executor
MEMEMEOW_AGENT_EXECUTOR_URL: http://mememeow-agent-runtime:8277
MEMEMEOW_AGENT_EXECUTOR_TOKEN_FILE: /run/agent-executor-secret/token
MEMEMEOW_AGENT_EXECUTOR_REQUEST_TIMEOUT_SECONDS: ${MEMEMEOW_AGENT_EXECUTOR_REQUEST_TIMEOUT_SECONDS:-1810}
MEMEMEOW_AGENT_EXECUTOR_MAX_TIMEOUT_SECONDS: ${MEMEMEOW_AGENT_EXECUTOR_MAX_TIMEOUT_SECONDS:-1800}
MEMEMEOW_AGENT_CALLBACK_SECRET: ${MEMEMEOW_AGENT_CALLBACK_SECRET:-}
MEMEMEOW_AGENT_CALLBACK_VERIFICATION_KEYS: ${MEMEMEOW_AGENT_CALLBACK_VERIFICATION_KEYS:-}
MEMEMEOW_AGENT_REVERSE_IMAGE_INTERNAL_URL: http://mememeow:8275/internal/reverse-image/search
MEMEMEOW_REVERSE_IMAGE_INTERNAL_URL: http://mememeow:8275/internal/reverse-image/search
MEMEMEOW_VISUAL_INTERNAL_URL: http://mememeow-visual:8276/internal/visual-embedding
MEMEMEOW_VISUAL_HEALTH_URL: http://mememeow-visual:8276/health
MEMEMEOW_VISUAL_INTERNAL_TOKEN: ${MEMEMEOW_VISUAL_INTERNAL_TOKEN:-}
MEMEMEOW_OPENCODE_MODEL: ${MEMEMEOW_OPENCODE_MODEL:-}
MEMEMEOW_OPENCODE_BASE_URL: ${MEMEMEOW_OPENCODE_BASE_URL:-}
MEMEMEOW_OPENCODE_API_KEY: ${MEMEMEOW_OPENCODE_API_KEY:-}
volumes:
- mememeow-agent-runtime-data:/app/data/opencode
- type: volume
source: mememeow-agent-executor-secret
target: /run/agent-executor-secret
read_only: true
- type: bind
source: ${MEMEMEOW_IMAGE_ROOT_HOST:-./data/images}
target: /app/data/images
networks:
- default
- visual-backend
networks:
visual-backend:
internal: true
volumes:
mememeow-postgres-data:
mememeow-agent-runtime-data:
# token 不落盘到 checkout 或宿主环境,只存在 Docker named volume。
mememeow-agent-executor-secret: