Введение
В этом уроке мы установим Ruby и Nginx на DigitalOcean Ubuntu Droplet и настроим Puma и Capistrano в нашем Rail-приложении. Nginx будет использоваться для захвата клиентских запросов и передачи их на веб-сервер Puma с Rails. Мы будем использовать Capistrano для автоматизации общих задач развертывания, поэтому каждый раз, когда нам понадобится развертывать новую версию нашего Rails-приложения на сервере, мы можем сделать это с помощью нескольких простых команд.
Требования
-
(регистрация)
- Пользователь deploy с root правами и sudo привилегиями
- Rails-приложение в GitHub репозитории, которое нужно развернуть на сервере
Все команды в этом руководстве должны запускаться от пользователя deploy. Если для команды требуется root-доступ, ей будет предшествовать sudo.
Шаг 1 — установка NGINX
Как только Вы снимете VPS, можно начать установку пакетов. Обновите индексные файлы пакетов:
Потом установите NGINX:
Шаг 2 — Установка базы данных
Установите базу данных, которую вы будете использовать в своем Rails-приложении. Поскольку можно использовать большое количество различных баз данных, мы не будем рассматривать установку каждой в этом руководстве. Вот инструкции для самых распространенных БД:
Также Вы можете прочесть:
При использовании PostgreSQL установите:
Шаг 3 — Установка RVM и Ruby
Мы не будем устанавливать Ruby напрямую. Вместо этого мы будем использовать Ruby Version Manager. Их очень много на выбор (rbenv, chruby и т. Д.), но в этом уроке мы будем использовать RVM. RVM позволяет Вам легко устанавливать и управлять несколькими версиями Ruby в одной и той же системе и использовать их в соответствии с вашим приложением. Это облегчает жизнь, когда вам нужно обновить Rails-приложение, чтобы использовать новую версию Ruby.
Перед установкой RVM вам необходимо импортировать ключ RVM GPG:
Затем установите RVM, чтобы усправлять версиями Ruby:
Эта команда использует curl чтобы загрузить установочный скрипт с https://get.rvm.io . Аргумент -sSL состоит из трех флагов:
- -s указывает curl загружать файл в 'фоновом режиме'
- -S указывает curl выводить ошибки в ходе загрузки
- -L указывает curl следовать по редиректам в процессе загрузки
Нам нужно загрузить скрипт RVM (как функцию), чтобы мы могли начать его использовать. Затем нам нужно запустить команду для автоматической установки необходимых зависимостей и файлов для RVM и Ruby для правильной работы:
Теперь мы можем установить Ruby. Мы будем устанавливать последнюю версию Ruby 2.2.1 в качестве стандартного Ruby:
Шаг 4 — Установка Rails и Bundler
После того, как Ruby настроен, мы можем начать установку Rubygems. Мы начнем с установки Rails gem-а, который позволит вашему Rails-приложению запускаться, а затем мы установим bundler, который сможет прочитать Gemfile вашего приложения и автоматически установить все необходимые gem-ы.
Чтобы установить Rails и Bundler используйте команду:
Шаг 5 — Установка SSH ключей
Поскольку мы хотим настроить плавное развертывание, мы будем использовать SSH-ключи для авторизации. Сначала установите ключи GitHub или любого другого Git репозитория, где размещен код вашего Rails-приложения:
Не беспокойтесь, если вы получите сообщение Permission denied (publickey). Теперь сгенерируйте SSH-ключ (пара открытого / закрытого ключа) для вашего сервера:
Добавьте созданный открытый ключ (
/ .ssh / id_rsa.pub) в ключи развертывания вашего репозитория:
Если все шаги были выполнены правильно, Вы сможете клонировать ваш репозиторий git (по протоколу SSH, а не HTTP) без ввода пароля:
Если вам нужно примерное приложение для тестирования, вы можете развить следующее тестовое приложение, специально созданное для этого учебника: Sample Rails App on GitHub
Команда git clone создаст каталог с тем же именем, что и ваше приложение. Например, будет создан каталог с именем testapp_rails.
Мы клонируем только чтобы проверить, работают ли наши ключи развертывания, нам не нужно клонировать наш репозиторий каждый раз, когда мы делаем изменения. Capistrano сделает все это за нас. Вы можете удалить этот клонированный каталог, если хотите.
Откройте терминал на локальной машине. Если у вас нет ключа SSH для локального компьютера, создайте его. В терминале локального компьютела введите:
Добавьте свой локальный ключ SSH в файл авторизованных ключей Droplet (не забудьте заменить номер порта на свой):
Шаг 6 — Настройка конфигураций в Rails-приложении
На вашем локальном компьютере создайте файлы конфигурации для Nginx и Capistrano в Rails-приложении. Начните с добавления этих строк в Gemfile в приложении Rails:
Используйте bundler для установки гемов, которые вы только что указали в своем Gemfile. Введите следующую команду, чтобы установить гемы в Rails-приложении:
После bundle выполните следующую команду для настройки Capistrano:
- Capfile в корневой директории приложения
- deploy.rb в папке config
- Папку deploy в папке config
Замените все что есть в вашем Capfile на следующее:
Этот Capfile загружает некоторые предопределенные задачи в ваши конфигурационные файлы Capistrano, чтобы автоматически развертывать ваши приложения:
- Выбирает версию Ruby
- Прекомпилирует Asset-ы
- Клонирует репозиторий с кодом приложения
- Устанавливает новые зависимости если Gemfile был изменен
Замените содержимое config / deploy.rb следующим, обновив поля, отмеченные жирным шрифтом с помощью параметров вашего приложения и Droplet:
Этот файл deploy.rb содержит некоторые дефолтные значения, готовые к использованию, чтобы помочь вам управлять развертыванием приложений и автоматически выполнять некоторые задачи при развертывании.
Теперь необходимо настроить Nginx. Создайте config/nginx.conf в каталоге проекта Rails и добавьте к нему следующее (опять же, заменив ваши параметры):
Как и в предыдущем файле, nginx.conf содержит значения по умолчанию, которые работают и готовы к использованию c конфигурацями в файле deploy.rb. Он прослушивает трафик на порту 80 и передает запрос на ваш сокет Puma, записывает журналы nginx в «текущую» версию вашего приложения, сжимает все asset-ы и кэширует их в браузере с максимальным сроком действия, обслуживает HTML-страницы в как статические файлы, и устанавливает максимальный Client Body Size и Request Timeout.
Шаг 7 — Развертывание приложения на сервере
Если вы используете свое собственное приложение Rails, закоммитьте ( git commit ) сделанные вами изменения и залейте ( git push ) их в удаленный репозиторий:
Cделайте первое развертывание:
Это развернет ваше приложение Rails на Droplet, установит все необходимые гемы для вашего приложения и запустит веб-сервер Puma. Это может занять от 5 до 15 минут в зависимости от количества гемов, которое использует ваше приложение. Вы увидите отладочные сообщения по мере протекания этого процесса.
Если все прошло гладко, мы готовы подключить ваш веб-сервер Puma к обратному прокси Nginx.
На Droplet, сделайте Symlink файла nginx.conf в каталог с sites-enabled:
Перезапустите сервис Nginx:
Теперь вы можете открыть веб-браузер, ввести IP-адрес сервера и увидеть свое приложение Rails в действии!
Обычные развертывания
Всякий раз, когда вы вносите изменения в свое приложение и хотите развернуть новую версию на сервере, закоммитьте изменения, залейте на свой git remote, как обычно, и запустите команду deploy:
Разработка и производство веб-приложений: развертывание приложения
Данное руководство поможет вам развернуть PHP-приложение, WordPress и частный DNS.
Пользователи будут получать доступ к приложению по HTTPS через доменное имя (в руководстве используется условный домен https://www.example.com). Домен указывает на балансировщик нагрузки, который работает как обратный прокси-сервер для серверов приложений, подключенных к серверу баз данных. Частный DNS позволяет ссылаться на частные сетевые адреса серверов по именам, что значительно облегчает процесс настройки серверов.
Для реализации такой инфраструктуры нужны шесть серверов, которые создаются в следующем порядке:
- Частные DNS-серверы (ns1 и ns2).
- Сервер баз данных (db1).
- Серверы приложений (app1 и app2).
- Балансировщик нагрузки (lb1).
1: Частные DNS-серверы
Использование имен в адресах помогает идентифицировать серверы, с которыми вы работаете, и упрощает обслуживание объемных инфраструктур (чтобы заменить сервер, достаточно просто обновить свои записи DNS в одном файле, а обновлять бесчисленные конфигурационные файлы не нужно). В данной настройке DNS используется для того, чтобы иметь возможность ссылаться на частные сетевые адреса серверов по имени вместо IP-адресов.
Ссылаться на частный сетевой адрес каждого сервера можно по имени хоста по поддомену nyc3.example.com. Например, частный сетевой адрес сервера базы данных будет db1.nyc3.example.com.
Примечание: Поддомен почти всегда выбирается произвольно (обычно при его выборе предпочтение отдается варианту, который упростит настройку).
В результате вы получите два BIND-сервера, ns1 и ns2. Если вы уже знаете IP-адреса всех серверов, добавьте их в DNS. Если вы не знаете адреса, добавляйте DNS-записи по мере создания серверов.
2: Сервер баз данных
Чтобы внедрить распределение нагрузки между серверами приложений (то есть, между Apache и PHP), нужно отделить от них базу данных, выделив для нее отдельный сервер. Отделение БД от приложения является важным этапом для горизонтального масштабирования многих типов приложений.
Установка MySQL
Перейдите на сервер БД, db1, и установите MySQL:
sudo apt-get update
sudo apt-get -y install mysql-server
По запросу введите root-пароль MySQL.
sudo mysql_install_db
sudo mysql_secure_installation
MySQL снова запросит root-пароль, а затем предложит изменить его. Если вы не хотите менять этот пароль, введите N. На остальные вопросы программы можно выбрать опции по умолчанию.
Настройка MySQL для прослушивания частного сетевого интерфейса
Откройте конфигурационный файл MySQL:
sudo vi /etc/mysql/my.cnf
Найдите параметр bind-address и укажите в нем частный сетевой адрес сервера базы данных:
Сохраните и закройте файл.
sudo service mysql restart
Создание пользователя и базы данных
Теперь нужно создать БД и пользователя, с помощью которого приложение сможет подключаться к БД.
Откройте консоль MySQL:
Введите root-пароль MySQL.
Чтобы создать БД, введите:
CREATE DATABASE app;
MySQL связывает своих пользователей с серверами, которые будут подключаться к базе данных. В данном случае у вас есть два сервера приложений, которые будут подключаться к БД, поэтому необходимо создать пользователя для каждого из них.
Создайте пользователя базы данных appuser, к которому можно подключиться с частного сетевого адреса каждого из серверов приложений (app1 и app2). Используйте один и тот же пароль для каждого пользователя:
CREATE USER ‘ appuser ‘@’ app1.nyc3.example.com ‘ IDENTIFIED BY ‘ password ‘;
CREATE USER ‘ appuser ‘@’ app2.nyc3.example.com ‘ IDENTIFIED BY ‘password’;
Теперь нужно передать пользователю appuser права на базу данных app.
GRANT ALL PRIVILEGES ON app .* TO ‘ appuser ‘@’ app1.nyc3.example.com ‘;
GRANT ALL PRIVILEGES ON app .* TO ‘ appuser ‘@’ app2.nyc3.example.com ‘;
FLUSH PRIVILEGES;
Теперь инсталлятор приложения сможет получить доступ к БД и внести туда все данные.
Примечание: Если вы используете больше двух серверов приложений, создайте пользователей базы данных для каждого из них.
Выйдите из командной строки MySQL:
3: Настройка серверов приложений
Серверы приложений будут запускать код приложения, который будет подключаться к серверу базы данных. В данном случае используется PHP-приложение WordPress, которое обслуживается веб-сервером Apache или Nginx. Чтобы реализовать балансировку нагрузки между серверами приложений, настройте два одинаковых сервера.
Установка Apache и PHP
На оба сервера приложений, app1 и app2, нужно установить Apache и PHP.
sudo apt-get update
sudo apt-get -y install apache2 php5-mysql php5 libapache2-mod-php5 php5-mcrypt
Настройка Apache
Для распределения нагрузки и обработки SSL-терминации будет использоваться HAProxy. Это предотвратит прямой доступ к приложению. Свяжите Apache с частным сетевым адресом каждого сервера.
На каждом сервере приложений (app1 и app2) откройте конфигурационный файл портов Apache. По умолчанию это файл ports.conf:
sudo vi /etc/apache2/ports.conf
Найдите строку Listen 80 и добавьте в нее свой внутренний IP-адрес:
Сохраните и закройте файл.
Теперь Apache прослушивает только частный сетевой интерфейс, что означает, что к нему нельзя получить доступ по внешнему IP-адресу или имени хоста.
Перезапустите Apache, чтобы обновить настройки:
sudo service apache2 restart
Теперь веб-сервер Apache доступен только через частный сетевой адрес серверов приложений. Чтобы веб-сервер мог получать запросы пользователей, настройте балансировку нагрузки.
Загрузка и настройка приложения
В данном примере используется приложение WordPress. Если вы используете другое PHP-приложение, самостоятельно загрузите его и выполните соответствующую настройку (например, добавьте информацию о подключении к базе данных), а затем перейдите к следующему разделу.
Загрузите архив WordPress на первый сервер приложений, app1.
tar xvf latest.tar.gz
Перейдите в полученный каталог:
Для загрузок WordPress необходим каталог wp-content/uploads.
В качестве шаблона конфигурационного файла WordPress можно использовать образец, который поставляется с установкой. Скопируйте его:
cp wp-config-sample.php wp-config.php
Откройте файл в редакторе:
Настройте подключение WordPress к БД приложения. Для этого укажите следующие данные:
/** The name of the database for WordPress */
define(‘DB_NAME’, ‘ app ‘);
/** MySQL database username */
define(‘DB_USER’, ‘ appuser ‘);
/** MySQL database password */
define(‘DB_PASSWORD’, ‘ password ‘);
/** MySQL hostname */
define(‘DB_HOST’, ‘db1.nyc3.example..com’);
Примечание: Вместо условных данных укажите свои данные.
Балансировщик нагрузки будет шифровать данные с помощью TLS/SSL. Добавьте в настройки WordPress следующие строки, чтобы приложение могло взаимодействовать с обратным прокси-сервером и поддерживать SSL.
define(‘FORCE_SSL_ADMIN’, true);
if ($_SERVER[‘HTTP_X_FORWARDED_PROTO’] == ‘https’)
$_SERVER[‘HTTPS’]=’on’;
Также нужно обновить ключи и соль, чтобы в случае необходимости иметь возможность сделать файлы cookies недействительными. Ключи и соль должны быть одинаковыми на всех серверах приложений.
Сохраните и закройте файл.
Копирование файлов приложения в корневой каталог
Теперь приложение настроено. Можно скопировать его файлы в корневой каталог (document root) веб-сервера Apache, из которого он будет обслуживать контент.
По умолчанию DocumentRoot Apache находится в каталоге /var/www/html.
Сначала удалите стандартный файл index.html:
sudo rm /var/www/html/index.html
С помощью rsync скопируйте файлы WordPress в /var/www/html и передайте права на них пользователю www-data (он запускает Apache).
/wordpress/ /var/www/html
sudo chgrp -R www-data /var/www/html/*
Первый сервер приложений готов. Теперь нужно настроить второй сервер.
4: Репликация файлов приложения на остальные серверы
Чтобы сохранить целостность файлов приложения на всех серверах, нужно настроить репликацию каталога document root.
При загрузке файлов и установке плагинов веб-интерфейс WordPress будет сохранять файлы на том сервере, который обрабатывает запрос. Если эти файлы не реплицируются на все серверы приложений, некоторые из пользователей будут получать неполные страницы с отсутствующими изображениями и неработающими плагинами.
Примечание: Если вы используете не WordPress, а другое приложение PHP, которое не сохраняет данные (например, загруженные файлы или загруженные плагины) на сервере приложений, вы можете просто один раз скопировать файлы приложения вручную. Для копирования файлов с app1 на app2 используйте rsync.
GlusterFS позволяет создать реплицированный том необходимых файлов.
Настроив репликацию данных между серверами приложений, можно приступать к настройке распределения нагрузки.
5: Настройка балансировщика нагрузки
В качестве балансировщика нагрузки используется HAProxy, который будет работать как обратный прокси-сервер для серверов приложений. Пользователи получат доступ к приложению через сервер балансировки нагрузки по домену (например, https://www.example.com).
Читайте также:
Копирование SSL-сертификата
Примечание: Данный раздел нужно выполнить на сервере lb1.
Откройте каталог, в котором хранится SSL-сертификат, и объедините все файлы сертификата в единый файл .pem. К примеру:
cat www.example.com.crt CAintermediate.ca-bundle www.example.com.key > www.example.com.pem
Затем скопируйте полученный файл в /etc/ssl/private:
sudo cp www.example.com.pem /etc/ssl/private/
HAProxy будет использовать этот файл для SSL-терминации.
Установка HAProxy
На сервер lb1 установите HAProxy.
sudo add-apt-repository ppa:vbernat/haproxy-1.5
sudo apt-get update
sudo apt-get -y install haproxy
Настройка HAProxy
Выберите параметры HAProxy, настройте SSL-терминацию, фронтенд и бэкенд.
Откройте конфигурационный файл.
sudo vi /etc/haproxy/haproxy.cfg
Общие настройки HAProxy
Для начала нужно выбрать значение maxconn. Этот параметр определяет максимальное количество одновременных соединений, которое будет поддерживать HAProxy. Это может повлиять на QoS и защитить веб-сервер от слишком большого количества запросов. Подберите оптимальное значение maxconn для вашей среды самостоятельно. Добавьте следующую строку в глобальные настройки и укажите выбранное значение.
Добавьте эту строку, чтобы настроить максимальный размер создаваемых временных ключей DHE:
В раздел defaults добавьте следующие строки под строкой mode http:
option forwardfor
option http-server-close
Если вы хотите включить страницу статистики HAProxy, добавьте следующие строки в раздел defaults (укажите имя пользователя и надежный пароль):
stats enable
stats uri /stats
stats realm Haproxy\ Statistics
stats auth user:password
Это предоставит вам доступ к странице статистики HAProxy (https://www.example.com/stats).
Настройки прокси
Добавьте интерфейс для обработки входящих HTTP-соединений. В конце файла укажите интерфейс www-http:
frontend www-http
bind www.example.com:80
reqadd X-Forwarded-Proto:\ http
default_backend app-backend
Этот интерфейс будет принимать HTTP-соединения, которые потом будут перенаправлены на HTTPS.
Теперь добавьте интерфейс для обработки входящих соединений HTTPS. Укажите свой pem сертификат.
frontend www-https
bind www.example.com:443 ssl crt /etc/ssl/private/www.example.com.pem
reqadd X-Forwarded-Proto:\ https
default_backend app-backend
Затем нужно настроить бэкенд. Добавьте в файл:
backend app-backend
redirect scheme https if !< ssl_fc >
server app1 app1.nyc3.example.com:80 check
server app2 app2.nyc3.example.com:80 check
Этот бэкенд указывает, на какие серверы приложений балансировщик нагрузки должен отправлять трафик. Кроме того, строка redirect scheme https указывает на перенаправление HTTP-соединений на HTTPS.
Теперь сохраните и выйдите из haproxy.cfg. HAProxy готов к запуску, но сначала нужно настроить логирование.
Логирование HAProxy
Откройте файл rsyslog.
sudo vi /etc/rsyslog.conf
Найдите следующие строки и раскомментируйте их, чтобы включить передачу сообщений Syslog через UDP.
$ModLoad imudp
$UDPServerRun 514
$UDPServerAddress 127.0.0.1
sudo service rsyslog restart
Настройка логирования HAProxy завершена. После запуска HAProxy создаст лог /var/log/haproxy.log.
Перезапуск HAProxy
Чтобы обновить настройки, перезапустите HAProxy.
sudo service haproxy restart
6: Настройка WordPress
Запустите сценарий установки WordPress, который подготовит БД.
Откройте в браузере:
На экране появится экран установки WordPress. Следуя его инструкциям. Заполните все формы и нажмите Install WordPress.
После установки WordPress приложение будет готово к работе.
Заключение
Теперь все компоненты приложения настроены и приложение готово к использованию. Вы можете войти в систему как пользователь admin. Пользователи могут получить доступ к сайту через HTTPS.
Обязательно проверьте работу приложения и убедитесь, что все работает должным образом.
Мысли о развёртывании веб-приложений на тестовом сервере
Нижеследующий текст − результат практического опыта и самообразовательных порывов человека, не имеющего систематического образования ни в одной из областей, о которых он (то есть я) берётся рассуждать. Поэтому заумные рассуждения здесь будут перемежаться банальностями. Бейте меня за первые и игнорируйте вторые. Для кого-то и они могут стать откровением.
Я постараюсь описать идеальные варианты настройки тестового веб-севера, хотя понимаю, какой бардак на них обычно творится. Буду ориентироваться на ситуацию, когда деплоить приходится часто, то есть на сервере живёт проект в стадии активной разработки либо несколько проектов на разных стадиях. Проектами занимаются разные разработчики или команды, поэтому проекты нужно изолировать друг от друга. Но сервер внутренний, поэтому такая степень изоляции и автоматизации процессов администрирования, как на серверах под сдачу в аренду, не нужна.
Основной упор я буду делать на применение разных версий Python в качестве языка поддерживаемых веб-приложений. Хотя многие вещи наверняка будут справедливы и для других языков, например, Ruby или Perl.
Взаимодействие программы на Python с веб-сервером происходит по протоколу WSGI (читается как “wiskey”), описанном в PEP 3333 и предшествовавшем ему документе, PEP 333.
Однозвенная архитектура
Достоинством однозвенного подхода является простота настройки и скромное потребление памяти в ущерб гибкости и, в некоторых случаях, производительности.
Самым популярным веб-сервером, работающим в таких условиях, настоящим «швейцарским армейским ножом» современного веба является Apache HTTPd (это название часто сокращают до “Apache”). Буду иметь в виду текущие версии Apache: 2.2 и 2.4.
Для подключения WSGI-приложений к Apache напрямую, без участия серверов второго звена, используется плагин (в терминологии Apache − модуль) mod_wsgi. WSGI − это продвинутый, Python-специфичный вариант протокола CGI. Технически интерпретатор Python (точнее, CPython) встроен в mod_wsgi как внешняя библиотека: статически или динамически − это определяется при сборке модуля. Понятно, что одновременно в mod_python может быть встроена только одна версия CPython, как и Apache может адресовать только один модуль mod_wsgi в одной инсталляции. Отсюда вытекает ограничение: в однозвенной архитектуре с mod_wsgi мы не можем использовать на сервере одновременно Python2 и Python3.
Существенным является для нас выбор модуля мультипроцессинга. Чтобы эффективно распределять нагрузку между разными вычислительными ядрами в мультиядерных (мультипроцессорных) компьютерных конфигурациях, веб-сервер должен уметь одновременно обрабатывать несколько запросов, инициируя запуск нескольких экземпляров веб-приложения. Есть две разновидности модулей мультипроцессинга, которые выполняют вышеописанную функцию разными способами.
prefork
Упрощённо выполнение CGI-запроса веб-сервером в UNIX-подобной среде выглядит так: получив запрос, сервер порождает процесс CGI-приложения, передавая ему параметры запроса в переменных среды, а тело − в стандартном потоке ввода. Самые первые оптимизации были вызваны тем, что порождение процесса происходит достаточно долго, что увеличивает время ответа на запрос. Чтобы не тратить время на запуск CGI-программы (в нашем случае − интерпретатора Python, встроенного в mod_wsgi), достаточно запустить его заранее и держать в подвешенном состоянии, не закрывая поток ввода, до того момента, пока на сервер не придёт запрос. Если сервер может выполнять одновременно несколько потоков кода, то имеет смысл запустить несколько обработчиков CGI и следить за тем, чтобы по мере их завершения запускались новые. Количество (размер пула) работающих и ждущих обработчиков можно конфигурировать. Обработав один запрос, процесс-обработчик может принять следующий. Если же запросов стало слишком мало, сервер убьёт лишние простаивающие процессы. Также сервер убивает процессы в профилактических целях, после того, как количество обработанных ими запросов достигнет определённого (также настраиваемого) предела.
Именно так и работают модули mod_prefork и mod_itk. Последний для нас наиболее интересен, так как способен обслуживать каждый виртуальный хост от имени пользователя, установленного в настройках этого виртуального хоста. Но об этом чуть позже.
worker
Второй способ «списать» расходы на порождение CGI-процесса, а заодно и снизить общую нагрузку на сервер − использовать один процесс для одновременного обслуживания нескольких запросов, иными словами − в многопоточном (multiphreaded) режиме.
Описанную стратегию реализуют модули mpm_worker и mpm_event. Последний также разгружает основную нить обработки запроса от некоторых бухгалтерских действий и пока имеет статус экспериментального.
Worker долгое время считался нестабильным и небезопасным − не сам по себе, а из-за того, что многие другие модули Apache не были приспособлены к многопоточному режиму работы или имели ошибки в его реализации. Но со временем эти проблемы были решены, и сейчас worker достаточно безопасно использовать в деле.
Надо сказать, что благодаря такой особенности CPython, как GIL, mod_wsgi достаточно легко был приспособлен к многопоточной работе, однако эта же особенность и не позволяет получить полную выгоду от многопоточности. GIL гарантирует, что в рамках одного процесса все потоки Python-кода будут исполняться последовательно квант за квантом, что создаёт только иллюзию одновременного исполнения. Но это не относится к C-коду, запускающему (или запускаемому из) Python, а разбор параметров запроса, отображение адреса на WSGI-обработчик и т. д. − всё это делается в C-коде. Соответственно, определённый выигрыш производительности от использования mpm_worker сайты, написанные на Python, всё же имеют.
Двухзвенная архитектура
Двухзвенная архитектура предполагает совместное использование веб-серверов разных уровней: внешнего (front end) и внутреннего (back end).
Внешний сервер служит посредником между пользователем и внутренними серверами при помощи “reverse proxy”-подобных протоколов и плагинов. Кроме того, внешний сервер занимается тем, для чего в 1990 году и создавался протокол HTTP: отдаёт клиенту статические файлы (assets). Хотя в последнее время эта функция в продакшн-среде часто передаётся сервисам CDN.
В качестве внешнего сервера можно использовать и Apache, но больше подходят на эту роль легковесные nginx или, в условиях ограниченных ресурсов, lighttpd. В данной роли от сервера требуются только быстрая реакция на запросы и скромность в отношении потребляемых ресурсов.
Внутренний сервер обычно зависит от архитектуры и языка реализации приложения. Python-приложения часто используют uWSGI, Green Unicorn или собственные серверы на основе многопоточных сетевых фреймворков типа twisted или Tornado. Многие веб-фреймворки также включают в себя веб-серверы, более или менее пригодные к работе в боевых условиях. Например, встроенный в Django веб-сервер предназначен исключительно для отладки, и должен заменяться в продакшне чем-то более серьёзным. Встроенный веб-сервер CherryPy, наоборот, оптимизирован под боевую нагрузку.
Особые требования к тестовому серверу (серверу CI)
Проблема прав доступа к файлам
Архитектура сети в POSIX-системах предполагает, что к портам 1-1024 имеет доступ только привилегированные пользователи. Порты 80 (HTTP) и 443 (HTTPS) попадают под это ограничение, поэтому родительский процесс веб-сервера (внешнего, если речь идёт о двухзвенной архитектуре), всегда имеет root-права.
Но CGI-скрипту нежелательно давать излишне широкие права по соображениям безопасности. Поэтому при работе со скриптами и данными веб-сервер всегда понижает привилегии до уровня обычного пользователя. Такой пользователь в Apache задаётся переменными среды APACHE_RUN_USER и APACHE_RUN_GROUP. В Debian и производных дистрибутивах этот пользователь имеет имя ‘www-data’. Для ещё большего усиления безопасности системы пользователь www-data, как и другие сервисные пользователи, не имеет оболочки и не может входить в систему.
Если веб-приложение устанавливается на сервер один раз и в дальнейшем не будет изменяться (за исключением разве что файлов, загружаемых пользователем), то логично будет зайти на сервер под пользователем с высокими привилегиями, развернуть файлы в соответствующий каталог (‘/var/www’ в Debian) и поменять владельца этих файлов:
Понятно, что для сервера разработки этот способ неудобен и вызывает опасения из-за постоянного использования привилегированного аккаунта. Вопрос об идеальных правах доступа к каталогам и данным веб-сервера с подробным ответом на него можно найти здесь. Статья попала в список каноничных вопросов на Serverfault, а это кое-что значит.
Суть статьи сводится к следующему:
- если есть один ответственный за развёртывание сайта, нужно сделать его пользователем-владельцем всех файлов и каталогов сайта, а группой-владельцем сделать ‘www-data’, коды доступа же установить в 750 для каталогов (770 для каталогов, в которые пользователи сайта смогут загружать файлы) и 640 для файлов (660 для файлов, записываемых сервером). umask в стартап-скриптах нужно установить в 027,
- если к сайту должны иметь доступ несколько человек, необходимо сделать владельцем пользователя root, группой-владельцем будет группа, в которой будут состоять все разработчики, а веб-сервер получит доступ к этим файлам за счёт младших трёх битов,
- либо установить владельцем данных www-data, а группой-владельцем − группу разработчиков, но тогда нужно будет после создания каждого файла менять его владельца. Другим отвечающим упоминается возможность установить на каталоги биты suid и guid, чтобы владельцем созданного объекта становился не создатель, а владелец каталога, в котором был создан объект. Такой нестандартный способ использования этих битов был перенесён в Linux из *BSD, и я даже не уверен, сработает ли он, если файлы будут создаваться веб-сервером,
- хотя в идеальном случае группе разработчиков стоит использовать средства автоматического развёртывания (Puppet, Chef) или CI-среду (Jenkins), чтобы свести ситуацию к одному ответственному пользователю, с той лишь разницей, что этим пользователем будет бот,
- в итоге делается вывод, что полной изоляции виртуальных хостов при помощи только установки прав доступа добиться невозможно: уязвимость одного сайта даст возможность злоумышленнику как минимум прочесть код всех веб-приложений на данном сервере.
Что не упоминается в дискуссии, так это зубодробительная сложность всех этих манипуляций с chmod и chown, и, как следствие, высокая вероятность человеческих ошибок, приводящих к возникновению уязвимостей.
Всё становится намного проще и безопасней, если воспользоваться средствами разделения привилегий, имеющимися в Apache, либо перейти на двухзвенную архитектуру.
Средства разделения привилегий Apache
mpm_itk
Модуль mpm_itk даёт нам возможность самым простым и естественным способом определить, под каким пользователем будет выполняться наше веб-приложение:
mod_suexec
Если по каким-то причинам мы вынуждены использовать другой мультипроцессорный модуль, то нам на помощь придёт mod_suexec. Его недостатком можно считать разве что относительную сложность в настройке, но это только в том случае, если мы хотим установить его из исходников. В дистрибутивах всё автоматизировано. А использование mod_suexec сводится опять-таки к одной директиве в конфигурации виртуального хоста:
Двухзвенная архитектура на тестовом сервере
Зачастую двухзвенная архитектура применяется для оптимизации производительности сервера. Разработчиков же больше волнует удобство развёртывания веб-приложения, нежели скорость его работы. Использование двухзвенной архитектуры на тестовом сервере также имеет определённые преимущества.
К преимуществам можно отнести то, что ответственность за установку и настройку внутреннего сервера несёт разработчик, он же определяет его оптимальные параметры и способ запуска. Внутренний сервер работает с привилегиями разработчика, и вопрос об оптимальной настройке прав доступа к программным файлам и каталогам проекта больше не стоит. Логи хоста также находятся в полном распоряжении разработчика. Веб-приложение может использовать ту версию Python, которая задана при помощи virtualenv, независимо от других проектов. Всё, о чём стоит позаботиться администратору сервера − каталоги со статикой и UNIX-сокет или непривилегированный порт, через который будут разговаривать внешний и внутренний серверы.
Разработчик также ответственен за автоматический запуск сервера и его бесперебойную работу. Если первое легко решается командой “crontab -e”, то второе можно обеспечить при помощи supervisor.
Перезагрузка веб-сервера
При использовании Apache как единственного сервера стоит добавить в настройки директиву “MaxRequestsPerChild 1”. С ней у разработчика не будет необходимости в перезагрузке сервера. На каждый запрос будет запускаться новая инстанция интерпретатора, следовательно, все изменения в коде сайта сразу же будут отражаться в его работе.
Разумеется, запрет на вторичное использование обработчика запроса снизит производительность сервера, но иначе придётся перезагружать сервер при каждом изменении кода. Есть два способа перезагрузить Apache: WSGI-специфический и общий.
- WSGI-специфический способ заключается в обновлении даты редактирования WSGI-скрипта. Это можно сделать командой touch. Этот метод работает только тогда, когда mod_wsgi настроен на работу в daemon mode. (В общем случае крайне желательно настроить mod_wsgi на работу в daemon mode, хотя по умолчанию этот режим выключен.)
- Общий метод − команда “apachectl restart” или аналогичная − требует прав root. Разумеется, можно обойтись соответствующей настройкой sudo, но использование sudo само по себе сложнее и опаснее, чем это может показаться. Поэтому такой способ использовать нежелательно.
Если используется двухзвенная архитектура, то в перезагрузке нуждается только внутренний сервер. Он работает от имени того же пользователя, от имени которого выполняется развёртывание, следовательно, никаких проблем с его перезагрузкой (или настройкой на работу без необходимости перезагрузки) не возникает.
Deployment
Когда вы готовы развернуть ваше приложение Angular на удаленном сервере,у вас есть различные варианты развертывания.
Простые варианты развертывания
Прежде чем полностью развернуть приложение,вы можете протестировать процесс,построить конфигурацию и установить поведение с помощью одной из этих промежуточных методик.
Сборка и подача с диска
Во время разработки вы обычно используете команду ng serve для создания, просмотра и обслуживания приложения из локальной памяти с помощью webpack-dev-server . Однако, когда вы будете готовы к развертыванию, вы должны использовать команду ng build для сборки приложения и развертывания артефактов сборки в другом месте.
И ng build , и ng serve очистки выходной папки перед сборкой проекта, но только команда ng build записывает сгенерированные артефакты сборки в выходную папку.
Выходная папка по умолчанию является dist/project-name/ . Для вывода в другую папку измените outputPath в angular.json .
По мере того,как вы приближаетесь к концу процесса разработки,обслуживание содержимого вашей выходной папки с локального веб-сервера может дать вам лучшее представление о том,как ваше приложение будет себя вести,когда оно будет развернуто на удаленном сервере.Вам понадобятся два терминала,чтобы получить опыт работы в реальном времени.
На первом терминале запустите ng build команды в часовом режиме для компиляции приложения в dist папки.
Как и команда ng serve , она восстанавливает выходные файлы при изменении исходных файлов.
На втором терминале установите веб-сервер (например, lite-server ) и запустите его для выходной папки. Например:
При выводе новых файлов сервер автоматически перезагрузит ваш браузер.
Этот метод предназначен только для разработки и тестирования и не является поддерживаемым или безопасным способом развертывания приложения.
Автоматическое развертывание с помощью CLI
Команда Angular CLI ng deploy (представленная в версии 8.3.0) выполняет компоновщик CLI для deploy связанный с вашим проектом. Ряд сторонних сборщиков реализуют возможности развертывания на разных платформах. Вы можете добавить любой из них в свой проект, запустив ng add [package name] .
Когда вы добавляете пакет с возможностью развертывания, он автоматически обновляет конфигурацию вашего рабочего пространства ( файл angular.json ) с разделом deploy для выбранного проекта. Затем вы можете использовать команду ng deploy для развертывания этого проекта.
Например,следующая команда автоматически развертывает проект на Firebase.
Команда интерактивная.В этом случае вы должны иметь или создать учетную запись Firebase и аутентифицироваться,используя эту учетную запись.Команда попросит вас выбрать проект Firebase для установки
Эта команда создает ваше приложение и загружает производственные активы в Firebase.
В таблице ниже вы можете найти список пакетов, которые реализуют функциональность развертывания на разных платформах. deploy команды для каждого пакета может потребоваться различные параметры командной строки. Вы можете прочитать больше, перейдя по ссылкам, связанным с именами пакетов ниже:
| Deployment to | Package |
|---|---|
| Firebase hosting | @angular/fire |
| Vercel | vercel init angular |
| Netlify | @netlify-builder/deploy |
| GitHub pages | angular-cli-ghpages |
| NPM | ngx-deploy-npm |
| Amazon Cloud S3 | @jefiozie/ngx-aws-deploy |
Если вы выполняете развертывание на самоуправляемом сервере или у вас нет сборщика для вашей любимой облачной платформы, вы можете либо создать конструктор, который позволяет использовать команду ng deploy , либо прочитать это руководство, чтобы узнать, как вручную развернуть ваше приложение. .
Базовое развертывание на удаленном сервере
Для простейшего развертывания создайте производственную сборку и скопируйте выходной каталог на веб-сервер.
Начните с производственного корпуса:
Скопируйте все содержимое выходной папки ( по умолчанию dist/project-name/ ) в папку на сервере.
Настройте сервер для перенаправления запросов на отсутствующие файлы в index.html . Узнайте больше о перенаправлениях на стороне сервера ниже .
Это самое простое развертывание вашего приложения,готового к производству.
Развертывание на страницах GitHub
Чтобы развернуть приложение Angular на GitHub Pages , выполните следующие шаги:
Настройте git в своем локальном проекте, добавив пульт, который указывает репозиторий GitHub, созданный на предыдущем шаге. GitHub предоставляет эти команды при создании репозитория, чтобы вы могли копировать и вставлять их в командной строке. Команды должны быть похожи на следующие, хотя GitHub заполняет настройки для вашего проекта за вас:
Когда вы вставляете эти команды из GitHub,они выполняются автоматически.
Создайте и проверьте ветку git с именем gh-pages .
Создайте свой проект, используя имя проекта Github, команду Angular CLI ng build и следующие параметры, где your_project_name — это имя проекта, который вы предоставили репозиторию GitHub на шаге 1.
Обязательно /your_project_name/ косые черты по обе стороны от имени вашего проекта, например / your_project_name / .
Когда сборка будет завершена, сделайте копию docs/index.html и назовите ее docs/404.html .
Сделайте ваши изменения и надавите.
На странице проекта GitHub перейдите в «Настройки» и прокрутите вниз до раздела «Страницы GitHub», чтобы настроить публикацию сайта из папки docs .
Щелкните ссылку GitHub Pages в верхней части раздела GitHub Pages, чтобы просмотреть развернутое приложение. Формат ссылки: https://<user_name>.github.io/<project_name> .
Проверьте angular-cli-ghpages , полнофункциональный пакет, который делает все это для вас и имеет дополнительную функциональность.
Server configuration
В этом разделе рассматриваются изменения,которые,возможно,придется внести на сервер или в файлы,развернутые на сервере.
Маршрутизированные приложения должны возвращаться к index.html
Приложения Angular-идеальные кандидаты на обслуживание с помощью простого статического HTML-сервера.Вам не нужен серверный движок для динамической компоновки страниц приложения,поскольку Angular делает это на стороне клиента.
Если приложение использует маршрутизатор Angular, вы должны настроить сервер так, чтобы он возвращал страницу хоста приложения ( index.html ) при запросе файла, которого у него нет.
Маршрутизируемое приложение должно поддерживать «глубокие ссылки». Глубокая ссылка является URL , который указывает путь к компоненту внутри приложения. Например, http://www.mysite.com/heroes/42 — это глубокая ссылка на страницу сведений о герое, на которой отображается герой с id: 42 .
Нет никаких проблем,когда пользователь переходит на этот URL изнутри работающего клиента.Угловой маршрутизатор интерпретирует URL и маршруты к этой странице и герою.
Но переход по ссылке в электронном письме, ввод ее в адресную строку браузера или просто обновление браузера на главной странице — все эти действия выполняются самим браузером, а не работающим приложением. Браузер делает прямой запрос на сервер для этого URL-адреса, минуя маршрутизатор.
Статический сервер обычно возвращает index.html , когда получает запрос на http://www.mysite.com/ . Но он отклоняет http://www.mysite.com/heroes/42 и возвращает ошибку 404 — Not Found если только он не настроен на возврат index.html .
Примеры обратной конфигурации
Не существует единой конфигурации,которая бы работала на каждом сервере.В следующих разделах описаны конфигурации для некоторых из наиболее популярных серверов.Этот список ни в коем случае не является исчерпывающим,но должен предоставить вам хорошую отправную точку.
| Servers | Details |
|---|---|
| Apache | Добавьте правило перезаписи в файл .htaccess , как показано ( ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess ): |
| Nginx | Используйте try_files , как описано в веб-приложениях шаблона переднего контроллера , измененные для обслуживания index.html : |
| Ruby | Создайте сервер Ruby с помощью ( sinatra ) с базовым файлом Ruby, который настраивает сервер server.rb : |
| IIS | Добавьте правило перезаписи в web.config , подобное показанному здесь : |
| GitHub Pages | Вы не можете напрямую настроить сервер GitHub Pages, но можете добавить страницу 404. Скопируйте index.html в 404.html . Он по-прежнему будет служить ответом 404, но браузер обработает эту страницу и загрузит приложение должным образом. Также рекомендуется использовать docs на главной странице и создать файл .nojekyll . |
| Firebase hosting | Добавьте правило перезаписи . |
Настройка правильного MIME-типа для активов JavaScript
Все файлы JavaScript вашего приложения должны обслуживаться сервером с заголовком Content-Type , установленным в text/javascript или другой MIME-тип, совместимый с JavaScript .
Большинство серверов и хостинг-услуг уже делают это по умолчанию.
Сервер с неправильно настроенным mime-типом для файлов JavaScript приведет к тому,что приложение не запустится со следующей ошибкой:
В этом случае вам нужно будет проверить конфигурацию вашего сервера и перенастроить его для обслуживания файлов .js с Content-Type: text/javascript . См. Руководство к вашему серверу, чтобы узнать, как это сделать.