Files
kolaytercih/Dockerfile
bilalgursen d41120c3ce fix(liste): üretim sonrası sunucu yanıt vermezse 502 sayfasına gidilmiyor
iPad raporu: liste üretildikten sonra ekran "Bad Gateway" gösterdi ve
tarayıcı `listem.txt` indirdi. Sıra şu: liste DB'ye yazıldı → `router.replace`
→ Next'in RSC isteği ters vekilden 502 aldı → Next tam-sayfa gezinmeye düştü
(WebKit düz metin cevabı dosya olarak kaydetti) → 6 sn'lik emniyet supabı
`window.location.replace` ile aynı 502'yi ekrana getirdi.

Üç değişiklik:

- Dockerfile runner katmanına `--max-old-space-size=768`. V8 cgroup sınırını
  görmez, heap üst sınırını host RAM'ine göre seçer; `mem_limit: 1g` ile
  birleşince V8 daha GC yapmadan konteyner OOM ile ölüyor ve açık istek 502'ye
  düşüyor. Sınır builder katmanında vardı, runner'a miras kalmıyordu.
- `/api/saglik`: DB/oturum/AI'ya dokunmayan en ucuz uç. Dockerfile HEALTHCHECK
  bunu yokluyor — süreç ölüp açılırken `docker ps` "unhealthy" gösterir, bir
  sonraki 502'nin kaynağı tahmine kalmaz.
- Emniyet supabı tam-sayfa gezinmeden önce sunucuyu yokluyor. Sunucu yoksa
  ekranda kalınıp "listen oluşturuldu ve kaydedildi, kredin yeniden harcanmaz"
  ekranı + sunucuyu yeniden yoklayan buton gösteriliyor; `liste_uretim_hata
  {kod:"SUNUCUYA_ULASILAMIYOR"}` olayı düşüyor. Sunucu yokken `router.refresh`
  de aynı tuzağa düştüğü için o dalda atlanıyor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 01:08:18 +03:00

101 lines
4.3 KiB
Docker
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.
# syntax=docker/dockerfile:1
FROM node:20-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable && corepack prepare pnpm@10.8.1 --activate
# ── Bağımlılık katmanı ──
FROM base AS deps
WORKDIR /app
# better-sqlite3 Alpine'de kaynaktan derleniyor (musl için prebuilt yok)
RUN apk add --no-cache python3 make g++
COPY package.json pnpm-lock.yaml ./
# pnpm store cache mount'ta yaşar: lockfile değişse bile inen paketler yeniden inmez
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
# ── Derleme katmanı ──
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
# Build girdileri açık listelenir: docs/, scripts/, CI dosyası değişiklikleri
# bu katmanı invalide edemez. next build'in okuduğu her şey burada:
# content/ (rehber.ts), data/ (db.ts prerender), public/ (opengraph-image).
COPY package.json next.config.ts tsconfig.json postcss.config.mjs ./
COPY scripts/standalone-linkleri-onar.sh ./scripts/
COPY public ./public
COPY data ./data
COPY content ./content
COPY src ./src
ENV NEXT_TELEMETRY_DISABLED=1
# V8 heap sınırı ana süreç + statik üretim worker'larına miras kalır.
# Turbopack'ın Rust tarafı bu sınırın dışında; onun hedefi next.config.ts'te
# turbopackMemoryLimit ile verilir (BUILD_KAYNAK_KISITLI bloğu).
ENV NODE_OPTIONS="--max-old-space-size=1024"
ENV BUILD_KAYNAK_KISITLI=1
RUN --mount=type=cache,id=next-cache,target=/app/.next/cache \
pnpm build
# Turbopack, ESM external'ların (@google/genai vb.) kök node_modules symlink'ini
# nft trace'ine yazmıyor; eksik linkler tamamlanmazsa runtime MODULE_NOT_FOUND
# ile düşer. Ayrıntı script'in başındaki açıklamada.
RUN sh scripts/standalone-linkleri-onar.sh .next/standalone
# ── Çalıştırma katmanı ──
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# V8, cgroup bellek sınırını GÖRMEZ: heap üst sınırını konteynerin değil host'un
# RAM'ine göre seçer (host 4 GB ise ~2 GB). docker-compose'daki mem_limit=1g ile
# birleşince sonuç, V8 daha GC yapmadan kernel'ın konteyneri OOM ile öldürmesi
# olur; o anda açık olan istek ters vekilde 502 "Bad Gateway" olarak düşer
# (22 Eyl 2026: liste üretiminin hemen ardından alınan 502 raporu). Sınır elle
# verilir: mem_limit'in ~%75'i; kalanı native taraf (better-sqlite3, mmap'lı
# SQLite sayfaları, Node'un kendi RSS'i) için ayrılır. mem_limit değişirse bu
# sayı da değişmeli.
ENV NODE_OPTIONS="--max-old-space-size=768"
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# standalone çıktı, file-tracing ile seçilmiş kendi minimal node_modules'unu
# içerir (serverExternalPackages dahil) — tam node_modules kopyalanmaz.
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
# Trace, data/yokatlas.db'yi de standalone'a koyuyor; runtime'da /app/data
# volume'ü bunu gölgelediği için imajdan atılır (~22M).
RUN rm -rf /app/data
# Seed veritabanları: entrypoint açılışta bunları /app/data volume'üne kopyalar
COPY data/ ./seed/
# Migration'lar: entrypoint açılışta db-goc.mjs ile volume'deki app.db'ye uygular
COPY drizzle ./drizzle
COPY scripts/db-goc.mjs ./scripts/db-goc.mjs
COPY scripts/docker-entrypoint.sh ./docker-entrypoint.sh
# SQLite veri dizini + Next'in runtime'da yazdığı image/fetch cache dizini
# (.next'in geri kalanı root'ta kalır; salt-okunur olması sorun değil)
RUN chmod +x /app/docker-entrypoint.sh && \
mkdir -p /app/data /app/.next/cache && \
chown -R nextjs:nodejs /app/data /app/seed /app/.next/cache
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# Konteyner gerçekten istek karşılıyor mu (alpine'de curl yok, Node 20'nin
# global fetch'i kullanılır). Uç bilerek en ucuz olanı: app/api/saglik DB'ye,
# oturuma, AI'ya dokunmaz. Süreç OOM ile ölüp yeniden açılırken `docker ps`
# durumu "unhealthy" gösterir — 502'lerin kaynağı tahmine kalmaz.
HEALTHCHECK --interval=30s --timeout=5s --start-period=45s --retries=3 \
CMD node -e "fetch('http://127.0.0.1:3000/api/saglik').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["/app/docker-entrypoint.sh"]