Подробный обзор nginx и Heroku

Разнообразие приложений с открытым исходным кодом является одновременно самым большим благом для движения за бесплатное и открытое программное обеспечение (FOSS) и самым большим препятствием для его внедрения. Приложение, которое вы используете, не всегда принадлежит вам, и оно часто сопровождается определенными мнениями и ограничениями, налагаемыми автором программного обеспечения - намеренно или иным образом.

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

Что такое nginx и зачем он вам нужен?

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

По данным Netcraft и W3Techs, nginx обслуживает примерно 31–36% активных веб-сайтов, что делает его идеальным выбором для Apache в качестве веб-сервера в мире. Это означает, что он не только пользуется уважением, заслуживает доверия, обладает достаточной производительностью для большой части производственных систем и совместим практически с любой архитектурой, но и имеет верных последователей инженеров и разработчиков, поддерживающих проект. Это ключевые факторы при рассмотрении долговечности вашего приложения, того, насколько оно может быть переносимым и где его можно разместить.

Heroku и nginx

Давайте рассмотрим ситуацию, когда вам может понадобиться nginx. В нашем примере вы создали приложение и развернули его на платформе как сервис (PaaS) - в нашем случае Heroku. С PaaS ваша жизнь становится проще, так как решения об инфраструктуре, мониторинге и поддержке уже приняты за вас, что гарантирует чистую среду, в которой вы можете легко запускать свои приложения. Однако, чтобы воспользоваться этими преимуществами PaaS, ваше приложение должно соответствовать ограничениям поставщика.

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

Итак, давайте рассмотрим три способа использования nginx для настройки поведения вашего приложения в Heroku.

  1. Динамическое назначение портов сервера во время выполнения контейнера
  2. Добавление базовой аутентификации в ваше приложение
  3. Зеркальное отображение трафика для тестирования изменений приложений без ущерба для вашей производственной службы

Среднеуровневое динамическое связывание портов

Во-первых, давайте посмотрим на динамическую привязку портов. Чтобы обслуживать трафик для веб-серверов Heroku, вам нужен доступ к переменной среды с именем PORT. Эта переменная изменяется при каждом развертывании и не объявляется перед запуском приложения. Это явный блокировщик для любой службы, у которой нет способа привязки к такому динамическому порту.

Heroku предлагает пакеты сборки, которые могут автоматизировать развертывание и настройку такого среднего уровня, но решение для динамических переменных не всегда может быть таким простым. Бывают случаи, когда нам может потребоваться решить эту или подобные проблемы без помощи поставщика. Итак, давайте посмотрим, как мы можем вручную создать решение, которое может преобразовать статически настроенное приложение в динамически настраиваемое с помощью BookStack.

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

Для подготовки нам понадобится несколько фрагментов из официальной документации BookStack и nginx, чтобы собрать Dockerfile и некоторые базовые файлы шаблонов. Вы можете увидеть весь проект здесь: https://github.com/Tokugero/bookstack-demo.

Посмотрим на Dockerfile:

Dockerfile

