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 делает процесс развертывания предсказуемым.
Следуя этим практикам, вы получите надежную, воспроизводимую и готовую к масштабированию инфраструктуру для ва