Развертывание Django на сервере с помощью Nginx, PostgreSQL и Gunicorn

Введение

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

В данной статье мы рассмотрим установку и конфигурацию, достаточную для разворачивания Django-приложения. В качестве ОС будем использовать Ubuntu 24.04, для конфигурации будет использоваться пользователь, отличный от root с sudo-привилегиями. Вместо стандартной SQLite будет использован PostgreSQL, для запуска приложения будет использоваться Gunicorn, в качестве веб-сервера и реверс-прокси – Nginx.

Установка необходимых пакетов

Для запуска сервера с Django в первую очередь нам потребуется установить требуемые зависимости из репозиториев Ubuntu. Для этого обновим индексы пакетов, после чего загрузим и установим следующие пакеты:

sudo apt update
sudo apt install python3-pip python3-dev python3-venv libpq-dev postgresql postgresql-contrib nginx

В результате выполнения команд будет установлен менеджер пакетов pip и необходимые зависимости для сборки пакетов Python, а также веб-сервер Nginx. На данном этапе мы можем приступить к настройке окружения нашего сервера с Django с пакетами Python.

Создание базы данных и пользователя PostgreSQL

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

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

Подробно процесс создания и настройки облачной БД PostgreSQL описан в нашем руководстве. Создать базу данных вы можете в разделе “Облако”.

Создание и настройка БД самостоятельно

Для создания БД самостоятельно нам потребуется дополнительно установить пакеты Postgresql и нужные ей библиотеки из репозиториев Ubuntu командой:

sudo apt install postgresql postgresql-contrib

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

В процессе установки PostgreSQL автоматически создается пользователь ОС postgres, а также соответствующий пользователь PostgreSQL с правами администратора. Для входа в интерактивную сессию Postgres используем команду sudo с соответствующим именем пользователя:

sudo -u postgres psql

В результате выполнения команды откроется консоль PostgreSQL. Создадим базу данных и пользователя для нее:

CREATE DATABASE myproject;
CREATE USER myprojectuser WITH PASSWORD 'password';

Далее изменим некоторые настройки для данного пользователя, что несколько ускорит операции с базой данных, поскольку корректные значения не потребуется выставлять при каждом запросе. В качестве стандартной кодировки выставим UTF-8, поскольку это кодировка, ожидаемая Django. В качестве часового пояса установим UTC, в качестве схемы изоляции транзакций – “read committed”. Данные настройки соответствуют рекомендованным в документации Django.

ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myprojectuser SET timezone TO 'UTC';

После чего выдадим права администратора для созданной БД нашему пользователю, а также сделаем его владельцем БД:

GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;
ALTER DATABASE myproject OWNER TO myprojectuser;

Для выхода из консоли PostgreSQL введите команду:

\q

Создание виртуального окружения Python для Django

Следующим шагом в настройке нашего сервера с Django является создание виртуального окружения. В качестве директории проекта будет использоваться директория myproject в домашней директории нашего пользователя. Создадим и перейдем в нее:

mkdir ~/myproject
cd ~/myproject

Для создания виртуального окружения воспользуемся модулем venv, встроенным в Python:

python3 -m venv myprojectenv

Активируем виртуальное окружение и установим необходимые модули:

source myprojectenv/bin/activate
pip install django gunicorn psycopg2
Обратите внимание!
В данном примере рассматривается установка и создание нового проекта. Если вы разворачиваете уже готовый проект, вам может потребоваться установка дополнительных зависимостей – обычно их список сохранен в файле requirements.txt. Поместите файлы вашего проекта в выбранную директорию. Для установки зависимостей из файла используйте команду pip install -r requirements.txt в директории, содержащей файл.

Создание проекта и его настройка

После подготовки виртуального окружения создадим наш проект командой django-admin:

django-admin startproject myproject ~/myproject

После выполнения команды структура проекта будет иметь следующий вид:

~/myproject/
├── manage.py - скрипт для управления проектом
├── myproject/ - директория с файлами проекта
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py - файл настроек проекта
│   ├── urls.py
│   └── wsgi.py
└── myprojectenv/ - директория с виртуальным окружением

Настройка проекта

Для редактирования настроек проекта откройте файл settings.py в любом удобном вам текстовом редакторе:

vim ~/myproject/myproject/settings.py

На строке, следующей за импортом pathlib, добавьте импорт os:

from pathlib import Path
import os

Затем нам потребуется внести домен и/или IP вашего сервера в разрешенные. Для этого отредактируйте директиву ALLOWED_HOSTS, внеся все необходимые значения в массив:

ALLOWED_HOSTS = ['example.com', '203.0.113.5']

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

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}

В поле NAME укажите название БД, в поле USER – созданного ранее пользователя, в поле PASSWORD – указанный при создании пользователя пароль.

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

После настройки БД внесем настройку для сборки статических файлов в одну директорию. Для этого добавьте директиву STATIC_ROOT, например, после директивы STATIC_URL:

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

После чего сохраните файл и закройте текстовый редактор.

Завершение процесса настройки локального сервера с Django

