Подготовка вашего приложения Django к производству

Оглавление

Начать работу с Django быстро и просто. По умолчанию Django поставляется с конфигурацией для локальной среды разработки, и хотя вы можете создавать сложные веб-приложения, работающие на вашей локальной машине, в какой-то момент вам захочется получить готовое к производству веб-приложение.

Многие настройки по умолчанию в приложении Django можно оставить как есть, но есть несколько параметров, которые почти наверняка придется изменить, прежде чем ваше приложение будет готово к производству.

В этой статье мы рассмотрим общие изменения, необходимые для настройки приложения для работы в производственной среде. Для демонстрационных целей в этой статье мы возьмем приложение Django под названием `foo`, созданное с помощью команды Django-admin startproject.

Управление средами с помощью DJANGO_SETTINGS_MODULE

При разработке производственного приложения обычно используется несколько сред, в которых будет работать приложение. Разработчикам необходимо запускать приложение локально, для тестирования может быть создано несколько промежуточных сред, а также производственная среда, которая будет общедоступна. Каждая из этих сред будет иметь специфическую для нее конфигурацию.

Использование DJANGO_SETTINGS_MODULE

Django предоставляет удобный способ указания различных файлов настроек, используемых для вашего приложения, с помощью переменной окружения DJANGO_SETTINGS_MODULE. Одним из вариантов управления различными окружениями является создание отдельного конфигурационного файла для каждого из окружений и использование переменной DJANGO_SETTINGS_MODULE для указания файла для своего окружения.

Преимущество в том, что вы можете легко увидеть точную конфигурацию для каждой среды, и это может быть удобным способом управления всеми конфигурациями в одном месте.

Однако такой подход имеет ряд недостатков:

  • Изменения конфигурации привязаны к коду приложения.
  • Чувствительные ключи доступа и пароли сохраняются в виде обычного текста в коде.
  • Вам потребуется файл для каждого окружения, что усложняет управление, если Вы хотите иметь возможность динамически создавать или уничтожать окружения.

Общие настройки

Если вы решили иметь несколько файлов настроек, подумайте о том, чтобы поместить общую конфигурацию в другой файл и импортировать его в файлы, специфичные для вашей среды. Например, предположим, что общая для всех окружений конфигурация находится в файле shared_settings.py в каталоге foo/.

Затем мы можем создать файл local_settings.py, который будет выглядеть примерно так:

ENV = 'local'
DEBUG = True


from .shared_settings import *

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

Использование переменных окружения

Другой вариант настройки Django-приложения - использование переменных окружения.

Использование переменных окружения позволяет отделить конфигурацию приложения от его кода. Это означает, что вы можете легко запускать приложение в любом количестве окружений, не изменяя код приложения.

Кроме того, он позволяет вводить конфиденциальную информацию, необходимую для настройки приложения, и легко хранить ее в безопасном месте.

Можно иметь несколько файлов настроек, если вы хотите избежать if-заявлений при работе с конфигурациями, специфичными для конкретного окружения. Например, можно иметь файл local_settings.py для локальной разработки и файл remote_settings.py для размещения приложения на удаленном сервере.

В любом случае использование переменных окружения обеспечивает дополнительную гибкость и безопасность. В качестве примера рассмотрим, как может выглядеть файл remote_settings.py:

import os

ENV = os.getenv('ENV')
DEBUG = False

from .default_settings import *

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv('DB_NAME', 'foo'),
        'USER': os.getenv('DB_USER', 'foo'),
        'PASSWORD': os.getenv('DB_PASS', 'bar'),
        'HOST': os.getenv('DB_HOST', '127.0..0.1'),
        'PORT': os.getenv('DB_PORT', '5432'),
    }
}

Здесь мы решили, что наше приложение будет работать с PostgreSQL во всех средах, но мы разрешаем использовать переменные среды для настройки того, где и как будет осуществляться доступ к базе данных.

Конфигурирование для производства

Django позволяет настраивать, заменять или полностью исключать множество различных компонентов, но большинство из них, установленных по умолчанию, могут работать в большинстве веб-приложений.

Однако некоторые настройки по умолчанию предназначены для того, чтобы вы могли сразу же приступить к локальной разработке, и не подходят для большинства производственных сред. Давайте рассмотрим основные элементы, которые необходимо изменить для того, чтобы подготовить простое веб-приложение к работе в производственной среде.

DEBUG флаг

Для целей разработки и отладки в Django имеется флаг debug, позволяющий включать и выключать некоторые функции, полезные для разработки, так как некоторые из них могут представлять угрозу безопасности. При подготовке приложения к работе следует установить флаг DEBUG = False.

