Компактный сервер для Django приложений

Введение


Многие начинающие веб разработчики размышляют о том, где бы разместить свое творение. Обычно для этих целей применяются машины под управлением *NIX подобных систем. Мой выбор остановился на Raspberry PI, поскольку малинка:

  • работает под управлением полноценного Linux,
  • долгое время лежит на столе и пылится.


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

image

Нам понадобятся


  1. Raspberry PI модели B, B+ или Raspberry PI 2 (поскольку на платах этих моделей имеется Ethernet) с установленной Raspbian и активированным SSH сервером. О настройке можно почитать здесь, здесь или здесь. Помимо Raspian для малинки существует большое количество альтернативных дистрибутивов. Тут, как говорится, «на любой вкус и цвет».
  2. Рабочее Django приложение.
  3. Роутер с поддержкой DDNS. Этот пункт не обязателен, поскольку DDNS можно настроить на самой малинке.


Я буду работать с малинкой модели B+.

Подготовка


На малинке установлена Raspbian 7.8.
Для начала необходимо найти малинку в сети, чтобы подключиться к ней по ssh.
nmap -sP 192.168.1.1/24 | grep raspberry
image

В моем случае в сети две малинки, одна из которых моя с IP адресом 192.168.1.100. В некоторых сетях nmap не показывает сетевые имена устройств.
098f181b8dec4e228a111f01017e6af0.png
В этом случае найти raspberry pi можно по MAC-адресу, который имеет префикс B8:27: EB.
sudo nmap -sP -n 192.168.1.1/24 | grep -B 2 B8:27:EB
aa211a27862e4d48a31e67d87b0c5fb0.png
Параметр -B для grep определяет какое количество предшествующих строк следует вывести.

Подключаемся к малинке по ssh.
ssh pi@192.168.1.100
4272f3486ccc4e6c9cc51cb0ad95e1b4.png
Для начала разгоним малинку до 1 ГГц с помощью raspi-config.
Устанавливаем питоновский менеджер пакетов
sudo apt-get install python-pip
Переходим к установке необходимых пакетов. В моем web приложении используется СУБД MySQL. В качестве Frontend и Backend используется nginx и gunicorn соответственно.
sudo apt-get install nginx gunicorn mysql-client mysql-server python-mysqldb
229159687734433bbc754389c01cd8be.png
В процессе установки mysql необходимо ввести данные для root пользователя СУБД. python-mysqldb — драйвер, необходимый при работе с моделями в Django. Django установим из питоновских репозиториев.
sudo pip install django
На момент написания статьи актуальные версии nginx и gunicorn в репозиториях для малинки 1.2.1 и 0.14.5 соответственно. Версия MySQL для малинки 5.5. Так же для работы с Django необходимо установить SciPy.
sudo apt-get install python-scipy
nginx 1.2.1 устарел. Более новый можно собрать из исходников. Свежий gunicorn можно установить из питоновских репозиториев.

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


Размещаем web-приложение на малинке (например в /home/pi).
Если у Вас есть рабочие конфиги, то достаточно их скопировать в соответствующие директории:

  • для nginx /etc/nginx/sites-enabled/
  • для gunicorn /etc/gunicorn.d/


C nginx ничего сложного нет. Я бы хотел обратить внимание на настройки для gunicorn.

CONFIG = {
    'mode': 'wsgi',
    'working_dir': '/home/pi/project', 
    #'working_dir': '/home/pi/project/project',
    'user': 'www-data',  
    'group': 'www-data',  
    'python': '/usr/bin/python',
    'args': (
        '--bind=127.0.0.1:8081',
        '--workers=5', # 5 достаточно для малинки
        '--graceful-timeout=60',
        '--timeout=60',
        #'--debug',
        #'wsgi:application',
        'project.wsgi',
    ),
}

Если working_dir (путь к файлу wsgy.py) указать '/home/pi/project/project', а в args указать 'wsgi:application', то на малинке воркеры сначала стартуют, потом умирают без указания причины (под Ubuntu, например, gunicorn работает с обоими вариантами настроек).