На данном этапе мы можем мигрировать схему нашей БД, используя скрипт manage.py:

~/myproject/manage.py makemigrations
~/myproject/manage.py migrate

После завершения миграции создайте аккаунт администратора:

~/myproject/manage.py createsuperuser

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

Также соберем статический контент в указанную ранее в конфиге директорию:

~/myproject/manage.py collectstatic

После подтверждения операции статика будет собрана в директорию static внутри проекта.

Если на вашем сервере установлен firewall, временно внесите в исключения 8000 порт для проверки работы сайта:

sudo ufw allow 8000

Для запуска тестового сервера выполните команду:

~/myproject/manage.py runserver 0.0.0.0:8000

В браузере откройте домен/IP вашего сервера с портом 8000

http://server_domain_or_IP:8000

Должна открыться стартовая страница Django:

стартовая страница Django

Также рекомендуем проверить работу админ-панели, добавив /admin в конец URL. На открывшейся странице введите логин и пароль пользователя, созданного ранее, и нажмите Log in:

работа админ-панели

После авторизации должна открыться стандартная админ-панель:

стандартная админ-панель

После проверки остановите тестовый веб-сервер, нажав CTRL+C в терминале.

Проверка работы Gunicorn

Перед выходом из виртуального окружения протестируем Gunicorn – нам необходимо убедиться, что приложение успешно запускается. Для этого в директории проекта (в нашем случае ~/myproject) выполните команду:

gunicorn --bind 0.0.0.0:8000 myproject.wsgi

Команда запустит Gunicorn на том же интерфейсе, что и тестовый сервер ранее.

Обратите внимание!
При открытии админ-панели стили не будут корректно отображаться, поскольку Gunicorn не может определить необходимые для этого статические CSS-файлы.

Для запуска Gunicorn используется синтаксис модулей Python – в данном случае был указан относительный путь до файла wsgi.py в модуле myproject. После завершения проверки работы сайта нажмите CTRL+C в терминале для остановки веб-сервера.

Создание и запуск Gunicorn с помощью сервиса systemd

Для запуска Gunicorn в рамках данной статьи будет использоваться сервис systemd, однако это не единственный возможный способ – если вам данный способ не подходит, вы можете ознакомиться с альтернативами в документации Gunicorn.

Создадим файл сервиса командой:

sudo vim /etc/systemd/system/gunicorn.service

Содержимое файла должно быть примерно следующим:

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=beget
Group=www-data
WorkingDirectory=/home/beget/myproject
ExecStart=/home/beget/myproject/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/beget/myproject/myproject.sock myproject.wsgi:application

[Install]
WantedBy=multi-user.target

В файле необходимо изменить:

  1. В поле User вместо beget укажите имя пользователя, созданного при настройке сервера.
  2. В WorkingDirectory измените путь на соответствующий директории вашего проекта.
  3. В ExecStart также скорректируйте путь до исполняемого файла Gunicorn и файла сокета, а также укажите нужные вам настройки Gunicorn – например, можно изменить количество воркеров с 3 на другое.

После чего сохраните файл. Теперь можно запустить сервер командой:

sudo systemctl enable --now gunicorn

Проверка работы сервиса systemd

Для проверки статуса выполните команду, в выводе будет отображаться, запустился ли сервис:

sudo systemctl status gunicorn

Затем проверьте наличие файла сокета в директории проекта:

ls -la ~/myproject | grep sock

Ожидаемый вывод:

srwxrwxrwx 1 beget www-data	0 May 28 15:19 myproject.sock

Если команда проверки статуса сообщает, что сервис не смог запуститься (Active: dead) или файл сокета в директории отсутствует, проверьте логи Gunicorn командой:

sudo journalctl -u gunicorn

В логах сервиса найдите текст ошибки. Причин в данном случае может быть несколько:

  • Владельцем файлов проекта является root вместо созданного вами пользователя. В таком случае измените владельца командой chown.
  • Пути в файле сервиса не ведут в нужную директорию. Убедитесь, что:
    • Путь в директиве WorkingDirectory ведет в директорию проекта.
    • Путь до исполняемого файла Gunicorn в ExecStart указывает на путь, содержащий в себе путь до виртуального окружения + /bin/gunicorn.
    • Путь до файла сокета содержит путь до директории проекта + название файла сокета.
    • Путь до wsgi-файла указан корректно в соответствии с синтаксисом модулей Python.

После внесения изменений в /etc/systemd/system/gunicorn.service выполните команды:

sudo systemctl daemon-reload
sudo systemctl restart gunicorn

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

Настройка Nginx в качестве реверс-прокси к Gunicorn

После того как Gunicorn успешно настроен, нам необходимо настроить Nginx для проксирования трафика процессу. Для начала создадим файл конфигурации в /etc/nginx/sites-available:

sudo vim /etc/nginx/sites-available/myproject

В файле добавим блок сервера:

server {
	listen 80;
	server_name server_domain_or_IP;

	location = /favicon.ico { access_log off; log_not_found off; }
	location /static/ {
    	root /home/beget/myproject;
	}

	location / {
    	include proxy_params;
    	proxy_pass http://unix:/home/beget/myproject/myproject.sock;
	}
}