FROM debian:stable-slim
ENV PORT="80"
ENV APP_URL="http://localhost/"
ADD https://github.com/BookStackApp/BookStack/archive/release.zip /bookstack/
ADD https://getcomposer.org/installer /root/composer-setup.php
RUN apt-get update && \
  apt-get install -y \
  unzip \
  php-cli \
  php-mbstring \
  php7.3-curl \
  php7.3-dom \
  php7.3-gd \
  php7.3-mysql \
  php7.3-tidy \
  php7.3-xml \
  php-fpm \
  nginx && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/*
RUN unzip /bookstack/release.zip -d / && \
  rm /bookstack/release.zip && \
  php /root/composer-setup.php --install-dir=/usr/local/bin --    filename=composer && \
  mkdir -p /var/lib/nginx && \
  touch /run/nginx.pid && \
  touch /var/log/php7.3-fpm.log
COPY config/bookstack.env /BookStack-release/.env
COPY config/nginx.conf /etc/nginx/nginx.conf
COPY scripts/run.sh /BookStack-release/run.sh
COPY config/nginx.htpasswd /BookStack-release/.htpasswd
RUN cd /BookStack-release && \
  composer install --no-dev && \
  chown -R www-data:www-data /BookStack-release/ && \
  chown -R www-data:www-data /etc/nginx/ && \
  chown -R www-data:www-data /var/lib/nginx/ && \
  chown -R www-data:www-data /etc/php/7.3/fpm/ && \
  chown www-data:www-data /run/nginx.pid && \
  chown www-data:www-data /var/log/php7.3-fpm.log && \
  chmod 600 .htpasswd
USER www-data
WORKDIR /BookStack-release/
ENTRYPOINT ["./run.sh"]

скрипты / run.sh

#!/bin/bash
sed -i -e 's/$PORT/'"$PORT"'/g' /etc/nginx/nginx.conf
sed -i -e 's,APPURL,'${APP_URL}',g' /BookStack-release/.env
sed -i -e 's,listen = /run/php/php7.3-fpm.sock,listen = 127.0.0.1:9000,g' /etc/php/7.3/fpm/pool.d/www.conf
sed -i -e 's,pid = /run/php/php7.3-fpm.pid,pid = php7.3-fpm.pid,g' /etc/php/7.3/fpm/php-fpm.conf
cd /BookStack-release/ && \
   echo yes | php artisan key:generate && \
   echo yes | php artisan migrate
php-fpm7.3 & \
   nginx -g 'daemon off;'

config / nginx.conf

worker_processes  4;
error_log  /dev/stderr;
user www-data;
include /etc/nginx/modules/*.conf;
events {
  worker_connections  4096;  ## Default: 1024
}
http {
  include    /etc/nginx/fastcgi.conf;
  include    /etc/nginx/mime.types;
  index    index.html index.htm index.php;
  default_type application/octet-stream;
  access_log   /dev/stdout;
  sendfile     on;
  tcp_nopush   on;
  server {
    #This is updated via sed in ./scripts/run.sh at runtimelisten           $PORT;
    server_name  _;
    root         /BookStack-release/public;
    client_max_body_size 0;
    location / {
      index index.php;
      try_files $uri $uri/ /index.php?$query_string;
    }
    location ~ \.php$ {
      fastcgi_split_path_info ^(.+?\.php)(/.*)$;
      if (!-f $document_root$fastcgi_script_name) {
        return 404;
      }
      # Mitigate https://httpoxy.org/ vulnerabilities
      fastcgi_param HTTP_PROXY "";
      fastcgi_pass 127.0.0.1:9000;
      fastcgi_index index.php;
      # include the fastcgi_param setting
      include fastcgi_params;
      # SCRIPT_FILENAME parameter is used for PHP FPM determining
      #  the script name. If it is not set in fastcgi_params file,
      # i.e. /etc/nginx/fastcgi_params or in the parent contexts,
      # please comment off following line:
      fastcgi_param  SCRIPT_FILENAME   $document_root$fastcgi_script_name;
    }
  }
}

config/bookstack.env

APP_KEY=replaceme
APP_URL=APPURL

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

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

...
ENV PORT="80"
ENV APP_URL="http://localhost/"
...
ENTRYPOINT ["./run.sh"]

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

docker run -it -d -e APP_URL=http://localhost:9876 -e PORT=8080 -p 9876:8080 bookstack-demo

Обратите внимание, как теперь мы можем объявить порт во время выполнения без какой-либо специальной настройки самого приложения - основное требование для предоставления сервиса на веб-серверах Heroku. Чтобы передать их, мы просто использовали sed для замены переменных среды жестко запрограммированными значениями во время выполнения.

sed -i -e 's/$PORT/'"$PORT"'/g' /etc/nginx/nginx.conf;
sed -i -e 's,APPURL,'${APP_URL}',g' /BookStack-release/.env;

В нашем run.sh скрипте, который запускает основную команду контейнера, мы можем использовать наш редактор потока / sed для замены предопределенных переменных в файле конфигурации nginx, а также в специальном файле среды приложения. Когда мы делаем это до инициализации nginx, мы гарантируем, что приложение запускается с портом, который Heroku определяет после того, как контейнер был запущен.

И еще одна последняя команда развертывания:

heroku container:push web -a bookstack-demo && heroku container:release web -a bookstack-demo

Простая базовая аутентификация

Некоторые функции просто недоступны в нашей новой инфраструктурной среде без специальных плагинов. Но, возможно, нам нужен простой пароль, чтобы предотвратить доступ произвольных запросов к нашему порталу, пока мы находимся на начальных этапах разработки проекта. nginx автоматически включает в себя большой набор возможностей, одной из которых является базовая аутентификация для каждого сервера или каждого местоположения. Это позволяет нам защитить паролем наше приложение FOSS без каких-либо действий на стороне Heroku или BookStack.

Выполнив всю предыдущую работу, мы можем просто добавить несколько строк в наш проект, чтобы принудительно выполнить базовую аутентификацию в нашем приложении. Используя auth_basic module вместе с инструментом Apache htpasswd, мы можем добавить:

config / nginx.conf

...
location / {
  auth_basic "Under Construction";
  auth_basic_user_file /BookStack-release/.htpasswd;
  index index.php;
  try_files $uri $uri/ /index.php?$query_string;
}
...

Не забудьте сгенерировать пароль и включить его в Dockerfile:

bookstack-demo$ htpasswd config/nginx.htpasswd myuser
New password: mypass
Re-type new password: mypass
Updating password for user myuser

Dockerfile

...
COPY config/nginx.htpasswd /BookStack-release/.htpasswd
...
RUN cd /BookStack-release && \
...
chmod 600 .htpasswd
...

Вот что мы теперь видим, пытаясь получить доступ к приложению:

Расширенное затенение трафика

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

config / nginx.conf

server {
  mirror /mirror;
  mirror_request_body on;
  ...
  location = /mirror {
    resolver 1.1.1.1 valid=30s;
    internal;
    proxy_pass https://bookstack-mirror-demo.herokuapp.com$request_uri;
  }
...

Инструментальные средства Heroku Native

Ранее мы упоминали buildpack Heroku nginx. Он может автоматизировать некоторые из этих функций управления портами, что позволяет нам начать работу без каких-либо специально подобранных функций из nginx. Наш пример напрямую сравнивается с этим подходом, но встроенные инструменты позволяют меньше перемещаться между ресурсами документации.

Чтобы создать наш зеркальный сайт, мы собираемся использовать собственный пакет сборки Heroku, чтобы добавить функциональность nginx в произвольный проект без всех настраиваемых докеризаций в примерах выше.

Просто создайте новый репозиторий:

mkdir bookstack-mirror-demo; cd bookstack-mirror-demo; git init;

Добавьте файлы шаблона:

config / nginx.conf.erb

daemon off;
# Heroku dynos have at least 4 cores.
worker_processes <%= ENV['NGINX_WORKERS'] || 4 %>;
events {
  use epoll;
  accept_mutex on;
  worker_connections <%= ENV['NGINX_WORKER_CONNECTIONS'] || 1024 %>;
}
http {
  gzip on;
  gzip_comp_level 2;
  gzip_min_length 512;
  server_tokens off;
  log_format l2met 'measure#nginx.service=$request_time   request_id=$http_x_request_id';
  access_log <%= ENV['NGINX_ACCESS_LOG_PATH'] ||   'logs/nginx/access.log' %> l2met;
  error_log <%= ENV['NGINX_ERROR_LOG_PATH'] || 'logs/nginx/error.log' %>;
  include mime.types;
  default_type application/octet-stream;
  sendfile on;
  # Must read the body in 5 seconds.
  client_body_timeout <%= ENV['NGINX_CLIENT_BODY_TIMEOUT'] || 5 %>;
  server {
    listen <%= ENV["PORT"] %>;
    server_name _;
    keepalive_timeout 5;
    client_max_body_size <%= ENV['NGINX_CLIENT_MAX_BODY_SIZE'] || 1 %>M;
    root /app/public; # path to your app
  }
}

Procfile

web: bin/start-nginx-solo

Добавьте свои файлы в свой Heroku Git:

heroku git:remote -a bookstack-mirror-demo
git add *; git commit -am “Initial commit”
heroku buildpacks:add https://github.com/heroku/heroku-buildpack-nginx -a bookstack-mirror-demo
git push heroku master

Теперь вы можете начать зеркалировать свой трафик и клонировать свой сервис!

Заключение

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

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