Евгений Легоцкой16 сентября 2016 г. 13:56

Django - Урок 001. Развёртывание сайта на Django + PostgreSQL + Gunicorn + Nginx

После того, как доступ к серверу с Ubuntu 16.04 был настроен в одной из предыдущих статей , настало время развернуть на нём всё необходимое для работы сайта, а именно:

  • Django - фреймворк для разработки web-приложений на языке Python;
  • PostgreSQL - базу данных SQL ;
  • Gunicorn - WSGI HTTP сервер на Python для UNIX систем;
  • Ngnix - HTTP-сервер и обратный прокси-сервер, почтовый прокси-сервер, а также TCP/UDP прокси-сервер общего назначения;
  • Supervisor - это менеджер процессов, который существенно упрощает управление долго работающими программами, в частности сайтами, которые необходимо автоматически перезапускать после падений.

Шаг 1 - обновление системы

Сделаем обновление существующих пакетов. Мало ли, система имеет устаревшие пакеты.

sudo apt-get update
sudo apt-get upgrade

Шаг 2 - Установка всех необходимых пакетов без установки виртуального окружения

Установим пачкой все необходимые пакеты, которые не будут использоваться через virtualenv, а именно Python 3, PostgreSQL, Nginx.

sudo apt-get install python3-dev python3-setuptools libpq-dev postgresql postgresql-contrib nginx

Pip я в итоге устанавливал отдельно через утилиту easy_install3 , которая идёт в комплекте python3-setuptools , во-первых потому, что тогда ставится последняя версия, а вторых в случае установки через apt-get были ошибки при установке пакетов в virtualenv.

sudo easy_install3 pip

Шаг 3 - Создание базы данных и пользователя базы данных

А теперь создадим базу данных и пользователя, дав расширенные права пользователю postgres через утилиту sudo, который создаётся при установке PostgreSQL.

sudo -u postgres psql

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

Создадим базу данных:

CREATE DATABASE myproject;

Создадим пользователя:

CREATE USER myprojectuser WITH PASSWORD 'password';

Далее настройка пользователя проекта. Кодировку он будет использовать UTF8, поскольку данная кодировка используется и Django, а Python файлы рекомендует хардкодить с кодировкой UTF8, особенно, если в них присутствует текст, который будет отображаться на страницах сайта. Также устанавливается уровень изоляции в базе данных. То есть когда будут доступны данные для чтения. В данном случае после подтверждения транзакции в общем случае. Конечно, имеются случаи, когда данные доступны до подтверждения, но это отдельная тема для разговора. А также устанавливается тип временных зон, который для Django по умолчанию UTC.

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;

Ну и выйдем из консоли PostgreSQL.

\q

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

Шаг 4 - Установка виртуального окружения

Установим и активируем виртуальное окружение:

pip3 install virtualenv
virtualenv ~/myprojectenv
source myprojectenv/bin/activate

Шаг 5 - Установка драйвера PostgreSQL

Произведём установку следующей командой

sudo pip install django psycopg2 
Внутри виртуального окружения необходимо пользоваться командой pip , а не pip3

Шаг 6 - Создание проекта

Перейдём в папку с виртуальным окружением, мы по-прежнему должны находиться в режиме работы в виртуальном окружении. И создадим проект.

cd ~/myprojectenv
django-admin.py startproject myproject

Шаг 7 - Настройка подключения к базе данных

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

nano ~/myproject/myproject/settings.py

Для этого найдите в файле следующий кусок кода:

...

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
   }
}

...

И замените его следующим, с учётом ваших данных для подключения к базе данных:

...

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

...

Шаг 8 - Выполнение миграций базы данных

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

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

А ещё создадим суперпользователя, который будет администратором с максимальными правами доступа к вашему сайту. Выполните следующую команду и следуйте инструкциям.

python manage.py createsuperuser

Шаг 9 - Установка Gunicorn

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

sudo pip install django gunicorn

Можете проверить, что сайт уже работает:

gunicorn myproject.wsgi:application --bind 111.222.333.44:8000  # Укажите ваш IP-адрес

Шаг 10 - Настройка статических файлов

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

Отредактируем его:

nano ~/myproject/settings.py

И добавим строку, которая будет указывать, куда собирать статические файлы.

STATIC_ROOT = '/home/user/myprojectenv/myproject/myproject/static/'

А теперь соберём всю статику в данный каталог:

python manage.py collectstatic

Шаг 11 - Настройка Nginx

Отредактируем конфигурационный файл Nginx.

sudo nano /etc/nginx/sites-available/default

Удалим всё его содержимое и замени следующим.

server {
    listen 80;
    server_name 111.222.333.44; # здесь прописать или IP-адрес или доменное имя сервера
    access_log  /var/log/nginx/example.log;

    location /static/ {
        root /home/user/myprojectenv/myproject/myproject/;
        expires 30d;
    }

    location / {
        proxy_pass http://127.0.0.1:8000; 
        proxy_set_header Host $server_name;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Перезапустим Nginx.

sudo service nginx restart

И запустим gunicorn

gunicorn myproject.wsgi:application

После этого вы обнаружите, что сайт доступен уже на порту 80.

Шаг 12 - Настройка supervisor

Для того, чтобы сайт был доступен в любое время суток, необходимо настроить supervisor, который висит в памяти как служба и будет следить, чтобы сайта всегда работал.

sudo apt-get install supervisor

Для стабильной работы Gunicorn необходимо создать его конфигурационный файл. Он будет располагаться рядом с файлом, который будет запускаемым для нашего web-приложения.

cd /home/user/myprojectenv/myproject/myproject
touch gunicorn.conf.py
nano gunicorn.conf.py

Добавим следующую конфигурационную информацию:

bind = '127.0.0.1:8000'
workers = 3
user = "nobody"

То есть мы указали, к какому порту привязываемся, и какой пользователь запускает процесс. А также количество рабочих процессов. В данном случае их три. Рассчитывается по следующей формуле:

workers =  N cpu + 1

После чего создадим конфигурацию самого supervisor

cd /etc/supervisor/conf.d/
touch myproject.conf
nano myproject.conf

Пропишем в него следующие настройки

[program:myproject]
command=/home/user/myprojectenv/bin/gunicorn myproject.wsgi:application -c /home/user/myprojectenv/myproject/myproject/gunicorn.conf.py
directory=/home/user/myprojectenv/myproject
user=nobody
autorestart=true
redirect_stderr=true

А теперь запустим supervisor. Есть один нюанс при установке supervisor. Он не стартанул в качестве службы после того, как был установлен. Поэтому нужно принудительно добавить его в автозагрузку и запустить вручную, если не хотите перезагружать сервер.

sudo update-rc.d supervisor enable
sudo service supervisor start

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

supervisorctl reread
supervisorctl update
supervisorctl status myproject
supervisor restart myproject

При перезагрузке сервера всё будет стартовать автоматически

Примечание

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

source ~/myprojectenv/bin/activate
sudo killall gunicorn

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

Для Django рекомендую VDS-сервера хостера Timeweb .

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
Поддержать автора Donate
A

С чем может быть связана ошибка? ставил всё на свою машину, статический ip не покупал.
(myprojectenv) ubuntu@ubuntu:~/myprojectenv/myproject$ gunicorn myproject.wsgi:application --bind мойайпишник:8000
[2017-10-28 11:54:39 +0000] [31426] [INFO] Starting gunicorn 19.7.1
[2017-10-28 11:54:39 +0000] [31426] [ERROR] Invalid address: ('мойайпишник', 8000)
[2017-10-28 11:54:39 +0000] [31426] [ERROR] Retrying in 1 second.
[2017-10-28 11:54:40 +0000] [31426] [ERROR] Invalid address: ('мойайпишник', 8000)
[2017-10-28 11:54:40 +0000] [31426] [ERROR] Retrying in 1 second.
[2017-10-28 11:54:41 +0000] [31426] [ERROR] Invalid address: ('мойайпишник', 8000)
[2017-10-28 11:54:41 +0000] [31426] [ERROR] Retrying in 1 second.
[2017-10-28 11:54:42 +0000] [31426] [ERROR] Invalid address: ('мойайпишник', 8000)
[2017-10-28 11:54:42 +0000] [31426] [ERROR] Retrying in 1 second.
[2017-10-28 11:54:43 +0000] [31426] [ERROR] Invalid address: ('мойайпишник', 8000)
[2017-10-28 11:54:43 +0000] [31426] [ERROR] Retrying in 1 second.
[2017-10-28 11:54:44 +0000] [31426] [ERROR] Can't connect to ('мойайпишник', 8000)

Этот ваш IP адрес случайно не внешний IP адрес роутера, за которым Вы сидите со своим ПК?

A

перепробывал все ip, которые нашёл у себя на компе. не подскажите, как найти нужный?

А Вы делали вообще bind на '127.0.0.1:8000'?

то есть
bind = '127.0.0.1:8000'
gunicorn слушает порт на внутренней петле
b
sudo pip install django gunicorn

sudo pip install django psycopg2





По моему sudo здесь лишнее. Разве sudo устанавливает внутри виртуального окружения?

Как ни странно - Да.

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

Все миграции необходимо хранить в репозитории.

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

S

Здравствуйте, подскажите пожалуйста, как исправить:
в Ubuntu 20.04, в каталоге проекта пытаюсь создать вертуальную среду
virtualenv env
вылезает ошибка:
ModuleNotFoundError: No module named 'virtualenv.seed.via_app_data'
Спасибо.

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

Установите venv:

sudo apt install python3-venv

после этого можно сделать

python -m venv .venv
source .venv/bin/activate

virtualenv - устаревший, venv - часть стандартной библиотеки. в Windows - venv ставится вместе с питоном, в убунте, к сожалению, даже куски стандартной библиотеки приходится доставлять руками.

S

Ок, спасибо, буду пробовать.

S

Все заработало, немного ругнулся, что лучше:
python3 -m venv .venv
но в итоге все сработало.
Спасибо!

АМ

Стави все по статье. Добрался до шага 12 и после него перестал запускатся сервер через gunicorn myproject.wsgi:application. выдает ошибку
[ERROR] Exception in worker process
Traceback (most recent call last):
File "/home/user/myproject/lib/python3.8/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker
worker.init_process()
File "/home/user/myproject/lib/python3.8/site-packages/gunicorn/wo rkers/base.py", line 119, in init_process
self.load_wsgi()
File "/home/user/myproject/lib/python3.8/site-packages/gunicorn/workers/base.py", line 144, in load_wsgi
self.wsgi = self.app.wsgi()
File "/home/user/myproject/lib/python3.8/site-packages/gunicorn/app/base.py", line 67, in wsgi
self.callable = self.load()
File "/home/user/myproject/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 49, in load
return self.load_wsgiapp()
File "/home/user/myproject/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 39, in load_wsgiapp
return util.import_app(self.app_uri)
File "/home/user/myproject/lib/python3.8/site-packages/gunicorn/util.py", line 358, in import_app
mod = importlib.import_module(module)
File "/usr/lib/python3.8/importlib/ init .py", line 127, in import _module
return _bootstrap._gcd_import(name[level:], package, level)
File " ", line 1014, in _gcd_import
File " ", line 991, in _find_and_load
File " ", line 973, in _find_and_load_un locked
ModuleNotFoundError: No module named 'myproject.wsgi'

На что обратить внимание?

АМ

После команд запуска supervisor'а

sudo update-rc.d supervisor enable
sudo service supervisor start

нужно ли вообще в виртуальном окружении запускать Gunicorn

я думаю, вам стоит кастовать сюда Евгения - я предпочитаю использовать systemd для запуска и менеджмента сервера

попробуйте призвать его через кнопку ответить

АМ

Евгений доброй ночи, можете что-то посоветовать?

привет
тут нужна твоя помощь=)
я в супервизоре - ноль=)