В конфигурации задаем 80 порт для прослушивания, в server_name указываем домен/IP сервера, по которому будет доступен наш Django-проект. Создаем location для favicon.ico – для него отключаем логирование в access.log. После чего добавляем location для статических файлов – в нашем случае /static/, в качестве корневой указываем директорию проекта. Также нам необходимо добавить location / – здесь мы настраиваем проксирование на созданный gunicorn-сокет. Все пути должны быть скорректированы с учетом используемого вами имени пользователя и директории проекта. После чего сохраните файл и закройте редактор.

Для того чтобы активировать конфиг для Nginx, создайте символьную ссылку на него в директории /etc/nginx/sites-enabled:

sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

После чего проверьте конфиг nginx на предмет синтаксических ошибок:

sudo nginx -t

Если при проверке обнаружены ошибки, откройте файл в /etc/nginx/sites-available и устраните их. Если ошибок нет, перезагрузите nginx командой:

sudo systemctl restart nginx

Также нам необходимо закрыть порт 8000, который мы открывали ранее, и открыть 80 порт на firewall. Для этого выполните команды:

sudo ufw delete allow 8000
sudo ufw allow 'Nginx Full'

После чего проверьте работу сайта по домену/IP сервера – сайт должен быть доступен.

Часто возникающие ошибки

Вместо приложения отображается стандартная заглушка Nginx

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

При открытии сайта отображается 502 код ответа

502 код ответа говорит о том, что Nginx не может успешно проксировать запрос. Причин подобного поведения может быть много – чтобы локализовать ошибку, проверим логи ошибок Nginx командой:

sudo tail -F /var/log/nginx/error.log

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

connect() to unix:/home/beget/myproject/myproject.sock failed (2: No such file or directory)

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

connect() to unix:/home/beget/myproject/myproject.sock failed (13: Permission denied)

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

namei -nom /home/beget/myproject/myproject.sock

Пример вывода с ошибкой в правах:

f: /home/beget/myproject/myproject.sock
 drwxr-xr-x root  root 	/
 drwxr-xr-x root  root 	home
 drwxr-x--- beget beget	beget
 drwxrwxr-x beget beget	myproject
 srwxrwxrwx beget www-data myproject.sock

В данном случае ошибка возникает из-за того, что у директории beget не выставлены права на чтение для пользователей, не являющихся владельцем файла и не находящихся в группе владельца (последние 3 – в drwxr-x---). Для исправления ошибки воспользуемся командой:

sudo chmod 755 /home/beget

После исправления прав вывод namei будет отображаться следующим образом:

f: /home/beget/myproject/myproject.sock
 drwxr-xr-x root  root 	/
 drwxr-xr-x root  root 	home
 drwxr-xr-x beget beget	beget
 drwxr-xr-x beget beget	myproject
 srwxrwxrwx beget www-data myproject.sock

Django выводит ошибку “could not connect to server: Connection refused”

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

OperationalError at /admin/login/
could not connect to server: Connection refused
    Is the server running on host "localhost" (127.0.0.1) and accepting
    TCP/IP connections on port 5432?

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

sudo systemctl status postgresql

Если сервис не запущен, включите и запустите его командой:

sudo systemctl enable --now postgresql

Если после запуска ошибка всё еще будет возникать, убедитесь, что в файле настроек сервера с Django (~/myproject/myproject/settings.py) указаны корректные доступы к базе данных.

Дальнейшая диагностика

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

  • Логи процессов nginx: sudo journalctl -u nginx
  • Логи запросов nginx: /var/log/nginx/access.log
  • Логи ошибок nginx: /var/log/nginx/error.log
  • Логи работы Gunicorn: sudo journalctl -u gunicorn

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

Если вы вносите изменения в код приложения Django, перезапустите процесс Gunicorn командой:

sudo systemctl restart gunicorn

Если вы вносите изменения в файл сервиса systemd gunicorn, перезапустите демон и сам процесс:

sudo systemctl daemon-reload
sudo systemctl restart gunicorn

Если вы вносите изменения в конфигурацию nginx, проверьте корректность синтаксиса конфига командой:

sudo nginx -t

И если ошибок нет, перезапустите nginx:

sudo systemctl restart nginx

Заключение

В данной статье мы рассмотрели развертывание Django. Рассказали, как использовать Django, Nginx и Gunicorn и подключить Postgresql к Django, разобрали создание и настройку веб-приложения Django в виртуальном окружении и конфигурацию Gunicorn для запуска приложения. Также мы настроили Nginx в качестве реверс-прокси для обработки внешних подключений и их передачи Gunicorn и отдачи статического контента.

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

Если возникнут вопросы, напишите нам, пожалуйста, тикет из панели управления аккаунта (раздел “Помощь и поддержка”), а если вы захотите обсудить эту статью, хостинг для Django, наши продукты или свой проект с коллегами по цеху и сотрудниками Бегета – ждем вас в нашем сообществе в Telegram.

8
6709