python

Мега-Учебник Flask, Часть 17: Развертывание на Linux (и даже на Raspberry Pi!)

  • воскресенье, 21 сентября 2014 г. в 03:10:21
http://habrahabr.ru/post/237489/

Это семнадцатая статья в серии, где я описываю свой опыт написания веб-приложения на Python с использованием микрофреймворка Flask.

Цель данного руководства — разработать довольно функциональное приложение-микроблог, которое я за полным отсутствием оригинальности решил назвать microblog.



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

В этой статье я исследую традиционные способы размещения приложения в сети Интернет, а потому сосредоточусь на использовании Linux серверов, а также на весьма популярном миникомпьютере Raspberry Pi. Облачные сервисы мы рассмотрим в следующей статье.

Для начала мы рассмотрим, наверное, самый устоявшийся вид хостинга. Мы установим наше приложение на выделенном хосте, работающем под управлением вэб сервера.

Где же нам найти хост для размещения? На сегодняшний день существует много предложений недорогого хостинга. Наименее затратным вариантом является использование VPS (virtual private server) — виртуальной машины, выглядящей для нас как выделенный сервер, однако на самом деле разделяющей физические ресурсы с другими пользователями хостинга. Вы можете изучить предложения на lowendbox.com, если хотите арендовать VPS для экспериментов (заметьте, ссылка не реферальная, а значит я не получу ни цента с вашего клика по ссылке).

Еще один простой способ раздобыть хост для установки нашего приложения — установка виртуальной машины на ваш собственный домашний компьютер. Если вы пользователь Windows или Mac и хотели бы приобрести опыт разворачивания приложения на сервере, управляемом Linux, и при этом не потратить ни копейки — тогда это ваш вариант. Установите VirtualBox, скачайте ISO образ Linux дистрибутива по вашему усмотрению, создайте Виртуальную Машину (VM) и установите на неё ОС при помощи ранее скачанного образа.

Давайте немного поговорим о выборе ОС. Я думаю, что в качестве платформы для размещения вэб сервера следует рассматривать следующие 4 операционные системы:
  • Linux
  • BSD
  • OS X
  • Windows

С технической точки зрения, вы можете использовать любую из этих ОС для хостинга своего python приложения, так как же сделать выбор?

По моему мнению, выбор сделать вовсе не сложно. Для меня важно, чтобы платформа была открытой, т. к. большое сообщество разработчиков и пользователей справляется с задачей поддержания надежности и безопасности продукта гораздо лучше, чем одна компания может сделать, работая в одиночку над своим закрытым продуктом. Кроме того, мы бы хотели иметь доступ к большому количеству софта для установки, т. к. в случае возникновения проблем, в этом случае у нас будет больше шансов решить их. Основываясь на описанных выше критериях, я предпочту использовать сервер под управлением Linux или BSD серверу, работающему на Windows и OS X.

Итак количество вариантов сократилось до двух. А вот выбор между Linux и BSD, как по мне, и вовсе элементарен: нет никакой разницы, что использовать. Оба являются отличными вариантами для размещения вэб сервера. В зависимости от хостинг провайдера, вы, скорее всего, будете иметь возможность выбрать Linux или BSD, поэтому я просто использую то, что есть в наличии.

Кроме того, существуют различные дистрибутивы Linux и BSD, как выбрать из них? Это, по большей части, дело личных предпочтений, но если мы хотим использовать наиболее популярный дистрибутив, мы можем разбить все дистрибутивы на группы согласно основным характеристикам, а уже затем выбрать наиболее популярный из отобранной группы:
  • построенные на базе RPM (RedHat, CentOS, Fedora)
  • построенные на базе Debian (Debian, Ubuntu, Mint)
  • построенные на базе BSD (FreeBSD, NetBSD, OpenBSD)
  • другие

Наверняка, моя весьма упрощенная классификация дистрибутивов оставила некоторые дистрибутивы без внимания. Я также уверен, что много кто посчитает своим долгом напомнить, что Mac OS Х также принадлежит семейству BSD и должна быть добавлена в мой список. Помните, это моя собственная классификация и я не рассчитываю, что всё вокруг с ней согласятся.

