Django в дикой природе: советы по выживанию при развертывании

django v dikoj prirode sovety po vyzhivaniyu pri razvertyvanii?v=1656574107

Али Алави

D6-FKRsVGvi72K1n0wh6g2AgzxjIkQ0ypLL2
Фото Тхана Трана на Unsplash

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

  1. Используйте pipenv (или requirements.txt+venv). Зафиксируйте Pipefile и Pipefile.lock (или requirements.txt). Назовите свой venv.
  2. Мать скрипт быстрого запуска.
  3. Напишите тесты. Используйте тестовый фреймворк Django и гипотеза
  4. Используйте окружение и 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 имеет достойную тестовую систему. Вы также можете использовать основы тестирования на основе свойств, таких как Hypothesis, которые помогут вам писать более короткие, математически суровые тесты для вашего кода. Во многих случаях написание тестов на основе свойств происходит быстрее. На практике вы можете использовать эти фреймворки для написания комплексных, легких для чтения и записи тестов.

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

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

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, где мы рассказываем гунириг отправить 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 
    }
    
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 
    
}

server {
    if ($host = www.example.com) {
        return 301 
    } # managed by Certbot
    
if ($host = example.com) {
        return 301 
    } # managed by Certbot
    
listen 80 default_server;
    listen [::]:80 default_server;
    server_name example.com www.example.com;
    return 301 
}

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

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *