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

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

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

Начать курс

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

Контейнеризация TypeScript-приложений с помощью Docker — это не просто упаковка кода в образ. На продвинутом уровне это искусство оптимизации, безопасности и автоматизации. В этой статье мы разберём, как создавать эффективные Docker-образы для TypeScript, управлять окружением и интегрировать контейнеризацию в CI/CD пайплайн. Вы узнаете, как уменьшить размер образа в 5-10 раз, ускорить сборку и улучшить изоляцию зависимостей.

1. Многоэтапная сборка (Multi-stage builds) для TypeScript

Классическая проблема Docker-образов для TypeScript — включение в финальный образ всех инструментов разработки: TypeScript компилятора, dev-зависимостей, исходных файлов. Это приводит к огромным размерам (часто > 1 ГБ). Решение — многоэтапная сборка.

Идея проста: первый этап (build) компилирует TypeScript в JavaScript и устанавливает все зависимости. Второй этап (production) копирует только готовый JavaScript и production-зависимости.

# Этап 1: Сборка
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=development
COPY . .
RUN npm run build

# Этап 2: Продакшн
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
EXPOSE 3000
CMD ["node", "dist/index.js"]

Такой подход сокращает размер образа с ~800 МБ до ~150 МБ. Обратите внимание на --only=development и --only=production — это ключевые флаги для разделения зависимостей.

2. Оптимизация кэширования слоёв Docker

Каждая инструкция в Dockerfile создаёт слой. Docker кэширует слои, если они не изменились. Правильный порядок инструкций критически важен для скорости повторных сборок.

Плохой пример (ломает кэш):

FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build
CMD ["node", "dist/index.js"]

Здесь любое изменение в исходном коде (даже в README.md) сбрасывает кэш для npm ci и сборки.

Хороший пример (сохраняет кэш):

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json tsconfig.json ./
RUN npm ci --only=development
COPY src ./src
RUN npm run build

Почему это работает? Зависимости меняются редко, а исходный код — часто. Копируя package*.json отдельно, мы гарантируем, что слой с npm ci пересобирается только при изменении этих файлов.

3. Работа с переменными окружения и секретами

Для TypeScript-приложений на Node.js использование переменных окружения — стандарт. Однако в Docker есть нюансы.

Типичная ошибка: передача чувствительных данных (API-ключи, пароли) через ENV в Dockerfile. Это оставляет секреты в истории слоёв образа.

Правильный подход:

  • Используйте --env-file при запуске контейнера
  • Для продакшна применяйте Docker Secrets (в Swarm) или Kubernetes Secrets
  • Для локальной разработки — файл .env с docker-compose

Пример docker-compose.yml:

version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    env_file:
      - .env.production
    volumes:
      - ./logs:/app/logs

В TypeScript-коде используйте библиотеку dotenv для загрузки переменных:

import dotenv from 'dotenv';
import { resolve } from 'path';

dotenv.config({ path: resolve(__dirname, '../.env') });

const config = {
  port: parseInt(process.env.PORT || '3000', 10),
  dbUrl: process.env.DATABASE_URL,
  apiKey: process.env.API_KEY
};

export default config;

Важно: никогда не коммитьте реальные .env файлы в репозиторий. Используйте .env.example как шаблон.

4. Интеграция Docker с CI/CD для TypeScript

Автоматизация сборки и деплоя TypeScript-приложения через Docker — обязательный шаг для продакшна. Рассмотрим типовой пайплайн на GitHub Actions.

Пример .github/workflows/deploy.yml:

name: Build and Deploy
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: user/app:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

Ключевые моменты:

  • Buildx — позволяет использовать многоэтапную сборку и кэширование
  • Cache from/to — использует GitHub Actions cache для ускорения сборки
  • Secrets — храните токены Docker Hub в секретах репозитория

Для тестирования перед сборкой добавьте шаг с линтингом и юнит-тестами:

      - name: Install dependencies
        run: npm ci
      
      - name: Lint
        run: npm run lint
      
      - name: Test
        run: npm test

Заключение

Продвинутая контейнеризация TypeScript-приложений — это не разовый шаг, а набор практик, которые экономят ресурсы и время. Многоэтапная сборка уменьшает размер образов, правильный порядок инструкций ускоряет сборки, а грамотная работа с секретами повышает безопасность. Интеграция с CI/CD завершает картину, делая процесс полностью автоматизированным.

Начните с малого: оптимизируйте один Dockerfile, затем добавьте

Блоги

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