Развертывание традиционных веб-фреймворков, таких как Django, на современных бессерверных платформах вроде Vercel, может показаться нетривиальной задачей. Vercel, изначально оптимизированный для статических сайтов и Serverless Functions на базе Node.js, предлагает возможности для хостинга и других технологий. Это руководство демонстрирует, как адаптировать Django-проект, ориентированный преимущественно на API, для работы в окружении Vercel Serverless Functions.
Введение в Django и Vercel
Django – это высокоуровневый Python веб-фреймворк, который поощряет быструю разработку и чистый, прагматичный дизайн. Он включает в себя ORM, шаблонизатор, систему маршрутизации и многое другое, что делает его полноценным решением для создания веб-приложений.
Vercel – это облачная платформа для фронтенд-разработчиков, предлагающая хостинг статических сайтов и Serverless Functions. Она известна своей простотой развертывания, глобальной CDN и автоматической интеграцией с Git-репозиториями.
Почему Vercel подходит для развертывания Django?
Vercel не является традиционной платформой для хостинга Django (как, например, VPS или PaaS типа Heroku/Render, где ваше приложение работает постоянно). Однако, если ваш Django-проект в основном функционирует как API или имеет компоненты, которые можно выполнить в рамках кратковременных запросов (Serverless Functions), Vercel может предложить преимущества:
Простота развертывания: Интеграция с Git и автоматические деплои.
Serverless архитектура: Масштабирование по запросу, оплата за фактическое использование (для Serverless Functions).
Глобальная CDN: Быстрая отдача статических файлов и ответов API по всему миру.
Экосистема: Удобство использования, если вы уже применяете Vercel для фронтенда.
Важно понимать, что такой подход неидеален для традиционных Django-приложений с интенсивным использованием сессий, фоновых задач или долгоживущих процессов. Он лучше всего подходит для API-centric проектов.
Обзор ключевых концепций: serverless функции и API
Serverless Functions (Бессерверные функции): Небольшие, одноцелевые функции, которые выполняются в ответ на события (например, HTTP-запрос). Они масштабируются автоматически и не требуют управления серверами. На Vercel они могут быть написаны на Python, Node.js, Go, Ruby и других языках.
API (Application Programming Interface): Набор правил и протоколов, позволяющих различным программным компонентам взаимодействовать друг с другом. В контексте веб-разработки, это обычно набор эндпоинтов, которые принимают запросы (GET, POST и т.д.) и возвращают данные (часто в формате JSON).
Мы будем использовать Vercel Serverless Functions для запуска нашего Django API по запросу.
Необходимые инструменты и предварительная подготовка
Перед началом убедитесь, что у вас установлены:
Python: Версия 3.7 или выше.
pip: Менеджер пакетов Python.
Vercel CLI: Устанавливается через npm install -g vercel.
Git: Для управления версиями и интеграции с Vercel.
Учетная запись Vercel: Зарегистрируйтесь на [vercel.com].
Внешняя база данных: Serverless Functions являются stateless и не имеют постоянного хранилища данных. Вам потребуется внешняя база данных (например, PostgreSQL на Render, Neon, ElephantSQL и т.д.). SQLite не подходит для этого сценария.
Убедитесь, что ваш Django-проект готов и работает локально, используя внешнюю базу данных.
Подготовка Django-проекта к развертыванию на Vercel
Чтобы ваш Django-проект мог работать в окружении Vercel Serverless Function, потребуется внести несколько изменений в конфигурацию и структуру проекта.
Настройка `settings.py` для работы с Vercel
Ваш файл settings.py должен быть адаптирован для продакшн-окружения и корректно работать с переменными окружения, предоставляемыми Vercel.
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.environ.get('SECRET_KEY', 'your-fallback-secret-key')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
# Vercel автоматически устанавливает ALLOWED_HOSTS
# Но лучше явно указать, чтобы избежать ошибок при локальном запуске или в других окружениях
ALLOWED_HOSTS = os.split(os.environ.get('ALLOWED_HOSTS', '*'), ',') if os.environ.get('ALLOWED_HOSTS') else ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'corsheaders', # Если ваше API будет вызываться с другого домена
# Ваши приложения здесь
'yourapp.apps.YourappConfig',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'corsheaders.middleware.CorsMiddleware', # Добавьте сюда, если используете corsheaders
'whitenoise.middleware.WhiteNoiseMiddleware', # Для статических файлов через whitenoise
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'yourproject.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'yourproject.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME'),
'USER': os.environ.get('DB_USER'),
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': os.environ.get('DB_HOST'),
'PORT': os.environ.get('DB_PORT', '5432'),
# Убедитесь, что ваше соединение с базой данных поддерживает пулы соединений,
# так как serverless функции могут открывать много параллельных соединений.
# Или используйте асинхронные драйверы и ASGI (вне рамок этого руководства).
}
}
# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') # Место, куда collectstatic будет собирать файлы
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# CORS settings (пример, адаптируйте под свои нужды)
# CORS_ALLOW_ALL_ORIGINS = True # Небезопасно для продакшена
CORS_ALLOWED_ORIGINS = [
'https://your-frontend-domain.com',
'https://your-vercel-app-url.vercel.app', # URL вашего приложения на Vercel
]
Используйте os.environ.get() для всех чувствительных или специфичных для окружения настроек.
Создание `vercel.json` файла конфигурации
Файл vercel.json в корне вашего проекта сообщает Vercel, как собирать и маршрутизировать ваше приложение. Он должен определить build команду и routes.
{
"build": {
"env": {
"DJANGO_SETTINGS_MODULE": "yourproject.settings"
}
},
"functions": {
"api/index.py": {
"runtime": "python3.9" // Или вашу версию Python
}
},
"routes": [
{
"src": "/(.*)",
"dest": "api/index.py"
}
]
}
build.env: Устанавливает переменные окружения, доступные на этапе сборки (например, указываем Vercel, где найти файл настроек Django).
functions: Определяет, какие файлы являются Serverless Functions и какой рантайм использовать. Здесь api/index.py – это наш Python скрипт, который будет запускать Django.
routes: Определяет, как входящие запросы маршрутизируются к функциям или статическим файлам. "src": "/(.*)" соответствует любому пути, а "dest": "api/index.py" перенаправляет все запросы к нашей функции.
Использование whitenoise для статических файлов
Хотя Vercel отлично подходит для статического хостинга, Serverless Function может самостоятельно отдавать статические файлы, собранные через collectstatic, используя whitenoise. Это может быть полезно для админки Django или небольшого количества статики, которая не требует максимальной производительности CDN. Для основных статических ассетов фронтенда лучше использовать нативные возможности Vercel, размещая их в корневой директории проекта, не обрабатываемой Django функцией.
Установите whitenoise:
pip install whitenoise
Настройте settings.py (как показано выше), добавив WhiteNoiseMiddleware и указав STATIC_ROOT. Затем выполните python manage.py collectstatic локально, чтобы собрать статику в папку staticfiles.
Настройка Gunicorn для работы с Vercel
В Serverless Function нет постоянного процесса WSGI-сервера. Наш входной скрипт (api/index.py) должен сам запустить WSGI-приложение Django. Gunicorn можно использовать для этого, но не в стандартном режиме демона. Вместо этого мы импортируем WSGI-приложение Django напрямую и используем минимальный сервер, который Vercel предоставляет для Python функций.
Установите Gunicorn:
pip install gunicorn
Обновите requirements.txt, добавив gunicorn и все остальные зависимости вашего проекта:
Django~=5.0
gunicorn~=21.2
psycopg2-binary~=2.9 # Или другой драйвер БД
whitenoise~=6.6
corsheaders~=4.0
# Другие зависимости ваших приложений
Развертывание Django API на Vercel с использованием Serverless Functions
Основной шаг – создание точки входа для Vercel Serverless Function, которая сможет инициализировать и выполнить Django WSGI-приложение.
Создание serverless функции для обработки запросов Django
Создайте директорию api в корне вашего проекта и внутри нее файл index.py.
# api/index.py
import os
import sys
# Указываем путь до корневой директории проекта Django
# Это нужно, чтобы Python мог найти manage.py и settings.py
# BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# sys.path.append(BASE_DIR)
# Альтернативный способ: добавить корневую директорию проекта в sys.path
# Предполагается, что api/index.py находится в корневой директории проекта или уровнем ниже
# Например, если проект: myproject/, api/index.py находится в myproject/api/index.py
# Тогда корневая директория myproject находится на 2 уровня выше api/index.py
# Найдем корневую директорию проекта, ориентируясь на manage.py
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.join(current_dir, os.pardir)
# Добавляем корневую директорию проекта в sys.path
# Теперь Python сможет найти модуль yourproject (там, где settings.py и wsgi.py)
sys.path.insert(0, project_root)
# Устанавливаем переменную окружения для Django settings
# Это можно также сделать в vercel.json build.env
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yourproject.settings')
# Импортируем и получаем WSGI-приложение Django
# Это должно произойти ПОСЛЕ настройки sys.path и DJANGO_SETTINGS_MODULE
try:
from django.core.wsgi import get_wsgi_application
# При необходимости, можно выполнить миграции при первом запуске функции
# Но это ОЧЕНЬ не рекомендуется из-за проблем с параллелизмом и таймаутами.
# Лучше выполнять миграции отдельно (например, вручную или через CI/CD хук).
# from django.core.management import execute_from_command_line
# execute_from_command_line(['manage.py', 'migrate', '--noinput'])
application = get_wsgi_application()
print("Django WSGI application loaded successfully.")
except Exception as e:
print(f"Error loading Django WSGI application: {e}")
# Логирование ошибки
application = None # Установим None, чтобы Vercel вернул ошибку 500
# Vercel Python Serverless Function entry point expects a callable named 'handler'
# This handler receives the event (request) and context
# We will adapt the Django WSGI application to fit this signature
# The Vercel Python runtime handles the WSGI conversion for us.
# Поэтому нам просто нужно предоставить WSGI-приложение.
# Vercel автоматически обернет его в HTTP handler.
# Если вам нужна кастомная обработка запроса перед передачей в Django,
# можно определить функцию `handler(request, context)` и вызвать `application(environ, start_response)` из нее.
# Но для большинства случаев достаточно просто экспортировать WSGI-приложение как `application`.
# Vercel ожидает callable, которое может быть либо ASGI, либо WSGI application.
# Поскольку мы используем get_wsgi_application(), это WSGI.
# Простой способ для Vercel Python Runtime:
# Vercel ищет callable с именем 'handler' ИЛИ 'application'.
# Если наше WSGI-приложение названо `application`, Vercel его подхватит.
# Если мы назвали его `handler`, оно тоже будет работать.
# Оставим `application`, т.к. это стандартный выход get_wsgi_application.
# Новая версия Vercel Runtime предпочитает функцию `handler`
# Давайте обернем наше WSGI приложение в функцию `handler`
# которая примет запрос (event) и контекст.
# Vercel Python Runtime автоматически преобразует входящий HTTP запрос
# в WSGI environment и вызовет наш application.
# Реальный обработчик для Vercel
def handler(request, context):
# Django WSGI application будет вызвана Vercel Runtime неявно,
# если `application` является WSGI callable.
# Если мы хотим добавить логику до/после вызова Django, делаем это здесь.
# Например, логирование или проверку CORS до попадания в Django middleware.
# Пример (просто для иллюстрации, Vercel runtime делает это):
# environ, start_response = vercel_request_to_wsgi_env(request)
# response = application(environ, start_response)
# return wsgi_response_to_vercel_response(response)
# В большинстве случаев, достаточно просто предоставить WSGI application
# с именем `application`.
# Vercel Runtime для Python Serverless Functions будет использовать `application`,
# если он определен как WSGI или ASGI callable.
# Если вы используете get_wsgi_application(), результатом является WSGI callable.
# Возвращаем наше WSGI приложение. Vercel Runtime его вызовет.
# Это немного контринтуитивно, но так работает Vercel Python Runtime.
return application
# Альтернативный вариант, который может работать в старых версиях Vercel Runtime
# или если vercel.json настроен по-другому:
# from django.core.wsgi import get_wsgi_application
# application = get_wsgi_application()
# Для новых Vercel Python Runtime, рекомендуется использовать функцию `handler`.
# Определяем `handler` как наше WSGI приложение.
# Vercel Runtime вызывает `handler` с event и context,
# а затем, если `handler` возвращает WSGI app, Runtime вызывает его.
# Переименовываем наше WSGI-приложение в `handler` для совместимости с новым рантаймом
handler = application # Теперь `handler` ссылается на наше Django WSGI приложение
# Дополнительная проверка, если application не загрузилось из-за ошибки
if handler is None:
def handler(request, context):
# В случае ошибки загрузки Django, вернем внутреннюю ошибку сервера
print("Django application failed to load.")
from http.client import INTERNAL_SERVER_ERROR
return {
'statusCode': INTERNAL_SERVER_ERROR,
'body': 'Internal Server Error: Django application failed to load.'
}Этот скрипт настраивает окружение Python так, чтобы Django мог быть загружен, устанавливает DJANGO_SETTINGS_MODULE, а затем импортирует ваше WSGI-приложение Django. Vercel Python Runtime автоматически вызывает это WSGI-приложение при получении запроса.
Настройка маршрутизации запросов в `vercel.json`
Мы уже создали базовый vercel.json с маршрутом "src": "/(.*)", "dest": "api/index.py". Это означает, что все входящие запросы к вашему приложению будут направлены на выполнение скрипта api/index.py. Внутри Django ваше urls.py будет обрабатывать конкретные пути (/api/items/, /admin/ и т.д.).
Если у вас есть статические файлы, которые вы хотите отдавать через Vercel CDN (предпочтительный способ), убедитесь, что они не попадают под этот маршрут или настройте отдельные маршруты для статики:
{
"build": {
"env": {
"DJANGO_SETTINGS_MODULE": "yourproject.settings"
}
},
"functions": {
"api/index.py": {
"runtime": "python3.9"
}
},
"routes": [
// Маршрут для статических файлов в корневой директории, например, index.html, favicon.ico
// "src": "/(.*\\.(?:html|css|js|png|jpg|gif|svg|ico))",
// "dest": "/$"
// Маршрут для статических файлов, если вы их разместили в отдельной папке 'static' в корне проекта
// "src": "/static/(.*)",
// "dest": "/static/$"
// Маршрут для всех остальных запросов к Django API
{
"src": "/(.*)",
"dest": "api/index.py"
}
]
}
Порядок маршрутов важен: Vercel применяет их сверху вниз. Статические маршруты должны идти перед маршрутом к API.
Обработка переменных окружения и секретных ключей
Vercel предоставляет безопасный способ управления переменными окружения через свой дашборд или CLI. Это критически важно для таких данных, как SECRET_KEY, учетные данные базы данных (DB_NAME, DB_USER, DB_PASSWORD, DB_HOST, DB_PORT) и других секретов.
В дашборде Vercel перейдите в настройки вашего проекта -> Environment Variables и добавьте все необходимые переменные. Убедитесь, что они доступны для окружений Production, Preview и Development (по вашему усмотрению).
В settings.py мы уже используем os.environ.get() для доступа к этим переменным.
# settings.py
# ...
SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = os.environ.get('DEBUG') == 'True'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME'),
'USER': os.environ.get('DB_USER'),
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': os.environ.get('DB_HOST'),
'PORT': os.environ.get('DB_PORT', '5432'),
}
}
# ...
Не храните секретные ключи непосредственно в коде или в vercel.json.
Развертывание и тестирование проекта на Vercel
С подготовленным проектом и настроенным vercel.json вы готовы к развертыванию.
Развертывание проекта с помощью Vercel CLI или GitHub integration
Выберите один из способов:
Vercel CLI: Откройте терминал в корне вашего проекта и выполните команду vercel. Следуйте инструкциям CLI. Для продакшн-развертывания используйте vercel --prod.
GitHub/GitLab/Bitbucket Integration: Подключите ваш Git-репозиторий к Vercel через дашборд. Vercel автоматически обнаружит vercel.json и будет развертывать новые версии при каждом пуше в ветку, связанную с продакшном (обычно main или master) или при создании пулл-реквестов (для preview-развертываний).
На этапе сборки Vercel установит зависимости из requirements.txt и, если настроено в vercel.json, выполнит другие команды.
Настройка доменного имени (по желанию)
После развертывания ваш проект будет доступен по адресу your-project-name.vercel.app. Вы можете привязать собственный домен через настройки проекта на дашборде Vercel, добавив соответствующие DNS-записи у вашего регистратора доменных имен.
Тестирование API и обработка ошибок
После успешного развертывания протестируйте ваши API эндпоинты (например, с помощью Postman, curl или через ваш фронтенд). Проверьте различные типы запросов (GET, POST и т.д.).
В случае ошибок смотрите логи. Vercel предоставляет удобный интерфейс для просмотра логов Serverless Functions на дашборде. Ошибки Django (например, трассировки стека) будут отображаться там.
Мониторинг и логирование работы приложения
Vercel предоставляет базовые средства мониторинга, включая количество вызовов функций, время выполнения и ошибки. Эти метрики доступны на дашборде проекта. Для более глубокого мониторинга и агрегации логов рассмотрите интеграцию с внешними сервисами логирования/мониторинга, если это необходимо для вашего продакшн-приложения.
Оптимизация и устранение неполадок при развертывании Django на Vercel
Развертывание Django в Serverless окружении имеет свои особенности, требующие внимания к оптимизации и отладке.
Кэширование данных и статических файлов
Статические файлы: Как упоминалось, Vercel CDN – лучший способ для отдачи статики фронтенда. Для статики админки Django, отдаваемой через whitenoise, кэширование настраивается в settings.py (STATICFILES_STORAGE). Vercel также кэширует ответы функций, но это требует настройки заголовков кэширования в ответах.
Кэширование данных API: В самом Django API используйте стандартные механизмы кэширования (например, кэш Django, Redis) для уменьшения нагрузки на базу данных и ускорения ответов. Учитывайте, что каждый вызов Serverless Function может быть на новом "холодном" контейнере, поэтому локальный кэш в памяти функции неэффективен.
Оптимизация времени cold start
"Холодный старт" (cold start) – это задержка при первом вызове Serverless Function после периода неактивности, вызванная временем, необходимым платформе для инициализации среды выполнения и загрузки кода. Для Python-приложений, особенно таких больших, как Django, cold start может быть ощутимым (несколько секунд).
Пути минимизации cold start на Vercel ограничены, но можно:
Убедиться, что импорт модулей в api/index.py и инициализация Django максимально быстрые.
Минимизировать размер зависимостей (проверьте requirements.txt).
Vercel предлагает функцию "Always On" для Serverless Functions (может быть платной), которая поддерживает функцию в активном состоянии, уменьшая cold starts.
Решение распространенных проблем и ошибок при развертывании
Ошибки сборки: Проверьте логи сборки на Vercel дашборде. Наиболее частые причины – проблемы с зависимостями (requirements.txt), ошибки в vercel.json, или ошибки в коде, мешающие выполнению collectstatic или других команд сборки.
Ошибки выполнения функции: Смотрите логи Serverless Functions. Django tracebacks будут видны здесь. Убедитесь, что переменные окружения установлены корректно, особенно для подключения к базе данных.
Проблемы с ALLOWED_HOSTS: Если вы видите ошибки, связанные с разрешенными хостами, проверьте настройки ALLOWED_HOSTS в settings.py и переменные окружения на Vercel.
Ошибки подключения к базе данных: Убедитесь, что ваша внешняя база данных доступна из Vercel (правила фаервола) и учетные данные верны.
Альтернативные подходы к развертыванию Django на Vercel (например, использование Docker)
Хотя основное внимание в этом руководстве уделяется Serverless Functions, Vercel также поддерживает развертывание с использованием Docker-образов. Этот подход может быть более подходящим для традиционных Django-приложений, которые плохо ложатся на модель Serverless Functions.
Используя Docker, вы упаковываете ваше Django-приложение (включая WSGI-сервер типа Gunicorn/Uvicorn) в контейнер. Vercel может развернуть этот контейнер. Этот метод дает больше контроля над средой выполнения и лучше подходит для приложений, требующих более сложных настроек или фоновых процессов (хотя фоновые процессы всё равно остаются вызовом в Vercel).
Развертывание через Docker:
Создайте Dockerfile для вашего Django-проекта.
Настройте vercel.json для использования Docker-сборки.
Vercel соберет Docker-образ и развернет его.
Этот подход, однако, может быть дороже и менее "бессерверным" в классическом понимании по сравнению с чистыми Serverless Functions.
В конечном итоге, выбор между Serverless Functions и Docker на Vercel (или даже другой платформой хостинга) зависит от специфики вашего Django-проекта и требований к его архитектуре и производительности.