ALLOWED_HOSTS

При установке DEBUG = False Django проверяет, что заголовок HTTP Host соответствует одной из записей в настройке ALLOWED_HOSTS. Это мера безопасности, предназначенная для защиты от атак HTTP Host header. В данной настройке необходимо разрешить имя хоста, на котором вы делаете доступным свое приложение. Если вы динамически создаете окружение, то, возможно, захотите разрешить указывать хосты в качестве переменных окружения, чтобы их можно было подставлять в приложение при его запуске.

Вот пример того, как это может выглядеть в файле remote_settings.py

import os

ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', 'mydefault.com').split(',')

Базы данных

Django предоставляет ORM, позволяющий определять модели данных, что дает возможность обращаться к персистентным данным, не заботясь о том, в каком бэкенде базы данных они будут храниться.

Если вы не вносили изменений в ORM или не использовали сторонние расширения, добавляющие специфические для базы данных функции, то можно легко переходить от одного бэкенда к другому, просто изменив конфигурацию DATABASES в настройках.

Вы должны быть осторожны с изменением бэкендов баз данных для уже существующего проекта. Однако, поскольку разные базы данных по-разному обрабатывают различные сценарии, ваш код может хорошо работать с одним бэкендом, но не так хорошо с другим. Если вы все же решили пойти по этому пути, необходимо провести тщательное тестирование.

По умолчанию Django поставляется настроенным на использование SQLite3, что удобно для локальной разработки и тестирования. Однако в производственной среде вам, скорее всего, захочется использовать что-то другое.

Примечание: существует ряд причин, по которым вы захотите изменить конфигурацию базы данных для локальной разработки и тестирования. Хорошо, если среда тестирования и локальной разработки будет максимально соответствовать рабочей среде, чтобы избежать ситуации, когда в разработке все работает, а в производстве - нет.

Наиболее популярными базами данных с открытым исходным кодом для использования с Django являются PostgreSQL и MySQL, но Django также официально поддерживает SQLite и Oracle, а также ряд сторонних бэкендов, позволяющих использовать другие базы данных.

Каждый бэкэнд базы данных может иметь свои особенности, но настройка Django для доступа к ним довольно похожа.

В демонстрационных целях ниже приведены примеры настройки PostgreSQL и MySQL.

PostgreSQL:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv('DB_NAME', 'foo'),
        'USER': os.getenv('DB_USER', 'foo'),
        'PASSWORD': os.getenv('DB_PASS', 'bar'),
        'HOST': os.getenv('DB_HOST', '127.0..0.1'),
        'PORT': os.getenv('DB_PORT', '5432'),
    }
}

MySQL:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql,
        'NAME': os.getenv('DB_NAME', 'foo'),
        'USER': os.getenv('DB_USER', 'foo'),
        'PASSWORD': os.getenv('DB_PASS', 'bar'),
        'HOST': os.getenv('DB_HOST', '127.0..0.1'),
        'PORT': os.getenv('DB_PORT', '3306'),
    }
}

Как видите, единственное различие между этими двумя конфигурациями заключается в том, где мы указываем движок базы данных (а порты по умолчанию для каждой базы данных разные).

Какой бы бэкэнд базы данных вы ни решили использовать, прочитайте документацию, чтобы понять его конфигурацию, а также ограничения, которые он может иметь в плане того, какие функции Django поддерживаются этим бэкэндом.

Статические файлы

Django предлагает фреймворк для управления статическими файлами и генерации правильного URI для доступа к файлу в соответствии с его конфигурацией в вашей среде. Это очень удобно, так как вы можете ссылаться на статические файлы в своем коде и не беспокоиться о деталях вашего текущего окружения.

Django также предоставляет удобную команду ./manage.py collectstatic, которая собирает статические файлы из всех ваших приложений Django (и любых других пользовательских каталогов, которые вы можете настроить) и затем сбрасывает их в локальную папку, определенную настройкой STATIC_ROOT.

Односерверная конфигурация

Простейшая конфигурация для размещения статических файлов - это использование конфигурации по умолчанию и просто установка параметра STATIC_ROOT в каталог на вашем сервере, где будут размещаться статические файлы.

Например, установите STATIC_ROOT = "/var/www/foo.com/static/" в файле настроек. Когда вы запустите collectstatic в такой конфигурации, все статические файлы окажутся в каталоге /var/www/foo.com/static/ на вашем сервере. Урлы, которые Django будет генерировать для доступа к вашим статическим активам, будут указывать на путь /static/.

