Установка Django на VestaCP с WSGI (CentOS 7)
- 0. Установка VestaCP
- I. Добавление шаблона для WSGI
- II. Создание сайта
- III. Установка свежей версии Python
- IV. Виртуальное окружение проекта
- V. Установка Django
- VI. Создаем проект Django
- VIII. Далее
- Решение возможных ошибок
Условимся что окружение Python будем создавать в папке:
/home/USERNAME/web/DOMAIN/private/env
А проект Django в папке:
/home/USERNAME/web/DOMAIN/private/src
0. Установка VestaCP
Будем предполагать, что панель управления у вас уже установлена и настроена как нужно. Если нет, то можно воспользоваться официальной инструкцией.
I. Добавление шаблона для WSGI
На панели VestaCP можно запуска приложения на Python для этого необходимо создать новый шаблон для сайтов и назначать его сайта на Python.
Шаблон можно скачать с официального сайта панели VestaCP, но там есть ошибки, например, ошибка конфига Include -> IncludeOptional. Ну и настройки нам не очень понравились=) Подробнее тут.
Ручное создание шаблона:
1. Перейдите в папке с шаблонами VestaCP
# cd /usr/local/vesta/data/templates/web/httpd
2. Создаем файл шаблона настроек Apache для домена без SSL
# vi wsgi.tpl
Вставляем код:
<VirtualHost %ip%:%web_port%>
ServerName %domain_idn%
%alias_string%
ServerAdmin %email%
DocumentRoot %docroot%
ScriptAlias /cgi-bin/ %home%/%user%/web/%domain%/cgi-bin/
Alias /vstats/ %home%/%user%/web/%domain%/stats/
Alias /error/ %home%/%user%/web/%domain%/document_errors/
SuexecUserGroup %user% %group%
CustomLog /var/log/%web_system%/domains/%domain%.bytes bytes
CustomLog /var/log/%web_system%/domains/%domain%.log combined
ErrorLog /var/log/%web_system%/domains/%domain%.error.log
<Directory %home%/%user%/web/%domain%/stats>
AllowOverride All
</Directory>
<IfModule mod_wsgi.c>
WSGIScriptAlias / %docroot%/wsgi.py
WSGIDaemonProcess %domain% socket-user=%user% processes=6 display-name=%{GROUP} python-home=%home%/%user%/web/%domain%/private/env python-path=%home%/%user%/web/%domain%/private/src
WSGIProcessGroup %domain%
WSGIApplicationGroup %{GLOBAL}
</IfModule>
<Directory %docroot%>
AllowOverride FileInfo
Options ExecCGI Indexes
MultiviewsMatch Handlers
Options +FollowSymLinks
Order allow,deny
Allow from all
</Directory>
IncludeOptional %home%/%user%/conf/web/%web_system%.%domain%.conf*
</VirtualHost>
Обратим внимание на некоторые настройки:
WSGIScriptAlias URL-path file-path|directory-path [options]
адрес wsgi скрипта в Django он находится по адресу /path/to/project/src/wsgi.py
Дополнительно необходимо разрешить доступ к этому скрипту через блок <Files wsgi.py>, т.к. он находится в приватной папке.
WSGIDaemonProcess name [options]
Настройки процесса, обрабатывающего запросы пользователей к сайту. Официальная документация.
- name - первый параметр
- имя процесса, должно быть уникальным для всей системы, самое простое это задать туда название домена.
- socket-user=USER
- это пользователь, из под которого создается файл сокета.
- user=USER
- это пользователь из под которого создается процесс, т.е. по логике можно установить значение «apache» и должен быть доступ к файлу сокета, но у нас не сработал.
- processes=num
- количество создаваемых процессов, влияет на количество одновременно обрабатываемых запросов, по-умолчанию 1.
- threads=num
- количество потоков для обработки запросов пользователей в каждом процессе, по умолчанию 15. Для обработки большего количества одновременных запросов лучше увеличить processes. Этот параметр следует увеличивать, если ваш код не использует много ресурсов процессора, но в нем много операций ввода-вывода.
- display-name=value
- имя процесса в списке процессов системы.
- python-home=directory
- путь до виртуального окружения Python, которое следует использовать процессу, должен указывать на папку сгенерированную при помощи venv, т.е. в нашем случае это папке env.
- python-path=directory
- список директорий с модулями Python, в нашем случае это папка с проектом Django.
WSGIProcessGroup %{GLOBAL}|%{ENV:variable}|name
Имя группы процесса. %{GLOBAL} = пустое имя группы.
3. Создаем файл шаблона настроек Apache для домена с SSL
# vi wsgi.stpl
Настройки идентичные, добавляются только настройки SSL:
<VirtualHost %ip%:%web_port%>
ServerName %domain_idn%
%alias_string%
ServerAdmin %email%
DocumentRoot %docroot%
ScriptAlias /cgi-bin/ %home%/%user%/web/%domain%/cgi-bin/
Alias /vstats/ %home%/%user%/web/%domain%/stats/
Alias /error/ %home%/%user%/web/%domain%/document_errors/
SuexecUserGroup %user% %group%
CustomLog /var/log/%web_system%/domains/%domain%.bytes bytes
CustomLog /var/log/%web_system%/domains/%domain%.log combined
ErrorLog /var/log/%web_system%/domains/%domain%.error.log
SSLEngine on
SSLVerifyClient none
SSLCertificateFile %ssl_crt%
SSLCertificateKeyFile %ssl_key%
%ssl_ca_str%SSLCertificateChainFile %ssl_ca%
<Directory %home%/%user%/web/%domain%/stats>
AllowOverride All
</Directory>
<IfModule mod_wsgi.c>
WSGIScriptAlias / %docroot%/wsgi.py
WSGIDaemonProcess %domain% socket-user=%user% processes=6 display-name=%{GROUP} python-home=%home%/%user%/web/%domain%/private/env python-path=%home%/%user%/web/%domain%/private/src
WSGIProcessGroup %domain%
WSGIApplicationGroup %{GLOBAL}
</IfModule>
<Directory %docroot%>
AllowOverride FileInfo
Options ExecCGI Indexes
MultiviewsMatch Handlers
Options +FollowSymLinks
Order allow,deny
Allow from all
</Directory>
IncludeOptional %home%/%user%/conf/web/%web_system%.%domain%.conf*
</VirtualHost>
4. При необходимость можно создать файл с bash скриптом который будет выполнять необходимые вам специфичные действия при создании сайта:
# vi wsgi.sh
Если вы не знаете, что должен делать ваш скрипт, то просто пропустите этот шаг.
II. Создание сайта
Далее в панели VestaCP необходимо создать и настроить сайт, при создании для параметра «Шаблон Web» необходимо выбрать новый «wsgi».
III. Установка свежей версии Python
По-умолчанию в CentOS 7 установлен Python 2.7. Зачастую для работы над современными проектами этого будет недостаточно, поэтому устанавливаем дополнительную версию.
На момент написания инструкции в репозитории epel доступн Python 3.6. Если этой версии достаточно, то это самый простой способ установки.
1. Подключаем репозиторий Epel:
# sudo yum install epel-release
2. Устанавливаем альтернативную версию с инструментами разработки и менеджером пакетов
# sudo yum install python3 python3-devel python3-pip
3. Проверяем установку. Данная версия будет доступна по алиасу:
# python3 -V
Python 3.6.3
4. Можно заменить стандартную версию, изменив символьную ссылку python с /usr/bin/python2 на /usr/bin/python3. Но перестанут работать некоторые пакеты, использующие Python (например, yum).
Если для работы требуется более свежая версия, то необходимо собрать ее из исходников с официального сайта Python.
IV. Виртуальное окружение проекта
На одном сервере могут работать несколько приложений и, например, какое-то приложение работает на старой версии Python и Django, а новые приложения должны работать на новых версиях для этого и требуется виртуальное окружение, т.е. Django и другие модули Python устанавливаются не глобально в систему, а непосредственно в папку проекта. Также это дает возможность устанавливать пакеты без прав суперпользователя.
В Python встроен менеджер виртуальных окружений venv. Официальная документация.
Для того чтобы исходные файлы проекта не были доступны извне можно установить исходники в стандартную папку домена в VestaCP «private».
1. Переключаемся на пользователя под которым создается проект:
# su username
2. Переходим в папку проекта:
# cd /home/USERNAME/web/DOMAIN/private
3. Создаем виртуальное окружение
# source env/bin/activate
4. Активируем виртуальное окружение:
# source env/bin/activate
Далее установка всех модулей Python для этого проекта должна проходить под эти окружением.
5. Дополнительно может потребоваться обновление менеджера пакетов pip:
# pip install --upgrade pip
И проверяем:
# pip -V
pip 21.3.1 from /home/USERNAME/web/DOMAIN/private/env/lib64/python3.6/site-packages/pip (python 3.6)
V. Установка Django
Рекомендуемый способ установки Django с помощью менеджера pip. При этом устанавливается последняя стабильная версия, совместимая в вашей версией Python.
Pip должен быть уже установлен и обновлен, см. выше. Если он устарел, вы узнаете об этом, потому что установка не будет работать.
1. Переходим в папку проекта (если вы еще не там):
# cd /home/USERNAME/web/DOMAIN/private
2. Устанавливаем Django при помощи Pip
# python3 -m pip install Django
Или же можно установить нужную версию Django:
# python3 -m pip install Django==2.1.*
3. Для проверки запускаем интерпретатор Python:
# python3
Python 3.6.8 (default, Nov 16 2020, 16:55:22)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> print(django.get_version())
3.2.12
>>> exit()
Или
# python3 -m django --version
3.2.12
VI. Создаем проект Django
Название пакета Python используется для импорта чего-либо из проекта. Можно задать произвольное название, например, по названию домена. Нельзя использовать в качестве названия проекта названия компонентов Python и Django. Это вызовет конфликты в работе приложения.
1. Создаем проект средствами Django:
# cd /home/USERNAME/web/DOMAIN/private
# mkdir src
# django-admin startproject PROJECTNAME ./src
Где PROJECTNAME это название вашего проекта.
2. Проверяем работу скрипта. Данный скрипт создаст файлы проекта:
src/
- manage.py
- PROJECTNAME/
-- __init__.py
-- settings.py
-- urls.py
-- wsgi.py
- src/
- это просто контейнер для проекта, Django не использует это название и его можно менять.
- manage.py
- Скрипт, который позволяет взаимодействовать с Django.
- PROJECTNAME
- это пакет Python вашего проекта.
- __init__.py
- файл, который указывает Python, что текущий каталог является пакетом Python.
- settings.py
- файл настроек проекта.
- urls.py
- файл роутинга адресов проекта.
- wsgi.py
- точка входа проекта для WSGI-совместимых веб-серверов.
3. Для того чтобы веб сервер имел доступ к точке входа (скрипт, который запускает весь сайт) необходимо перенести файл точки входа в публичную папку «public_html»:
# cd /home/USERNAME/web/DOMAIN/private/src/PROJECTNAME
# cp wsgi.py ../../../public_html/
4. Для того чтобы сайт заработал необходимо в конфиге Django указать домен с которого вы будете открывать этот сайт. Для этого откройте любым удобным способом файл settings.py, найдите строчку:
ALLOWED_HOSTS = []
и укажите там ваш домен:
ALLOWED_HOSTS = [‘mysite.ru’]
5. Можно переходить по адресу вашего домена и проверять работу сайта. Вы должны увидеть примерно следующее:
VIII. Далее
Далее можно выполнить какие-то базовые настройки проекта или начать разработку.
Для выполнения настроек откройте файл settings.py в текстовом редакторе.
1. Можно настроить нужную вам систему управления базами данных. По-умолчанию в Django это SQLite, но для работы объёмных сайтов рекомендуется использовать более надёжную СУБД, например, MySQL или PostgreSQL.
Для работы с SQLite необходимо веб-серверу Apache предоставить доступ на чтение и запись файла базы данных (.../private/src/db.sqlite3) и его родительской папки.
Для этого нужно предоставить группе-владельцу права на запись и чтение. По умолчанию файл БД называется и должен находиться в каталоге проекта:
Переходим в папку:
# cd /home/USERNAME/web/DOMAIN/private/
Apache должен быть владельцем родительского каталога файла. Меняем группу владельца папки:
# sudo chown :apache ./src
Расширяем права на запись на папку:
# sudo chmod 0775 ./src
Меняем группу владельца файла:
# sudo chown :apache ./src/db.sqlite3
Расширяем права на запись на файл:
# sudo chmod 0664 ./src/db.sqlite3
2. Важную роль играет настройка каталога статических файлов Django, в которой будут храниться публичные файлы такие как CSS, JS и изображения.
Можно использовать для этих целей публичную директорию домена:
/home/USERNAME/web/DOMAIN/public_html
Для этого в конец файла добавьте директиву STATIC_ROOT, которая сообщает фреймворку Django местонахождение статических файлов:
STATIC_ROOT = Path(BASE_DIR, "../../public_html/static/")
3. Можно импортировать структуру (если такая уже была ранее создана) БД SQLite при помощи скрипта:
# cd /home/USERNAME/web/DOMAIN/private/src
# ./manage.py makemigrations
# ./manage.py migrate
4. Создать учетную запись администратора сайта:
# ./manage.py createsuperuser
Нужно будет задать имя пользователя, адрес электронной почты и пароль.
5. После этого нужно поместить статический контент в указанный каталог:
# ./manage.py collectstatic
Все статические файлы будут помещены в каталог public_html/static/ в каталоге домена.
6. Можно воспользоваться встроенным веб сервером разработки Django для проверки работы сайта:
# ./manage.py runserver 0.0.0.0:8000
И откройте в браузере этот адрес:
Чтобы остановить сервер разработки нажмите CTRL-C в консоли.
7. В Django уже включена админка. Чтобы войти в нее, добавьте в ссылку секцию /admin, а затем введите имя и пароль.
Решение возможных ошибок
Ошибки в логах
Unable to connect to WSGI daemon process
В файле логов:
[pid 4842] (13)Permission denied: [client 127.0.0.1:36046] mod_wsgi (pid=4842): Unable to connect to WSGI daemon process 'DOMAIN' on '/path/to/socket/wsgi.4813.0.1.sock' as user with uid=1005.
Эта ошибка говорит о том что у процесса WSGI нет доступа к редактированию файла сокета. Варианты решения:
А. Файл сокета может создаваться пользователем apache, а владельцем процесса WSGI может быть владелец сайта. В данном случае доступа к файлу не будет, т.к. разные владельцы. Для этого в настройках сайта необходимо явно указать одного владельца и для файла и для процесса при помощи параметров «socket-user» и «user», например:
WSGIDaemonProcess NAME socket-user=USER user=USER
Б. Файл сокета может создаваться в недоступном для пользователя процесса месте (подробнее). Для этого необходимо глобально изменить место создания файла сокета. Например, для CentOS:
# vi /etc/httpd/conf.modules.d/10-wsgi.conf
WSGISocketPrefix /var/run/wsgi
Timeout when reading response headers
В файле логов:
Timeout when reading response headers from daemon process wsgi.py
Скорей всего не верно указан параметр WSGIDaemonProcess «python-home», он должен явно указывать на корень виртуального окружения.
Не верно:
python-home=/path/to/project/env/lib/python3.6/site-packages
Верно:
python-home=/path/to/project/env
Unable to connect to WSGI daemon process
В файле логов:
Script timed out before returning headers: wsgi.py
или
mod_wsgi (pid=16981): Unable to connect to WSGI daemon process 'NAME' on '/var/run/wsgi.16970.0.1.sock' after multiple attempts.
В данном случае скорей всего используется старая/несовместимая версия модуля Apache mod_wsgi, требуется обновить модуль WSGI.
ModuleNotFoundError: No module named 'PROJECTNAME'
В файле логов:
ModuleNotFoundError: No module named 'PROJECTNAME'
Эта ошибка говорит о неверно настройке параметра python-path в конфиге домена Apache, он должен указывать на папку с модулями проекта, а именно на папку с файлом «manage.py»:
python-path=%home%/%user%/web/%domain%/private/src
Invalid HTTP_HOST header: 'DOMAIN'
В файле логов:
Invalid HTTP_HOST header: 'DOMAIN'. You may need to add 'DOMAIN' to ALLOWED_HOSTS.
Это ошибка системы безопасности Django, в файле settings.py добавьте ваш домен в массив разрешенных доменов ALLOWED_HOSTS.
django.core.exceptions.ImproperlyConfigured: SQLite 3.9.0 or later is required (found 3.7.17)
В файле логов:
File "/home/dev/web/listit.club.bb/private/env/lib64/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 69, in check_sqlite_version
'SQLite 3.9.0 or later is required (found %s).' % Database.sqlite_version
django.core.exceptions.ImproperlyConfigured: SQLite 3.9.0 or later is required (found 3.7.17).
Это говорит о том, что Python использует старую версию SQLite. Это может быть в нескольких случаях:
А. Версия SQLite устаревшая, требуется обновить версию.
Проверяем версию независимо от Python:
Проверяем установку:
# sqlite3 -version
3.38.2 2022-03-26 13:51:10 d33c709cc0af66bc5b6dc6216eba9f1f0b40960b9ae83694c986fbf4c1d6f08f
Проверяем версию в Python:
# python3 -c "import sqlite3; print(sqlite3.sqlite_version)"
3.38.2
Б. Сборка Python использует неверную библиотеку для SQLite. Это может производит при альтернативной установке новой версии SQLite и в данном случае система будет использовать новую версию, но указать в Python будет на старую библиотеку. Для этого необходимо программе LD указать путь к новой динамической библиотеке:
# vi /etc/ld.so.conf.d/usr-lib.conf
и добавить строки:
/usr/local/lib
/usr/local/lib64
На будущее прописываем путь и для 64-х разрядных библиотек.
Применяем настройки путей к библиотекам:
# ldconfig -v
В. Добавить адрес библиотеки в виртуальное окружение.
# vi venv/bin/activate
и добавить строки:
export LD_LIBRARY_PATH="/usr/local/lib"
ModuleNotFoundError: No module named '_ssl'
В файле логов:
ModuleNotFoundError: No module named '_ssl'
Нет модуля SSL в сборке Python - нужно пересобрать Python c флагом with-openssl, предварительно проверив установлен ли openssl и его версию (свежему Python нужна версия не ниже 1.1.1.).
the ssl module in Python is not available
В файле логов:
centos pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.
Эта ошибка может возникнуть при попытке установки модуля Python при помощи менеджера пакетов PIP. Ошибка говорит о том, что в Python нет модуля SSL, необходимо переустановить Python с этим модулем.