В качестве упражнения, при подготовке этой статьи, я протестировал несколько дистрибутивов Linux (дело в том, что на моем VPS отсутствует опция установки BSD). Я опробовал Fedora, Ubuntu, Debian и CentOS. Ни один из этих дистрибутивов не оказался простым в установке, но из них всех, установка и настройка CentOS прошла наиболее гладко, так что дальше речь пойдет именно о CentOS.

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

Готовы? Тогда начнем!

Хостинг на CentOS 6


Я предполагаю, что у нас есть свежеустановленный дистрибутив CentOS 6 на выделенном сервере или на VPS. Виртуальная машина на вашем PC также подойдет, но прежде чем продолжить, установите ОС.

Настройка клиентской стороны

Мы будем управлять сервером удаленно с нашего домашнего ПК, поэтому нам требуется инструмент для регистрации в системе и запуска команд. Если ваш ПК работает под управление Linux или OS X, то у вас уже установлен OpenSSH. Если же вы пользователь ОС от Майкрософт, тогда у вас есть выбор из двух вариантов:
  • Cygwin (выберите пакет OpenSSH в установщике)
  • Putty

Cygwin это гораздо больше чем просто SSH, он предоставляет Linux подобное окружение внутри Windows. Putty, с другой стороны, является просто SSH клиентом. Т.к. я планирую осветить самые основы, инструкции ниже будут для пользователей Linux, OS X и Cygwin. Если вы решите использовать Putty с Windows, тогда может потребоваться некоторая адаптация представленных ниже инструкций.

Давайте начнем с авторизации на нашем новеньком CentOS 6 сервере. Откройте командную строку на вашем ПК (или Cygwin консоль если вы используете Windows) и запустите команду:

$ ssh root@<your-server>

Эта команда означает, что вы хотите войти на сервер под учетной записью пользователя root, администратора системы. Вы должны заменить <your-server> на IP адрес или имя вашего сервера. Обратите внимание, что вы не должны вводить символ $, это всего лишь приглашение командной строки.

Далее вам будет предложено ввести пароль пользователя root. Если это выделенный сервер или VPS, то вы вводили пароль во время процесса установки. Если это ваша собственная виртуальная машина, то вы установили пароль во время инсталляции.

Установка пакетов с программным обеспечением

Теперь, когда мы вошли в систему, нам необходимо установить всё требуемое программное обеспечение, которое наверняка будет включать Python и вэб сервер. Кроме того, мы заменим sqlite на более надежную СУБД — MySQL.

Команда, используемая для установки ПО в CentOS называется yum. Мы можем установить все требуемые пакеты одной командой:

$ yum install python python-devel httpd httpd-devel mysql-server mysql-devel git gcc sudo

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

Мы только что установили следующие пакеты:
  • python и python-devel: интерпретатор Python и его инструменты разработчика (заголовочные файлы и статические библиотеки)
  • httpd и httpd-devel: вэб сервер Apache и его инструменты разработчика
  • mysql-server и mysql-devel: сервер СУБД MySQL и его инструменты разработчика
  • git: распределённая система управления версиями файлов (мы будем использовать её для скачивания и обновления приложения)
  • gcc: компилятор C/C++ (нужен для компиляции расширений Python)
  • sudo: инструмент для запуска команд от имени другого пользователя.

Создание отдельного пользователя для нашего приложения

Теперь мы настроим отдельный аккаунт под которым и будет запускаться наше приложение. Если причины этого не ясны, поясню — пользователь root имеет максимальные права, т.ч. мы легко может удалить или повредить важные компоненты системы в результате ошибки. Если же мы будем работать под ограниченным (пользовательским) аккаунтом, то важные компоненты системы будут недосягаемы для нас, а это защитит нас от ошибок. И вторая, возможно, более значительная причина не использовать аккаунт root состоит в том, что если злоумышленник завладеет этим аккаунтом, он получит полный контроль над сервером. Мы собираемся скрыть аккаунт root так, как это только возможно, я предпочитаю вообще отключать возможность удаленного входа под учетной записью root.

