Перед развертыванием веб-приложения Django в реальном мире вам необходимо убедиться, что ваш проект готов к работе. Лучше начать их реализовывать раньше. Это сэкономит вам и вашей команде много времени и избавит от головной боли. Вот несколько моментов, которые я узнал по пути:

  1. Используйте pipenv (или requirements.txt + venv). Зафиксируйте Pipefile и Pipefile.lock (или requirements.txt). Назовите свой Venv.
  2. Создайте сценарий быстрого запуска.
  3. Напишите тесты. Используйте Тестовый фреймворк Django и Гипотеза.
  4. Используйте Environment и direnv для управления своими настройками и автоматической загрузки переменных среды.
  5. Убедитесь, что все разработчики совершили свои миграции. Миграции сквоша время от времени. При необходимости сбросьте их. Создайте архитектуру своего проекта для более плавной миграции. Читайте о миграциях.
  6. Используйте непрерывную интеграцию. Защитите свою главную ветку.
  7. Пройдите официальный контрольный список развертывания Django.
  8. Не управляйте собственным сервером, но при необходимости используйте правильную структуру каталогов и используйте Supervisord, Gunicorn и NGINX.

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

Прочтите, чтобы обсудить каждый из пунктов.

Правильно управляйте своими зависимостями и виртуальными средами

Вы и ваша команда должны согласовать способ управления своими зависимостями и виртуальными средами. Я рекомендую либо использовать pipenv, который представляет собой новый способ управления виртуальными средами и зависимостями, либо использовать старый добрый подход создания venv и отслеживания ваших зависимостей с помощью файла requirements.txt.

Использование подхода requirements.txt подвержено человеческой ошибке, поскольку разработчики часто забывают об обновлении списка пакетов. Это не проблема pipenv, поскольку он автоматически обновляет Pipefile. Недостаток pipenv в том, что он существует недостаточно долго. Несмотря на то, что он официально рекомендован Python Software Foundation, у вас могут возникнуть проблемы с его запуском на некоторых машинах. Лично я все еще использую старый подход, но в следующем проекте я буду использовать pipenv.

Использование venv и requirements.txt

Если вы используете Python ≥ 3.6 (должно быть), вы можете просто создать его с помощью python -m venv ENV. Убедитесь, что вы назвали свою виртуальную среду (вместо использования .). Иногда вам нужно удалить виртуальную среду и создать ее заново. Так легче. Кроме того, вам следует добавить каталог ENV в ваш .gitignore файл (я предпочитаю имя ENV вместо venv, .env, ... поскольку оно стоит когда я ls папку проекта).

Для управления зависимостями каждый разработчик запускает pip freeze > requirements.txt каждый раз, когда устанавливает новый пакет, а также добавляет и фиксирует его в репозитории. Они будут использовать pip install -r requirements.txt всякий раз, когда будут извлекать данные из удаленного репозитория.

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

Если вы используете pipenv, вам просто нужно добавить Pipfile и Pipfile.lock в свое репо.

Есть сценарий быстрого запуска

Это помогает убедиться, что ваши разработчики тратят как можно меньше времени на работу над вещами, не имеющими прямого отношения к их работе.

Это не только экономит время и деньги, но и гарантирует, что все они работают в одинаковых средах (например, в одинаковых версиях Python и pip).

Итак, постарайтесь автоматизировать как можно больше задач по настройке.

Более того, наличие одношагового сценария сборки - это то же самое, что и 2-й шаг Джоэла Теста.

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

#!/bin/bash
python3.6 -m venv ENV
source ENV/bin/activate
pip install --upgrade pip
pip install -r requirements.txt 
source .envrc
python ./manage.py migrate
python ./manage.py loaddata example-django/fixtures/quickstart.json
python ./manage.py runserver

Напишите тесты

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

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

Используйте переменные среды для настроек

В вашем файле settings.py хранятся все важные настройки проекта: URL-адрес вашей базы данных, пути к медиафайлам, статические папки и т. Д. У них будут разные значения на вашем компьютере для разработки и на рабочем сервере. Лучший способ решить эту проблему - использовать переменные среды. Первый шаг - обновить ваш settings.py для чтения из переменных среды с помощью environment :

import environ
import os
root = environ.Path(__file__) - 2 # two folders back (/a/b/ - 2 = /)
env = environ.Env(DEBUG=(bool, False),) # set default values and casting
GOOGLE_ANALYTICS_ID=env('GOOGLE_ANALYTICS_ID')
SITE_DOMAIN = env('SITE_DOMAIN') 
SITE_ROOT = root()
DEBUG = env('DEBUG') # False if not in os.environ
DATABASES = {
    'default': env.db(), # Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
}
public_root = root.path('./public/')
MEDIA_ROOT = public_root('media')
MEDIA_URL = '/media/'
STATIC_ROOT = public_root('static')
STATIC_URL = '/static/'
AWS_ACCESS_KEY_ID = env('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = env('AWS_SECRET_ACCESS_KEY')
..

Чтобы избежать загрузки ваших envvars вручную, настройте direnv на ваших машинах для разработки и сохраните envvars в файле .envrc в каталоге вашего проекта. Теперь, когда вы cd заходите в папку проектов, переменные среды загружаются автоматически. Добавьте .envrc в свой репозиторий (если все разработчики используют одинаковые настройки) и убедитесь, что вы запускаете direnv allow при каждом изменении в .envrc файле.

Не используйте direnv на рабочем сервере. Вместо этого создайте файл с именем .server.envrc, добавьте его в .gitignore и поместите туда производственные настройки. Теперь создайте сценарий runinenv.sh для автоматического получения переменных среды из .server.envrc, активируйте виртуальную среду и выполните указанную команду. Вы увидите, как это используется, в следующем разделе. Вот как должен выглядеть runinenv.sh (ссылка на GitHub).

#!/bin/bash
WORKING_DIR=/home/myuser/example.com/example-django
cd ${WORKING_DIR}
source .server.envrc
source ENV/bin/activate
exec $@

Правильно управляйте миграциями

Миграции Django - это здорово, но работать с ними, особенно в команде, не так просто.

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

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

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

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

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

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

Использовать непрерывную интеграцию

Идея CI проста: запускать тесты, как только будет добавлен новый код.

Используйте решение, которое хорошо интегрируется с вашей платформой управления версиями. В итоге я использовал CircleCI. CI особенно полезен, когда вы работаете с несколькими разработчиками, поскольку вы можете быть уверены, что их код проходит все тесты, прежде чем они отправят запрос на перенос. Опять же, как видите, очень важно иметь хорошо продуманные тесты. Более того, убедитесь, что ваша основная ветка защищена.

Контрольный список развертывания

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

Если вам необходимо управлять собственным сервером…

Есть много веских причин не управлять собственным сервером. Docker обеспечивает большую мобильность и безопасность, а бессерверные архитектуры, такие как AWS Lambda, могут предоставить вам еще больше преимуществ за меньшие деньги.

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

Используйте правильную структуру каталогов

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

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

example.com/
   example-django/
   homepage/

Используйте Supervisord, NGINX и Gunicorn

Конечно, manage runserver работает, но только для быстрой проверки. Для чего-нибудь серьезного вам нужно использовать подходящий сервер приложений. Gunicorn - это то, что вам нужно.

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

Supervisord нужен файл конфигурации, в котором мы сообщаем, как мы хотим запускать наши процессы. И это не ограничивается нашим сервером приложений. Если у нас есть другие длительные процессы (например, сельдерей), мы должны определить их в /etc/supervisor/supervisord.conf. Вот пример (также на GitHub):

[supervisord]
nodaemon=true
logfile=supervisord.log
[supervisorctl]
[inet_http_server]
port = 127.0.0.1:9001
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[program:web-1]
command=/home/myuser/example.com/example-django/runinenv.sh gunicorn example.wsgi --workers 3 --reload --log-level debug --log-file gunicorn.log --bind=0.0.0.0:8000
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/var/log/example-django/web-1.log
stderr_logfile=/var/log/example-django/web-1.error.log
user=myuser
directory=/home/myuser/example.com/example-django
[program:celery-1]
command=/home/myuser/example.com/example-django/runinenv.sh celery worker --app=example --loglevel=info
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/var/log/example-django/celery-1.log
stderr_logfile=/var/log/example-django/celery-1.error.log
user=myuser
directory=/home/myuser/example.com/example-django
[program:beat-1]
command=/home/myuser/example.com/example-django/runinenv.sh celery beat --app=example --loglevel=info
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/var/log/example-django/beat-1.log
stderr_logfile=/var/log/example-django/beat-1.error.log
user=myuser
directory=/home/myuser/example.com/example-django
[group:example-django]
programs=web-1,celery-1,beat-1

Обратите внимание, как мы используем здесь runinenv.sh (строки 14, 24 и 34). Также обратите внимание на строку 14, где мы говорим gunicorn отправить 3 рабочих. Это число зависит от количества ядер вашего сервера. Рекомендуемое количество рабочих: 2 * количество_корей + 1.

Вам также понадобится обратный прокси-сервер для подключения вашего сервера приложений к внешнему миру. Просто используйте NGINX, так как он имеет широкую пользовательскую базу и его очень легко настроить (вы также можете найти этот код на GitHub):

server {
    server_name www.example.com;
    access_log  /var/log/nginx/example.com.log;
    error_log    /var/log/nginx/example.com.error.log debug;
    root  /home/myuser/example.com/homepage;
    sendfile on;
# if the uri is not found, look for index.html, else pass everthing to gunicorn
    location / {
 index index.html;
 try_files $uri $uri/
     @gunicorn;
    }
# Django media
    location /media  {
        alias /home/myuser/example.com/example-django/public/media;      # your Django project's media files
    }
# Django static files
    location /static {
        alias /home/myuser/example.com/example-django/public/static;   # your Django project's static files
    }
location @gunicorn {
proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
 #proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_redirect off;
proxy_pass http://0.0.0.0:8000;
    }
client_max_body_size 100M;
listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
    server_name example.com;
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; # managed by Certbot
    return 301 https://www.example.com$request_uri;
}
server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
listen 80 default_server;
    listen [::]:80 default_server;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

Сохраните файл конфигурации в /etc/nginx/sites-available и создайте символическую ссылку на него в /etc/nginx/sites-enabled.

Надеюсь, этот пост оказался для вас полезным. Пожалуйста, дайте мне знать, что вы думаете об этом, и покажите это ❤, если хотите.