Перейти к содержимому

12. Node.js в Docker

Node.js — один из самых популярных стеков для контейнеризации. Правильно составленный Dockerfile для Node.js-приложения обеспечивает быструю сборку, минимальный размер образа и безопасный запуск.

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Критически важен для уменьшения контекста сборки и предотвращения копирования ненужных файлов:

.dockerignore
node_modules/
npm-debug.log
.git/
.gitignore
.env
.env.*
*.test.js
*.spec.js
coverage/
dist/
.DS_Store
Dockerfile*
docker-compose*
README.md

npm ci — рекомендуется для продакшена и Docker:

  • Точно воспроизводит зависимости из package-lock.json
  • Удаляет node_modules перед установкой
  • Не обновляет package-lock.json
  • Быстрее в CI/CD окружениях
Окно терминала
# В Dockerfile всегда используйте npm ci
RUN npm ci --only=production
# Для разработки
RUN npm ci
# ─── Этап 1: Зависимости ───────────────────────────
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
# ─── Этап 2: Сборка TypeScript ─────────────────────
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# ─── Этап 3: Production ────────────────────────────
FROM node:20-alpine AS production
# Создать непривилегированного пользователя
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
# Только production зависимости
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# Скомпилированный код из builder
COPY --from=builder /app/dist ./dist
# Переключиться на непривилегированного пользователя
USER appuser
ENV NODE_ENV=production
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]

Для горячей перезагрузки при разработке используйте bind mount и nodemon:

Dockerfile.dev
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
EXPOSE 3000
CMD ["npx", "nodemon", "server.js"]
docker-compose.dev.yml
services:
api:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app # синхронизация кода
- /app/node_modules # анонимный том для node_modules
ports:
- "3000:3000"
environment:
- NODE_ENV=development

Node.js по умолчанию не обрабатывает SIGTERM корректно в PID 1. Используйте dumb-init или tini:

FROM node:20-alpine
# Установить dumb-init
RUN apk add --no-cache dumb-init
WORKDIR /app
COPY --chown=node:node . .
RUN npm ci --only=production
USER node
EXPOSE 3000
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["node", "server.js"]
Базовый образРазмерКогда использовать
node:201.1 GBНикогда в prod
node:20-slim240 MBЕсли нужны системные утилиты
node:20-alpine130 MBРекомендуется
gcr.io/distroless100 MBМаксимальная безопасность