Перенос MySQL


Дамп имеющейся БД можно сделать с помощью утилиты mysqldump.
mysqldump -u root -p dbname > dbname.sql
Полученный файл состоит из набора SQL-инструкций, которые восстанавливают структуру, а так же информацию, хранимую в базе данных.
6acbaebf94ba414b8b31da037fc3a817.png
На малинке создаем базу данных. Запускаем mysql shell.
mysql -u root -p
Добавляем новую базу данных.

mysql> create database dbname character set utf8 collate utf8_general_ci;
mysql> grant all privileges on dbname.* to someusr@localhost identified by 'somepassword';


Восстанавливаем данные с дампа. При размере дампа в 162 Мб время восстановления составило около 10 минут.
mysql -u root -p dbname < dbname.sql
Следует отметить, что базы данных лучше хранить на внешнем накопителе, иначе micro SD карта может быстро придти в негодность из-за частых операций записи. Как это сделать можно почитать здесь. Конфиг mysql расположен по пути /etc/mysql/my.cnf

Проверка


Перезапускаем nginx и gunicorn. Если frontend и backend настроены верно, можно открыть наше web приложение.
4128734a95604dd2a636bae004816fec.png
Переходим к нагрузочному тестированию. Установим apache benchmark.
sudo apt-get install apache2-utils
Протестируем в 4 потока 1000 запросами Raspberry PI модель B+.
ab -c 4 -n 1000 http://192.168.1.100/

vladislav@vladislav-N53SV:~$ ab -c 4 -n 1000 http://192.168.1.100/
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.1.100 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.8.0
Server Hostname:        192.168.1.100
Server Port:            80

Document Path:          /
Document Length:        24839 bytes

