Введение


Многие начинающие веб разработчики размышляют о том, где бы разместить свое творение. Обычно для этих целей применяются машины под управлением *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 не показывает сетевые имена устройств.

В этом случае найти raspberry pi можно по MAC-адресу, который имеет префикс B8:27:EB.
sudo nmap -sP -n 192.168.1.1/24 | grep -B 2 B8:27:EB

Параметр -B для grep определяет какое количество предшествующих строк следует вывести.

Подключаемся к малинке по ssh.
ssh pi@192.168.1.100

Для начала разгоним малинку до 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

В процессе установки 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-инструкций, которые восстанавливают структуру, а так же информацию, хранимую в базе данных.

На малинке создаем базу данных. Запускаем 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 приложение.

Переходим к нагрузочному тестированию. Установим 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, поскольку пользуюсь им несколько лет. Рассмотрим бесплатное использование.
Регистрация хоста
Если у вас есть учетная запись — проходим авторизацию, иначе регистрируемся. После авторизации попадаем сюда.

Кликаем AddHost и заполняем форму.

Внизу кликаем кнопку AddHost
Хост добавлен. Справа от имени хоста отображается внешний IP адрес Вашей сети.


Настраиваем DDNS на роутере

Для примера я настрою DDNS на ASUS RT-N56U с прошивкой от padavan версии 3.4.3.9-091. Открываем в страницу меню роутера
(например 192.168.1.1). WAN->DDNS.

Выбираем сервис no-ip.com, указываем регистрационные данные, а так же наш добавленный хост (technopark-test.ddns.net).
Остальные параметры выставляем по собственному желанию.

Теперь при смене внешнего IP адреса наше приложение остается доступным в сети.
Настройка переадресации портов

Нам нужно, чтобы при обращении к хосту малинка отдавала веб приложение. Роутер занимается перенаправлением входящих пакетов, пришедших из вне с порта X на внутренный порт Y. В меню роутера переходим WAN->Переадресация портов. Необходимо перенаправлять внешний 80 порт на 80 порт малинки. Добавим новое правило и применим изменения.

Теперь малинка обрабатывает все приходящие пакеты на 80 порту. Проверим, введя в адресной строке браузера хост, полученный в No IP.

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

Этот вариант не подходит, если малинка имеет частный IP, поскольку она будет оправлять свой локальный IP адрес на на сервис No IP. Это еще один способ узнать IP адрес малинки локальной сети. Установим DDNS клиент.
sudo apt-get install ddclient
Во время установки необходимо выбрать сервис. Выбираем other и вводим dynupdate.no-ip.com, протокол dyndns2, имя пользователя, пароль, интерфейс — eth0, имя хоста.

Для проверки я выставил интервал обновления IP в 60 секунд. В файле /etc/default/ddclient необходимо выставить значение daemon_interval=«60».

Десерт


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




Для управления 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).

