Урок 10

Практика: полный деплой

Собрали весь путь от кода до продакшена.

Подготовка проекта

package.json

{
    "scripts": {
        "dev": "next dev",
        "build": "next build",
        "start": "next start",
        "lint": "next lint",
        "typecheck": "tsc --noEmit",
        "test": "vitest",
        "test:e2e": "playwright test"
    }
}

next.config.ts

const nextConfig = {
    output: process.env.DOCKER ? 'standalone' : undefined,
    images: {
        remotePatterns: [
            { protocol: 'https', hostname: 'images.example.com' },
        ],
    },
    async headers() {
        return [
            {
                source: '/(.*)',
                headers: [
                    { key: 'X-Content-Type-Options', value: 'nosniff' },
                    { key: 'X-Frame-Options', value: 'DENY' },
                    { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
                ],
            },
        ];
    },
};

export default nextConfig;

Валидация env

// lib/env.ts
import { z } from 'zod';

const envSchema = z.object({
    DATABASE_URL: z.string().url(),
    AUTH_SECRET: z.string().min(32),
    NEXT_PUBLIC_API_URL: z.string().url(),
    NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
});

export const env = envSchema.parse(process.env);

Путь через Vercel

1. Пуш на GitHub

git init
git add .
git commit -m "Initial"
git remote add origin git@github.com:user/repo.git
git push -u origin main

2. Импорт на Vercel

  1. vercel.com → New Project
  2. Импортируй репозиторий
  3. Vercel определит Next.js
  4. Добавь переменные окружения
  5. Deploy

3. Переменные

В Settings → Environment Variables:

DATABASE_URL=postgresql://...
AUTH_SECRET=<32+ символов>
NEXT_PUBLIC_API_URL=https://api.example.com

Разные для Production и Preview.

4. Домен

Settings → Domains → добавь example.com.

Настрой DNS у регистратора.

5. Автоматический деплой

Каждый пуш в main — деплой в production. PR — preview.

Путь через Docker + VPS

1. VPS

Hetzner, DigitalOcean, Vultr. Ubuntu 22.04.

2. Установка

# Docker
curl -fsSL https://get.docker.com | sh

# Nginx
sudo apt install nginx

# Certbot
sudo apt install certbot python3-certbot-nginx

3. Клонирование

cd /var/www
git clone git@github.com:user/repo.git my-app
cd my-app

4. .env.production

DATABASE_URL=postgresql://postgres:password@db:5432/myapp
AUTH_SECRET=<32+ символов>
NEXT_PUBLIC_API_URL=https://example.com

5. Docker Compose

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
      - AUTH_SECRET=${AUTH_SECRET}
      - NEXT_PUBLIC_API_URL=https://example.com
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  postgres_data:

6. Запуск

docker compose up -d --build

7. Миграции

docker compose exec app npx prisma migrate deploy

8. Nginx

/etc/nginx/sites-available/example.com:

server {
    listen 80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

9. SSL

sudo certbot --nginx -d example.com -d www.example.com

10. CI/CD

.github/workflows/deploy.yml:

name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd /var/www/my-app
            git pull
            docker compose up -d --build
            docker compose exec -T app npx prisma migrate deploy

Чек-лист релиза

  • Тесты проходят
  • Линтер без ошибок
  • Типы проверены
  • Сборка работает
  • Переменные окружения настроены
  • Секреты не в репозитории
  • SSL работает
  • Домен настроен
  • Nginx reverse proxy
  • Sentry подключён
  • Health-check доступен
  • Бэкапы БД
  • Мониторинг
  • Логи

Откат

Если что-то сломалось:

Vercel

Deployments → выбери предыдущий → Promote to Production.

Docker

docker compose down
git checkout <предыдущий-коммит>
docker compose up -d --build

База

psql -U roman myapp < backup.sql

Итоги

  • Vercel — просто
  • Docker + VPS — контроль
  • CI/CD автоматизирует
  • Nginx и SSL на сервере
  • Чек-лист перед релизом
  • План отката