Мы назовем новый аккаунт apps, предполагая, что со временем у нас будет работать несколько приложений на этом сервере. Т.к. приложения будут запускаться под учетной записью вэб сервера, мы добавим наш аккаунт apps в группу вэб сервера (которая была создана во время установки Apache), таким образом, вэб сервер будет иметь права на чтение и запись файлов нашего приложения. Если вы не знакомы с системой прав пользователя в Unix, тогда краткое руководство может быть найдено в Википедии.

Команда создания пользователя apps в CentOS выглядит так:

$ adduser -g apache apps

Ключ -g делает главной группой создаваемого пользователя группу apache, которая на CentOS является группой пользователя apache. Далее, у вас запросят пароль для создаваемого пользователя.

Команда adduser создаст домашнюю папку для нового аккаунта, расположенную по адресу /home/apps. Однако, если проверить права доступа этой папки, мы обнаружим что она доступна только своему владельцу:

$ ls /home -l
total 7
drwx------ 2 apps  apache 4096 Apr  7 11:46 apps

Мы решили, что пользователи группы apache должны иметь доступ к этой папке, поэтому выдадим им максимальные права:

$ chmod 775 /home/apps

Обращайтесь к документации команды chmod для получения более детальной информации. Права 775 разрешают полный доступ к папке владельцу и членам его группы, а всем остальным дают все права кроме права на запись.

Вход без пароля

Следующий шаг — разрешить вход под этой учетной записью с нашего домашнего ПК без использования пароля. SSH поддерживает еще один способ аутентификации, по публичному ключу. Если вы используете ОС отличную от Windows, то весьма вероятно, что у вас уже имеются установленные в системе ключи.

Теперь откройте терминал на вашем ПК (пользователи Windows — bash-консоль Cygwin-а). Для проверки наличия ключей, запустите следующую команду:

$ ls ~/.ssh
id_rsa  id_rsa.pub

Если в папке имеются файлы id_rsa и id_rsa.pub, как показано выше, вы можете двигаться дальше. Если этих файлов нет, тогда выполните следующую команду:

$ keygen -t rsa

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

После выполнения этой команды, у вас появятся файлы, перечисленные выше. Файл id_rsa.pub — это ваш публичный ключ, им вы можете свободно обмениваться с другими пользователями, при это не подвергаясь угрозам безопасности. Файл id_rsa — это ваш приватный ключ, которым вы ни в коем случае не должны делиться с кем бы то ни было.

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

$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCjwaK4JVuY6PZAr8HocCIOszrLIzzCjO0Xlt9zkFNKvVpP1B92u3JvwwiagqR+k0kHih2SmYnycmXjAcE60tvu+sIDA/7tEJZh4kO4nUYM5PJ17E+qTqUleBXQM74eITydq/USkOqc5p++qUUgA60gUUuNum3igbZiNi71zK4m8g/IDywWYk+5vzNt2i7Sm8NEuauy/xWgnWhCBXZ/tXfkgWgC/4HzpmsfO+nniNh8VgTZp8Q+y+4psSE+p14qUg7KdDbf0Wo/D35wDkMvto96bIT8RF0np9dTkFj8TgNW8inP+6MC+4vCd8F/NpESCVt8hRlBVERMF8Xv4f/0+7WT miguel@miguelspc

Теперь мы должны скопировать эти данные в буфер обмена, а затем переключиться в CentOS, где выполнить следующую команду:

$ mkdir /home/apps/.ssh
$ echo <paste-your-key-here> > /home/apps/.ssh/authorized_keys

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

$ mkdir /home/apps/.ssh
$ echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCjwaK4JVuY6PZAr8HocCIOszrLIzzCjO0Xlt9zkFNKvVpP1B92u3JvwwiagqR+k0kHih2SmYnycmXjAcE60tvu+sIDA/7tEJZh4kO4nUYM5PJ17E+qTqUleBXQM74eITydq/USkOqc5p++qUUgA60gUUuNum3igbZiNi71zK4m8g/IDywWYk+5vzNt2i7Sm8NEuauy/xWgnWhCBXZ/tXfkgWgC/4HzpmsfO+nniNh8VgTZp8Q+y+4psSE+p14qUg7KdDbf0Wo/D35wDkMvto96bIT8RF0np9dTkFj8TgNW8inP+6MC+4vCd8F/NpESCVt8hRlBVERMF8Xv4f/0+7WT miguel@miguelspc > /home/apps/.ssh/authorized_keys