Комментарии (17)


  1. VBKesha
    30.07.2015 15:39

    >> Для управления GPIO необходимо запускать сервер с правами root. Это потенциальная уязвимость.

    А равзе нельзя поменять права устройства GPIO чтобы его мог использовать сервер без прав root?


    1. bagrusss Автор
      30.07.2015 15:50

      Работа с GPIO осуществляется через /dev/mem, требующий права суперпользователя.


      1. VBKesha
        30.07.2015 15:54

        Тогда только свой драйвер писать.


        1. dmbreaker
          30.07.2015 17:08

          Либо микросервис, который не будет торчать наружу.


        1. bagrusss Автор
          30.07.2015 20:23

          Один из вариантов — запустить процесс с правами суперпользователя, который управляет GPIO, и осуществлять обмен данными между этим процессом и воркерами django.


          1. CRImier
            31.07.2015 00:13

            Тогда посмотрите это.
            Ещё один вариант — прописать в автозапуске экспорт пинов от рута, а затем уже спокойно переключать их, как хотите.


            1. bagrusss Автор
              01.08.2015 01:50

              Спасибо за информацию. Поправлю.


  1. Shik_shik
    30.07.2015 17:16

    У меня для подобных целей используется Odroid C1. Отличное решение!


    1. Imp5
      30.07.2015 21:09
      +2

      У меня на холодильнике лежит Fly IQ434


      1. uscr
        30.07.2015 21:30
        +1

        Сервер, который можно скормить коту во время маски-шоу!


  1. uscr
    30.07.2015 21:52
    +2

    При использовании малинки появилось стойкое ощущение, что это самизнаетечто в красивой обёртке. Сетевой интерфейс и USB, висящие на одной шине превращают компьютер с хорошими характеристиками в тупящего монстра, на графики IOwait которого страшно смотреть. И что бы вы ни задумали, вам придётся выбирать: закупить вагон microsd, которые будут дохнуть из-за частой записи, либо мириться с фризами, если вы подключите внешний носитель по USB.
    Когда я купил малину 2, я хотел заменить свой музейный PII, который качал торренты и раздавал их по сети. Выглядело всё красиво: малинка качает, она же и показывает через kodi (бывший XMBC). Но при добавлении торрента на загрузку, она переставала отвечть по ssh, вебморда торрен-клиента обновлялась по 5 минут. Иногда даже пинги пропадали. Торренты при этом качались со скоростью 0,5МБ/с и это была пиковая скорость. Попробовал качать не на USB флешку, а на microSD — стало веелее, но не сильно. Смотреть фильм и одновременно что-то качать невозможно.
    После недели мучений я заказал banana pi. При стоимость в те же $35 она мало того, что оснащена разъёмом SATA, но даже на флешку качает на пределе скорости записи самой флешки. После подключения ноутбучного HDD я вовсе стал счастлив. Одна беда — нет аппаратного ускорения для видео. В итоге сейчас такая схема: банана качает в NFS шару, малинка оттуда показывает всё на телевизоре. Ради интереса посмотрел рип на 40Гб с каким-то нереальным битрейтом и ни разу не увидел «лага». Плюс у производителей бананы много интересных железяк, вроде такой.


    1. CRImier
      31.07.2015 00:26

      Вы, безусловно, правы, но вот вступление оставляет желать лучшего. Я даже и не пробовал запускать торренты на Малинке, посмотрев бенчмарки, заботливо сделанные другими пользователями, но у меня язык не повернётся назвать её «самизнаетечто в красивой обёртке». Уж сильно обобщает такое определение — полезных проектов на RPi слишком много, чтобы просто так можно было взять и сказать «говно».
      Я вот нашёл применение — использую Pi2 в качестве достаточно безопасного десктопа, да ещё и переносного — в случае чего могу спокойно запитать от двух 18650 и зайти по VNC. Смотрю HD- видео (не BluRay, но по SMB), пишу скрипты на Python/C, копаюсь в Интернете, при этом никаких вирусов. Правда, Jessie ещё не до конца допилен — мало рабочих браузеров, но — может, за два месяца ситуация и изменилась =) USB 3.0 сильно не хватает, да.


      1. uscr
        31.07.2015 09:32

        Когда я говорил, малинка — говно в красивой обёртке, я возможно и был слишком категоричен. Однако, за эти 35$ банана сильно выигрывает. При этом малина сильно разрекламирована и когда я выбирал плату, я руководствовался принципом: не может же она быть плохой, если все только о ней и говорят. И был… раздосадован :(


        1. CRImier
          31.07.2015 14:39

          Банана выигрывает в плане закачки торрентов, это без вариантов. Однако если нужны, к примеру:

          • полностью рабочие драйвера на GPIO, которые гарантированно пофиксят в случае проблем с ними (очень важный пункт)
          • помощь сообщества и куча готовых гайдов
          • большое количество готовых шилдов
          • максимально свободные и отлаженные драйвера+софт (не буду напоминать, что там с дровами на видео под чистый Линукс у большинства SBC)
          • большое количество очень специфичного софта (из того, что я использую — драйвера под LCD дисплеи на GPIO и сопутствующие утилиты, всякие оптимизированные memset/memcpy и тому подобное)
          • доступность везде (в силу популярности)

          Тут малинка, без вариантов. Собственно, я перечисляю причины для использования её мной. Я уверен, кто-нибудь ещё найдёт ещё причины.


          1. uscr
            31.07.2015 15:31

            Но они полностью совместимы во всём, что связано с GPIO. Это не так? Если нет, то конечно, я был не прав.


            1. CRImier
              31.07.2015 23:27

              Хмм… Вы знаете, я нарыл информации. С GPIO всё лучше, чем я предполагал =) Не уверен, что они совместимы до конца, той же гребёнки на 40 пинов у Банана ещё нет, к примеру, но с последнего раза, когда я читал об этой плате, всё намного улучшилось =) Рад видеть, что здесь поддержка намного лучше, и прошу прощения за непроверенный аргумент.


  1. grozaman
    31.07.2015 01:38
    +2

    Гарнитуры в вашем приложении ужасные! Они там абсолютно не применимы! А такая жуткая смесь заставляет меня плакать кровавыми слезами… </зануда-дизайнер>