Docker и Nest.js: настройка приложения и базы в Docker Compose

“Но у меня на компьютере всё работает…” — шутка отражает реальную проблему: среда разработки и среда развертывания часто различаются. Основная боль — согласовать версии ПО и зависимости. Решение — контейнеризация: упаковка приложения вместе с зависимостями в образ, который гарантированно запускается одинаково в любой среде.
Понимание Docker и Docker Compose
Docker — это открытая платформа для контейнеризации приложений. Контейнеры изолируют приложение и его зависимости, но не эмулируют полноценную виртуальную машину: они делят ядро ОС и обеспечивают лёгкую портируемость.
Определение в одну строку: контейнер — лёгкая изолированная среда для запуска приложения и зависимостей.
Docker Compose — инструмент для описания и управления многоконтейнерными приложениями через единый YAML-файл. Compose полезен, когда приложение состоит из нескольких сервисов (веб-сервер, БД, кеш и т.п.).

Важно понимать различие:
- Docker управляет отдельными образами и контейнерами.
- Docker Compose описывает, как эти контейнеры связываются и запускаются как единое приложение.
Прежде чем начать: установите Docker Desktop и убедитесь, что ваша система соответствует требованиям платформы (документация Docker содержит актуальные шаги установки).
Код приложения можно версионировать и хранить в репозитории (например, GitHub) — это удобно при CI/CD.
Создание проекта Nest.js
В примере мы развернём два контейнера: один с приложением Nest.js, другой — с PostgreSQL.
Установите CLI Nest.js:
npm i -g @nestjs/cliСоздайте новый проект:
nest new docker-nest-appВыберите предпочитаемый менеджер пакетов (npm или yarn). Пример использует npm.
Перейдите в каталог проекта и запустите dev-сервер для быстрой проверки:
cd docker-nest-app
npm run startМодуль базы данных
Установите необходимые зависимости:
npm install pg typeorm @nestjs/typeorm @nestjs/configСоздайте файл .env в корне проекта и добавьте конфигурацию подключения к базе:
DATABASE_HOST="db"
DATABASE_PORT=5432
DATABASE_USER="testUser"
DATABASE_PASSWORD="mypassword123"Примечание: для локальной разработки удобно использовать простые пароли, но в продакшене храните секреты в менеджерах секретов (Vault/Secrets Manager) или в Docker секретах.
Создайте модуль базы данных:
nest g module databaseОткройте файл database/database.module.ts и вставьте конфигурацию TypeORM (как в исходном коде). Этот модуль подключит Nest приложение к PostgreSQL при запуске контейнера.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
type: 'postgres',
host: configService.get('DATABASE_HOST'),
port: configService.get('DATABASE_PORT'),
username: configService.get('DATABASE_USER'),
password: configService.get('DATABASE_PASSWORD'),
synchronize: true,
}),
inject: [ConfigService],
}),
],
})
export class DatabaseModule {} Короткое пояснение: опция synchronize: true удобна в разработке — TypeORM автоматически синхронизирует схему. В продакшене используйте миграции.
Обновление AppModule
Добавьте DatabaseModule и ConfigModule в главный модуль приложения:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DatabaseModule } from './database/database.module';
@Module({
imports: [
ConfigModule.forRoot({
envFilePath: '.env',
}),
DatabaseModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}Теперь приложение готово к контейнеризации: оно читает конфигурацию из .env (в контейнере переменные будут передаваться через Compose).
Создание Dockerfile
Dockerfile описывает шаги сборки образа приложения. В корне проекта создайте файл с именем Dockerfile и добавьте:
FROM node:16.3.0-alpine3.13
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
CMD [ "npm", "run", "start:dev" ]Разбор инструкций:
- FROM — базовый образ Node.js на Alpine Linux (лёгкий). При необходимости можно выбрать более новую версию node:18 или образ с распространёнными зависимостями.
- WORKDIR — рабочая директория внутри контейнера.
- COPY package*.json ./ — копирует package.json и package-lock.json для установки зависимостей до копирования остального кода (ускоряет кеширование слоёв).
- RUN npm install — устанавливает зависимости.
- COPY . . — копирует исходники.
- RUN npm run build — собирает TypeScript в папку dist.
- CMD — команда по умолчанию при запуске контейнера.
Советы и альтернативы:
- Для production используйте multi-stage build: сначала собрать артефакты в builder-стадии, затем скопировать только нужные файлы в минимальный runtime-образ (node:16-alpine или даже scratch). Это уменьшит размер образа и поверхность атаки.
Пример упрощённого multi-stage Dockerfile:
# Build stage
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Production stage
FROM node:16-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --only=production
CMD [ "node", "dist/main.js" ]- Добавьте файл .dockerignore (пример ниже), чтобы не копировать лишние файлы в образ.
Пример .dockerignore:
node_modules
dist
.env
.git
npm-debug.logБез .dockerignore вы рискуете увеличением размера образа и утечками конфиденциальных файлов.
Создание docker-compose.yml
В корне создайте docker-compose.yml с таким содержимым:
version: '3.9'
services:
server:
build: .
ports:
- '3000:3000'
depends_on:
- db
db:
image: 'postgres'
ports:
- '5432:5432'
environment:
POSTGRES_PASSWORD: 'mypassword123'
POSTGRES_USER: 'testUser'
volumes:
- data:/var/lib/postgresql/data
volumes:
data:Пояснения:
- services.server.build: . — сборка образа приложения из локального Dockerfile.
- depends_on гарантирует порядок запуска, но не ждёт готовности сервиса — можно добавить healthcheck для проверки состояния БД.
- volumes.data сохраняет данные базы между перезапусками контейнеров.
Рекомендации:
- Для надёжного запуска добавьте healthcheck к сервису db и используйте условие ожидания в сервере или внешнем скрипте ожидания (wait-for-it, dockerize).
- На macOS/Windows Docker Desktop может иметь особенности работы с файловой системой — это влияет на производительность и категории прав доступа для томов.
Запуск контейнеров
Соберите и запустите контейнеры:
docker compose upИли запустить в фоне:
docker compose up -dПросмотр логов:
docker compose logs -fОсновные проблемы при запуске и как их решать:
- “Connection refused” от приложения к БД: проверьте переменные окружения, name сервиса в docker-compose (DATABASE_HOST должно быть “db”), и убедитесь, что БД успела подняться. Используйте healthcheck и ожидание.
- Порт уже занят: убедитесь, что локальный порт 3000 или 5432 свободен, или измените сопоставление портов.
- Разрешения тома PostgreSQL: иногда файлы в томе создаются с UID, отличным от UID внутри контейнера; решается выбором подходящего пользователя или использованием volume драйверов.

После успешного старта расширяйте приложение: добавьте CRUD API, миграции и тесты.
Публикация образа в Docker Hub
Публикация образа в Docker Hub похожа на пуш в Git. Короткая инструкция:
- Зарегистрируйтесь и войдите в Docker Hub.
- Создайте репозиторий (Public или Private).

- В терминале выполните логин:
docker login- Пометьте (tag) локальный образ в формате
/ : :
docker tag /:latest - Запушьте образ:
docker push /:latest Замечания:
- Убедитесь, что вы корректно указали тег — без него push не попадёт в требуемый репозиторий.
- Для автоматической публикации в CI используйте docker login с токеном и секреты в CI-системе.
Когда контейнеризация не помогает (ограничения и контрпримеры)
- Приложения с требованием к GPU и низкоуровнего доступа к оборудованию сложнее контейнеризовать; чаще используют специализированные образы и runtime (NVIDIA Container Toolkit).
- Для GUI-приложений контейнеры не решают проблему локального рендеринга без дополнительной настройки X11/Wayland или VNC.
- В высоконагруженных продакшен-сценариях иногда выгоднее использовать управляемые сервисы БД (RDS, Cloud SQL), а не контейнеры с базой данных.
Безопасность и соответствие требованиям конфиденциальности
- Не храните секреты в Dockerfile или в образе. Используйте переменные окружения в Compose, Docker secrets или менеджеры секретов.
- Не запускайте процессы в контейнере с root, если это не требуется. Укажите безопасного пользователя в Dockerfile (USER).
- Минимизируйте размер образа и устанавливайте только необходимые пакеты.
- Не храните персональные данные в образах и логах. Для соответствия GDPR удаляйте/анонимизируйте данные и используйте политики хранения.
Проверки, чек-листы и роли
Чек-лист для разработчика:
- Приложение запускается локально без Docker.
- Создан .env и пример .env.example без секретов.
- TypeORM настроен на использование переменных окружения.
- Есть .dockerignore для исключения лишних файлов.
Чек-лист для DevOps:
- Dockerfile оптимизирован (многоступенчатая сборка для продакшена).
- Есть healthcheck для БД и приложения.
- Том для данных БД настроен и бэкапится.
- Тесты CI собирают и пушат образ в реестр.
Чек-лист для QA:
- Приложение проходит интеграционные тесты с контейнеризированной БД.
- Мобильные/регрессионные тесты автоматизированы и запускаются в контейнерной среде, если возможно.
Мини-методология: как контейнеризовать приложение за 5 шагов
- Подготовьте конфигурацию приложения к чтению из переменных окружения.
- Напишите Dockerfile и .dockerignore.
- Создайте docker-compose.yml для локальной разработки с томами и переменными.
- Добавьте healthchecks и проверку готовности сервисов.
- Настройте CI для сборки и публикации образов в реестр.
Полезные шаблоны и сниппеты
.env (пример):
DATABASE_HOST=db
DATABASE_PORT=5432
DATABASE_USER=testUser
DATABASE_PASSWORD=mypassword123.dockerignore (пример уже показан выше).
Multi-stage Dockerfile для продакшена — был приведён ранее.
Команды для работы с Compose:
docker compose up -d # запустить в фоне
docker compose down # остановить и удалить контейнеры
docker compose logs -f # смотреть логиТермины в одну строку
- Образ: неизменяемый артефакт, из которого создаётся контейнер.
- Контейнер: выполняемый экземпляр образа с изолированной средой.
- Volume: постоянное хранилище данных Docker, независимое от жизненного цикла контейнера.
- Tag: метка образа в реестре.
Итог и рекомендации
Контейнеризация с Docker и Docker Compose упрощает разработку и развёртывание многосервисных приложений. Для разработки используйте Compose с томами и sync-файлами, а для продакшена — многоступенчатые сборки, управление секретами и управляемые БД при необходимости. Всегда тестируйте ожидание готовности зависимостей и добавляйте healthcheck’и.
Важно: рассмотрите альтернативы и ограничения — не всё подходит под контейнеры «из коробки». Для критичных к производительности систем или для сложных аппаратных зависимостей планируйте архитектуру заранее.
Ключевые ссылки и действия:
- Документация Docker: https://docs.docker.com
- Документация Nest.js: https://docs.nestjs.com
Похожие материалы
Несколько аккаунтов Skype: Multi Skype Launcher
Журнал для работы: повысить продуктивность
Персональные звуки уведомлений на Android
Скачивание шоу Hulu для офлайн‑просмотра
Microsoft Start: персонализированная новостная лента