В результате выполнения этих команд, ваш публичный ключ будет записан в файл authorized_keys на сервере. Таким образом, мы сделали наш публичный ключ известным OpenSSH на сервере. Следующей задачей является обеспечение безопасности папки .ssh и файла authorized_keys внутри неё:

$ chown -R apps:apache /home/apps/.ssh
$ chmod 700 /home/apps/.ssh
$ chmod 600 /home/apps/.ssh/authorized_keys

Эти команды изменят владельца этих файлов на аккаунт apps, а затем сделают папку и файл доступными только новому владельцу.

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

$ ssh apps@<your-server>

Если все прошло гладко, вам не придется вводить пароль для получения доступа.

Установка приложения

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

Приложение должно быть расположено на git сервере, доступном с нашего сервера. Я буду использовать свой репозитарий на github. Вы также можете использовать его, или, если вы предпочитаете, вы можете клонировать репозитарий и сделать его своим собственным.

Для установки microblog на наш сервер, нам будет достаточно команды git clone:

$ git clone git://github.com/miguelgrinberg/microblog.git
$ cd microblog

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

Первое, имеется несколько новых файлом на самом верхнем уровне — runp-sqlite.fcgi, runp-mysql.fcgi and killpython. Файлы *.fcgi представляют собой стартовые скрипты, используемые вэб серверами, которые работают с FastCGI протоколом. Скрипт killpython будет перезапускать приложение после обновлений. Мы рассмотрим это всё позже.

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

SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')

Теперь будем это делать так:

if os.environ.get('DATABASE_URL') is None:
    SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
else:
    SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']

Это простое изменение позволит нам изменять базу данных приложения, простой установкой переменной окружения. В следующем разделе, вы увидите как можно это использовать.

Еще одно изменение имеется в файле setup.py, который занимался настройкой виртуального окружения. Теперь этот скрипт устанавливает также пакет mysql-python, который необходим SQLAlchemy для работы с СУБД MySQL. Кроме того, пакет Flask-WhooshAlchemy теперь устанавливается из моего собственного форка, т. к. у меня не получилось связаться с автором оригинального пакета и договориться о включении моих исправлений в основной дистрибутив.

Настройка MySQL

База данных sqlite, которую мы использовали всё это время прекрасно подходит для простых приложений, но когда мы перебираемся на полноценный сервер, который может обслуживать несколько одновременных запросов, лучше будет использовать более надежнуюю СУБД. Поэтому мы настроим MySQL для нашего microblog-а.

Мы уже установили MySQL, так что нам осталось лишь создать базу данных и пользователя с правами на эту базу. Для управления сервером базы данных мы используем консоль mysql:

$ mysql -u root -p
Enter password: (enter the mysql root password, or empty if one is not defined)
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 11
Server version: 5.1.67 Source distribution

Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

И теперь, в командной строке mysql, мы создадим базу данных apps и пользователя с тем же именем:

mysql> create database apps character set utf8 collate utf8_bin;
mysql> create user 'apps'@'localhost' identified by 'apps';
mysql> grant all privileges on apps.* to 'apps'@'localhost';
mysql> flush privileges;
mysql> quit;

Заметьте, что вы должны указать пароль пользователя apps в секции identified by. Для простоты, я установил пароль apps, но при реальной настройке БД необходимо использовать более надежные пароли. Не путайте пользователя apps базы данных с пользователем apps системы.

Инициализация приложения

Теперь, когда база данных готова, мы можем инициализировать наш microblog.

Для начала создадим виртуальное окружение:

$ ./setup.py

Затем создаем базу данных:

$ DATABASE_URL=mysql://apps:apps@localhost/apps ./db_create.py

Заметьте, как мы установили переменную окружения DATABASE_URL. Теперь приложение будет использовать MySQL базу данных вместо sqlite.

Далее мы компилируем все сообщения в нашей базе переводов:

$ ./tr_compile.py

И в конце, мы даем право на запись группе в две папки, которые должны быть доступны вэб серверу:

$ chmod -R g+w search.db tmp

Папка search.db используется нашей базой полнотекстового поиска Whoosh. Папка же tmp должна быть доступна для записи, для чего — станет ясно позднее.

Настройка Apache

Последнее, что нужно сделать — настроить вэб сервер Apache.

Мы будем использовать модуль mod_fcigd для обработки FastCGI диалога с нашим приложением. Многие дистрибутивы предлагают готовые пакеты для поддержки mod_fcgid, но, к сожалению, CentOS не принадлежит к их числу, поэтому мы просто соберем этот модуль из исходников.

Вот список команд, которые соберут и установят текущий выпуск mod_fcgid:

$   wget http://mirror.metrocast.net/apache//httpd/mod_fcgid/mod_fcgid-2.3.7.tar.gz
$ tar xvzf mod_fcgid-2.3.7.tar.gz
$ cd mod_fcgid-2.3.7
$ APXS=/usr/sbin/apxs ./configure.apxs
$ su
(enter root password)
$ make install

Вы можете свериться с документацией mod_fcgid, если вас интересуют детали представленных выше команд.

Установив этот модуль, мы приступаем непосредственно к настройке нашего сервера. Для этого мы должны отредактировать конфигурационный файл Apache. Различные дистрибутивы Linux так и не пришли к единому мнению по поводу расположения и именования этого файла, т.ч. вам придется разобраться с этим вопросом самостоятельно. Например, в CentOS, конфигурационный файл расположен по адресу /etc/httpd/conf/httpd.conf.

Сюда мы просто добавим определение нашего сервера. В конце файла добавьте следующие строки (делайте это от имени администратора root, т. к. этот файл не доступен на запись для простых пользователей):

FcgidIPCDir /tmp
AddHandler fcgid-script .fcgi
<VirtualHost *:80>
    DocumentRoot /home/apps/microblog/app/static
    Alias /static /home/apps/microblog/app/static
    ScriptAlias / /home/apps/microblog/runp-mysql.fcgi/
</VirtualHost>

Параметр FcgidIPCDir указывает на папку, где будут создаваться файлы сокетов. Я обнаружил, что по умолчанию они создаются в папке, в которой пользователь apache не имеет права на запись, поэтому я решил размещать эти файлы в /tmp.

Затем мы указываем AddHandler, чтобы сообщить apache что все файлы с расширением .fcgi должны восприниматься как файлы FastCGI и выполняться через модуль mod_fcgid, который мы только что установили. Помните новый файл runp-mysql.fcgi который расположен в нашей корневой директории? Этот файл использует python модуль flup, в качестве адаптера чтобы наше приложение смогло взаимодействовать с FastCGI протоколом. Давайте взглянем на этот файл:

#!flask/bin/python
import os
os.environ['DATABASE_URL'] = 'mysql://apps:apps@localhost/apps'

from flup.server.fcgi import WSGIServer
from app import app

if __name__ == '__main__':
    WSGIServer(app).run()

Apache будет выполнять этот файл для запуска нашего приложения. Заметьте, как мы поместили название базы MySQL в окружение, чтобы Apache мог узнать его.

Секция определяет хост который будет обслуживать вэб сервер. Обозначение *:80 означает, что все запросы поступившие серверу на любой хост на 80-й порт, будут обслуживаться этим виртуальным сервером. В конфигурационном файле могут быть определены несколько секций , каждая из которых определяет другой сервер. Для того, чтобы отличать сервера друг от друга, вы можете использовать различные IP адреса, домены/субдомены или порты.

Определение нашего виртуального хоста довольно простое. Оператор DocumentRoot сообщает Apache где искать статические файлы. Все запросы файлов, будут обслуживаться из этой папки, так например, когда браузер запросит файл /favicon.ico, для отображения маленькой иконки рядом с полем ввода адреса в браузере, Apache будет искать его именно здесь. Однако, все статичные файлы, используемые в нашем приложении, будут иметь префикс /static, поэтому чтобы Apache не искал другую папку static, мы применяем оператор Alias, который означает, что все запросы начинающиеся с /static должны попадать в нашу папку со статикой. И наконец, оператор ScriptAlias сообщает Apache, что получив запрос начинающийся с / (вообще-то, это все запросы кроме запросов статики) необходимо вызвать скрипт указанный вторым аргументом, а это и есть наш .fcgi скрипт.

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

Для активации произведенных изменений, мы должны перезапустить сервер из-под аккаунта root:

# service httpd restart

Теперь, перейдя по адресу http://<your-server> в вашем браузере, вы должны увидеть страницы нашего микроблога.

Установка обновлений приложения

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

Предположим, мы развернули приложение и оно успешно проработало какое-то время на нашем сервере. И вот пришло время выкатить обновление, которое должно исправить некоторые обнаруженные ошибки или просто добавить новые возможности.

Мы используем git и наш собственный инструментарий для этого. Если мы авторизированы на сервере под аккаунтом apps, мы можем обновиться до последнего релиза в репозитарии при помощи команд:

$ cd microblog
$ git pull
$ DATABASE_URL=mysql://apps:apps@localhost/apps ./db_upgrade.py
$ ./tr_compile.py

Команда git pull скачает все новые и обновленные файлы с нашего сервера git. Затем, мы обновим нашу базу данных и перекомпилируем файлы перевода, если они изменились. И это всё!

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

И в этом случае нам пригодится sudo. Команда sudo позволяет пользователям запускать приложения от имени другого пользователя. Мы не хотели бы давать нашему пользователю apps слишком много прав, поэтому дадим ему только право отправлять сигналы останова FastCGI процессам, запущенным пользователем apache.

Скрипт killpython из нашей папки microblog, делает как раз это:

killall /home/apps/microblog/flask/bin/python

Загвоздка в том, что если мы запустим этот скрипт от имени пользователя apps, у нас не будет необходимых привелегий для остановки процессов, принадлежащих пользователю apache. Чтобы позволить пользователю apps прерывать эти процессы, мы должны внести небольшие изменения в файл /etc/sudoers:

apps    ALL=(apache) NOPASSWD:/home/apps/microblog/killpython
Defaults: apps    !requiretty

Заметьте, что эти изменения вы должны вносить от имени пользователя root, т. к. /etc/sudoers это системный файл.

Первая трудночитаемая команда дает пользователю apps право запускать killpython от имени пользователя apache без необходимости вводить пароль. Вторая строка разрешает пользователю apps использовать команду sudo вызывая её из скрипта, а не только из консоли. Вы можете ознакомиться с руководством по файлу sudoers для получения детальной информации о синтаксисе кофигурационного файла sudo.

Теперь, войдя под учетной записью пользователя apps, мы можем прерывать процессы python выполнив команду:

$ sudo -u apache ./killpython

Если процессы FastCGI будут прерваны, Apache перезапустит их, получив следующий запрос к microblog-у.

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

ssh apps@<your-server> "cd microblog;git pull;sudo -u apache ./killpython;DATABASE_URL=mysql://apps:apps@localhost/apps ./db_upgrade.py;./tr_compile.py"

Если вы сохраните эту команду в скрипт, то вы сможете выкатывать обновление на сервер одной командой!

Что еще можно добавить

Я не рассмотрел несколько обычных операций, которые рекомендуются к применению для обеспечения еще большей безопасности сервера в условиях «враждебной среды Интернета». Это такие операции как:
  • Запрет удаленного входа пользователя root
  • Отключение всех неиспользуемых сервисов, вроде FTP, CIFS, и т.д.
  • Настройка файервола.
  • Своевременная установка обновлений безопасности для ПО сервера.
  • Некоторые другие.

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

Хостинг на Raspberry Pi


Raspberry Pi это революционный миниатюрный компьютер, работающий под управлением Linux, стоимостью около $35. Он обладает очень низким энергопотреблением, что делает его превосходным устройством для хостинга домашних вэб приложений, доступных онлайн в режиме 24/7 без необходимости держать постоянно включенным большой ПК.

