Дикий Григорий

Full-stack веб-разработчик

Django на сервере: Nginx + uWSGI + Postgres + Ubuntu 16.04

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

Перед тем как начать развертывание приложения стоит определиться с тем, на чем вы его собираетесь разворачивать (деплоить). Для этих целей уже может не подойти обычный shared hosting и вам нужно будет заказывать VPS  или VDS сервер. Благо стоят они не дорого, а их конфигураций вам будет вполне достаточно для того, чтобы ваш проект жил и выдерживал трафик проходящий через него. Соответсвенно перед тем как деплоить вам нужен VSP и VDS сервер. Предположим, что он у вас есть с установленной на нем Ubuntu 16.04.

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

Для того, чтобы работать дальше нам нужно создать пользователя в нашей системе, но, чтобы он мог использовать sudo. Таким образом мы обезопасим наш сервер и не дадим root пользователю "случайно" сломать наш сервер. Такой подход используется исключительно в целях безопасности. Для этого создадим пользователя введя команду в консоль:

$ adduser user

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

$ usermod -aG sudo dikiigr

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

$ su - user

Также сразу же добавим нашего пользователя также в группу www-data, так как именно используя данную группу будет запущенны ngix и uwsgi:

$ sudo usermod -aG www-data  user

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

$ shh user@ваш_ip

После чего вводит пароль пользователя и получаете доступ к серверу.

Внимание! Везде, в командах выше замените user на имя вашего пользователя!

Установка пакетов

Перед тем как двигаться дальше, нам нужно установить нужные пакеты для деплоя. Стоит отметить, что мой Django проект использует python 3 и соответственно деплой будет происходить под него, но вам также не составит труда по аналогии деплоить и для python 2.7. Для установки нужных пакетов выполним команды:

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

Первая команда обновляет данные о репозиториях пакетов, а втора устанавливает pip3, postges, nginx и их зависимости. 

Создание базы данных Postges

После установки postges, он по умолчанию создал пользователя postges с sudo  привилегиями. Для создания нашей базы данных мы должны войти в консольное приложение  psql, для чего выполним команду:

$ sudo -u postgres psql

Таким образом мы аунтифицируемся как postgres пользователь и нам не нужно вводить пароль для него (мы также могли аунтифицироваться под нашим пользователем). После открытия приложения мы можем выполнять команды SQL, которые "понимает" postgres БД. Сперва создадим базу данных:

postgres=# CREATE DATABASE myproject;

Замените myproject на имя вашей БД. Создадим пользователя БД и зададим ему пароль:

postgres=# CREATE USER myprojectuser WITH PASSWORD 'password';

Замените myprojectuser на вашего пользователя и задайте ему пароль. Также нам нужно дать все привелегии нашему пользователю БД для управления ею.

postgres=# GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;

После данных действий выйдем из консольного интерфейса командой:

postgres=# \q

Виртуальное окружение: virtualenv и virtualenvwrapper

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

$ sudo -H pip3 install --upgrade pip
$ sudo -H pip3 install virtualenv virtualenvwrapper

virtualenv  и virtualenvwrapper позволят нам удобным образом работать с виртуальными окружениями.  virtualenvwrapper позволяет нам хранить все виртуальные окружения в одной директории, после чего переключаться между ними используя команду workon project. После установки данных программ нам нужно в локальном .bashrc прописать настройки нашей программы, для этого выполним команды:

$ echo "export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3" >> ~/.bashrc
$ echo "export WORKON_HOME=~/venv" >> ~/.bashrc
$ echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.bashrc

VIRTUALENVWRAPPER_PYTHON - указываем какой python использовать по умолчанию. WORKON_HOME - путь к директории с виртуальными окружениями. Для того, чтобы применить изменения для текущей сессии выполним команду:

$ source ~/.bashrc

Теперь у нас создастся директория venv в домашней директории нашего пользователя и установятся скрипты для работы с ней. 

Установка Django

Перед тем, как работать с Django нам нужно его установить, но для начала создадим виртуальное окружение:

$ mkvirtualenv myproject

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

$ deactivate

А для того, чтобы вернуться обратно выполнить команду:

$ workon myproject

Теперь установим django и пакет для работы с postgers находясь в виртуальном окружении:

$ pip install django psycopg2

После установки создадим наш проект:

$ django-admin.py startproject myproject ~/myproject

После этого, нам нужно провести некоторые правки в settings.py,  а именно настроим проект для работы с базой, а также разрешим наш хост и укажем настройки для статики:

$ nano ~/myproject/myproject/settings.py
...
ALLOWED_HOSTS = ['*']
...
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}
...
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

Теперь применим миграции, создадим супер пользователя и переместим статику Django к себе в проект:

$ ~/myproject/manage.py makemigrations
$ ~/myproject/manage.py migrate
$ ~/myproject/manage.py createsuperuser
$ ~/myproject/manage.py collectstatic

Django и uWSGI

Теперь нам нужно настроить связку Django и uWSGI. Идея простая: uWSGI должен запускать проложение Django, получать запросы от пользователей и выдавать ответ. Для этого нужно выйти из виртуального окружения, чтоб установить его глобально:

$ deactivate
$ sudo -H pip3 install uwsgi

Для сохранения наших настроек для uWSGI  создадим директорию:

$ sudo mkdir -p /etc/uwsgi/sites

А в ней создадим скрипт с настройками для нашего проекта:

$ sudo nano /etc/uwsgi/sites/myproject.ini

Зададим ему следующее содержимое:

[uwsgi]
project = myproject
uid = user
base = /home/%(uid)
chdir = %(base)/%(project)
home = %(base)/venv/%(project)
module = %(project).wsgi:application
master = true
processes = 5
socket = /run/uwsgi/%(project).sock
chown-socket = %(uid):www-data
chmod-socket = 660
vacuum = true
  1. project - переменная, название вашего проекта
  2. uid - переменная, созданный нами пользователь
  3. base - переменная, путь к домашней директории пользователя
  4. chdir - путь к корню нашего проекта
  5. home - путь к скриптам (в нашем случае виртуальное окружение)
  6. processes - количество одновременно запущенных процессов для обслуживания нашего проекта
  7. socket - путь к файлу сокету
  8. chown-socket - указываем, кто имеет доступ к нашему сокету
  9. chmod-socket - указываем права (660 - только чтение и запись для группы и пользователя)
  10. vacuum = true - позволяет очищать файл сокета, когда сервер остановится

Создание systemd Unit файла

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

$ sudo nano /etc/systemd/system/uwsgi.service

В данном файле будет следующее содержимое:

[Unit]
Description=uWSGI Emperor service
[Service]
ExecStartPre=/bin/bash -c 'mkdir -p /run/uwsgi; chown user:www-data /run/uwsgi'
ExecStart=/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites
Restart=always
KillSignal=SIGQUIT
Type=notify
NotifyAccess=all
[Install]
WantedBy=multi-user.target
  1. Description -  метаданные, описание задачи
  2. ExecStartPre - команда выполняемая перед стартом. В нашем случае мы создаем папку /run/uwsgi, где будет хранится наш сокет файл, а также добавляем владельцев для этой папки в качестве user:www-data,  чтоб была возможность создавать сокет-файл, считывать с него и писать в него (так работает сервер). 
  3. ExecStart - команда старта. В нашем случае мы указали uWSGI флаг --emperor который позволит запускать множество скриптов для разных проектов, а также указали пусть к папке с конфигами нашего проекта. Таким образом мы можем создавать множество проектов, а uWSGI позаботится о их работе.
  4. WantedBy - уровень запуска. В нашем случае это многопользовательский режим.

Настройка Nginx

Теперь нам нужно настроить nginx, который выполняет роль proxy сервера, а также может обслуживать статику нашего сайта. Для этого создадим файл конфига:

$ sudo nano /etc/nginx/sites-available/myproject

Со следующим содержимым:

server {
    listen 80;
    server_name myproject.com www.myproject.com;
    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/user/myproject;
    }
    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/run/uwsgi/myproject.sock;
    }
}
  1. server_name - название нашего сайта
  2. root - путь к папке со статикой (сама папка не указывается и должна совпадать с location значением, иначе используйте alias)
  3. include uwsgi_params - вставляем uwsgi параметры, которые находятся в файле /etc/nginx/uwsgi_params и предоставляются nginx по умолчанию
  4. uwsgi_pass - указываем путь с сокет-файлу

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

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

Запуск приложения

Мы создали с вами Unit файл, который запускал наш uWSGI сервер, но мы так его и не запустили, для его запуска выполним команду:

$ sudo systemctl start uwsgi

А также перезапустим nginx, чтоб применились изменения:

$ sudo systemctl restart nginx

А чтобы при запуске сервера nginx и  uwsgi автоматически запускались выполним команды:

$ sudo systemctl enable nginx
$ sudo systemctl enable uwsgi

Теперь осталось перезапустить сервер и проверить работоспособность:

$ sudo reboot

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