File size: 5,129 Bytes
eb60b6b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# BrainAPI production profile: light (PostgreSQL/pgvector + Redis).

x-brainapi-common: &brainapi-common
  image: ${BRAINAPI_IMAGE:?BRAINAPI_IMAGE is required}
  env_file:
    - ${BRAINAPI_ENV_FILE:?BRAINAPI_ENV_FILE is required}
  environment: &brainapi-environment
    ENV: production
    PLUGIN_FAILURE_POLICY: ${PLUGIN_FAILURE_POLICY:-fail}
    REDIS_HOST: redis
    REDIS_PORT: "6379"
    REDIS_PASSWORD: ${REDIS_PASSWORD:?REDIS_PASSWORD is required}
    POSTGRES_HOST: postgres
    POSTGRES_PORT: "5432"
    POSTGRES_USERNAME: ${POSTGRES_USERNAME:-brainapi}
    POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
    POSTGRES_SYSTEM_DATABASE: ${POSTGRES_SYSTEM_DATABASE:-brainapi}
    POSTGRES_MAINTENANCE_DATABASE: postgres
    BRAINAPI_PLUGINS: ${BRAINAPI_PLUGINS:-}
    PLUGIN_REGISTRY_URL: ${PLUGIN_REGISTRY_URL:-}
    PLUGIN_PUBLISHER_ID: ${PLUGIN_PUBLISHER_ID:-}
    PLUGIN_PUBLISHER_API_KEY: ${PLUGIN_PUBLISHER_API_KEY:-}
  volumes:
    - brainapi-plugins:/app/plugins
  networks: [appnet]
  restart: unless-stopped

services:
  nginx:
    image: nginx:1.28.2-alpine3.23
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
    depends_on:
      brainapi:
        condition: service_healthy
      brainapi-mcp:
        condition: service_healthy
    networks: [appnet]
    restart: unless-stopped
    read_only: true
    tmpfs: [/var/cache/nginx, /var/run, /tmp]
    security_opt: [no-new-privileges:true]
    mem_limit: 64m
    cpus: 0.10
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost/health"]
      interval: 15s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7.4.7-alpine3.21
    environment:
      REDIS_PASSWORD: ${REDIS_PASSWORD:?REDIS_PASSWORD is required}
    command: [redis-server, --requirepass, "${REDIS_PASSWORD:?REDIS_PASSWORD is required}", --appendonly, "yes", --maxmemory, 192mb, --maxmemory-policy, allkeys-lru, --save, "900 1", --save, "300 10", --save, "60 10000"]
    volumes: [redis-data:/data]
    networks: [appnet]
    restart: unless-stopped
    security_opt: [no-new-privileges:true]
    mem_limit: 256m
    cpus: 0.10
    healthcheck:
      test: ["CMD-SHELL", "redis-cli -a \"$${REDIS_PASSWORD}\" ping 2>/dev/null | grep -q PONG"]
      interval: 10s
      timeout: 3s
      retries: 5

  postgres:
    image: pgvector/pgvector:0.8.1-pg16-bookworm
    environment:
      POSTGRES_USER: ${POSTGRES_USERNAME:-brainapi}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
      POSTGRES_DB: ${POSTGRES_SYSTEM_DATABASE:-brainapi}
    volumes: [postgres-data:/var/lib/postgresql/data]
    networks: [appnet]
    restart: unless-stopped
    security_opt: [no-new-privileges:true]
    mem_limit: 768m
    cpus: 0.35
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
      interval: 5s
      timeout: 5s
      retries: 12

  brainapi:
    <<: *brainapi-common
    command: [-m, uvicorn, src.services.api.app:app, --host, 0.0.0.0, --port, "8000", --workers, "1", --timeout-keep-alive, "30", --limit-concurrency, "50"]
    depends_on:
      redis: {condition: service_healthy}
      postgres: {condition: service_healthy}
    mem_limit: 768m
    cpus: 0.50
    security_opt: [no-new-privileges:true]
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:8000/health"]
      interval: 20s
      timeout: 5s
      retries: 8
      start_period: 60s

  brainapi-worker:
    <<: *brainapi-common
    command: [-m, celery, -A, src.workers.app, worker, --loglevel=info, --pool=threads, --without-gossip, --without-mingle, --concurrency=1, --max-tasks-per-child=50]
    depends_on:
      redis: {condition: service_healthy}
      postgres: {condition: service_healthy}
    mem_limit: 768m
    cpus: 0.50
    security_opt: [no-new-privileges:true]
    healthcheck:
      test:
        - CMD-SHELL
        - >-
          python -m celery -A src.workers.app inspect ping --timeout 5 >/dev/null 2>&1 &&
          python -c 'import os,redis,sys; r=redis.Redis(host="redis",password=os.environ["REDIS_PASSWORD"]); sys.exit(r.llen("ingest_data") >= int(os.getenv("CELERY_QUEUE_HEALTH_THRESHOLD","50")))'
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 90s

  brainapi-mcp:
    <<: *brainapi-common
    environment:
      <<: *brainapi-environment
      MCP_OAUTH_ISSUER_URL: ${MCP_OAUTH_ISSUER_URL:-}
      MCP_RESOURCE_SERVER_URL: ${MCP_RESOURCE_SERVER_URL:-}
    command: [-m, uvicorn, src.services.mcp.app:app, --host, 0.0.0.0, --port, "8001", --timeout-keep-alive, "30"]
    depends_on:
      redis: {condition: service_healthy}
      postgres: {condition: service_healthy}
    mem_limit: 768m
    cpus: 0.50
    security_opt: [no-new-privileges:true]
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:8001/"]
      interval: 20s
      timeout: 5s
      retries: 5
      start_period: 45s

networks:
  appnet:
    driver: bridge

volumes:
  redis-data:
  postgres-data:
  brainapi-plugins: