# syntax=docker/dockerfile:1 # ── 왜 Alpine 이 아니라 debian-slim(glibc) 인가 ────────────────────────── # better-sqlite3 는 musl(Alpine) 용 prebuilt 바이너리를 배포하지 않는다. # 그래서 Alpine 베이스에서 `npm install` 하면 prebuild-install 이 실패하고 # node-gyp 풀 빌드로 떨어지는데, 빌드 체인(python3/make/g++)이 없으면 # "not found: make" 로 죽는다. 또한 better-sqlite3 의 특정 버전/Node ABI # 조합은 glibc 에서도 prebuilt 가 없어 컴파일이 필요할 수 있다. # # 그래서 build 스테이지에 빌드 체인을 깔아 better-sqlite3 를 항상 소스에서 # 컴파일하고, 컴파일된 node_modules 를 runtime 스테이지로 복사한다. # runtime 스테이지엔 빌드 체인이 없어 이미지가 가볍고, prebuilt 존재 여부에 # 의존하지 않아 어디서 빌드해도 재현 가능하다. # # Alpine 을 꼭 써야 한다면 같은 원리로 build 단계에 # RUN apk add --no-cache python3 make g++ # 를 깔고 runtime 엔 `apk add ffmpeg yt-dlp` 로 바꾸면 된다. # ── build 스테이지: 네이티브 모듈 컴파일 + TS 빌드 ─────────────────────── FROM node:20-bookworm-slim AS build WORKDIR /app # better-sqlite3 네이티브 컴파일용 toolchain. RUN apt-get update \ && apt-get install -y --no-install-recommends python3 make g++ \ && rm -rf /var/lib/apt/lists/* COPY package*.json ./ RUN npm ci COPY tsconfig.json ./ COPY src ./src RUN npm run build # devDependencies 제거 — 컴파일된 better-sqlite3 .node 바이너리는 유지된다. RUN npm prune --omit=dev # ── runtime 스테이지 ───────────────────────────────────────────────────── FROM node:20-bookworm-slim AS runtime WORKDIR /app ENV NODE_ENV=production # 앱 기본값(src/app.ts)은 HOST=127.0.0.1 이라 컨테이너 안에만 바인딩돼 # `docker run -p` 로도 호스트에서 못 붙는다. 컨테이너에선 0.0.0.0 으로 열어 # 포트 매핑이 동작하게 한다. (`docker run -e HOST=...` 로 덮어쓰기 가능) ENV HOST=0.0.0.0 ENV PORT=3000 # ffmpeg/ffprobe: 트림·60fps·FHD 변환. ca-certificates: yt-dlp https. # wget 은 yt-dlp 바이너리 받는 용도로만 쓰고 같은 레이어에서 제거. RUN apt-get update \ && apt-get install -y --no-install-recommends ffmpeg ca-certificates wget \ && mkdir -p bin \ && wget -qO bin/yt-dlp https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux \ && chmod +x bin/yt-dlp \ && apt-get purge -y wget && apt-get autoremove -y \ && rm -rf /var/lib/apt/lists/* # build 스테이지에서 컴파일·프루닝된 node_modules 그대로 가져온다 (재컴파일 없음). COPY --from=build /app/node_modules ./node_modules COPY --from=build /app/dist ./dist COPY package.json ./ COPY public ./public COPY views ./views # 기본 admin 계정 파일. 운영에서는 볼륨/시크릿으로 덮어쓰세요 (초기값 admin). COPY account.json ./account.json # 영상/DB/잡 상태가 쌓이는 영속 디렉터리. VOLUME ["/app/data"] EXPOSE 3000 CMD ["node", "dist/app.js"]