Docker и TypeScript: контейнеризация приложений от А до Я

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Docker и TypeScript: контейнеризация приложений от А до Я

Контейнеризация стала стандартом современной разработки. Docker позволяет упаковать TypeScript-приложение вместе со всеми зависимостями и окружением, гарантируя его стабильную работу на любой платформе — от локального компьютера разработчика до production-сервера. В этой статье мы разберем полный цикл: от базового Dockerfile до оптимизированной production сборки.

1. Основы Docker для TypeScript-разработчика

Docker — это платформа для создания, доставки и запуска приложений в изолированных средах (контейнерах). Для TypeScript-приложений, которые компилируются в JavaScript и работают на Node.js, Docker решает три ключевые задачи:

  • Воспроизводимость окружения: забудьте фразу "у меня работает". Версии Node.js, npm и системных пакетов фиксированы в образе.
  • Изоляция: каждое приложение работает в своем контейнере, не влияя на другие сервисы.
  • Масштабирование: контейнеры легко реплицируются и оркестрируются.

Перед началом работы убедитесь, что у вас установлены Docker и Docker Compose. Проверить можно командами:

docker --version
docker-compose --version

2. Создание Dockerfile для TypeScript-приложения

Начнем с простого TypeScript-приложения. Допустим, у нас есть файл src/index.ts:

import express from 'express';

const app = express();
const port = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.send('Hello from Docker + TypeScript!');
});

app.listen(port, () => {
  console.log(`Server started on port ${port}`);
});

Теперь создадим Dockerfile в корне проекта:

# ---- Базовый образ для разработки ----
FROM node:18-alpine AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# ---- Сборка TypeScript ----
FROM base AS build
COPY tsconfig.json ./
COPY src/ ./src/
RUN npm ci && npm run build

# ---- Production образ ----
FROM node:18-alpine AS production
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=base /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]

Это многоступенчатая сборка (multi-stage build). Она позволяет:

  • Установить все зависимости (включая devDependencies) только на этапе сборки
  • В итоговый образ попадает только скомпилированный JavaScript и production-зависимости
  • Размер образа уменьшается в 2-3 раза

Соберите образ и запустите контейнер:

docker build -t my-typescript-app .
docker run -p 3000:3000 my-typescript-app

3. Настройка docker-compose для разработки и продакшена

Docker Compose упрощает работу с многоконтейнерными приложениями. Создадим docker-compose.yml:

version: '3.8'

services:
  app:
    build:
      context: .
      target: development
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
    command: npm run dev

  # Продакшен версия
  app-prod:
    build:
      context: .
      target: production
    ports:
      - "80:3000"
    environment:
      - NODE_ENV=production

Для удобства добавим отдельный Dockerfile для разработки (можно расширить базовый образ):

# Dockerfile.dev
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]

Запуск в режиме разработки:

docker-compose up app

Благодаря volumes изменения в коде сразу подхватываются (hot-reload), а /app/node_modules исключен, чтобы не перезаписывать зависимости из контейнера.

4. Оптимизация и CI/CD интеграция

Для production-сборки важно минимизировать размер образа и повысить безопасность. Вот несколько продвинутых техник:

4.1. Использование .dockerignore

Создайте файл .dockerignore:

node_modules
.git
.env
*.md
dist
coverage

4.2. Healthcheck и non-root пользователь

FROM node:18-alpine AS production
WORKDIR /app
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001
COPY --from=build /app/dist ./dist
COPY --from=base /app/node_modules ./node_modules
USER nodejs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node -e "require('http').get('http://localhost:3000', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
CMD ["node", "dist/index.js"]

3. Интеграция с CI/CD (GitHub Actions пример)

# .github/workflows/deploy.yml
name: Build and Push Docker Image

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Build the Docker image
        run: docker build . --target production -t my-app:latest
      - name: Push to registry
        run: |
          docker tag my-app:latest registry.example.com/my-app:${{ github.sha }}
          docker push registry.example.com/my-app:${{ github.sha }}

Заключение

Мы прошли полный путь контейнеризации TypeScript-приложения с помощью Docker: от базового Dockerfile до production-оптимизации и CI/CD. Ключевые выводы:

  • Многоступенчатая сборка — обязательный паттерн для уменьшения размера образа.
  • Docker Compose незаменим для локальной разработки с hot-reload.
  • Безопасность — используйте non-root пользователя и healthcheck.
  • Автоматизация — интеграция с CI/CD делает процесс развертывания предсказуемым.

Следуя этим практикам, вы получите надежную, воспроизводимую и готовую к масштабированию инфраструктуру для ва

Блоги

Рекомендаций по книгам