From 83b391c0a8aa7d76cc981a1862b08fdaa3b22229 Mon Sep 17 00:00:00 2001 From: "Claude (owner)" Date: Mon, 1 Jun 2026 21:39:53 +0900 Subject: [PATCH] build: add Dockerfile that compiles better-sqlite3 in build stage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 도커 빌드가 `RUN npm install` 에서 better-sqlite3 네이티브 빌드 실패로 죽던 문제 해결. 근본 원인: - 베이스가 Alpine(musl) 이라 better-sqlite3 prebuilt 바이너리가 없음 (prebuild-install: "No prebuilt binaries found ... libc=musl"). - node-gyp 풀 빌드로 폴백하는데 이미지에 make/g++/python3 가 없어 "not found: make" 로 실패. 해결: - 멀티스테이지 Dockerfile 추가. build 스테이지에 python3/make/g++ 를 깔아 better-sqlite3 를 소스에서 항상 컴파일하고, `npm prune --omit=dev` 로 정리한 node_modules(컴파일된 .node 포함)를 runtime 스테이지로 복사. runtime 엔 빌드 체인이 없어 가볍고, prebuilt 존재 여부에 의존하지 않음. - glibc(debian-slim) 베이스라 musl 갭 자체를 회피. 번들 yt-dlp 표준 바이너리도 glibc 에서 동작. - runtime 에 ffmpeg/ffprobe(트림·60fps·FHD 변환) + yt-dlp(릴리스 바이너리) 설치. data 를 VOLUME 으로 노출, 3000 포트 EXPOSE. - .dockerignore 로 node_modules/dist/data/.git/.env 등 컨텍스트 제외. 검증 (로컬 docker 29.4.1): - `docker build` 성공 (better-sqlite3 컴파일 통과) - 컨테이너 기동 → `GET /` HTTP 200 - ffmpeg 5.1.9 / ffprobe 5.1.9 / yt-dlp 2026.03.17 / better-sqlite3 in-memory 쿼리 모두 정상 Co-Authored-By: Claude Opus 4.7 --- .dockerignore | 8 +++++++ Dockerfile | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4e6ce93 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +dist +bin +data +.git +.env +*.log +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0bd614f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,66 @@ +# 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 + +# 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"]