Существует несколько дистрибутивов, предназначенных для Raspberry Pi. Мы установим наше приложение на Raspbian, официальный дистрибутив.

Кстати, заметьте, что Raspbian является ответвлением дистрибутива Debian, а это значит что приведенные ниже инструкции будут применимы (возможно, с минимальными корректировками) и для серверов на базе Debian/Ubuntu.

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

Настройка клиентской стороны

Raspberry Pi представляет собой полноценный компьютер. Вы можете подключить HDMI монитор, клавиатуру, мышь и работать прямо на нем для настройки серверного ПО. Вроде того, как мы делали на CentOS (да и вообще на любом дистрибутиве Linux), вы можете просто подключить Raspberry Pi к сети, а затем подключиться к ней при помощи ssh. Смотрите подраздел «Настройка клиентской стороны» выше, чтобы настроить ssh на своем ПК.

Заметьте, что я не затронул установку и настройку самой Raspberry Pi, т. к. об этом написана масса статей. Я предполагаю, что Raspberry Pi уже запущена и подключена к сети.

Установка пакетов с программным обеспечением

Т.к. Pi всё же представляет собой машину с ограниченной мощностью, мы не будет устанавливать полновесный Apache/MySQL стэк, как мы сделали это на CentOS. Вместо этого, мы применим легкий подход. В качестве вэб сервера мы используем Lighttpd, небольшой вэб сервер с очень хорошей и эфективной реализацией FastCGI. В качестве СУБД мы оставим sqlite.

Для CentOS мы использовали yum для установки пакетов. В дистрибутивах же семейства Debian, используется менеджер пакетов apt-get:

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install python python-dev lighttpd git

Команда update скачивает и обновляет список пакетов на ваш Pi. Команда upgrade обновляет все установленные пакеты до их последних версий. Рекомендуется запускать эти команды периодически, чтобы сохранять актуальность используемого ПО. И, наконец, команда install устанавливает необходимые нам пакеты.

Создание отдельного пользователя для нашего приложения

Мы решили, что наш Raspberry Pi сервер будет расположен в доверенной сети. Поэтому безопасность не является нашим главным приоритетом в этих условиях, и мы не будем создавать отдельного пользователя, а вместо этого используем аккаунт по умолчанию — pi.

Если вы планируете держать Raspberry Pi подключенной к Интернет, то вы должны применить подход, использованный нами для CentOS и создать отдельный ограниченный аккаунт, чтобы в случае взлома этого аккаунта, нанесенный урон был ограничен.

Вход без пароля

Заведение публичных ключей также не очень важная процедура если приложение будет работать в локальной сети, но если вам понадобится эта функциональность — процедура будет полностью аналогична той, что мы использовали на CentOS.

Установка приложения

Приложение устанавливается при помощи git:

$ git clone git://github.com/miguelgrinberg/microblog.git
$ cd microblog

Инициализация приложения

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

$ ./setup.py
$ ./db_create.py
$ ./tr_compile.py

Заметьте, что setup.py попытается установить поддержку для MySQL и эта попытка будет провалена, но это и не страшно, т. к. мы не используем MySQL.

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

$ chmod -R 777 *

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

Я хотел бы пояснить, что Raspberry Pi is прекрасно совместима с конфигурацией, основанной на группах и правах доступа, подобно той что мы использовали для сервера под управлением CentOS. Мы выбрали упрощенную схему настройки, т. к. мы будем использовать этот сервер в полностью контролируемом окружении.
Настройка Lighttpd
Lighttpd имеет встроенную поддержку FastCGI, а значит нам не придется заниматься установкой отдельного модуля для этих целей.
Всё что нам нужно сделать — добавить наш сайт в конфигурационный файл, расположенный по адресу /etc/lighttpd/lighttpd.conf:

fastcgi.server = ("/microblog" =>
    ((
        "socket" => "/tmp/microblog-fcgi.sock",
        "bin-path" => "/home/pi/microblog/runp-sqlite.fcgi",
        "check-local" => "disable",
        "max-procs" => 1
    ))
)

alias.url = (
    "/microblog/static/" => "/home/pi/microblog/app/static/",
)

