File size: 9,845 Bytes
e327f0d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# docker-compose.yml (repo root)
# ----------------------------------------------------------------------
# Local DEV environment for arac-hasar-v2.
#
# What it brings up:
#   - backend (FastAPI, hot-reload, mounts ./services/backend:/app)
#   - worker  (Celery, same image)
#   - postgres 16
#   - redis 7
#   - minio + minio-init (S3-mock, bucket auto-created)
#
# What it does NOT bring up (run with pnpm dev):
#   - apps/web      -> cd apps/web      && pnpm dev   (port 3000)
#   - apps/mobile   -> cd apps/mobile   && pnpm start
#   - apps/desktop  -> cd apps/desktop  && pnpm tauri dev
#
# Quick start:
#   docker compose up -d postgres redis minio minio-init
#   docker compose up backend worker        # foreground, see logs
#
# For the full GPU/CUDA compose with bundled web container,
# see services/backend/docker-compose.yml.
# ----------------------------------------------------------------------

name: arac-hasar-v2-dev

services:
  backend:
    build:
      context: ./services/backend
      dockerfile: Dockerfile
    image: hasarui-api:dev
    container_name: hasarui-backend
    # NOTE: --reload-exclude tames watchfiles churn from ML shim copies,
    # __pycache__ writes, training log spew, and visualization PNGs that
    # otherwise trigger a uvicorn restart loop in dev.
    command: ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port 8000 --reload --reload-dir /app --reload-exclude '*.log' --reload-exclude '*.png' --reload-exclude '*.jpg' --reload-exclude '__pycache__/*' --reload-exclude 'tests/*' --reload-exclude 'migrations/versions/*'"]
    ports:
      - "8000:8000"
    env_file:
      - ./services/backend/.env
    environment:
      # Override URLs to point to compose service names
      DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/arac_hasar"
      REDIS_URL: "redis://redis:6379/0"
      S3_ENDPOINT: "http://minio:9000"
      S3_ACCESS_KEY: "minioadmin"
      S3_SECRET_KEY: "minioadmin"
      S3_BUCKET: "inspections"
      S3_REGION: "us-east-1"
      ML_DEVICE: "cpu"
      # Entrypoint skips S3 model fetch — we mount weights as a volume.
      SKIP_MODEL_FETCH: "1"
      RUN_MIGRATIONS: "1"
      ENVIRONMENT: "development"
      # Web (3000), Tauri desktop dev (1420) + prod webview origins, Expo mobile (8081)
      CORS_ORIGINS: "http://localhost:3000,http://localhost:1420,http://tauri.localhost,tauri://localhost,http://localhost:8081"
      # Single-source ML modules: services/ml/ mounted read-only at /app/ml.
      # PYTHONPATH includes /app/ml so `from pipeline import DamagePipeline`
      # resolves to services/ml/pipeline.py without scripts/prepare-ml-shim.*
      # having to copy files into services/backend/. /app still takes
      # priority — backend-local override remains possible during a cutover.
      PYTHONPATH: "/app:/app/ml"
    volumes:
      # Hot-reload: source mounted on top of image
      - ./services/backend:/app
      # ML single-source mount: services/ml/ read-only at /app/ml.
      # Eliminates services/backend/pipeline.py drift vs services/ml/pipeline.py.
      # prepare-ml-shim.{sh,ps1} is now only needed for the PRODUCTION docker
      # build (CI bakes files in); dev runs straight off this mount.
      # NOT: pretrained/ alt klasoru runtime'da yazilabilir (model_manager
      # download bir kez .pt indirir, sonraki boot'larda cache hit). Kalan
      # services/ml/ icerigi kod ve egitim verisi — dev'de read-write OK.
      - ./services/ml:/app/ml
      # Local model weights for dev (point at your snapshot dir or symlink).
      # If this path is missing, run:
      #   $env:MODEL_SNAPSHOT_DIR = "<absolute path to latest snapshot>"
      # or edit the path below to the latest run under services/ml/runs/bundles/.
      - ${MODEL_SNAPSHOT_DIR:-./services/ml/runs/bundles/full_20260515_044630/_SNAPSHOT_FOR_BUILD}:/app/models:ro
    networks:
      - hasarui-net
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      minio-init:
        condition: service_completed_successfully
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    # Dev guardrail. Observed peak ~1.7 GiB (uvicorn 2 workers + YOLO ensemble
    # + cv2/numpy + torch CPU). Render starter (512 MB) is undersized — we
    # have to deploy on standard (2 GB) or trim to a single uvicorn worker
    # + lazy model load. NOT mirrored to render.yaml in this change.
    deploy:
      resources:
        limits:
          memory: 3G
          cpus: "4.0"
        reservations:
          memory: 1G
    restart: unless-stopped

  worker:
    build:
      context: ./services/backend
      dockerfile: Dockerfile
    image: hasarui-api:dev
    container_name: hasarui-worker
    command: ["sh", "-c", "celery -A worker.celery_app worker --loglevel=info --concurrency=1"]
    env_file:
      - ./services/backend/.env
    environment:
      DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/arac_hasar"
      REDIS_URL: "redis://redis:6379/0"
      S3_ENDPOINT: "http://minio:9000"
      S3_ACCESS_KEY: "minioadmin"
      S3_SECRET_KEY: "minioadmin"
      S3_BUCKET: "inspections"
      S3_REGION: "us-east-1"
      ML_DEVICE: "cpu"
      SKIP_MODEL_FETCH: "1"
      RUN_MIGRATIONS: "0"
      ENVIRONMENT: "development"
      CORS_ORIGINS: "http://localhost:3000,http://localhost:1420,http://tauri.localhost,tauri://localhost,http://localhost:8081"
      # Celery prefork spawn temizler sys.path '' entry'sini; explicit PYTHONPATH
      # zorunlu — yoksa worker `from pipeline import DamagePipeline` başarısız.
      # /app/ml eklendi: services/ml/ tek-kaynak modülleri (pipeline.py vb.)
      PYTHONPATH: "/app:/app/ml"
    volumes:
      - ./services/backend:/app
      # Worker da ML modüllerini aynı tek-kaynak mount'tan okur.
      # NOT: pretrained/ alt klasoru runtime'da yazilabilir (model_manager
      # download bir kez .pt indirir, sonraki boot'larda cache hit). Kalan
      # services/ml/ icerigi kod ve egitim verisi — dev'de read-write OK.
      - ./services/ml:/app/ml
      - ${MODEL_SNAPSHOT_DIR:-./services/ml/runs/bundles/full_20260515_044630/_SNAPSHOT_FOR_BUILD}:/app/models:ro
    networks:
      - hasarui-net
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      minio-init:
        condition: service_completed_successfully
      backend:
        condition: service_started
    # Override Dockerfile's HTTP /health check — worker has no HTTP server.
    # Use Celery's own broker ping (returns "pong" from each running worker).
    healthcheck:
      test: ["CMD-SHELL", "celery -A worker.celery_app inspect ping -d celery@$$HOSTNAME -t 5 || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    # Worker ensemble inference 12 foto için peak ~2 GiB gözlemlendi.
    # Concurrency=1 ile zaten serialize ediyoruz; 3 GiB tampon yeterli.
    deploy:
      resources:
        limits:
          memory: 3G
          cpus: "4.0"
        reservations:
          memory: 1G
    restart: unless-stopped

  postgres:
    image: postgres:16-alpine
    container_name: hasarui-postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: arac_hasar
    ports:
      - "5432:5432"
    volumes:
      - pg_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d arac_hasar"]
      interval: 5s
      timeout: 5s
      retries: 10
      start_period: 10s
    networks:
      - hasarui-net
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    container_name: hasarui-redis
    command: ["redis-server", "--appendonly", "yes", "--save", "60", "1"]
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
    networks:
      - hasarui-net
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    restart: unless-stopped

  minio:
    image: minio/minio:latest
    container_name: hasarui-minio
    command: ["server", "/data", "--console-address", ":9001"]
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    ports:
      - "9000:9000"   # S3 API
      - "9001:9001"   # Web console -> http://localhost:9001
    volumes:
      - minio_data:/data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    networks:
      - hasarui-net
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    restart: unless-stopped

  minio-init:
    image: minio/mc:latest
    container_name: hasarui-minio-init
    depends_on:
      minio:
        condition: service_healthy
    networks:
      - hasarui-net
    # Idempotent: --ignore-existing makes mb / anonymous safe to re-run.
    entrypoint:
      - /bin/sh
      - -c
      - |
        set -e
        until mc alias set local http://minio:9000 minioadmin minioadmin >/dev/null 2>&1; do
          echo "minio-init: waiting for minio..."
          sleep 1
        done
        mc mb --ignore-existing local/inspections
        mc mb --ignore-existing local/models
        mc anonymous set download local/inspections || true
        echo "MinIO buckets ready: inspections, models"
    restart: "no"

networks:
  hasarui-net:
    driver: bridge

volumes:
  pg_data:
  redis_data:
  minio_data: