Рубрики
Без рубрики

Окончательное руководство по развертыванию Tomcat

Непрерывная интеграция и доставка (CI/CD) – общая цель команд DevOps для снижения затрат и inc… С тегами tomcat, java, осьминог, учебник.

Непрерывная интеграция и доставка (CI/CD) – общая цель команд разработчиков, направленная на снижение затрат и повышение гибкости команд разработчиков программного обеспечения. Но конвейер CI/CD – это гораздо больше, чем просто тестирование, компиляция и развертывание приложений. Надежный конвейер CI/CD решает ряд проблем, таких как:

  • Высокая доступность (ГА)
  • Несколько сред
  • Развертывание с нулевым временем простоя
  • Миграции баз данных
  • Балансировщики нагрузки
  • Протокол HTTPS и управление сертификатами
  • Развертывания филиалов функций
  • Испытание на задымление
  • Стратегии отката

То, как эти цели реализуются, зависит от типа развертываемого программного обеспечения. В этом посте я рассмотрю, как создать непрерывную доставку (или развертывание) половины конвейера CI/CD путем развертывания приложений Java в Tomcat. Затем я создаю стек поддерживающей инфраструктуры, который включает веб-сервер Apache для балансировки нагрузки, PostgreSQL для базы данных, сохраняемый для высокодоступных балансировщиков нагрузки и Octopus для организации развертываний.

Заметка на сервере PostgreSQL

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

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

Как реализовать HA в Tomcat

Говоря о HA, важно точно понимать, какими компонентами платформы необходимо управлять, чтобы решить проблему недоступности отдельного экземпляра Tomcat, например, когда экземпляр недоступен из-за планового обслуживания или сбоя оборудования.

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

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

@RequestMapping("/pageCount")
public String index(final HttpSession httpSession) {
    httpSession.setAttribute("pageCount", ObjectUtils.defaultIfNull((Integer)httpSession.getAttribute("pageCount"), 0) + 1);
    return (Integer)httpSession.getAttribute("pageCount");
}

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

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

Tomcat предлагает три решения для обеспечения репликации сеансов:

  1. Использование сохраняемости сеанса и сохранение сеанса в общей файловой системе (менеджер сохраняемости + хранилище файлов).
  2. Использование сохранения сеанса и сохранение сеанса в общей базе данных (PersistenceManager + JDBCStore).
  3. Использование репликации в памяти и использование кластера SimpleTcpCluster, поставляемого с Tomcat ( lib/catalina-tribes.jar + lib/catalina-ha.jar ).

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

Мы отметили, что приведенный выше пример кода сохранен, поскольку он не учитывает тот факт, что кэш сеанса не является потокобезопасным. Даже этот простой пример подвержен условиям гонки, которые могут привести к неправильному подсчету страниц. Решение этой проблемы заключается в использовании традиционных функций блокировки потоков и синхронизации, доступных в Java, но эти функции действительны только в пределах одной JVM. Это означает, что мы должны гарантировать, что клиентские запросы всегда направляются на один экземпляр Tomcat, что, в свою очередь, означает, что только один экземпляр Tomcat содержит единственную достоверную копию состояния сеанса, которая затем может обеспечить согласованность с помощью блокировок потоков и синхронизации. Это достигается с помощью липких сеансов.

Фиксированные сеансы обеспечивают возможность проверки клиентских запросов балансировщиком нагрузки, а затем их направления на один веб-сервер в кластере. По умолчанию в приложении сервлета Java клиент идентифицируется с помощью файла cookie JSESSIONID , который отправляется веб-браузером и проверяется балансировщиком нагрузки для идентификации экземпляра Tomcat, в котором хранится сеанс, а затем сервером Tomcat для связывания запроса с существующим сеансом.

Таким образом, наше решение HA Tomcat:

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

Сохраняемый для балансировщиков нагрузки HA

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

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

Для поддержания высокой доступности нам нужны как минимум два балансировщика нагрузки. Но как мы можем надежно разделить одно входящее сетевое подключение между двумя балансировщиками нагрузки? Вот тут-то и вступает в игру Keepalived.

Keepalived – это служба Linux, работающая в нескольких экземплярах и выбирающая один главный экземпляр из пула исправных экземпляров. Keepalived довольно гибок, когда дело доходит до определения того, что делает этот главный экземпляр, но в нашем сценарии мы будем использовать Keepalived для назначения виртуального плавающего IP-адреса экземпляру, который выполняет роль главного. Это означает, что наш входящий сетевой трафик будет отправляться на плавающий IP-адрес, назначенный исправному балансировщику нагрузки, а затем балансировщик нагрузки перенаправляет трафик экземплярам Tomcat. Если один из балансировщиков нагрузки будет переведен в автономный режим, Keepalived гарантирует, что оставшемуся балансировщику нагрузки будет назначен плавающий IP-адрес.

Таким образом, наше решение для балансировки нагрузки HA:

  • Реализует Apache с плагином mod_jk для направления трафика на экземпляры Tomcat.
  • Реализует функцию Keepalived для обеспечения того, чтобы одному балансировщику нагрузки был назначен виртуальный IP-адрес.

Схема сети

Вот схема сети, которую мы создадим:

Развертывание с нулевым временем простоя и откаты

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

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

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

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

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

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

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

Таким образом, наше решение для развертывания с нулевым временем простоя:

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

Создание инфраструктуры

Показанный здесь пример инфраструктуры развернут на виртуальных машинах Ubuntu 18.04. Большинство инструкций будет зависеть от распространения, хотя некоторые имена пакетов и расположения файлов могут измениться.

Настройка экземпляров Tomcat

Установите пакеты

Мы начинаем с установки Tomcat и приложения Manager:

apt-get install tomcat tomcat-admin

Добавьте разъем AJP

Связь между веб-сервером Apache и Tomcat осуществляется с помощью соединителя AJP. AJP – это оптимизированный двоичный протокол HTTP, который понимает плагин mod_jk для Apache и Tomcat. Соединитель добавляется к элементу Service в /etc/tomcat9/server.xml файл:


  
  
    
    
  

Определите имена экземпляров Tomcat

Каждому экземпляру Tomcat необходимо добавить уникальное имя к элементу Engine в /etc/tomcat9/server.xml файл. Элемент по умолчанию Engine выглядит следующим образом:


Имя экземпляра Tomcat определено в атрибуте jvmRoute . Я позвоню в первый экземпляр Tomcat работник1 :


Второй экземпляр Tomcat называется рабочий 2 :


Добавление пользователя-менеджера

Octopus выполняет развертывание в Tomcat через приложение Manager. Это то, что мы установили с пакетом tomcat-admin ранее.

Для аутентификации в приложении Manager необходимо определить нового пользователя в /etc/tomcat9/tomcat-users.xml файл. Я позвоню этому пользователю tomcat с паролем Пароль 01! , и он будет принадлежать ролям менеджер-скрипт и менеджер-графический интерфейс .

Роль менеджер-скрипт предоставляет доступ к API менеджера, а роль менеджер-графический интерфейс предоставляет доступ к веб-консоли менеджера.

Вот копия /etc/tomcat9/tomcat-users.xml файл с tomcat определенным пользователем:


  
  
  

Добавьте jar-файл драйвера JDBC PostgreSQL

Каждый экземпляр Tomcat будет взаимодействовать с базой данных PostgreSQL для сохранения данных сеанса. Для того, чтобы Tomcat мог взаимодействовать с базой данных PostgreSQL, нам необходимо установить JAR-файл драйвера JDBC PostgreSQL. Это делается путем сохранения файла https://jdbc.postgresql.org/download/postgresql-42.2.11.jar как /var/lib/tomcat9/lib/postgresql-42.2.11.jar .

Включить репликацию сеанса

Чтобы включить сохранение сеанса в базе данных, мы добавляем новое определение Manager в файл /etc/tomcat9/context.xml . Этот менеджер использует org.apache.catalina.сеанс. PersistentManager для сохранения сведений о сеансе в базе данных, определенной во вложенном элементе Store .

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

Нам также нужно добавить Клапан загрузки org.apache.catalina.ha.сессии. JvmRouteBinderValve класс. Этот клапан важен, когда клиент перенаправляется с экземпляра Tomcat, который больше недоступен, на другой экземпляр Tomcat в кластере. Мы увидим этот клапан в действии после того, как развернем наш образец приложения.

Вот копия /etc/tomcat9/context.xml файл с Менеджером , Хранилищем и Клапан определенные элементы:


  
      
  
  
  WEB-INF/web.xml
  WEB-INF/tomcat-web.xml
  ${catalina.base}/conf/web.xml

Настройка базы данных PostgreSQL

Установите пакеты

Нам нужно инициализировать PostgreSQL с новой базой данных, схемой и таблицей. Для этого мы используем инструмент командной строки psql , который устанавливается с:

apt-get install postgresql-client

Добавьте базу данных, схему и таблицу

Если вы посмотрите на атрибут connectionURL из /etc/tomcat9/context.xml файл, определенный выше, вы увидите, что мы сохраняем информацию о сеансе в:

  • База данных называется tomcat .
  • Схема называется сессия .
  • Таблица называется tomcat_sessions .

Чтобы создать эти ресурсы на сервере PostgreSQL, мы выполняем ряд команд SQL.

Сначала сохраните следующий текст в файл с именем createdb.sql . Эта команда создает базу данных, если она не существует (см. этот StackOverflow сообщение для получения дополнительной информации о синтаксисе):

SELECT 'CREATE DATABASE tomcat' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'tomcat')\gexec

Затем выполните SQL со следующей командой, заменив postgres server с именем хоста вашего сервера PostgreSQL:

cat createdb.sql | /usr/bin/psql -a -U postgres -h postgresserver

Далее мы создадим схему и таблицу. Сохраните следующий текст в файл с именем create schema.sql . Обратите внимание, что столбцы таблицы tomcat_sessions соответствуют атрибутам элемента Storage в /etc/tomcat9/context.xml файл:

CREATE SCHEMA IF NOT EXISTS session;

CREATE TABLE IF NOT EXISTS session.tomcat_sessions
(
  session_id character varying(100) NOT NULL,
  valid_session character(1) NOT NULL,
  max_inactive integer NOT NULL,
  last_access bigint NOT NULL,
  app_name character varying(255),
  session_data bytea,
  CONSTRAINT tomcat_sessions_pkey PRIMARY KEY (session_id)
);

CREATE INDEX IF NOT EXISTS app_name_index
  ON session.tomcat_sessions
  USING btree
  (app_name);

Затем выполните SQL со следующей командой, заменив postgres server с именем хоста вашего сервера PostgreSQL:

psql -a -d tomcat -U postgres -h postgresserver -f /root/createschema.sql

Теперь у нас есть таблица в PostgreSQL, готовая для сохранения сеансов Tomcat.

Настройка балансировщиков нагрузки

Установите пакеты

Нам нужно установить веб-сервер Apache, плагин mod_jk и сервис Keepalived:

apt-get install apache2 libapache2-mod-jk keepalived

Настройка балансировщика нагрузки

Плагин mod_jk настраивается с помощью файла /etc/libapache2-mod - jk/рабочие.недвижимость . В этом файле мы определяем количество работников, на которых может быть направлен трафик. Поля в этом файле задокументированы здесь .

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

worker.list=loadbalancer

Затем мы определяем два экземпляра Tomcat, которые были созданы ранее. Обязательно замените worker1_ip и рабочий 2_ip с IP-адресами соответствующих экземпляров Tomcat.

Обратите внимание, что имена работников определены здесь как работник 1 и рабочий 2 соответствует значению атрибута jvmRoute в элементе Движка в /etc/tomcat9/server.xml файл. Эти имена должны совпадать, так как они используются mod_jk для реализации липких сеансов:

worker.worker1.type=ajp13
worker.worker1.host=worker1_ip
worker.worker1.port=8009

worker.worker2.type=ajp13
worker.worker2.host=worker2_ip
worker.worker2.port=8009

Наконец, мы определяем балансировщик нагрузки worker как балансировщик нагрузки, который направляет трафик на worker1 и worker2 работников с включенными сеансами привязки:

worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=worker1,worker2
worker.loadbalancer.sticky_session=1

Вот полная копия файла /etc/libapache2-mod-jk/workers.свойства :

# All traffic is directed to the load balancer
worker.list=loadbalancer

# Set properties for workers (ajp13)
worker.worker1.type=ajp13
worker.worker1.host=worker1_ip
worker.worker1.port=8009

worker.worker2.type=ajp13
worker.worker2.host=worker2_ip
worker.worker2.port=8009

# Load-balancing behaviour
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=worker1,worker2
worker.loadbalancer.sticky_session=1

Добавление виртуального хоста Apache

Для того, чтобы Apache принимал трафик, нам нужно определить Виртуальный хост , который мы создаем в файле /etc/apache2/сайты-включены/000-по умолчанию.conf . Этот виртуальный хост будет принимать HTTP-трафик через порт 80, определять некоторые файлы журналов и использовать директиву JkMount для пересылки трафика рабочему, называемому loadbalancer :


  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined
  JkMount /* loadbalancer

Настройка Сохраняемого значения

У нас есть два балансировщика нагрузки, чтобы гарантировать, что один из них может быть отключен для обслуживания в любой момент времени. Keepalived – это служба, которую мы используем для назначения виртуального IP-адреса одной из служб балансировки нагрузки, которую Keepalived называет главной.

Функция Keepalived настраивается с помощью файла /etc/keepalived/keepalived.conf .

Мы начнем с присвоения имени экземпляру балансировщика нагрузки. Первый балансировщик нагрузки называется балансировщик нагрузки 1 :

vrrp_instance loadbalancer1 {

государство МАСТЕР определяет активный сервер:

state MASTER

Параметр interface присваивает имя физического интерфейса данному конкретному экземпляру виртуального IP-адреса:

Вы можете найти имя интерфейса, выполнив ifconfig .

interface ens5

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

virtual_router_id 101

Параметр приоритет определяет порядок, в котором назначенный интерфейс переходит на другой режим при отработке отказа; чем больше число, тем выше приоритет. Это значение приоритета должно находиться в диапазоне от 0 до 255, а сервер балансировки нагрузки настроен как состояние У ГЛАВНОГО должно быть значение приоритета, равное большему числу, чем значение приоритета сервера, настроенного как состояние РЕЗЕРВНОЕ КОПИРОВАНИЕ :

priority 101

объявление_int определяет, как часто отправлять объявления VRRP:

advert_int 1

Блок аутентификация определяет тип аутентификации ( auth_type ) и пароль ( auth_pass ), используемые для аутентификации серверов для синхронизации при отказе. ПРОПУСК указывает аутентификацию по паролю:

authentication {
    auth_type PASS
    auth_pass passwordgoeshere
}

unicast_src_ip является IP-адресом этого балансировщика нагрузки:

unicast_src_ip 10.0.0.20

unicast_peer перечисляет IP-адреса других балансировщиков нагрузки. Поскольку у нас всего два балансировщика нагрузки, здесь можно перечислить только один другой балансировщик нагрузки:

unicast_peer {
  10.0.0.21
}

адрес virtual_ip определяет виртуальный или плавающий IP-адрес, который Keepalived назначает главному узлу:

virtual_ipaddress {
    10.0.0.30
}

Вот полная копия файла /etc/keepalived/keepalived.conf для первого балансировщика нагрузки:

vrrp_instance loadbalancer1 {
    state MASTER
    interface ens5
    virtual_router_id 101
    priority 101
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass passwordgoeshere
    }
    # Replace unicast_src_ip and unicast_peer with your load balancer IP addresses
    unicast_src_ip 10.0.0.20
    unicast_peer {
      10.0.0.21
    }
    virtual_ipaddress {
        10.0.0.30
    }
}

Вот полная копия файла /etc/keepalived/keepalived.conf для второго балансировщика нагрузки.

Обратите внимание, что имя было установлено в балансировщик нагрузки 2 , состояние было установлено в РЕЗЕРВНОЕ КОПИРОВАНИЕ , приоритет ниже на 100 , и одноадресная рассылка_src_ip и unicast_peer IP-адреса были перевернуты:

vrrp_instance loadbalancer2 {
    state BACKUP
    interface ens5
    virtual_router_id 101
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass passwordgoeshere
    }
    # Replace unicast_src_ip and unicast_peer with your load balancer IP addresses
    unicast_src_ip 10.0.0.21
    unicast_peer {
      10.0.0.20
    }
    virtual_ipaddress {
        10.0.0.30
    }
}

Перезапустите службу keepalived на обоих балансировщиках нагрузки с помощью команды:

systemctl restart keepalived

На первом балансировщике нагрузки выполните команду ip addr . Это покажет виртуальный IP-адрес, назначенный интерфейсу, для которого Keepalived был настроен для управления с помощью вывода inet 10.0.0.30/32 глобальная область ens5 :

$ ip addr
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens5:  mtu 9001 qdisc mq state UP group default qlen 1000
    link/ether 0e:2b:f9:2a:fa:a7 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.21/24 brd 10.0.0.255 scope global dynamic ens5
       valid_lft 3238sec preferred_lft 3238sec
    inet 10.0.0.30/32 scope global ens5
       valid_lft forever preferred_lft forever
    inet6 fe80::c2b:f9ff:fe2a:faa7/64 scope link
       valid_lft forever preferred_lft forever

Если первый балансировщик нагрузки выключен, второй балансировщик нагрузки примет виртуальный IP-адрес, а второй веб-сервер Apache будет действовать в качестве балансировщика нагрузки.

Постройте конвейер развертывания

Наш конвейер развертывания будет включать развертывание приложения Случайные кавычки образец. Это простое приложение для загрузки Spring с отслеживанием состояния, использующее Flyway для управления миграциями баз данных.

При нажатии кнопки Обновить из базы данных загружается новая цитата, в сеансе увеличивается счетчик, и счетчик отображается в поле Количество цитат на странице. Версия приложения отображается в поле Версия .

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

Получите экземпляр осьминога

Если у вас еще не установлен Octopus, самый простой способ получить экземпляр Octopus – это зарегистрироваться в облачной учетной записи . Эти экземпляры бесплатны для 10 целей.

Создавайте среду

Для этого примера мы создадим две среды: Dev и Прод . Это означает, что мы настроим в общей сложности восемь целевых объектов: четыре балансировщика нагрузки и четыре экземпляра Tomcat.

Разверните щупальце

Мы установим щупальце на каждую из наших виртуальных машин, чтобы мы могли выполнять задачи развертывания, обновления и обслуживания системы. Инструкции по установке программного обеспечения Tentacle находятся на странице загрузки Octopus . Поскольку в этом примере в качестве базовой ОС используется Ubuntu, мы устанавливаем Щупальце с помощью команд:

apt-key adv --fetch-keys https://apt.octopus.com/public.key
add-apt-repository "deb https://apt.octopus.com/ stretch main"
apt-get update
apt-get install tentacle

После установки щупальца мы настраиваем экземпляр с помощью команды:

/opt/octopus/tentacle/configure-tentacle.sh

Установка предоставляет вам выбор между щупальцами для опроса или прослушивания . Какой вариант вы выберете, часто зависит от ваших сетевых ограничений. Опросные щупальца требуют, чтобы виртуальная машина, на которой размещается Щупальце, могла достичь сервера Octopus, в то время как прослушивающие щупальца требуют, чтобы сервер Octopus мог достичь виртуальной машины. Выбор стиля связи зависит от того, имеют ли сервер Octopus или виртуальные машины фиксированные IP-адреса и открыты ли правильные порты в брандмауэре. Любой из вариантов является допустимым выбором и не влияет на процесс развертывания.

Вот снимок экрана среды Dev с щупальцами для экземпляров Tomcat и балансировщика нагрузки. Экземпляры Tomcat имеют роль tomcat , а экземпляры балансировщика нагрузки имеют роль балансировщика нагрузки :

Создайте внешний канал

Пример приложения случайных цитат был отправлен в Maven Central в качестве файла ВОЙНЫ . Это означает, что мы можем развернуть приложение непосредственно из канала Maven.

Создайте новый канал Maven, указывающий на Создайте новый канал Maven, указывающий на

Проверьте ленту, выполнив поиск com.octopus: случайные цитаты . Здесь мы видим, что наше приложение найдено в репозитории:

Создайте процесс развертывания

Создайте метку времени

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

Этот номер версии использует сравнение строк для определения последней версии. Типичные схемы управления версиями, такие как SEMVER, используют формат major.minor.patch , например 1.23.4 , для определения версий. Во многих случаях эти традиционные схемы управления версиями можно сравнить как строки, чтобы определить их порядок.

Однако заполнение может привести к проблемам. Например, версия 1.23.40 ниже, чем 01.23.41 , но прямое сравнение строк возвращает противоположный результат.

По этой причине мы используем время развертывания в качестве версии Tomcat. Поскольку версия должна быть согласована между целевыми объектами, мы начинаем с шага сценария, который генерирует временную метку и сохраняет ее в выходной переменной с кодом:

$timestamp = Get-Date -Format "yyMMddHHmmss"
Set-OctopusVariable -name "TimeStamp" -value $timestamp

Развертывание Tomcat

Наш процесс развертывания начинается с развертывания приложения на каждом экземпляре Tomcat с помощью шага Развертывание в Tomcat через диспетчер . Мы назовем этот шаг Случайные кавычки и запустите его на tomcat цели:

Мы развертываем пакет com.octopus: случайные цитаты из канала Maven, который мы настроили ранее:

Поскольку Щупальце расположено на виртуальной машине, на которой размещен Tomcat, расположение API-интерфейса диспетчера равно http://localhost:8080/manager . Затем мы предоставляем учетные данные менеджера, которые были введены в в tomcat-users.xml файл, когда мы настроили Tomcat:

Контекстный путь составляет путь в URL-адресе, по которому доступно развернутое приложение. Здесь мы раскрываем приложение на пути //случайные цитаты :

Версия развертывания устанавливается на отметку времени, которая была сгенерирована на предыдущем шаге путем ссылки на выходную переменную как #{Octopus. Действие [Получить метку времени].Выход. Отметка времени} :

Проверка дымом развертывания

Чтобы убедиться, что наше развертывание прошло успешно, мы отправляем HTTP-запрос и проверяем код ответа. Для этого мы используем шаблон шага сообщества под названием HTTP -Тестовый URL (Bash) .

Как и прежде, этот шаг будет выполняться на экземплярах Tomcat:

На этом шаге будет предпринята попытка открыть index.html страница из недавно развернутого приложения, ожидающая HTTP-кода ответа 200 :

Выполните начальное развертывание

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

Затем Octopus загружает файл WAR из репозитория Maven, отправляет его в экземпляры Tomcat и развертывает его в Tomcat через менеджер. По завершении выполняется тест на задымление, чтобы убедиться, что приложение может быть успешно открыто:

Проверка запроса через стек

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

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

Вот скриншот приложения, открытого с помощью инструментов разработчика Chrome:

На этом скриншоте следует отметить три вещи:

  1. Файл cookie JSESSIONID устанавливается со случайным идентификатором сеанса и именем экземпляра Tomcat, который ответил на запрос. В этом примере экземпляр Tomcat, для которого jvmRoute было установлено значение worker1 , ответил на запрос.
  2. Мы открыли версию 0.1.6.1 приложения.
  3. Значение Количество цитат равно 1, но оно будет увеличиваться по мере нажатия кнопки Обновить .

Давайте увеличим Количество цитат , нажав кнопку Обновить . Это значение сохраняется на сервере Tomcat в сеансе, связанном с файлом cookie JSESSIONID :

Давайте теперь завершим работу экземпляра worker1 Tomcat. После завершения работы мы снова нажимаем кнопку Обновить :

На этом скриншоте следует отметить три вещи:

  1. Суффикс в файле JSESSIONID cookie изменился с рабочий 1 на рабочий 2 .
  2. Идентификатор сеанса JSESSIONID cookie остался прежним.
  3. Количество цитат увеличилось до 6.

Когда Tomcat будет корректно выключен, он запишет содержимое любых сеансов в базу данных. Затем, поскольку экземпляр worker1 Tomcat больше не был доступен, запрос был направлен на экземпляр worker 2 Tomcat. рабочий 2 Экземпляр Tomcat загрузил информацию о сеансе из базы данных и увеличил счетчик. JvmRouteBinderValve valve переписал файл cookie сеанса, чтобы добавить текущее имя экземпляра Tomcat в конец, и ответ был возвращен в браузер.

Теперь мы можем видеть, что важно, чтобы имена рабочих в балансировщике нагрузки / /и т. д./пакета libapache2-мод-Джоан/работников.свойства файла совпадают имена, назначенные jvmRoute В /и т. д./В Tomcat 9/server.xml

Поскольку Количество цитат не было сброшено обратно на 1, мы знаем, что сеанс был сохранен в базе данных и реплицирован на другие экземпляры Tomcat в кластере. Мы также знаем, что запрос был обработан другим экземпляром Tomcat, потому что файл cookie JSESSIONID показывает новое имя работника.

Даже если бы мы вернули worker1 в оперативный режим, этот сеанс браузера продолжал бы обрабатываться worker 2 потому что балансировщики нагрузки реализуют фиксированные сеансы, проверяя файл cookie JSESSIONID . Это также означает, что балансировщикам нагрузки не нужно делиться состоянием, так как им требуется только значение файла cookie для направления трафика.

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

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

journalctl -u keepalived -f

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

Apr 01 03:15:00 ip-10-0-0-21 Keepalived_vrrp[32485]: VRRP_Instance(loadbalancer2) Transition to MASTER STATE
Apr 01 03:15:01 ip-10-0-0-21 Keepalived_vrrp[32485]: VRRP_Instance(loadbalancer2) Entering MASTER STATE

Приняв на себя роль ведущего, балансировщику нагрузки будет назначен виртуальный IP-адрес, и он будет распределять трафик, как это делал предыдущий ведущий.

После перезапуска предыдущего главного экземпляра он снова возьмет на себя роль главного, поскольку он настроен с более высоким приоритетом, и виртуальный IP-адрес будет назначен обратно.

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

Вкратце:

  • Файл cookie JSESSIONID содержит идентификатор сеанса и имя экземпляра Tomcat, который обработал запрос.
  • Балансировщики нагрузки реализуют фиксированные сеансы на основе имени работника, добавляемого к файлу cookie JSESSIONID .
  • JvmRouteBinderValve valve перезаписывает файл cookie JSESSIONID , когда экземпляр Tomcat получает трафик для сеанса, за который он изначально не отвечал.
  • Keepalived назначает виртуальный IP-адрес балансировщику нагрузки резервного копирования, если мастер переходит в автономный режим.
  • Главный балансировщик нагрузки повторно принимает виртуальный IP-адрес, когда он возвращается в оперативный режим.
  • Стек инфраструктуры может пережить потерю одного экземпляра Tomcat и одного балансировщика нагрузки и при этом сохранить доступность.

Развертывание с нулевым временем простоя

Теперь мы успешно развернули версию 0.1.6.1 нашего веб-приложения для Tomcat. Эта версия приложения использует очень простую структуру таблицы для хранения имен тех, кому приписаны цитаты, помещая как имя, так и фамилию в один столбец с именем АВТОР .

Эта структура таблицы изначально была создана сценарием базы данных Flyway со следующим SQL:

create table AUTHOR
(
    ID INT auto_increment,
    AUTHOR VARCHAR(64) not null
);

В следующей версии нашего приложения имена будут разделены на ИМЯ и ФАМИЛИЯ столбец. Мы добавляем эти столбцы с помощью нового сценария Flyway, который содержит следующий SQL:

ALTER TABLE AUTHOR
ADD FIRSTNAME varchar(64);

ALTER TABLE AUTHOR
ADD LASTNAME varchar(64);

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

В этом сценарии мы должны сохранить старый столбец АВТОР и скопировать данные, которые в нем содержались, в новые столбцы ИМЯ и ФАМИЛИЯ :

UPDATE AUTHOR SET FIRSTNAME = 'Rob', LASTNAME = 'Siltanen' WHERE ID = 1;
UPDATE AUTHOR SET FIRSTNAME = 'Albert', LASTNAME = 'Einstein' WHERE ID = 2;
UPDATE AUTHOR SET FIRSTNAME = 'Charles', LASTNAME = 'Eames' WHERE ID = 3;
UPDATE AUTHOR SET FIRSTNAME = 'Henry', LASTNAME = 'Ford' WHERE ID = 4;
UPDATE AUTHOR SET FIRSTNAME = 'Antoine', LASTNAME = 'de Saint-Exupery' WHERE ID = 5;
UPDATE AUTHOR SET FIRSTNAME = 'Salvador', LASTNAME = 'Dali' WHERE ID = 6;
UPDATE AUTHOR SET FIRSTNAME = 'M.C.', LASTNAME = 'Escher' WHERE ID = 7;
UPDATE AUTHOR SET FIRSTNAME = 'Paul', LASTNAME = 'Rand' WHERE ID = 8;
UPDATE AUTHOR SET FIRSTNAME = 'Elon', LASTNAME = 'Musk' WHERE ID = 9;
UPDATE AUTHOR SET FIRSTNAME = 'Jessica', LASTNAME = 'Hische' WHERE ID = 10;
UPDATE AUTHOR SET FIRSTNAME = 'Paul', LASTNAME = 'Rand' WHERE ID = 11;
UPDATE AUTHOR SET FIRSTNAME = 'Mark', LASTNAME = 'Weiser' WHERE ID = 12;
UPDATE AUTHOR SET FIRSTNAME = 'Pablo', LASTNAME = 'Picasso' WHERE ID = 13;
UPDATE AUTHOR SET FIRSTNAME = 'Charles', LASTNAME = 'Mingus' WHERE ID = 14;

Кроме того, новый класс сущностей JPA должен игнорировать старый столбец AUTHOR (через аннотацию @Transient ). Затем метод getAuthor() возвращает объединенные значения методов getFirstName() и getLastName() :

@Entity
public class Author {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Integer id;
    @Column(name = "FIRSTNAME")
    private String firstName;
    @Column(name = "LASTNAME")
    private String lastName;

    @OneToMany(
            mappedBy = "author",
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    private List quotes = new ArrayList<>();

    protected Author() {

    }

    public Integer getId() {
        return id;
    }

    @Transient
    public String getAuthor() {
        return getFirstName() + " " + getLastName();
    }

    public List getQuotes() {
        return quotes;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }
}

Хотя это тривиальный пример, который легко реализовать благодаря тому, что таблица AUTHOR доступна только для чтения, он демонстрирует, как изменения в базе данных могут быть реализованы обратно совместимым способом. Можно было бы написать целые книги о стратегиях поддержания обратной совместимости, но для целей этого поста мы оставим это обсуждение здесь.

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

С помощью наших сценариев миграции, написанных с обратной совместимостью, мы можем развернуть новую версию нашего приложения. Для удобства эта новая версия была перенесена в Maven Central как версия 0.1.7 :

После завершения развертывания откройте приложение менеджера по адресу http://tomcatip:8080/manager/html . Обратите внимание, что, хотя вы можете получить доступ к менеджеру через балансировщик нагрузки, вы не можете выбрать, каким экземпляром Tomcat вы будете управлять, поскольку балансировщик нагрузки выбирает экземпляр Tomcat для вас. Это означает, что лучше подключаться к экземпляру Tomcat напрямую, минуя балансировщик нагрузки:

На этом скриншоте следует отметить две вещи:

  1. У нас есть два приложения в разделе путь //случайные цитаты , каждый с уникальной версией.
  2. Приложение с более ранней версией имеет связанный с ним сеанс. Это был сеанс, который мы создали, получив доступ к версии 0.1.6.1 предыдущая версия 0.1.7 был развернут.

Если мы вернемся в браузер, в котором мы открыли версию 0.1.6.1 приложения, мы можем продолжить обновлять кавычки. Счетчик увеличивается, как мы и ожидали, а номер версии, отображаемый в нижнем колонтитуле, остается на уровне версии 0.1.6.1 .

Если мы снова откроем приложение в окне приватного просмотра, нам будет гарантировано, что мы не будем повторно использовать старые файлы cookie сеанса, и мы перейдем к версии 0.1.7 приложения:

Благодаря этому мы продемонстрировали развертывание с нулевым временем простоя. Поскольку наши миграции баз данных были разработаны для обеспечения обратной совместимости, версия 0.1.6.1 и версия 0.1.7 нашего приложения может выполняться параллельно с использованием параллельных развертываний Tomcat. Лучше всего то, что сеансы для старых развертываний все еще могут передаваться между экземплярами Tomcat, поэтому мы сохраняем высокую доступность наряду с параллельными развертываниями.

Стратегии отката

До тех пор, пока поддерживается совместимость базы данных между последней и текущей версиями приложения (версиями 0.1.6.1 и 0.1.7 в этом примере) откат так же прост, как создание нового развертывания с предыдущей версией приложения.

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

Обратите внимание, что любые существующие сеансы для версии 0.1.7 , будет оставлен до естественного истечения срока действия благодаря параллельным развертываниям Tomcat. Если эту версию необходимо перевести в автономный режим (например, если возникла критическая проблема и ее нельзя оставить в обслуживании), мы можем использовать Запустить/остановить приложение в Tomcat , чтобы остановить развернутое приложение.

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

Мы начнем с добавления запрашиваемой переменной, которая будет заполнена меткой времени версии Tomcat, соответствующей развертыванию, которое мы хотим завершить:

Затем runbook настраивается с помощью приложения Start/stop в Tomcat шаг. Версия развертывания устанавливается в значение запрашиваемой переменной:

При запуске runbook нам будет предложено остановить версию приложения с меткой времени:

После завершения работы runbook мы можем убедиться, что приложение было остановлено, открыв консоль диспетчера. На скриншоте ниже вы можете увидеть версию 200401140129 не работает. Эта версия больше не отвечает на запросы, и все будущие запросы будут направлены на последнюю версию приложения:

Развертывания филиалов функций

Обычной практикой разработки является завершение функции в отдельной ветви SCM, известной как ветвь функций.

Сервер CI обычно отслеживает наличие ветвей функций и создает развертываемый артефакт из зафиксированного там кода.

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

Как вы можете видеть на изображении выше, фиксация в ветке функций под названием моя функция создает версию, подобную 1.2.1-myfeature.1+1 . Это, в свою очередь, создает артефакт с именем файла, таким как myapp.1.2.1-myfeature.1+1.zip .

Несмотря на то, что такие инструменты, как Git Version, генерируют строки версии SemVer, тот же формат можно использовать для артефактов Maven. Однако здесь есть одна загвоздка.

SemVer закажет версию с ветвью функций ниже, чем версия без какого-либо предварительного компонента. Например, 1.2.1 считается более высоким номером версии, чем 1.2.1-моя функция . Это ожидаемый порядок, так как ветвь функций в конечном итоге будет объединена обратно в главную ветвь.

Когда ветвь функций добавляется к версии Maven, она считается классификатором. Maven допускает любые квалификаторы, но распознает некоторые специальные, такие как СНИМОК , финал , га и т.д. Полный список можно найти в сообщении в блоге Объясненные версии Maven . Версии Maven с непризнанными квалификаторами (а имена ветвей функций являются непризнанными квалификаторами) рассматриваются как более поздние версии, чем неквалифицированные версии.

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

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

Вот канал по умолчанию для развертывания нашего приложения. Обратите внимание, регулярное выражение ^$ для поля Предрелизный тег . Это регулярное выражение соответствует только пустым строкам, что означает, что канал по умолчанию будет развертывать артефакты только без строки предварительного выпуска или квалификатора:

Далее у нас есть канал ответвления функций, который определяет регулярное выражение .+ для поля Предрелизный тег . Это регулярное выражение соответствует только непустым строкам, что означает, что канал ветвления функций будет развертывать артефакты только со строкой предварительного выпуска или квалификатора:

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

Вот список версий, для которых Octopus позволяет создавать релизы с использованием канала ветки функций. Все эти версии имеют квалификатор, встраивающий имя ветви функции:

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

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

/randomquotes#{Octopus.Action.Package.PackageVersion | Replace "^([0-9\.]+)((?:-[A-Za-z0-9]+)?)(.*)$" "$2"}

Использование регулярного выражения выше в версии 1.2.1 - моя функция.1+1 сделаем следующее:

  • ^([0-9\.]+) группирует все цифры и точки в начале версии как группу 1, которая соответствует 1.2.1 .
  • ((?:-[A-Za-z0-9]+)?) |/группирует начальную черточку и любые последующие буквенно-цифровые символы (если таковые имеются) в группу 2, которая соответствует - моя функция .
  • (.*)$ группирует любые последующие символы (если таковые имеются) в группу 3, которая соответствует .1+1 .

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

  • Версия 1.2.1 - моя функция.1+1 генерирует контекстный путь /случайные кавычки - моя функция .
  • Версия 1.2.1 генерирует контекстный путь //случайные цитаты .

Вот скриншот шага развертывания Tomcat с примененным новым контекстным путем:

Проект SemVer предоставляет более надежное регулярное выражение , которое надежно фиксирует группы в версии SemVer.

Регулярное выражение с именованными группами захвата является:

^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P
(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

Регулярное выражение без именованных групп является:

^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

Управление общедоступными сертификатами

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

Получить сертификат

Для этого поста я получил сертификат Let's Encrypt, сгенерированный через нашего DNS-провайдера. Точный процесс создания сертификатов HTTPS мы здесь не рассмотрим, но вы можете обратиться к своему DNS или поставщику сертификатов за конкретными инструкциями.

На скриншоте ниже вы можете увидеть различные варианты, доступные для загрузки сертификата. Обратите внимание, что, хотя инструкции и загрузки предоставляются для Apache, мы загружаем файл PFX, предоставленный в соответствии с инструкциями для IIS. Файлы PFX содержат открытый сертификат, закрытый ключ и цепочку сертификатов в одном файле. Нам нужен этот единственный файл, чтобы загрузить сертификат в Octopus. Здесь мы загружаем PFX-файл для осьминога.технология домен:

Создайте сертификат в Octopus

Развертывание сертификатов - это непрерывная операция. В частности, срок действия сертификатов, предоставляемых Let's Encrypt, истекает каждые три месяца, и поэтому их необходимо часто обновлять.

Это делает развертывание сертификатов отличным вариантом использования для runbook. В отличие от обычного развертывания, runbook не нужно проходить через среды, и вам не нужно создавать развертывание. Мы создадим runbook для развертывания сертификата в подсистемах балансировки нагрузки Apache.

Во-первых, нам нужно загрузить сертификат PFX, сгенерированный поставщиком DNS. На скриншоте ниже вы можете увидеть сертификат Let's Encrypt, загруженный в хранилище сертификатов Octopus:

Развертывание сертификата

Создайте новый проект в Octopus и добавьте сертификат, который мы только что загрузили, в качестве переменной:

Затем создайте runbook с одним Запуском сценария шаг.

Первым шагом в сценарии является включение mod_ssl с помощью команды:

a2enmod ssl

Затем создайте несколько каталогов для хранения сертификата, цепочки сертификатов и закрытого ключа:

[ ! -d "/etc/apache2/ssl" ] && mkdir /etc/apache2/ssl
[ ! -d "/etc/apache2/ssl/private" ] && mkdir /etc/apache2/ssl/private

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

  • Сертификат. Сертификат Pem , который является общедоступным сертификатом.
  • Сертификат. Закрытый ключ Pem , который является закрытым ключом.
  • Сертификат. Цепочка Pem , которая является цепочкой сертификатов.

Мы печатаем содержимое этих переменных в три файла:

get_octopusvariable "Certificate.CertificatePem" > /etc/apache2/ssl/octopus_tech.crt
get_octopusvariable "Certificate.PrivateKeyPem" > /etc/apache2/ssl/private/octopus_tech.key
get_octopusvariable "Certificate.ChainPem" > /etc/apache2/ssl/octopus_tech_bundle.pem

Если вы помните, ранее в этом посте мы создали файл /etc/apache2/с поддержкой сайтов/000-default.conf со следующим содержимым:


  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined
  JkMount /* loadbalancer

Мы хотим изменить этот файл, вот так:


  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined
  JkMount /* loadbalancer
  SSLEngine on
  SSLCertificateFile /etc/apache2/ssl/octopus_tech.crt
  SSLCertificateKeyFile /etc/apache2/ssl/private/octopus_tech.key
  SSLCertificateChainFile /etc/apache2/ssl/octopus_tech_bundle.pem

Это делается путем повторения нужного текста в файл /etc/apache2/с поддержкой сайтов/000-default.conf :

{
cat << EOF

  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined
  JkMount /* loadbalancer
  SSLEngine on
  SSLCertificateFile /etc/apache2/ssl/octopus_tech.crt
  SSLCertificateKeyFile /etc/apache2/ssl/private/octopus_tech.key
  SSLCertificateChainFile /etc/apache2/ssl/octopus_tech_bundle.pem

EOF
} > /etc/apache2/sites-enabled/000-default.conf

Последним шагом является перезапуск службы apache2 для загрузки изменений:

systemctl restart apache2

Вот полный сценарий для справки:

a2enmod ssl

[ ! -d "/etc/apache2/ssl" ] && mkdir /etc/apache2/ssl
[ ! -d "/etc/apache2/ssl/private" ] && mkdir /etc/apache2/ssl/private
get_octopusvariable "Certificate.CertificatePem" > /etc/apache2/ssl/octopus_tech.crt
get_octopusvariable "Certificate.PrivateKeyPem" > /etc/apache2/ssl/private/octopus_tech.key
get_octopusvariable "Certificate.ChainPem" > /etc/apache2/ssl/octopus_tech_bundle.pem

{
cat << EOF

  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined
  JkMount /* loadbalancer
  SSLEngine on
  SSLCertificateFile /etc/apache2/ssl/octopus_tech.crt
  SSLCertificateKeyFile /etc/apache2/ssl/private/octopus_tech.key
  SSLCertificateChainFile /etc/apache2/ssl/octopus_tech_bundle.pem

EOF
} > /etc/apache2/sites-enabled/000-default.conf
systemctl restart apache2

После завершения работы с runbook мы можем проверить, что приложение доступно по протоколу HTTPS:

Внутреннее управление сертификатами

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

Создание самозаверяющих сертификатов

Поскольку наши экземпляры Tomcat не доступны через имя хоста, у нас нет возможности получить для них сертификат . Чтобы включить HTTPS, нам нужно создать самозаверяющие сертификаты, что можно сделать с помощью OpenSSL:

openssl genrsa 2048 > private.pem
openssl req -x509 -new -key private.pem -out public.pem -days 3650
openssl pkcs12 -export -in public.pem -inkey private.pem -out mycert.pfx

Добавьте сертификат в Tomcat

Сертификат настраивается в Tomcat с помощью Разверните сертификат в Tomcat шаг.

Путь Tomcat CATALINA_HOME установлен в /usr/общий доступ/tomcat 9 и путь Tomcat CATALINA_BASE установлен в /var/lib/tomcat 9 .

Мы ссылаемся на переменную сертификата для поля Выберите переменную сертификата . Значение по умолчанию Catalina подходит для Имени службы Tomcat .

У нас есть несколько вариантов того, как Tomcat обрабатывает сертификат. Вообще говоря, параметры Блокирующий ввод-вывод , Неблокирующий ввод-вывод , Неблокирующий ввод-вывод 2 и Apache Portable Runtime имеют повышенный уровень производительности. Apache Portable Runtime - это дополнительная библиотека, которой может воспользоваться Tomcat, и она предоставляется пакетами Tomcat, которые мы установили с помощью apt-get , поэтому имеет смысл использовать этот вариант.

Чтобы разрешить Tomcat использовать новую конфигурацию, нам нужно перезапустить службу с помощью шага сценария с помощью команды:

systemctl restart tomcat9

Теперь мы можем загрузить консоль менеджера из https://tomcatip:8443/manager/html .

Масштабирование до нескольких сред

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

Связывая Щупальца, назначенные новому экземпляру Tomcat и балансировщика нагрузки, с дополнительными средами в Octopus, мы получаем возможность продвигать развертывания в производство:

Вывод

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

Подводя итог, в этом посте мы:

  • Настроена репликация сеанса Tomcat с базой данных PostgreSQL и перезапись файлов cookie сеанса с помощью JvmRouteBinderValve valve.
  • Настроенные веб-серверы Apache, выполняющие функции балансировщиков нагрузки с помощью плагина mod_jk.
  • Реализована высокая доступность среди балансировщиков нагрузки с помощью Keepalived.
  • Выполнил развертывания с нулевым временем простоя с помощью функции параллельного развертывания Tomcat и Flyway, выполняющей миграции баз данных с обратной совместимостью.
  • Дым протестировал развертывание с помощью шагов сообщества в Octopus.
  • Реализовано развертывание филиалов функций с учетом ограничений стратегии управления версиями Maven с каналами Octopus.
  • Посмотрел, как приложения могут быть откатаны или удалены из службы.
  • Добавлены сертификаты HTTPS в Apache.
  • Повторите процесс для нескольких сред.

Оригинал: "https://dev.to/octopus/the-ultimate-guide-to-tomcat-deployments-5dkk"