
Доброго дня друзья, коллеги и все неравнодушные к продуктам компании Directum лица). Хочу поговорить немного про Portal, его запуск и сопровождение после внедрения. Учитывая, что продукт довольно новый и еще не оброс мясом из «питона» как все продукты Directum, у него есть ряд нюансов, с которыми придется столкнуться при внедрении и сопровождении.
Немного напомню: Portal очень удобная и гибкая структура, позволяющая создать онлайн-портал или, если угодно, сайт компании, соединить его с какими угодно сервисами (в частности виджеты Directum RX) для удобства и наполнить любым контентом. Все это счастье сделано на основе свободно распространяемого программного обеспечения Liferay с добавлением сервисов идентификации, используемых в HR Pro, приправлено менеджером сообщений и Elasticsearch, завернуто в docker и иногда посыпано каким-либо прокси (nginx, traefik, haproxy).
Подробнее о истории продукта и его подноготной можно почитать в моей прошлой статье Directum Portal – единое пространство на базе Liferay
Итак установка и настройка портала описана в прилагаемой вендором инструкции, повторяться не будем. Мы в компании "СТАРКОВ Групп" после внедрения продукта у заказчиков выявили несколько моментов, которые дополняют настройку и делают более расширенной. В качестве первого пункта нашего экскурса хочу остановиться именно на них.
На сервисе идентификации есть параметры, отвечающие за время сессии, в частности параметр Authentication__SessionCookieLifetime. Данная информация есть в справке по сервису идентификации на любой версии HR Pro, но есть одно «НО»: этот параметр отвечает за время жизни cookies на самом сервисе идентификации, а не в портале. Чтобы увеличить время жизни сессии на портале, необходимо сделать следующее: выдернуть из контейнера с порталом файлик web.xml, он лежит по пути /opt/liferay/tomcat/webapps/ROOT/WEB-INF/web.xml. В нем необходимо поправить параметр session-timeout в блоке session-config по примеру:
<session-config>
<session-timeout>10080</session-timeout>
<cookie-config>
<http-only>true</http-only>
</cookie-config>
</session-config>
Далее - сохранить исправленный web.xml в директории portal и добавить его в docker-compose следующей строкой:
./web.xml:/opt/liferay/tomcat/webapps/ROOT/WEB-INF/web.xml:rwx
После загрузки/перезагрузки портала время сессии внутри самого портала изменится.
При начальном конфигурировании и запуске портала он не использует все ядра и оперативную память машины, на которой запущен - у портала есть строго заданные ограничения. Если даже вы добавите оперативной памяти на машину и увеличите количество ядер, плюсом внесете кучу виджетов и тяжелого контента, портал будет работать все медленнее и медленнее с каждым разом, потому что выделенные ему ресурсы не менялись с самого старта системы. Вся эта история с выделенными ресурсами тянется из Liferay. Чтобы портал работал с определенным количеством ресурсов, либо не выедал все и сразу, были созданы настройки и ограничения с помощью компонента сервера приложений Apache Tomcat – Catalina. За настройку Catalina отвечает небольшой скрипт настройки переменных сред (setenv.sh), который находится по пути в контейнере portal: /opt/liferay/tomcat/bin/setenv.sh.
В этом скрипте настраиваются не только выделенные для портала ресурсы, но и очень много других немаловажных параметров. Нас интересует только строка «CATALINA_OPTS=», именно в ней мы будем перечислять все необходимые настройки:
Это были четыре обязательные опции, которые есть по умолчанию, и их менять не обязательно. Едем дальше:
В итоге конечная запись в скрипте setenv.sh будет иметь вид:
CATALINA_OPTS="$CATALINA_OPTS -Dfile.encoding=UTF-8 -Djava.locale.providers=JRE,COMPAT,CLDR -Djava.net.preferIPv4Stack=true -Duser.timezone=GMT -Xms8g -Xmx16g -XX:MaxNewSize=1536m -XX:MaxMetaspaceSize=1g -XX:MetaspaceSize=1g -XX:NewSize=1536m -XX:SurvivorRatio=12 -XX:MaxTenuringThreshold=15 -XX:+UseLargePages -XX:LargePageSizeInBytes=256m -XX:ParallelGCThreads=8 -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=85 -XX:+UseCompressedOops -XX:+DisableExplicitGC -XX:-UseBiasedLocking -XX:InitialCodeCacheSize=32m -XX:ReservedCodeCacheSize=96m -XX:ActiveProcessorCount=8"

