Настраиваем Django + virtualenv + nginx + gunicorn + PostgreSQL + memcached + letsencrypt на Ubuntu 16.04

В данной статье я опишу процесс настройки связки Django + virtualenv + nginx + gunicorn + PostgreSQL + memcached + letsencrypt на Ubuntu 16.04 на VPS DigitalOcean для своего форума https://pythonworld.club.

DigitalOcean

Из всех VPS-провайдеров я выбрал DigitalOcean, так как:

  • Начальный бонус 10$ после регистрации по ссылке (это месяц использования VPS в подарок)
  • Бонус в 50$ для студентов
  • Высокое качество и надёжность (за 3 года использования проблемы были только 2 раза, о них старались предупреждать заранее, и устранялись в кратчайшие сроки), а также низкая цена.

Впрочем, большая часть статьи подходит для VPS от любого провайдера.

Для активации аккаунта и получения стартового бонуса необходимо либо привязать банковскую карту, либо заплатить хотя бы 5$ через PayPal.

После этого можно выбрать тариф: для начала достаточно и самого дешёвого за 5$, но как только нагрузка на ваше приложение увеличится, ресурсов может не хватить. Тарифа за 10$ мне хватает вполне, поэтому, если у вас нет ничего экстраординарного, выше брать не стоит.

Ubuntu

Необходимо произвести начальную настройку. Если кратко:

  • Создать нового пользователя (не будем же мы всё из-под root-а делать?)
  • Дать ему права на выполнение команд через sudo
  • (опционально) авторизация не по паролю, а по ключу

Подробнейшие инструкции на русском есть на сайте DigitalOcean, не считаю нужным повторяться.

Предположим, нового пользователя назовём steve (это понадобится в дальнейшем, когда будем прописывать пути, будьте внимательны и меняйте на имя созданного Вами пользователя!)

PostgreSQL

Устанавливаем пакеты

sudo apt-get update
sudo apt-get install libpq-dev postgresql postgresql-contrib

Запускаем консоль PostgreSQL

sudo -u postgres psql
CREATE DATABASE myproject;
CREATE USER myprojectuser WITH PASSWORD 'difficultpassword';
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;

Некоторые приложения (и Misago в том числе) могут устанавливать расширения (extension) для PostgreSQL. Если у вас именно такой случай, необходимо ещё на время дать права суперпользователя базы новому пользователю:

ALTER ROLE myprojectuser superuser;

Теперь можно выйти из консоли PostgreSQL

\q

virtualenv

Устанавливаем pip и virtualenv:

sudo apt-get update
sudo apt-get install python3-pip
sudo pip3 install virtualenv

Создаём новое виртуальное окружение:

mkdir ~/newproject
cd ~/newproject
virtualenv newenv

Если вам нужно будет удалить виртуальное окружение, достаточно сделать rm -rf ~/newproject/newenv/.

Теперь активировать окружение можно с помощью команды

source newenv/bin/activate

запущенной из директории newproject.

Вы увидите, что после этой команды изменится приглашение интерпретатора bash: в начале появится (newenv).

Django

Установить Django в виртуальное окружение можно с помощью

pip install django psycopg2

в вашем окружении (заметьте, не pip3, несмотря на третий Python!)

Для выхода из окружения используйте команду deactivate (заметьте, она работает только в виртуальном окружении!)

По данным 2 пунктам есть туториал на DigitalOcean (на английском) (https://www.digitalocean.com/community/tutorials/how-to-install-the-django-web-framework-on-ubuntu-14-04).

Создаём Django-проект

django-admin.py startproject myproject ~/newproject

В моём случае (приложение Misago) была отдельная команда

pip install misago
cd ~/newproject
misago-admin testforum

Меняем настройки для доступа к базе данных. В settings.py прописываем:

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

Далее, стандартные действия:

python manage.py makemigrations
python manage.py migrate
python manage.py collectstatic
python manage.py createsuperuser
sudo -u postgres psql

После чего следует отобрать права суперпользователя для пользователя базы данных (если вы их выдавали).

ALTER ROLE myprojectuser nosuperuser;
\q

nginx

Устанавливаем свежий nginx

nginx=stable
sudo add-apt-repository ppa:nginx/$nginx
sudo apt-get update
sudo apt-get install nginx

Конфигурируем наш сервер

nano /etc/nginx/sites-available/testforum
server {

    server_name pythonworld.club; # Подставьте свой домен
    listen 80;

    location /.well-known {
        root /var/www/html; # Понадобится для letsencrypt
    }

    # Статические файлы
    location = /favicon.ico {
        alias /home/steve/newproject/testforum/static/favicon.ico;
    }
    location = /robots.txt {
        alias /home/steve/newproject/testforum/static/robots.txt;
    }

    location /static/ {
        root /home/steve/newproject/testforum/;
    }
    location /media/ {
        root /home/steve/newproject/testforum/;
    }

    # Взаимодействуем с Django-приложением через unix-сокет
    location / {
        include proxy_params;
        proxy_pass http://unix:/home/steve/newproject/forum.sock;
    }
}
sudo ln -s /etc/nginx/sites-available/testforum /etc/nginx/sites-enabled

gunicorn

Вернёмся в виртуальное окружение (если вы из него выходили), и установим gunicorn

pip install gunicorn

Создадим файл конфигурации для gunicorn

sudo nano /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=steve
Group=www-data
WorkingDirectory=/home/steve/newproject/testforum/
ExecStart=/home/steve/newproject/newenv/bin/gunicorn --access-logfile - --error-logfile error.log --workers 2 --bind unix:/home/steve/newproject/forum.sock testforum.wsgi:application

[Install]
WantedBy=multi-user.target

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

sudo systemctl start gunicorn
sudo systemctl enable gunicorn

Подробно на английском (с решением некоторых возможных проблем) описано в статье на DigitalOcean.

memcached

sudo apt-get install memcached libmemcached-dev
sudo nano /etc/memcached.conf
# Start with a cap of 64 megs of memory. It's reasonable, and the daemon default
# Note that the daemon will grow to this size, but does not start out holding this much
# memory
-m 256  # Ставим побольше, например 256Мб

# Default connection port is 11211
#-p 11211  Комментируем, так как хотим связать с помощью UNIX-сокета

# Run the daemon as root. The start-memcached will default to running as root if no
# -u command is present in this config file
-u memcache

# Specify which IP address to listen on. The default is to listen on all IP addresses
# This parameter is one of the only security measures that memcached has, so make sure
# it's listening on a firewalled interface.
#-l 127.0.0.1 Также комментируем
# А UNIX-сокеты добавляем
-s /tmp/memcached.sock
-a 0766

Устанавливаем Python библиотеку (в виртуальное окружение) для связывания Django и memcached

pip install pylibmc

В settings.py Django-проекта добавляем

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
        'LOCATION': '/tmp/memcached.sock',
    }
}

И, наконец, перезапускаем memcached:

sudo systemctl restart memcached

letsencrypt

Устанавливаем letsencrypt

sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install --upgrade letsencrypt

Создаём файл конфигурации

sudo nano /etc/letsencrypt/cli.ini
authenticator = webroot
webroot-path = /var/www/html
post-hook = service nginx reload
text = True

Регистрация в letsencrypt

sudo certbot register --email my@email.com

Попробуем получить необходимый сертификат в режиме для тестов

sudo certbot certonly --dry-run -d pythonworld.club -d www.pythonworld.club

В конце вы должны получить сообщение

The dry run was successful.

Если всё прошло успешно, то можно получить сами сертификаты

sudo certbot certonly -d pythonworld.club -d www.pythonworld.club

После этого, редактируем настройки nginx

sudo nano /etc/nginx/sites-available/testforum
server {
    server_name pythonworld.club www.pythonworld.club default_server;
    listen 80;

    return 301 https://pythonworld.club;
}

server {
    server_name www.pythonworld.club;
    listen 443 ssl http2;

    ssl_certificate /etc/letsencrypt/live/pythonworld.club/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/pythonworld.club/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/pythonworld.club/chain.pem;

    ssl_stapling on;
    ssl_stapling_verify on;

    add_header Strict-Transport-Security "max-age=31536000";

    return 301 https://pythonworld.club$request_uri;
}

server {

    server_name pythonworld.club;
    listen 443 ssl http2;

    ssl_certificate /etc/letsencrypt/live/pythonworld.club/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/pythonworld.club/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/pythonworld.club/chain.pem;

    ssl_stapling on;
    ssl_stapling_verify on;

    add_header Strict-Transport-Security "max-age=31536000";

    add_header X-Frame-Options "SAMEORIGIN";

    location /.well-known {
        root /var/www/html; # Понадобится для letsencrypt
    }

    # Статические файлы
    location = /favicon.ico {
        alias /home/steve/newproject/testforum/static/favicon.ico;
    }
    location = /robots.txt {
        alias /home/steve/newproject/testforum/static/robots.txt;
    }

    location /static/ {
        root /home/steve/newproject/testforum/;
    }
    location /media/ {
        root /home/steve/newproject/testforum/;
    }

    # Взаимодействуем с Django-приложением через unix-сокет
    location / {
        include proxy_params;
        proxy_pass http://unix:/home/steve/newproject/forum.sock;
    }
}

Перезапускаем nginx

sudo systemctl restart nginx

Автопродление сертификатов

sudo crontab -e
45 */12 * * * certbot renew --quiet --allow-subset-of-names

Ещё про настройку letsencrypt: англ, рус.

Проверка правильности настройки SSL.

Вот и всё. Теперь Ваше Django-приложение доступно в Интернет! Пример применения данной статьи можно найти по адресу https://pythonworld.club.

Для вставки кода на Python в комментарий заключайте его в теги <pre><code class="python3">Ваш код</code></pre>
Опечатка в тексте:
Послать сообщение об ошибке автору?