Ошибка в конфиге инжинкса про статику в root последний слеш лишний путь в таком виде получается

/home/user/myprojectenv/myproject/myproject//static

На продакшене только выполняю обновление структуры базы данных

День добрый.
А как вы это делаете?

python manage.py migrate

А разве это не миграция? И как тогда быть с вашим утверждением о миграциях:

в случае с продакшеном, миграции там делать нельзя. (точнее, конечно, можно, но нельзя)

я имел в виду, что на проде нельзя делать

python manage.py makemigrations

правильнее было бы сказать, что на проде нельзя создавать миграции. мигрировать БД - без этого, естественно, никак

Всё, теперь понял. Спасибо!

Ещё вопрос: как правильно обновлять кодовую базу, если произошли изменения в ней?

обновлять где?
если на проде -

git pull && python manage.py migrate && python manage.py collectstatic --noinput && sudo systemctl service restart && sudo service nginx restart

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

То есть я правильно понимаю, что на боевом сервере нужно ставить помимо всего ещё и git?

в целом, тут все не просто

в зависимости от способа доставки это может быть k8s, docker, rsync, ansible, git... да хоть wget-ом качать архив с условного гитхаба

и для каждого способа свои способы доставки и развертывания

для начала, если у вас 1 сервер и минимум каких-то особенностей, git pull вполне хватит. И да, для этого нужно, чтобы был git установлен

Ясно. Спасибо за развёрнутый ответ.

Думаю, что мне для моих целей хватит и git. Локально я им пользоваться умею, а вот с удалёнными серверами не сталкивался, только с github, отсюда и вопросы.

Сижу, ковыряюсь с git pull на виртуалке, и ни черта не понимаю.

Как правильно залить проект на удалённый сервер, чтобы он по git pull принимал изменения? Отправить изменения с локальной машины у меня получилось, а вот на git pull ругается:

There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

    git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream-to=<remote>/<branch> master

Всё, разобрался.

В виртуальном окружении ругается на отсутствие pip:

sudo: pip: command not found

pip ставил вот так:

sudo apt-get install python3-pip

поскольку easy_install3 почему-то не поставился вместе с setuptools.

в виртуальном окружении не надо использовать sudo

используя sudo вы ставите пакеты глобально. Если у вас один проект на сервере - ок. но если нет, все плюсы от venv-ов идут на смарку.
кстати, советую посмотреть в сторону poetry - на мой взгялд, это проект уже production-ready. пакеты ставятся в несколько потоков, фиксируются не только зависимости, но и зависимости зависимостей. А venv-вы создаются автоматически и не в проекте. может быть не очень удобно, но можно переопределить поведение локально и глобально. в целом, советую

Ага, спасибо, завелось!

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

На шаге 11 вылезла вот такая ошибка:

DisallowedHost at /
Invalid HTTP_HOST header: '10.0.3.15'. You may need to add '10.0.3.15' to ALLOWED_HOSTS

Проблема решилась добавлением в settings.py в ALLOWED_HOSTS IP-адреса 10.0.3.15.

После шага 12 сайт вместо стартовой страницы Django показывает: "502 Bad Gateway".

Что не так?

покажите конфиг nginx и ка запускаете gunicorn

Всё по мануалу выше, буква в букву.

gunicorn mysite.wsgi:application
server {
        listen 80;
        server_name 10.0.3.15;
        access_log /var/log/nginx/example.log;

        location /static/ {
                root /home/django/.env/mysite/mysite/;
                expires 30d;
        }

        location / {
                proxy_pass http://127.0.0.1:8000;
                proxy_set_header Host $server_name;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

Всё по мануалу выше, буква в букву.

gunicorn mysite.wsgi:application
server {
        listen 80;
        server_name 10.0.3.15;
        access_log /var/log/nginx/example.log;

        location /static/ {
                root /home/django/.env/mysite/mysite/;
                expires 30d;
        }

        location / {
                proxy_pass http://127.0.0.1:8000;
                proxy_set_header Host $server_name;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

нужна твоя помощь=)

Поднял сервис с помощью systemd, вот по этому мануалу: https://habr.com/ru/post/501414/

D

А почему нельзя? Где можно об этом почитать? Киньте, пожалуйста, в меня ссылкой.

L
  • #
  • 29 мая 2021 г. 21:44

При любых попытках взаимодействовать с супервизаром, выбивает в такую ошибку

error: <class 'FileNotFoundError'>, [Errno 2] No such file or directory: file: /usr/local/lib/python3.8/dist-packages/supervisor/xmlrpc.py line: 560

Помогите пожалуйста, уже все волосы выдрал

D

Это на какую команду так отвечает?

L

Отвечал на все команды после

sudo service supervisor start

Исправил таким образом

sudo killall supervisord
sudo supervisord -c /etc/supervisor/supervisord.conf

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
Timeweb

Позвольте мне порекомендовать вам отличный хостинг, на котором расположен EVILEG.

В течение многих лет Timeweb доказывает свою стабильность.

Для проектов на Django рекомендую VDS хостинг

Посмотреть Хостинг
L

C++ - Тест 001. Первая программа и типы данных

  • Результат:80баллов,
  • Очки рейтинга4
ДЦ

C++ - Тест 001. Первая программа и типы данных

  • Результат:46баллов,
  • Очки рейтинга-6
СБ

Qt - Тест 001. Сигналы и слоты

  • Результат:94баллов,
  • Очки рейтинга8
Популярные публикации за последние 90 дней
Последние комментарии
ЯО

Как соответствовать новым требованиям Google Play

Ку всем! Вопрос! Как с помощью JNI вызвать оплату на android устройстве? Попробовал Qt Purchasing вроде пошло... но android пишет что библиотека устарела. В qt6 пока поддержки purchasing нет. Р…

Django - Урок 014. Вывод списка популярных статей на Django

Я как понял, этот метод создает статистику каждый день (на каждый день), не удаляя старые данные за день и выводит все данные за сегодняшние просмотры у всех статей?

Qt/C++ - Урок 006. QSqlQueryModel - Таблицы в Qt с помощью SQL-запросов

Изменить запрос при создании модели.
L
  • Livis
  • 30 мая 2021 г. 13:18

Django - Урок 001. Развёртывание сайта на Django + PostgreSQL + Gunicorn + Nginx

Отвечал на все команды после sudo service supervisor start Исправил таким образом sudo killall supervisordsudo supervisord -c /etc/supervisor/supervisord.conf
D

Django - Урок 001. Развёртывание сайта на Django + PostgreSQL + Gunicorn + Nginx

Это на какую команду так отвечает?
Сейчас обсуждают на форуме
v
  • vika
  • 17 июня 2021 г. 0:19

Вопрос новичка ui-форма

все получилось. создала обьект, исправила ошибки и все заработало. спасибо, что есть с кем поговорить.
s

Ключевое слово class

Разобрался,на стаковерфлоу нашел топик и понял почему так происходило .
KM

Django как воспользоваться результатом метода?

Priv = 2020 Now = 2021 Next = 2022 Rok = ( (None, "Nie wybran"), (Priv, '2020'), (Now, '2021'), (Next, '2022') ) Rok = models.IntegerField(choices=Rok, de…

QScrollArea dynamically add QCheckBoxes

Всё правильно. Это просто спамер, который отправился в вечный бан.

Папка с шаблоном сайта

Есть проект с поддержкой динамического создания поддоменов. Как назначить разные пути к шаблонам в templates from django.shortcuts import renderfrom horticulture.utilities import get_horticu…
О нас
Услуги
© EVILEG 2015-2021
Рекомендует хостинг TIMEWEB