После внесения изменений в setenv.sh сохраняем его в директории с portal, вносим строчку монтирования в контейнер, монтируется данный скрипт, и стартуем/перегружаем портал для применения:
- ./setenv.sh:/opt/liferay/tomcat/bin/setenv.sh:rwx
Также данные настройки можно указывать в файле настроек портала - portal-ext.properties. Но практика показала, что не все параметры оттуда считываются, и лучше непосредственно указывать в самом скрипте.
Мы с вами прошлись по дополнительным настройкам portal, которые помогут оптимизировать запуск и работу приложения в рамках плавного распределения нагрузки на имеющиеся ресурсы, также после внесения данных изменений вы можете наблюдать за ресурсами портала на странице «Администрирование сервера»:

Далее вторым и заключительным пунктом нашего экскурса будет настройка перезагрузки портала. При стандартной перезагрузке методом “docker-compose up -d” у нас поднимается контейнер портала, загружается liferay и показывается стандартная страница приветствия Portal без наших доработок, виджетов, плагинов и так далее. Это происходит из-за того, что не применяется разработка, которая хранится в каталоге /opt/liferay/deploy, хотя у нас изначально в файле docker-compose.yml есть точка монтирования согласно инструкции к развертыванию ./deploy:/opt/liferay/deploy. Данная проблема решается копированием текущей разработки из хранилища (в контейнере /mnt/liferay/deploy): нужно из папки ./mnt/liferay/deploy скопировать в папку ./opt/liferay/deploy все содержимое (делается в контейнере). После этих нехитрых действий в логе Liferay отражается тема sungero и вся наша текущая разработка, а web-страница портала загружается уже с привычным нам видом.
Едем дальше… Полный комплект сервисов портала представляет собой несколько контейнеров, а именно:
Потихоньку мы подобрались к вопросу перезагрузки. Все это добро приходится перезапускать по отдельности, делать заранее копию базы данных на всякий случай, копию разработки, метаданных портала, потом по каждому docker-compose.yml запускать “UP -d”, потом еще проверять лог, а поднялось ли, и перезапускать отдельно какой-либо сервис.
В прошлой статье, уже описывался скрипт, который перегружает портал и делает бэкап, однако я его немного изменил, добавил «мяса». Для оптимизации процесса я изменил скрипт, который теперь работает как служба, запускает или перезапускает все сервисы portal, проверяет запустились ли они, предварительно делает бэкап базы данных и метаданных портала, после загрузки применяет разработку и, плюсом, логирует все свои действия:
#!/bin/bash
# Логирование
LOG_FILE="/srv/logs/portal-restart.log"
exec > >(tee -a "$LOG_FILE") 2>&1
echo "========================================="
echo "Запуск скрипта перезапуска Portal"
echo "Время запуска: $(date)"
echo "========================================="
# Проверка, что Docker запущен
if ! systemctl is-active --quiet docker; then
echo "Ошибка: Docker не запущен. Запускаю Docker..."
systemctl start docker
sleep 10
fi
# Проверка доступности Docker
if ! docker info >/dev/null 2>&1; then
echo "Ошибка: Docker недоступен"
exit 1
fi
c_name="portal-directum"
yml="/srv/portal/portal/docker-compose.yml"
ymlMB="/srv/portal/MB/docker-compose.yml"
ymlBD="/srv/portal/postgres/docker-compose.yml"
ymlRab="/srv/portal/rabbit/docker-compose.yml"
ymlIds="/srv/portal/Ids/docker-compose.yml"
ymlNg="/srv/portal/nginx/docker-compose-nginx.yml"
ymlELK="/srv/portal/elastic/docker-compose.yml"
c_name_bd="postgres"
bd_name="portal_01"
DATE=$(date +"%Y%m%d_%H%M")
# Функция для ожидания готовности контейнера
wait_for_container() {
local container_name=$1
local max_attempts=$2
local attempt=1
echo "Ожидание запуска контейнера $container_name..."
while [ $attempt -le $max_attempts ]; do
if docker ps --format '{{.Names}}' | grep -q "^${container_name}$"; then
if docker exec "$container_name" sh -c "echo 'Container is ready'" >/dev/null 2>&1; then
echo "Контейнер $container_name готов"
return 0
fi
fi
echo "Попытка $attempt/$max_attempts: контейнер $container_name не готов"
sleep 5
((attempt++))
done
echo "Ошибка: контейнер $container_name не запустился за отведенное время"
return 1
}
# Бэкап базы данных
echo "=== Бэкап базы данных ==="
if wait_for_container "$c_name_bd" 12; then
echo "Выполняю бэкап БД в контейнере $c_name_bd"
docker exec "$c_name_bd" sh -c "pg_dump -U user -d $bd_name -f /srv/backup/${bd_name}_${DATE}.dump"
echo "Бэкап создан: /srv/backup/${bd_name}_${DATE}.dump"
else
echo "Пропускаем бэкап БД, контейнер не доступен"
fi
echo ""
echo "=== Перезагрузка сервисов на сервере ==="
# Перезагрузка прокси
echo "> Перезагрузка Прокси"
docker-compose -f "$ymlNg" down
docker-compose -f "$ymlNg" up -d
# Перезагрузка ELK
echo "> Перезагрузка ELK Стека"
docker-compose -f "$ymlELK" down
docker-compose -f "$ymlELK" up -d
# Бэкап Portal
echo "> Бэкап Portal"
rm -rf /srv/backup_portal/portal
cp -r /srv/portal/portal /srv/backup_portal
cp /srv/backup_portal/portal/portal-ext.properties /srv/portal/portal
# Остановка portal и сервиса идентификации
echo "> Остановка portal и сервиса идентификации"
docker-compose -f "$yml" down
docker-compose -f "$ymlIds" down
# Перезагрузка Базы данных
echo "> Перезагрузка Базы данных"
docker-compose -f "$ymlBD" down
docker-compose -f "$ymlBD" up -d
# Ждем запуска БД
wait_for_container "$c_name_bd" 30
# Запуск Сервиса идентификации
echo "> Запуск Сервиса идентификации"
docker-compose -f "$ymlIds" up -d
# Перезагрузка брокеров сообщений
echo "> Перезагрузка основного и дополнительного Брокеров сообщений"
docker-compose -f "$ymlRab" down
docker-compose -f "$ymlRab" up -d
docker-compose -f "$ymlMB" down
docker-compose -f "$ymlMB" up -d
# Запуск Portal
echo "> Запуск Portal"
docker-compose -f "$yml" up -d
# Ожидание загрузки Portal
echo "Ожидание загрузки Portal..."
if wait_for_container "$c_name" 180; then
echo "Portal загружен, копируем файлы разработки"
# Даем дополнительное время на полный запуск Liferay
sleep 30
# Копирование разработки
echo "> Копирование разработки в контейнер"
docker exec "$c_name" sh -c "cp /mnt/liferay/deploy/* /opt/liferay/deploy/ 2>/dev/null || echo 'Нет файлов для копирования или директория не существует'"
else
echo "Ошибка: Portal не запустился за отведенное время"
exit 1
fi
echo "=== Скрипт успешно завершен ==="
Пример вывода лога:



Как описано выше, скрипт работает в связке со службой. Чтобы ее сделать, создаем в systemd юнит со следующем содержимым:
[Unit]
Description=Portal Restart Service
After=docker.service network.target
Requires=docker.service
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
RemainAfterExit=yes
User=root
Group=root
WorkingDirectory=/srv/portal
ExecStart=/srv/portal/restart-portal.sh
Restart=on-failure
#RestartSec=30s
# Настройки журналирования
StandardOutput=journal
StandardError=journal
SyslogIdentifier=restart-portal
# Таймауты (увеличьте при необходимости)
#TimeoutStartSec=1800
#TimeoutStopSec=300
# Переменные окружения (добавить при необходимости)
#Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
#Environment="DOCKER_HOST=unix:///var/run/docker.sock"
[Install]
WantedBy=multi-user.target
Alias=portal.service
Соответственно, исходя из настроек службы нам достаточно выполнить systemctl restart/start portal.service - и все . Выполнится бэкап метаданных, базы данных, перезапустятся все контейнеры, накатится разработка и все залогируется.
Мы рассмотрели дополнительные технические нюансы портала. Надеюсь, вам они помогут при настройке и развертывании. После того как вы развернете портал, возможно, потребуется установить плагины, виджеты, настроить CSS, об этом есть отдельная статья нашего разработчика Никиты Вершинина (Опыт внедрения Portal) и его же статья на Directum Awards 2026 про внедрение портала в нашей организации (Цифровое сердце СТАРКОВ Групп на Directum Portal).
Пишите в комментариях вопросы, буду рад обсудить!
Авторизуйтесь, чтобы написать комментарий