Базовый способ деплоя Django-проекта на Ubuntu

django

 

Цель этого поста – доступно описать один из базовых и простых способов деплоя Django-проекта на чистую Ubuntu.

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

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

Однако, в этом посте я хочу помочь тем, кто разбирается с Django, возможно пришел из PHP и ищет информацию о том, с чего начать и как настроить свой первый product сервер с django-проектом, который до этого запускался только на локальной машине с помощью python manage.py runserver

Шаг 1. Выбираем хостинг

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

  • DigitalOcean – VPS от $5 за 512мб оперативки и 20гб места на диске.
  • Linode.com –  VPS от $10 за 2гб оперативки и 24гб места на диске. 
  • Webfaction.com – Shared или VPS в зависимости от тарифного плана. 10$ - 1гб оперативки, 100гб SSD диска. Удобная панель, которая поможет хостить любые виды сайтов, управлять почтой и DNS. Один из первых хостингов, которые хостили не только php сайты. В случае с Webfaction, вам не придется заниматься настройкой сервера, а базы данных, веб-сервер не входят в ваш тариф по оперативной памяти.

Шаг 2. Настройка сервера

Для запуска python-проекта нам потребуется:

  • python 3.5
  • nginx
  • postgresql
  • supervisor
  • rabbitmq
  • git

python 3.5 по-умолчанию не идет в установке убунты, поэтому мы его ставим из кастомного ppa-репозитория.

nginx - веб-сервер, который будет проксировать запросы к нашему django-приложению, а также обслуживать отдачу статических и медиа файлов.

postgresql - наиболее удобная реляционная СУБД, с множеством разных возможностей.

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

rabbitmq - брокер сообщений. Получает и перенаправляет сообщения. Нужен для хранения очереди сообщений, которая понадобится на случай использования celery для асинхронного выполнения ресурсоемких функций django-проекта.

git - система контроля версий. По-умолчанию Ubunutu комплектуется очень старой версией гита, поэтому мы его ставим из ppa-репозитория.

Кроме того, мы поставим несколько удобных утилит вроде mc, htop и smem.

Все команды настройки в данном примере следует вводить от пользователя root. Для этого рекомендую запустить bash от имени суперпользователя:

sudo bash

и ввести ваш пароль.

Добавим нужные источники пакетов.

add-apt-repository ppa:git-core/ppa -y
add-apt-repository ppa:fkrull/deadsnakes -y
echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" >> /etc/apt/sources.list.d/pgdg.list
echo "deb-src http://ru.archive.ubuntu.com/ubuntu/ xenial main restricted
deb-src http://ru.archive.ubuntu.com/ubuntu/ xenial universe
deb-src http://ru.archive.ubuntu.com/ubuntu/ xenial-backports main restricted universe multiverse
deb-src http://security.ubuntu.com/ubuntu xenial-security multiverse" > /etc/apt/sources.list.d/sources.list
wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O - | sudo apt-key add -
echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list
wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -

Обновим данные из репозиториев, а затем обновим все установленные пакеты у которых доступны обновления

apt-get update
apt-get upgrade -y

Установим часовой пояс сервера, а также выберем русскую локаль

sudo su -c 'echo "Europe/Moscow" > /etc/timezone'
sudo dpkg-reconfigure -f noninteractive tzdata
locale-gen ru_RU.UTF-8
update-locale LANG=ru_RU.UTF-8 LC_MESSAGES=POSIX

Установим пакеты, относящиеся к Python3

apt-get install -y python3.5 python3.5-venv python3.5-dev
apt-get build-dep -y python-imaging
apt-get install -y python-pip python-virtualenv python-setuptools

Установим rabbitmq-server

apt-get install -y rabbitmq-server

Установим postgresql и некоторые вспомогательные утилиты и библиотеки

apt-get install -y postgresql postgresql-contrib postgresql-server-dev-9.5 htop mc nginx smem supervisor libjpeg-dev libfreetype6-dev zlib1g-dev libxml2-dev libxslt1-dev links
service supervisor restart

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