Concurrency Level:      4
Time taken for tests:   1309.607 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      25018000 bytes
HTML transferred:       24839000 bytes
Requests per second:    0.76 [#/sec] (mean)
Time per request:       5238.429 [ms] (mean)
Time per request:       1309.607 [ms] (mean, across all concurrent requests)
Transfer rate:          18.66 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   0.1      1       1
Processing:  4924 5237  91.4   5227    6419
Waiting:     4919 5227  91.3   5217    6403
Total:       4925 5238  91.4   5228    6420

Percentage of the requests served within a certain time (ms)
  50%   5228
  66%   5245
  75%   5255
  80%   5265
  90%   5296
  95%   5335
  98%   5382
  99%   5667
 100%   6420 (longest request)


Запросы идут медленно, поскольку большую часть времени запроса занимает работа с БД. Ко мне недавно пришла Raspberry PI 2 модель B. Посмотрим на что она способна c теми же настройками и данными.

vladislav@vladislav-N53SV:~$ ab -c 4 -n 1000 http://192.168.1.14/
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.1.14 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.8.0
Server Hostname:        192.168.1.14
Server Port:            80

Document Path:          /
Document Length:        24838 bytes

Concurrency Level:      4
Time taken for tests:   170.083 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      25017000 bytes
HTML transferred:       24838000 bytes
Requests per second:    5.88 [#/sec] (mean)
Time per request:       680.330 [ms] (mean)
Time per request:       170.083 [ms] (mean, across all concurrent requests)
Transfer rate:          143.64 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   0.1      1       1
Processing:   569  678 104.6    650    1338
Waiting:      567  676 104.1    647    1334
Total:        569  679 104.6    651    1338

Percentage of the requests served within a certain time (ms)
  50%    651
  66%    682
  75%    708
  80%    727
  90%    796
  95%    890
  98%   1045
  99%   1138
 100%   1338 (longest request)


Raspberry PI 2 обрабатывает запросы в среднем в 6,16 раз быстрее. Разработчики малинки не обманули.

Настройка DDNS


Настроить DDNS можно на роутере или на самой малинке. Я выбираю No IP, поскольку пользуюсь им несколько лет. Рассмотрим бесплатное использование.

Регистрация хоста
Если у вас есть учетная запись — проходим авторизацию, иначе регистрируемся. После авторизации попадаем сюда.
61444796ac244daca68d6046f3b73c8f.png
Кликаем AddHost и заполняем форму.
a9312d6948ef4a50b5dde353f16430b0.png
Внизу кликаем кнопку AddHost
Хост добавлен. Справа от имени хоста отображается внешний IP адрес Вашей сети.
d2f0add902a64a6794be0b910909a772.png


Настраиваем DDNS на роутере
Для примера я настрою DDNS на ASUS RT-N56U с прошивкой от padavan версии 3.4.3.9–091. Открываем в страницу меню роутера
(например 192.168.1.1). WAN→DDNS.
1dd72f16dd624f55bd1ec7667f65bd73.png
Выбираем сервис no-ip.com, указываем регистрационные данные, а так же наш добавленный хост (technopark-test.ddns.net).
Остальные параметры выставляем по собственному желанию.
2caeea316fa9470484816ed30037431f.png
Теперь при смене внешнего IP адреса наше приложение остается доступным в сети.
Настройка переадресации портов
Нам нужно, чтобы при обращении к хосту малинка отдавала веб приложение. Роутер занимается перенаправлением входящих пакетов, пришедших из вне с порта X на внутренный порт Y. В меню роутера переходим WAN→Переадресация портов. Необходимо перенаправлять внешний 80 порт на 80 порт малинки. Добавим новое правило и применим изменения.
f2516ff2cbdf4a0c8758f6dc0236a842.png
Теперь малинка обрабатывает все приходящие пакеты на 80 порту. Проверим, введя в адресной строке браузера хост, полученный в No IP.
f65f5ed5ff234c5fb559631500d9e212.png
Теперь наше веб приложение доступно для пользователей сети Интернет.
Настраиваем DDNS на малинке
Этот вариант не подходит, если малинка имеет частный IP, поскольку она будет оправлять свой локальный IP адрес на на сервис No IP. Это еще один способ узнать IP адрес малинки локальной сети. Установим DDNS клиент.
sudo apt-get install ddclient
Во время установки необходимо выбрать сервис. Выбираем other и вводим dynupdate.no-ip.com, протокол dyndns2, имя пользователя, пароль, интерфейс — eth0, имя хоста.
ec1d28a2022241a98d6994ecd121ec99.png
Для проверки я выставил интервал обновления IP в 60 секунд. В файле /etc/default/ddclient необходимо выставить значение daemon_interval=»60».

Десерт


Моя малинка давно лежала на столе и пылилась, вместе с tm1638 и DHT11, выводя показания температуры и влажности в помещении и прочей информации.
Все же мне было интересно попробовать управлять GPIO Raspberry PI из django. Я разработал простое web приложение, которое объединило мои ранние наработки. Оно позволяет посмотреть температуру и влажность, измеренные с помощью DHT11, некоторую полезную информацию, управляет 8-ми релейным модулем (который может быть использован для управления электроприборами) и отправляет текст на tm1638.
88d42665e80a49f7a429bce92208487b.jpg

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

Заключение


Имея Raspberry PI моделей B, B+ или Raspberry PI 2, power bank, а так же «open» Ethernet jack получаем компактный сервер, который можно использовать для демонстрации своих наработок. Настройка сервера для Django приложений на Raspberry PI под управлением Raspbian мало чем отличится от любой другой сборки Linux. Пакеты в репозиториях могут быть устаревшими. Для работы с новыми версиями можно вручную собирать программы из исходников.

Исходный код демо приложения.

P.S. Хочу поблагодарить коллег по Технопарку за помощь при подготовке материала.
P.S. S. Я готов выслушать дельные советы и комментарии, а затем поправить материал.

Статья создана в рамках конкурса статей Технопарка@Mail.Ru (park.mail.ru).

© Geektimes