И после этого наше приложение уже должно быть доступно он-лайн по адресу http:// <IP адрес Raspberry Pi>.

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

Выражение fastcgi.server определяет поведение нашего FastCGI сервера, доступного по адресу /microblog. Причина, по которой мы не разместили приложение по адресу / проста, возможно позднее мы запустим больше одного приложения, и в таком случае размещение всех адресов нашего приложения в /microblog будет играть роль пространства имен.

Внутри определения FastCGI мы задаем расположение сокет файла в папке /tmp и путь к нашему файлу runp-sqlite.fcgi, который Lighttpd будет запускать для старта FastCGI процессов. Опция check-local сообщает Lighttpd о необходимости отправлять запросы FastCGI серверу, даже если запрошенный путь соответствует файлу на диске. max-procs ограничивает количество процессов FastCGI до 1, что для небольшого сервера вполне достаточно, а также поможет избежать потенциальных проблем при нескольких одновременных запросах на запись в базу sqlite.

Секция alias.url отражает запросы к статике на локальный путь к соответствующему файлу..

Скрипт runp-sqlite.fcgi очень похож на тот, что мы использовали на CentOS, только без перезаписи настроек базы данных:

from flup.server.fcgi import WSGIServer
from app import app

if __name__ == '__main__':
    WSGIServer(app).run()

Некоторые необходимые исправления

Тестирование этой установки обозначит несколько проблем в работе нашего приложния.

Во-первых, наши файлы javascript и css не загружаются. Проблему легко решить, заглянув в исходный код страницы на странице входа. В шаблоне base.html эти файлы заданы абсолютными путями, которые начинаются со /static, однако же мы добавили префикс /microblog к нашим адресам. Например, вот как указана ссылка на файл CSS:

В то время, когда на самом деле мы бы хотели видеть здесь /microblog/static/bootstrap.min.css, и, конечно же, мы не можем просто добавить /microblog в шаблон base.html потому что это приведет к неработоспособности нашего приложения на сервере разработки, запущенном на нашем ПК. Решением является генерация этих адресов автоматически при помощи url_for:

<link href="{{ url_for('.static', filename = 'css/bootstrap.min.css') }}" rel="stylesheet" media="screen">

После обновления, все js и css файлы будут загружаться корректно.

Но попытка залогиниться, обнаружит новую проблему. Сразу после авторизации, мы получаем ошибку 500. К счастью для нас, у нас включено логгирование, поэтому быстрое изучение /home/pi/microblog/tmp/microblog.log приводит нас к следующий ошибке:

ProgrammingError: (ProgrammingError) SQLite objects created in a thread can only be used in that same thread.The object was created in thread id -1260325776 and this is thread id -1243548560

Это еще что? Мы не запускаем несколько потоков, но очевидно, что наше приложение всё же использует несколько потоков. Единственное отличие между сервером для разработки и этим состоит в том, что тут мы используем flup для FastCGI сервера. Заглянув в исходный код этого модуля, мы узнаем, что по умолчанию flup запускает многопоточный сервер.

Возможным решением может быть использование одного потока сервера FastCGI, предоставляемого flup. Однако, это может навредить производительности в случае нескольких конкурирующих подключений. Другим, более перспективным путем решения этой проблемы, может стать разрешение многопоточности в sqlite (которая, кажется, полностью поддерживается). Многопоточность можно разрешить в SQLAlchemy, установив check_same_thread параметр в False в нашем конфигурационом файле config.py:

SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db') + '?check_same_thread=False'

Этим изменением мы позволили запускать много потоков для внесения изменений в базу данных, синхронизацию же множественного доступа берет на себя библиотеку sqlite.

И, наконец, мы запустии наше приложение на Raspberry Pi!

Установка обновлений приложения

Для установки обновлений мы можем просто войти на Raspberry Pi и вызвать команды, выполняющие обновление исходного кода, базы данных и файлов перевода:

$ cd microblog
$ git pull
$ ./db_upgrade.py
$ ./tr_compile.py

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

Заключение


Обновленное приложение доступно, как всегда, на github. Или же вы можете скачать его в виде zip архива по ссылке:
Скачать microblog 0.17.

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

Увидимся!

Miguel