12. Node.js в Docker
Node.js — один из самых популярных стеков для контейнеризации. Правильно составленный Dockerfile для Node.js-приложения обеспечивает быструю сборку, минимальный размер образа и безопасный запуск.
Базовый Dockerfile для Node.js
Заголовок раздела «Базовый Dockerfile для Node.js»FROM node:20-alpine
WORKDIR /app
COPY package*.json ./RUN npm ci --only=production
COPY . .
EXPOSE 3000CMD ["node", "server.js"]Файл .dockerignore
Заголовок раздела «Файл .dockerignore»Критически важен для уменьшения контекста сборки и предотвращения копирования ненужных файлов:
node_modules/npm-debug.log.git/.gitignore.env.env.**.test.js*.spec.jscoverage/dist/.DS_StoreDockerfile*docker-compose*README.mdnpm ci vs npm install
Заголовок раздела «npm ci vs npm install»npm ci — рекомендуется для продакшена и Docker:
- Точно воспроизводит зависимости из
package-lock.json - Удаляет
node_modulesперед установкой - Не обновляет
package-lock.json - Быстрее в CI/CD окружениях
# В Dockerfile всегда используйте npm ciRUN npm ci --only=production
# Для разработкиRUN npm ciProduction-готовый Dockerfile с TypeScript
Заголовок раздела «Production-готовый Dockerfile с TypeScript»# ─── Этап 1: Зависимости ───────────────────────────FROM node:20-alpine AS depsWORKDIR /appCOPY package*.json ./RUN npm ci
# ─── Этап 2: Сборка TypeScript ─────────────────────FROM node:20-alpine AS builderWORKDIR /appCOPY --from=deps /app/node_modules ./node_modulesCOPY . .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
# Скомпилированный код из builderCOPY --from=builder /app/dist ./dist
# Переключиться на непривилегированного пользователяUSER appuser
ENV NODE_ENV=productionEXPOSE 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:
FROM node:20-alpineWORKDIR /appCOPY package*.json ./RUN npm installEXPOSE 3000CMD ["npx", "nodemon", "server.js"]services: api: build: context: . dockerfile: Dockerfile.dev volumes: - .:/app # синхронизация кода - /app/node_modules # анонимный том для node_modules ports: - "3000:3000" environment: - NODE_ENV=developmentОбработка сигналов: используйте dumb-init
Заголовок раздела «Обработка сигналов: используйте dumb-init»Node.js по умолчанию не обрабатывает SIGTERM корректно в PID 1. Используйте dumb-init или tini:
FROM node:20-alpine
# Установить dumb-initRUN apk add --no-cache dumb-init
WORKDIR /appCOPY --chown=node:node . .RUN npm ci --only=production
USER nodeEXPOSE 3000ENTRYPOINT ["/usr/bin/dumb-init", "--"]CMD ["node", "server.js"]Размеры образов Node.js
Заголовок раздела «Размеры образов Node.js»| Базовый образ | Размер | Когда использовать |
|---|---|---|
| node:20 | 1.1 GB | Никогда в prod |
| node:20-slim | 240 MB | Если нужны системные утилиты |
| node:20-alpine | 130 MB | Рекомендуется |
| gcr.io/distroless | 100 MB | Максимальная безопасность |