Я закончил курсы "Fullstack разработчик на Python" от одной известной компании. Обучение завершено успешно, но не было ощущения полноценности — на курсах не учили, как сделать самостоятельно деплой приложения на Django. И никто из студентов не задавался эти вопросом 😁
Так что я решил закрыть этот вопрос и все-таки пройти путь по развертыванию django-приложения, так как, возможно, больше никогда такого случая не представится. Потом я уже могу не вернуться к этой теме, так как это потребует мучительного вспоминания и повторного прохождения материала. Все должно быть вовремя.
В этой статье я хочу дать небольшой гайд для выпускников подобных курсов и заодно рассказать про российский облачный сервис (еще раз внимательно прочитал их статью и увидел, что она опубликована буквально за 3 недели до моих тестов - то есть мне повезло).
Если ты хочешь узнать, как запустить DJango-приложение и не потерять время, как я...
Мой путь джедая
Публикацию первого приложения на Django для студента курсов можно разбить на 3 этапа:
Написание приложения во время обучения. Приложение стартует, радуемся когда страница открывается, гордимся собою.
Поиск сервиса для деплоя приложения, выбор и изучение сервиса, набивание шишек.
Внезапное открытие, что приложение, которое успешно запускается локально, не хочет работать в проде.
Еще в середине курса, когда питон закончился и начался веб, я стал посматривать статьи на тему деплоя. Нашел среди них статью про heroku здесь на хабре, отложил себе в заметки. Этот путь казался самым простым и понятным. Уже тогда я откуда-то знал, что на курсах этого не будет и мне предстоит самостоятельно пройти этот путь (на самом деле я не собирался разворачивать приложение в продашене, но жаба заставила).
Вариант с развертыванием полноценного сайта на Linux сервере я не стал рассматривать, так как это на уровень сложнее. Хотя, сейчас, после того как я смог добиться работы 3-х приложений в облачном сервисе, есть понимание, как сделать это. Но все таки я считаю, что наиболее правильно для студента будет именно тот путь, который прошел я. И только потом идти на следующий этап - развертывание сервера. Итак, начнем.
Выбор сервиса
Начинаю поднимать свои заметки и искать гайды по деплою в Heroku. И в одной из статей прошлого года на хабре в комментах нахожу информацию, что "Heroku уже всё" - окончательно скурв монетизировался и стал неинтересен. Там же в комментах нахожу упоминание Amvera, которое мне ничего не говорит.
Также нахожу в интернете статью по pythonanywhere, вроде как альтернатива (я собрал список статей на тему деплоя в конце). Начинаю вникать и понимаю, что они не подходят. Не то, чтобы я не мог себе это позволить, но просто не было желания платить деньги за сильно урезанные возможности и доплаты за любой чих. Дело в принципе. В итоге я чуть ли ни в тот же день решил попробовать облачный сервис Amvera. Было это в субботу...
Знакомство с Amvera: как я провел первые выходные
Регаюсь, быстро пробегаюсь по документации и статье на хабре — вижу, что это одно и то же. Автор статьи аккуратно пропустил возможные проблемы и показал как опубликовать простейшее Flask-приложение. Я не знаком с этим фреймворком, хотя вижу, что, действительно, запускается как любое python приложение, ничего сложного вроде. Но django — "это другое", и единственное, что я понял — есть такой файл amvera.yml, который является конфигурационным файлом для сборки и развертывания Docker (внезапно эта тема немного пригодилась на уровне папки /data, возникли ассоциации 😃 )
Была — не была, приступаем. Но начать я хочу с простейшего Django-приложения, чтобы в будущих разбирательствах с ошибками максимально исключить косяки приложения и тратить время только на особенности сервиса. Так как в Django я далеко не спец, и наложение ошибок из разных областей может принести много боли.
Начинаю искать у себя на компьютере простейший проект, у которого есть файл requirements.txt и нахожу. К сожалению, файл не самый правильный, он был создан с помощью команды pip freeze > requirements.txt
поэтому избыточен.asgiref==3.6.0
attrs==22.2.0
autobahn==23.1.2
Automat==22.10.0
cffi==1.15.1
channels==4.0.0
constantly==15.1.0
cryptography==40.0.1
daphne==4.0.0
Django==4.2
djangorestframework==3.14.0
hyperlink==21.0.0
idna==3.4
incremental==22.10.0
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycparser==2.21
pyOpenSSL==23.1.1
pytz==2023.3
service-identity==21.1.0
six==1.16.0
sqlparse==0.4.3
Twisted==22.10.0
twisted-iocpsupport==1.0.3
txaio==23.1.1
typing_extensions==4.5.0
tzdata==2023.3
zope.interface==6.0
И это оказалось даже хорошо, так как покажет, почему в requirements.txt нужно вручную писать только конкретные пакеты. В остальном проект очень простой - это чат. Полгода назад, когда на курсах дали задание по написанию чата, я решил сначала найти что-то в Youtube, чтобы понять, как это делать. Это было отличное задание — все студенты на нём испытывают шок, потому что оно требует знания материалов, которые мы не проходили.
При написании статьи я поднял свои заметки и выяснил, что это проект я делал по этому видео — https://www.youtube.com/watch?v=IpAk1Eu52GU, к нему есть и код в репозитории https://github.com/tomitokko/django-chat-app, так что желающие могут пройти такой же путь и запустить его в Amvera Cloud.
Вот запись из моего дневника:
Клонировал его и запустил - действительно работает без Channels - просто каждую секунду выполняет запрос к серверу.
На этом можно переходить к самому процессу заливки проекта на Amvera. Идем на https://amvera.ru/ и регистрируемся - сразу получаем 111 рублей на знакомство.
Создаю проект и 2 часа пытаюсь добиться результата.
Как видите, я уже был готов отказаться от Amvera и поискать что-то другое. Но в итоге вернулся и очень рад. Потому что нужно просто знать, как работать с этим сервисом.
Я долго бился головой о репозиторий и не мог понять, почему не могу запушить изменение в репозиторий amvera. Хотя ответ лежал на поверхности, но я не мог поверить, что это правда. Потом то в переписке поддержка подтвердила, что я сам дурак.
В ответ я получил объяснение
Когда Вы создаете проект, у Вас есть два пути
Загрузить через интерфейс
Загрузить через гит.
На сколько я понял, Вы воспользовались вторым. Потом на этапе задания конфигурации стоит предупреждение, что это создаст новый коммит в репозитории. Соответственно, если Вы задали конфигурацию, то оттуда и взялся коммит.
Конечно, я был неправ, но я почему-то не заметил этого. Хотя видно, что разработчики старались сделать интерфейс максимально простым и понятным, некоторые вещи я стал замечать и понимать только после того, как смог успешно запустить первое приложение.
Итак, вот как работает создание проекта.
Вы жмете "Создать" и появляется окно. Вводите название проекта (Test) и выбираете "Тарифный план"
Жмете "Далее" и попадаете на этап "Загрузка данных". Я рекомендую ничего не менять и остаться на "Через Git". Когда разберетесь, можете попробовать вариант "Через интерфейс". Сам я второй способ еще не использовал.
После нажатия "Далее" попадаем на третий этап "Конфигурация". И вот тут есть 2 момента. Первый — вы не можете пройти дальше (нажать "Завершить"), если не задали конфигурацию. Насколько я помню, я указал в качестве окружения Python и выбрал версию 3.9. Это действие и создало коммит в репозиторий amvera.
И второй момент - если даже нажать "Отмена", то проект уже создан. И можно в него пушить из локального репозитория. Что, в общем-то, неплохо 😉.
Поэтому, кратко повторим как создать проект:
Нажимаем "Создать", пишем название проекта (чем короче, тем лучше — вам потом его еще удалять нужно будет). Выбираем "Тарифный план" и жмем "Далее"
На этапе "Загрузка данных" ничего не меняем и жмем "Далее"
На последнем этапе "Конфигурация" задаем любую конфигурацию и жмем "Завершить". Либо жмем "Отмена" — проект в любом случае уже будет создан.
Теперь осталось залить наш локальный проект в репозиторий amvera, чтобы началась сборка. Для этого необходимы 2 файла:
requirements.txt со списком модулей/зависимостей. Вы из курса знаете как его написать или создать.
Конфигурационный файл amvera.yml, который определяет процесс установки зависимостей, сборки проекта и запуска приложения.
Файл amvera.yml в статье (и в документации) выглядит так, берем его за основу:
meta:
environment: python
toolchain:
name: pip
run:
command: gunicorn --bind 0.0.0.0:5000 app:app
containerPort: 5000
Добавляем в файл requirements.txt строчку gunicorn==20.1.0 как написано в статье. В будущем вы можете указать и другую версию.
Команду запуска в amvera.yml нужно изменить. Если на локальном компьютере при отладке вы запускаете Django-приложение с помощью python manage.py runserver
, то здесь вам уже понадобится WSGI-сервер gunicorn, который будет использоваться для запуска вашего Django-приложения. Статья на эту тему - Введение в WSGI-серверы: Часть первая
У меня такая структура проекта:
djangochat/
├── djangochat/
│ ├── init.py
│ ├── settings.py
│ ├── wsgi.py
│ └── ...
├── manage.py
└── .
Поэтому, команда запуска в amvera.yml должна выглядеть так:
run:
command: gunicorn djangochat.wsgi:application --bind 0.0.0.0:80
Таким образом, у меня получается такой amvera.yml:
meta:
environment: python
toolchain:
name: pip
version: 3.9
build:
requirementsPath: requirements.txt
run:
command: gunicorn djangochat.wsgi:application --bind 0.0.0.0:80
persistenceMount: /data
containerPort: 80
Настало время отправить наш проект в репозиторий проекта amvera. Как пишут разработчики сервиса в статье:
Если у вас уже есть приложение, которое вы хотите развернуть в Amvera, но он уже использует другой репозиторий git , можно привязать дополнительный remote к вашему репозиторию.
Это означает, "что если у вас уже есть готовое приложение (например, находится в другом репозитории git), и вы хотите развернуть его в Amvera, вы можете связать дополнительный удаленный репозиторий (remote) с вашим текущим репозиторием git".
Я всегда работаю с git через командную строку. Для этого встаем в корень проекта и вызываем cmd. Далее выполняем привязку к репозиторию amvera
git remote add amvera URL
Посмотреть список привязанных репозиториев можно с помощью командыgit remote -v
В случае ошибок (если никак не удается запустить проекта в amvera) вам также понадобится отвязать удаленный репозиторий amvera , для этого служит команда git remote remove amvera
В самом проекте есть готовые команды-подсказки для для привязки и пуша изменений в репозиторий проекта. Наведите мышку и появится иконка копирования справа в конце строки.
Конечно, нужно предварительно перед пушем комитить сделанные изменения с помощью соответствующих команд:
git add .
git commit -m "текст комита"
Итак, всё готово для пуша в репозиторий amvera, но у меня не вышло. Потратив кучу времени (переписка выше), смог сделать паш с флагом force, то есть -f или --force :git push amvera master -f
Будьте готовы к такому варианту. Выполняю принудительный пуш и получаю ошибку на стадии сборки проекта:
Трудно сказать, сколько в доGPT-шную пору я бы искал причину ошибки, так как сразу же обратился к коллективному разуму. И сразу получил ответ:
Ошибка указывает на проблему с поиском заголовочного файла io.h. Этот файл обычно ассоциируется с библиотекой Windows и может отсутствовать в среде выполнения Linux.
Ваш проект, вероятно, использует библиотеку twisted_iocpsupport, которая, похоже, предназначена для Windows. Попытка компиляции этой библиотеки на Linux вызывает ошибку, так как io.h не является стандартным заголовочным файлом в Linux.
Возможные пути решения:
...
Я не стал проверять все варианты решения, а просто в requirements.txt тупо закомментил строку , которая вызывает ошибку — typing_extensions==4.5.0:
Twisted==22.10.0
twisted-iocpsupport==1.0.3
txaio==23.1.1
# typing_extensions==4.5.0
tzdata==2023.3
zope.interface==6.0
Если бы файл requirements.txt писался правильно, вручную с указанием только необходимых пакетов, то наверняка эту ошибку не получили бы. Не проверял.
Теперь после правки requirements.txt сборка проекта проходит без ошибок, но приложение не запускается. У меня не сохранились все логи моих попыток (их было много), в итоге пришел к такой конфигурации amvera.jml, которая почти запустилась
Но это была еще не победа, дальше упали на запуске приложения
Я решил и эту проблему, добавив настройку окружения --env в запуск сервера gunicorn
Из скриншота видно, что я передаю полный путь к файлу wsgi внутри папки проекта djangochat, внутри которого одноименная папка проекта djangochat, внутри которого находится файл wsgi, в котором и есть приложение application. Звучит длинно и непонятно для новичков. С помощью полного абсолютного пути к wsgi и параметра --env для указания файла settings.py мне удалось запустить приложение. Но оно тут же упало на следующем шаге, когда из settings.py попыталось обратиться к приложению chat:
Тут я понял, что эта игра может продолжаться вечно, даже если я внесу какие-то правки в settings.py. Я что-то делаю в корне неправильно.
И тут я вспоминаю, что для локального запуска django приложения мы должны сначала спуститься на один уровень вниз с помощью команады cd имя_папки_проекта.
Тоже самое нужно сделать и при запуске gunicrom: когда мы запускаем gunicorn, то он запускается на самом верхнем уровнее проекта. Чтобы он смог найти файл по штатному пути djangochat/wsgi.py, нам нужно выполнить команду cd djangochat. Напомню структуру проекта:
djangochat/
├── djangochat/
│ ├── init.py
│ ├── settings.py
│ ├── wsgi.py
│ └── ...
├── manage.py
└── .
Вспоминаем, что в Linux мы можем объединять команды с помощью &&. Поэтому команда запуска будет такой:
После этого приложение смогло запуститься и проблемы с поиском settings.py и приложения chat.py пропали. Да и указывать параметр --env уже не требуется. Когда пройдешь весь путь, то понимаешь, что всё логично и просто.
Не помню, выдавало в браузере по адресу проекта https://django-chat-james.amvera.io/, но это и не важно уже. Минут через 20 все же догадался опять обратиться у ментору, который никогда не спит и знает почти всё:
Вносим правки и запускаем, почти получилось
Вносим правки в settings.py и Аллилуйя!
Но не всё так просто
Если попыться зайти в комнату, то опять падаем.
На самом деле здесь оказалась все проще, нужно добавить в settings еще параметр CSRF_TRUSTED_ORIGINS, и на этом проблемы конкретно с этим приложением завершились.
После этой правки приложение запустилось и стало работать. А всего мне понадобилось 38 комитов, чтобы запустить это приложение в amvera. Я не мог и предположить, что убью столько времени на первый деплой простого чат-приложения.
Рекомендации по деплою Django приложения:
Пишите вручную зависимости в requiremants.txt, указывайте конкретные имена пакетов/библиотек, необходых для работы. Не используйте для этого
pip freeze > requirements.txt
Для запуска в синхронном режиме укажите в requirements.txt пакет gunicorn.
Если какой-то пакет при сборе дает оишбку, попробуйте отключить его через комментирование.
Для запроса лога сборки используйте кнопку внизу (я не догадался, пришлось общаться с техподдержкой)
Для запуска сервера gunicorn необходимо спуститься внутрь проекта командой cd имя_проекта. Конфигурационный файл amvera.yml выглядит примерно так:
meta:
environment: python
toolchain:
name: pip
version: 3.9
build:
requirementsPath: requirements.txt
run:
command: cd <имя_папки_проекта> && ...
persistenceMount: /data
containerPort: 80Для запуска чата (или вообще для работы в асинхронном режиме) вместо wsgi используется asgi. Если вы запускали django-channels локально, то видели в логах примено такой вывод 'Starting ASGI/Daphne':
System check identified 1 issue (0 silenced).
April 09, 2023 - 13:33:36
Django version 4.2, using settings 'chatapp.settings'
Starting ASGI/Daphne version 4.0.0 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
Приведу просто ответ ChatGPTПри удачном запуске проекта вы увидите логи приложения:
Для полноценной работы проекта необходимо в settings.py прописать страницы, с которых будут идти обращения к вашему приложению. Они пишутся в переменные ALLOWED_HOSTS и CSRF_TRUSTED_ORIGINS.
Данное приложение не использует css-файлов и js-скриптов, поэтому больше никаких требований для запуска нет. Лучше всего для изучения пройти весь этот путь самостоятельно, склонировав проект из https://github.com/tomitokko/django-chat-app. Мой вариант этого проекта будет доступен какое-то время по адресу https://django-chat-james.amvera.io/.
Выводы по работе с сервисом:
Сервис простой и понятный когда набьешь шишки
Если все сделал правильно, но приложение так и не запускается — просто удалите проект в amvera и создайте заново. Проверено — помогает. Техподдержка в курсе, но пока не может победить такие моменты.
Читайте логи сборки и запуска приложения — если там ошибки, покажите боту ChatGPT. В 99% случаев он знает причину и вам даже не нужно ничего объяснять ему. В некоторых случаях придется гуглить, но к этому моменту вы уже будете примерно знать возможные причины и получите от ChatGPT варианты запросов к поисковикам. Можете даже спросить у ChatGPT как сформулировать на английском запрос для поисковика по данной ошибке. Не пробовал, но уверен на 100%, то он поможет.
Поднимаем Django проект посложнее: 2-ые выходные
Вдохновившись успехом, решаю развернуть другое приложение — это финальный проект курса, причем он проще промежуточного. Так бывает :)
Он не использует асинхронных запросов или вебсокетов, это просто сервисная книжка со справочниками и журналами работ и поломок. Сразу приведу содержимое файла amvera.jml, видно что оно отличается только командой запуска:
meta:
environment: python
toolchain:
name: pip
version: 3.9
build:
requirementsPath: requirements.txt
run:
command: cd project && gunicorn project.wsgi:application --bind 0.0.0.0:80
persistenceMount: /data
containerPort: 80
Параметр persistenceMount в обоих случах мне не нужен был, но я указываю его, чтобы вы знали, где должны быть ваша база данных, если вам нужно сохранять её между перезапусками проекта в Amvera/
Данный проект я развернул по адресу https://silant-2-james.amvera.io/. Первая версия была в https://silant-james.amvera.io/, но я не смог добиться ее запуска, хоть и убил вторые выходные. Никто пока так и не знает, как ее заставить работать, хотя техподдежка проверила на своей стороне и подтвердила, что у меня в проекте ошибок нет. Просто я в процессе многочисленных экспериментов внёс какие-то изменения в проекте на стороне amvera и никакие чистки и пересборки не помогают.
Так вот, второе приложение я запустил со второй попытки уже без моих ошибок в amvera.jml и оно сразу поднялось. Но выглядело странно
Сразу стало понятно по логам и по F12, что не заружаются файлы стилей. Сначала я пробовал решить вопрос через настройку STATIC_ROOT и STATICFILES_STORAGE, даже выполнил команду python manage.py collectstatic
и отправил пуш с папкой собранных файлов в в репозиторий amvera. После этого первая версия проекта Silant больше никогда не запустилась. В итоге пришлось создавать второй.
В конечном итоге я понял, что все это не работает и опять задал вопрос:
И это помогло - проект запустился по адресу https://silant-2-james.amvera.io/ (будет скоро потушен).
Выводы:
В каждом проекте Django могут возникнуть новые ошибки, ответы на которые новички не знают. Может потребоваться некоторое время, чтобы попробовав разные варианты, найти решения.
Если проект упорно не запускается — убейте его и создайте заново. При этом имя проекта лучше изменить — первый проект назывался silant, а второй уже silant2. Почему нельзя сохранять старое имя? Попробуйте, будут новые интересные моменты. Хотя, если время между удалением старого и созданием нового проекта с одинаковым именем пройдет достаточно много времени, то возможно, никаких проблем не будет. Но я не хочу это проверять.
Перед удалением проекта в amvera не забудьте отвязать свой локальный репозиторий от удаленного репозитория в amvera. Может ничего страшного и не случится, но я бы не проверял на себе. Хотя, может вам будет интересно.
Третье и последнее приложение
Изначально у меня была цель запустить два разных учебных проекта — финальный проект курса, который я описал только что и приложение для общения в чат-комнатах.
Теперь я знаю, как запускать ASGI, как обеспечить загрузку стилей и картинок, как прописать в settings.py страницы. Проблем быть не должно. Но все приложения в Django имеют особенности. То что работало локально через python manage.py runserver
, вдруг отказывается запускаться на проде. Вот финальное содержимое amvera.yml :
meta:
environment: python
toolchain:
name: pip
version: 3.9
build:
requirementsPath: djangochat/requirements2.txt
run:
command: cd djangochat && gunicorn -k uvicorn.workers.UvicornWorker django_chat.asgi:application --bind 0.0.0.0:80
persistenceMount: /data
containerPort: 80
Можно увидеть из файла, что я даже использовал опцию preload, но она не помогла. Хотя так и осталась в конфигурации запуска.
После долгих пыток ChatGPT была найдена причина
То есть ChatGPT говорит, что импорт нужно перенесте непосредственно в те функции, где они используются. Это обеспечит загрузку нужных модулей до их использования. Догадаться самому новичку практически невозможно. Вот такую небольшуюя сделал и приложение наконец-то запустилось.
В итоге я опубликовал третье и последнее приложение https://djangochat-e6-james.amvera.io/ (будет доступно какое-то время после публикации статьи).
Выводы по работе с Django
Всего 3 простых приложения на django, а сколько при этом нового узнал. Уверен, что практически для каждого нового проекта на django будут вылезать свои ошибки и нюансы. И мой опыт показывает, что все эти вопросы решаются даже без обращения к опытным разработчикам.
Если бы не было ChatGPT, то, возможно, я бы никогда не добрался до конца. Потому что пинг до ментора занимает от нескольких часов до нескольких дней. И если на каждом вопросе будут такие задержки, то проще всё бросить, чем биться об стену.
Я не привожу здесь конкретных блоко кода и не даю ссылки на свои репозитории, так как цель статьи в том, чтобы показать новичку, как решать возникающие вопросы.
Я потратил на свои опыты примерно 2 недели, по моим следам вы сможет повторить всё в течение одного выходного дня. Удачи!
Статьи, которые могут быть полезными. Порядок отражает мое личное предпочтение:
Как запустить сайт с помощью Gunicorn: Django, Flask, что угодно
Деплой приложения на сервер через push в Git. Или как перенести сайт или бот на хостинг в 3 действия
Как установить Django, Nginx и Gunicorn на виртуальный сервер
Еще варианты в статье 10 Лучших Альтернатив Heroku в 2023 Году