Для него мы создадим каталог /sites, сгенерируем rsa ключ, который может потребоваться внести в настройки github/bitbucket  как deploy key, чтобы разрешить серверу read-only доступ к репозторию для установки вашего проекта.

useradd -d /sites -m -s /bin/bash -U sites
mkdir /sites/.ssh
ssh-keygen -t rsa -b 2048 -f /sites/.ssh/id_rsa -N ''
chown sites:sites /sites/.ssh
chown sites:sites /sites/.ssh/*
chmod 700 /sites/.ssh && chmod 600 /sites/.ssh/*
cat /sites/.ssh/id_rsa.pub
chown -R sites:sites /sites

 Готово. Мы подготовили сервер, создали пользователя и каталог в которой будут жить ваши проекты и можем приступить к запуску вашего проекта!

Все команды, которые были перечислены выше я собрал в отдельный bash-скрипт, который можно скачать и запустить на чистой установке Ubuntu 16.

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

Помните, что эти запускать его надо от root.

wget http://static.kpavlovsky.pro/deploy-django/setup_python_server_ubuntu16.sh
bash setup_python_server_ubuntu16.sh

Этот скрипт сделает за вас изначальную настройку сервера.

Раз мы закончили с установкой и настройкой сервера, теперь можно заняться деплоем django-проекта.

Шаг 3. Деплой Django-проекта

Каталог /sites будет содержать подкаталоги, в которых мы будем размещать наши django-проекты.

Создадим первый каталог для нашего django-проекта.

Все команды здесь мы выполняем от рута. Так как файлы проекта должны принадлежать пользователю от которого мы запускаем проект, а именно sites, то после всех манипуляций по установке, мы сменим владельца файлов и каталогов на нужный.

cd /sites
mkdir tutorial
cd tutorial
mkdir logs static media configs

В каталоге tutorial мы создали 4 каталога для логов, статических файлов, медиа файлов и для конфигов. В последний мы положим файлы с конфигами для этого проекта для supervisor и nginx

Теперь создадим виртуальное окружение для python-проекта.

Виртуальные окружения (virtualenv) нужны для того, чтобы изолирвоать установленные python-пакеты для каждого проекта. С помощью virtualenv, можно делать множество установок разных питонячих проектов и они не будут мешать друг другу, если одному потребуется одна версия какого-то пакета, а другому - другая.

python3.5 -m venv env

Виртуальное окружение в каталоге env создано.

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

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

cd /sites/tutorial/configs
touch supervisor.conf
touch nginx.conf

Приведу пример конфига nginx.conf:

server {
    listen 80;
    server_name 127.0.0.1 example.com www.example.com;
    access_log  /sites/tutorial/logs/nginx_access.log;
    client_max_body_size 100M;

    location /media  {
        alias /sites/tutorial/media;
        expires 30d;
        add_header Pragma public;
        add_header Cache-Control "public";
    }
    location /static {
        alias /sites/tutorial/static;
        expires 30d;
        add_header Pragma public;
        add_header Cache-Control "public";
    }

    location / {
        proxy_pass http://127.0.0.1:9023;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout       600;
        proxy_send_timeout          600;
        proxy_read_timeout          600;
        send_timeout                600;
    }

  }

В данном конфиге стоит обратить внимание на то, что django-проект будет запущен на порте 9023, а логи веб-сервера будут храниться в файле logs/nginx_access.log

Этот конфиг можно скачать по этой ссылке: http://static.kpavlovsky.pro/deploy-django/nginx.conf

Теперь конфиг супервизора, supervisor.conf

[program:tutorial_web]
command=/sites/tutorial/env/bin/gunicorn project.wsgi:application --workers 2 --bind 127.0.0.1:9023  --user sites --timeout 300
directory=/sites/tutorial/project
user=sites
stderr_logfile=/sites/tutorial/logs/tutorial_web-stderr.log
stdout_logfile=/sites/tutorial/logs/tutorial_web-stdout.log
autostart=true
autorestart=true
redirect_stderr=true
environment=PYTHONPATH=/sites/tutorial/project,DJANGO_SETTINGS_MODULE=project.settings

Этот конфиг можно скачать по этой ссылке: http://static.kpavlovsky.pro/deploy-django/supervisor.conf

До того как мы применим эти конфиги, нам надо создать сам проект. 

Как видно из конфига supervisor.conf я предлагаю хранить сам проект в каталоге project (/sites/tutorial/project).

В рамках этой статьи, мы создадим django-проект с помощью утилиты django-admin.py

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

cd /sites/tutorial
source /sites/tutorial/env/bin/activate
pip install --upgrade pip
pip install django==1.10.2
pip install gunicorn==19.6.0

Теперь в виртуальном окружении проекта есть утилита django-admin. Создадим базовый Django-проект.

django-admin startproject project

После выполнения этой команды у нас появился каталог project.

Теперь можно применить наши конфиги. 

ln -s /sites/tutorial/configs/supervisor.conf /etc/supervisor/conf.d/tutorial.conf
ln -s /sites/tutorial/configs/nginx.conf /etc/nginx/sites-enabled/tutorial.conf

Этими строчками мы сделали символические ссылки (симлинки) до наших конфигов в тех каталогах, где nginx и supervisor ожидают увидеть доступные конфиги приложений. Для supervisor важно, чтобы файл с конфигом имел расширение .conf. В противном случае он его проигнорирует. Для nginx это не важно, но для единообразия я предлагаю в обоих случаях иметь одинаковый формат названий.

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

service nginx reload
supervisorctl update
supervisorctl restart tutorial_web

Теперь nginx будет проксировать запросы к вашему приложению на порт 9023, а supervisor будет пытаться его запустить.

Обращу ваше внимание на то, что для реального проекта нужно сделать еще несколько шагов, например создать базу в postgres, в конфиге django-проекта прописать пути до статических файлов и URL для них. В данном случае настройки будут иметь такой вид:

STATIC_ROOT = '/sites/tutorial/static'
STATIC_URL = '/static'
MEDIA_ROOT = '/sites/tutorial/media'
MEDIA_URL = '/media'

После того как эти настройки будут указаны можно собрать статические файлы в каталог со статикой и nginx сможет их отдавать.

Чтобы сделать это, при активированном виртуальном окружении проекта (source /sites/tutorial/env/bin/activate) следует выполнить следующие команды:

cd /sites/tutorial/project
python3.5 manage.py collectstatic --noinput

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

links http://127.0.0.1/

Вы увидите Django-проект в demo-режиме.

Чтобы открыть сайт со своего компьютера, вам скорее всего придется изменить настройки nginx, прописав в разделе server_name IP сервера.

Бонус: для тех кто дочитал статью до конца, вторую часть статьи я выделил во второй скрипт, который можно запустить после скрипта установки. Он выполнит всё базовую настройку за вас. Вам останется только протестировать работу сайта.

Адрес скрипта: http://static.kpavlovsky.pro/deploy-django/project_setup.sh

Запустить скрипт на сервере можно сразу после его скачивания!

wget http://static.kpavlovsky.pro/deploy-django/project_setup.sh
bash project_setup.sh

Замечания:

Во время прогона этих двух скриптов я наткнулся на ошибки связанные с тем, что установка через apt заблокирована другим выполняющимся процессом. Это связано с тем, что в убунте включены автоматические обновления и в списке процессов (команда ps aux) был виден процесс unattended-upgr. Если вы его там увидите – дождитесь окончания его работы и можете запускать скрипт установки сервера. К скрипту установки проекта это не относится.

Есть вопросы по статье? Задайте их в коментариях!

Если эта статья вам показалась полезной – поделитесь её с тем, кто осваивает Django!

Опубликовано: 11 октября 2016 г.

Simple is better than complex.

PEP20: The Zen of Python