o2o-site-AEO/solution/backend/Dockerfile.worker
Mina Choi f409c11624 feat(solution): 발행 워커·버전 관리와 예약·미리보기 정리
상시 프리렌더와 중복 예약 안내를 없애고, 검수된 발행 버전을 보존한다. 미리보기는 실제 렌더 완료까지 스피너를 표시한다.

사이트 81건, 발행·롤백·서치콘솔 45건, 프로세스 수명 3건 통과. 빌더·사이트 빌드 및 compose 설정 검증 통과.
2026-09-15 16:10:12 +09:00

71 lines
4.4 KiB
Docker

# 워커 전용 이미지 — solution-backend(:9800)·admin-backend(:9801) 와 **다른 이미지**다.
#
# ★ 왜 갈랐나 (프리렌더를 워커 실행으로 통합, 2026-09-15)
# BUILD·ROLLBACK 잡은 이제 컴파일된 Node 렌더러(solution/site)를 subprocess 로 직접
# 돌린다(services/render_service.py) — 예전에 별도 컨테이너(solution-prerender)가 하던
# 일이다. 그 렌더러가 필요한 건 **워커뿐**이다. Dockerfile(api/admin 겸용)에 Node 를
# 같이 넣으면 API 를 여는 요청만 받는 컨테이너에도 쓰지 않는 Node 런타임과 렌더러
# 번들이 실려 나간다 — 이미지가 커지고 공격 표면도 는다.
#
# ★ 기동·잡 실행 중에 npm install 을 하지 않는다. site-builder 스테이지가 이미지 빌드
# 시점에 한 번 굽고, 최종 이미지는 컴파일된 산출물만 COPY 한다.
# ★ node_modules 는 안 들어간다 — solution/site/vite.config.ts 가 SSR 번들에
# `ssr.noExternal: true` 로 react 등 의존성까지 전부 접어 넣는다(실측: node_modules 를
# 지우고 실행해도 그대로 돈다, dist/prerender/prerender.js 단일 파일 1.8MB).
#
# ★ 빌드 컨텍스트는 solution-backend 와 같은 **레포 루트**다 — solution/site 와
# solution/backend 를 한 이미지에 담아야 한다.
# ── 1단계: 렌더러 컴파일 (Node) ──────────────────────────────────────────────
FROM node:24-bookworm AS site-builder
WORKDIR /app
COPY package.json package-lock.json tsconfig.base.json ./
COPY solution/shared/package.json solution/shared/
COPY solution/frontend/package.json solution/frontend/
COPY solution/site/package.json solution/site/
COPY admin/frontend/package.json admin/frontend/
# ★ npm workspaces 는 루트 package.json 의 workspaces 목록에 있는 자리마다 package.json 을
# 요구한다 — site 만 빌드해도 4개를 전부 COPY 해야 npm ci 가 성공한다(nginx/Dockerfile 과 같다).
RUN npm ci
COPY solution/shared ./solution/shared
COPY solution/site ./solution/site
RUN npm run build:client -w @o2o/site \
&& npm run build:prerender -w @o2o/site
# ── 2단계: Node 런타임만 (바이너리를 뽑아 쓴다) ──────────────────────────────
# ★ python:3.12-slim 과 같은 계열(Debian bookworm)의 태그를 써야 한다 — glibc ABI 가
# 어긋나면 바이너리만 복사해서는 못 돈다("cannot execute binary file" 류로 죽는다).
FROM node:24-bookworm-slim AS node-runtime
# ── 3단계: 최종 워커 이미지 ───────────────────────────────────────────────
FROM python:3.12-slim-bookworm
WORKDIR /app
COPY solution/backend/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# ★ yanolja_adapter.py 가 Playwright 로 페이지를 렌더링한다(수집 파이프라인, 워커 전용 경로) —
# 패키지(pip)만으로는 브라우저 실행 파일이 없다. --with-deps 가 Chromium 이 필요로 하는
# 시스템 라이브러리(libnss3 등)까지 apt 로 같이 깐다.
RUN playwright install --with-deps chromium
# Node 바이너리만 옮겨 온다 — npm·corepack 은 쓰지 않는다(빌드는 site-builder 스테이지가
# 이미 끝냈다). /usr/local/bin/node 하나로 `node dist/prerender/prerender.js` 를 돌리는
# 데 충분하다(실측: 아래 COPY 만으로 `node --version` 과 렌더 스모크 테스트가 통과한다).
COPY --from=node-runtime /usr/local/bin/node /usr/local/bin/node
COPY solution/backend ./solution/backend
# ★ SITE_ROOT(solution/site/scripts/prerender.ts)가 자기 파일 위치 기준 상대경로로
# payloads·songs·out 을 찾는다 — dist·public 이 이 자리(/app/solution/site/)에 있어야
# 워커가 컨테이너 안에서 렌더러를 그대로 실행할 수 있다.
COPY --from=site-builder /app/solution/site/dist ./solution/site/dist
COPY --from=site-builder /app/solution/site/public ./solution/site/public
ENV APP_ENV=local
ENV PYTHONPATH=/app/solution/backend
WORKDIR /app/solution/backend
# 워커는 포트가 없다 — 이미지 공통 HEALTHCHECK 를 두지 않는다(compose 가 disable 한다).
CMD ["python", "worker_main.py"]