Однако, когда Django настроен на производство и DEBUG=False, он не будет обслуживать статические файлы.

Согласно документации Django, их метод обслуживания этих файлов при разработке "крайне неэффективен и, вероятно, небезопасен". Вам необходимо настроить свой веб-сервер (nginx, apache и т.д.) на обслуживание запросов к пути /static/ из каталога /var/www/foo.com/static/. Для небольшого сайта такая настройка вполне подойдет, но для большинства проектов, скорее всего, потребуется что-то более сложное.

Многосерверная конфигурация

Для сайтов, обслуживающих большое количество запросов, целесообразно размещать статические файлы на другом сервере, что позволит снизить нагрузку на сервер приложений.

Одним из вариантов здесь является фактическое размещение статических файлов на отдельном сервере. Существует множество различных способов перенести статические файлы на другой сервер. Решения этой проблемы варьируются от простого использования внешних инструментов типа rsync или fabric до создания собственного бэкенда StaticFileStorage.

В любом случае необходимо изменить параметр STATIC_URL в файле настроек так, чтобы он указывал на URL, который может быть направлен на другой сервер (например, https://static.foo.com/bar/).

Облачный хостинг и сети доставки контента (CDN)

Еще один способ снизить нагрузку на серверы приложений для сайтов с высокой посещаемостью - разместить статические файлы на облачном сервисе или использовать CDN (Content Delivery Network).

Популярная конфигурация для размещения статических файлов в производстве - это размещение их на AWS S3 (или аналогичном сервисе) с последующим использованием CDN типа CloudFront для кэширования статических файлов в сети глобальных серверов.

Это снижает нагрузку на серверы приложений и позволяет ускорить время отклика при загрузке статических файлов по всему миру. Если вы пойдете по этому пути, то вам придется сделать собственный бэкенд StaticFileStorage или использовать расширение Django, например django-storages. Оно решит за вас большинство вопросов, а также позволит указать несколько простых параметров для работы с пользовательской конфигурацией.

Если вы решите использовать CDN (что является целесообразным), то размещение статических файлов на отдельном сервере будет не столь важно, поскольку большинство запросов будет обрабатываться непосредственно CDN. Другим сторонним инструментом, работающим на основе этого предположения, является WhiteNoise, который позволяет упростить конфигурацию и сохранить все приложение в едином пакете.

Медиафайлы (загрузка)

Настройка Django для поддержки загрузки файлов очень похожа на настройку статических файлов - проблема с медиафайлами в основном такая же, как и со статическими файлами, за исключением обработки фактической загрузки файла.

Настройки для работы с загрузкой медиафайлов в Django даже названы аналогично настройкам для статических файлов. Есть настройка MEDIA_URL для указания базового пути для обслуживания запросов на получение медиафайлов и настройка MEDIA_ROOT для указания места хранения загруженных файлов.

Как и в случае статических файлов, если у вас небольшой сайт с одним сервером, можно обойтись простой конфигурацией, в которой загруженные файлы хранятся на сервере приложений в каталоге, расположенном за вашим веб-сервером (nginx, apache и т.д.).

Однако при необходимости масштабирования за пределы одного сервера это решение не масштабируется. Если медиафайл загружен на один сервер, а другой сервер получает запрос на этот медиафайл, то он будет недоступен.

И снова решение состоит в том, чтобы перегрузить хранение ваших медиафайлов на другой сервер, который будет обрабатывать эти запросы. Некоторые из сторонних решений для работы со статическими файлами (например, django-storages) также позволяют решить проблему работы с медиафайлами. Вероятно, их стоит изучить для экономии времени - на случай, если уже есть готовое решение.

Что дальше?

После того как вы настроили приложение для нескольких сред и выяснили все производственные параметры, вы можете решить, как вы будете развертывать свое приложение!

На самом деле запуск приложения - это целый ряд решений, которые необходимо принять: какой веб-сервер запустить, какой сервер приложений запустить, какую среду хостинга использовать, будет ли использоваться виртуализация.

В конечном счете, вы захотите автоматизировать процесс развертывания, чтобы упростить повторение и масштабирование в дальнейшем. Если вы хорошо сконфигурировали свой проект Django для работы в производственной среде, то ни одно из этих решений не будет влиять на работу и функционирование вашего приложения.

В идеале, кто-то другой сможет прийти, взять приложение в том виде, в котором мы его сконфигурировали, и (зная, какие переменные окружения управляют теми или иными настройками) запустить приложение так, как он считает нужным.

Вернуться на верх