К содержанию
ActionPulse

Развёртывание через Docker Compose

Установка ActionPulse на одном сервере из поставляемого комплекта — проверка архива, окружение, миграции, порядок запуска сервисов, журналы и безопасная остановка.

Кому
Инженерам, которые ставят установку на одном сервере
Проверено
На этой странице

Так ActionPulse ставится на один сервер: вы получаете подписанный комплект, заполняете окружение, применяете миграции одноразовым контейнером и поднимаете сервисы. Всё — из каталога комплекта; репозиторий продукта для установки не нужен.

Страница описывает первую установку на чистую машину. Если у вас уже есть работающие тома PostgreSQL, ClickHouse или NATS, это не инструкция обновления: переход версий stateful-сервисов идёт отдельным поэтапным маршрутом.

Что нужно на сервере

Коротко: Linux amd64 или arm64, Docker Engine 24 или новее с Docker Compose v2 (docker compose version), открытые входящие порты 80 и 443, файл ключа лицензии от поставщика и публичный ключ вендора для офлайн-проверки. Стартовая конфигурация из документов продукта — 4 vCPU, 8 ГБ памяти, 50 ГБ диска.

Комплект и границы поставки

Комплект приходит из аутентифицированного канала поставки тремя файлами: архив actionpulse-onprem-<версия>.tgz, файл контрольной суммы .tgz.sha256 и подписанный .release-manifest.json. Внутри архива — единственный каталог actionpulse-onprem/:

actionpulse-onprem/
├── docker-compose.onprem.yml   # единственный compose, запускается из корня
├── .env.example                # шаблон конфигурации; версия и образы уже подставлены
├── preflight.sh                # проверка окружения до первого запуска
├── Caddyfile                   # обратный прокси
├── OPERATIONS.md               # полное руководство по эксплуатации
├── redis-users.acl.example     # шаблон ACL Redis
├── nats-server.conf.example    # шаблон конфигурации NATS
├── clickhouse/                 # pp-server.xml, pp-users.xml, cluster/
├── postgres/roles.sql          # роли PostgreSQL по сервисам
├── backup/                     # named collection для ClickHouse, шаблон и README
├── observability/              # prometheus.yml, alertmanager, grafana, filebeat
├── monitoring/prometheus/      # actionpulse.rules.yml — правила алертов
├── jaeger/, helm/actionpulse/ # трейсинг и чарт для Kubernetes
├── docs/                       # CONTRACTS.md, SECURITY_PRIVACY.md, openapi.yaml
├── empty/, VERSION
└── release/                    # images.lock, SBOM, provenance, отчёты сканера

Порядок установки

Шаг 1. Проверьте архив и распакуйте его

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

sha256sum -c actionpulse-onprem-vX.Y.Z.tgz.sha256
tar xzf actionpulse-onprem-vX.Y.Z.tgz
cd actionpulse-onprem

В release/images.json перечислены точные ссылки на образы в форме tag@sha256 и digest’ы по платформам, в release/sbom/ — CycloneDX и SPDX, в release/provenance/ — provenance. Содержимое release/trivy/ — сырые кандидаты сканера, а не список подтверждённых уязвимостей: каждый пункт требует отдельной оценки достижимости.

Шаг 2. Заполните окружение

install -m 0600 .env.example .env

Права 0600 обязательны: preflight.sh отказывается работать с файлом, у которого есть групповые или общие права, и с символической ссылкой. В шаблоне уже указаны точные ссылки на образы — не заменяйте их ссылками только по тегу.

Значения секретов не берите из примеров — генерируйте своё независимое:

openssl rand -base64 32   # по одному вызову на каждый отдельный секрет

Обязательный минимум перед первым запуском. Часть значений проверяет сам Compose, часть — preflight.sh, часть — сервисы при старте; практической разницы нет, без любого из них рабочей установки не получится.

Группа Переменные (по именам)
Домен и публичные origin’ы PP_DOMAIN, APP_BASE_URL, PUBLIC_INGEST_URL, ADMIN_APP_BASE_URL, CORS_ORIGINS
Ссылки на образы PP_INGEST_IMAGE_REF, PP_QUERY_IMAGE_REF, PP_WRITER_IMAGE_REF, PP_WORKER_IMAGE_REF, PP_BACKUP_IMAGE_REF, NATS_IMAGE_REF, CLICKHOUSE_IMAGE_REF
Секреты приложения JWT_SECRET, ALERT_WEBHOOK_ENC_KEY, IMPORT_ENC_KEY, IMPORT_HMAC_CURRENT_VERSION, IMPORT_HMAC_CURRENT_SECRET, IMPORT_FENCE_HMAC_CURRENT_VERSION, IMPORT_FENCE_HMAC_CURRENT_SECRET, IMPORT_PRIVACY_HMAC_CURRENT_VERSION, IMPORT_PRIVACY_HMAC_CURRENT_SECRET
PostgreSQL, Redis, NATS PG_PASSWORD, REDIS_URL, NATS_URL, REDIS_ACL_FILE, NATS_CONFIG_FILE
ClickHouse CH_DEFAULT_PASSWORD, CH_MIGRATE_PASSWORD, CH_WRITE_PASSWORD, CH_READ_PASSWORD, CH_BACKUP_PASSWORD, CH_CONSOLE_PASSWORD и полные CH_MIGRATE_DSN, CH_WRITE_DSN, CH_READ_DSN, CH_CONSOLE_DSN
Наблюдаемость GRAFANA_ADMIN_PASSWORD, ELASTIC_PASSWORD, KIBANA_SYSTEM_PASSWORD, ELASTICSEARCH_FILEBEAT_USERNAME, ELASTICSEARCH_FILEBEAT_PASSWORD, ELASTIC_CA_FILE, ELASTICSEARCH_CERT_FILE, ELASTICSEARCH_KEY_FILE, KIBANA_CERT_FILE, KIBANA_KEY_FILE
Лицензия LICENSE_PUBKEY — публичный ключ вендора для офлайн-проверки
Первый владелец BOOTSTRAP_EMAIL, BOOTSTRAP_PASSWORD — только до создания первого владельца
Подпись резервных копий BACKUP_MANIFEST_SIGNING_KEY, BACKUP_MANIFEST_PUBLIC_KEYS — требуются всегда, даже если профиль резервного копирования не включён

Необязательные переменные (журналирование, телеметрия, SMTP, GeoIP, пределы JetStream, отдельные учётные записи хранилищ на каждый сервис) имеют рабочие значения по умолчанию — полный перечень в Конфигурации.

Шаг 3. Подготовьте файлы секретов

ACL Redis, аутентифицированная конфигурация NATS и приватные ключи TLS для Elasticsearch и Kibana лежат отдельными файлами и монтируются как файловые секреты Docker.

install -d -m 0700 secrets
install -m 0600 redis-users.acl.example secrets/redis-users.acl
install -m 0600 nats-server.conf.example secrets/nats-server.conf

Шаблоны намеренно нерабочие: вместо паролей в них заглушки, и до вашей правки проверка их не пропустит. Заполните пароли пользователей, синхронизируйте их с REDIS_URL и NATS_URL, затем положите CA, сертификаты и приватные ключи по путям из ELASTIC_* и KIBANA_* — ключи с правами 0600. Подробности — в статье TLS и секреты.

Шаг 4. Проверьте конфигурацию до первого запуска

./preflight.sh .env
./preflight.sh --validate-acl-contracts secrets/redis-users.acl secrets/nats-server.conf
docker compose -f docker-compose.onprem.yml --env-file .env config --quiet

preflight.sh — это отказ по умолчанию, а не подсказка. Он проверяет, что .env — обычный файл с правами 0600 и ровно одной записью на ключ; что двенадцать обязательных секретов не короче 24 символов, без пробелов и не совпадают с известными заглушками; что все семь ссылок на образы имеют вид tag@sha256; что NATS_URL и REDIS_URL содержат учётные данные; что роли в DSN соответствуют назначению; что файлы ACL, конфигурации NATS и приватных ключей существуют, не являются ссылками и имеют права 0600; что в ACL Redis пользователь default выключен, а в конфигурации NATS включён JetStream. Успех — одна строка о том, что окружение, точные ссылки на образы и внешние файлы секретов проверку прошли. Второй вызов отдельно сверяет контракты доступов в ACL Redis и в учётных записях NATS.

Шаг 5. Поднимите зависимости, миграции и сервисы

На чистой машине достаточно одной команды — compose сам соблюдает порядок через зависимости и состояние здоровья:

docker compose -f docker-compose.onprem.yml --env-file .env up -d

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

docker compose -f docker-compose.onprem.yml --env-file .env up -d \
  postgres clickhouse redis nats

docker compose -f docker-compose.onprem.yml --env-file .env up \
  query-api-nats-bootstrap query-api-migrate

docker compose -f docker-compose.onprem.yml --env-file .env up -d

query-api-migrate применяет миграции PostgreSQL и ClickHouse: идемпотентно, под advisory-локами от гонок, и обязан завершиться успешно до старта долгоживущего API. query-api-nats-bootstrap идемпотентно создаёт и обновляет ровно три потока JetStream — EVENTS, REPLAY и IMPORT_FENCES — и обязан завершиться до ingest-api: приём событий в on-prem потоки не создаёт, он только читает их состояние и отказывается стартовать, если подготовка не выполнена.

Шаг 6. Проверьте работоспособность

PP_HOST=https://YOUR_ACTIONPULSE_HOST   # адрес установки — тот же, что PP_DOMAIN в .env

docker compose -f docker-compose.onprem.yml --env-file .env ps
curl -fsS "$PP_HOST/healthz"

В ps долгоживущие сервисы должны быть running или healthy, а одноразовые — exited (0): это query-api-nats-bootstrap, query-api-migrate, elastic-security-setup и backup-init. Ответ /healthz{"status":"ok"}; на стороне query-api он проверяет доступность PostgreSQL, на стороне ingest-api — соединение с NATS.

Шаг 7. Лицензия, первая организация и первый проект

Первая организация и её владелец создаются автоматически при первом старте HTTP-сервиса — из BOOTSTRAP_EMAIL и BOOTSTRAP_PASSWORD. Отдельной команды для этого нет, и повторно шаг не выполняется: если в базе уже есть пользователи, он пропускается.

Войдите этим владельцем и загрузите лицензию. Пароль вводится интерактивно, тело запроса идёт через канал — так секрет не попадёт ни в историю команд, ни в список процессов:

PP_HOST=https://YOUR_ACTIONPULSE_HOST
read -rp  "e-mail владельца: " PP_OWNER_EMAIL
read -rsp "пароль владельца: " PP_OWNER_PASSWORD; echo
export PP_OWNER_EMAIL PP_OWNER_PASSWORD

TOKEN=$(python3 -c 'import json,os;print(json.dumps({
      "email": os.environ["PP_OWNER_EMAIL"],
      "password": os.environ["PP_OWNER_PASSWORD"]}))' |
  curl -fsS -X POST "$PP_HOST/api/v1/auth/login" \
    -H "Content-Type: application/json" --data-binary @- |
  python3 -c 'import json,sys;print(json.load(sys.stdin)["access_token"])')
unset PP_OWNER_PASSWORD

python3 -c 'import json,sys;print(json.dumps({"key":sys.stdin.read().strip()}))' \
    < license.key |
  curl -fsS -X PUT "$PP_HOST/api/v1/license" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" --data-binary @-

curl -fsS "$PP_HOST/api/v1/license" -H "Authorization: Bearer $TOKEN"

Ответ GET содержит valid, read_only, reason, срок действия и список включённых возможностей. Без действующей лицензии запись отвечает 403 license_invalid, а чтение работает; после истечения срока действует семидневный период только для чтения. Замена ключа через PUT подхватывается всеми сервисами примерно за секунду, перезапуск не нужен.

Затем создайте проект и ключ приёма — методы описаны в разделах Проекты и Ключи API — и вставьте сниппет сбора на сайт по инструкции Тег скрипта.

Состав сервисов и порядок запуска

Сервис Что делает Порт внутри сети
postgres учётные записи, организации, проекты, отчёты, импорты, задания приватности 5432
clickhouse события и аналитические таблицы 8123, 9000, метрики 9363
redis счётчики, дедупликация, кэш 6379
nats транспорт событий JetStream 4222, мониторинг 8222
query-api-nats-bootstrap одноразовый: создаёт потоки EVENTS, REPLAY, IMPORT_FENCES
query-api-migrate одноразовый: миграции PostgreSQL и ClickHouse
query-api основной API и обслуживание кабинета 8080
ingest-api приём событий 8081
ch-writer запись событий из JetStream в ClickHouse
worker письма, алерты, импорты, удаление данных субъекта
caddy обратный прокси; единственный публикует 80 и 443 80, 443

Вместе с ними обычной командой up -d поднимается внутренний стек наблюдаемости: prometheus, alertmanager, grafana, node-exporter, postgres-exporter, redis-exporter, nats-exporter, elasticsearch, одноразовый elastic-security-setup, kibana, filebeat, filebeat-metrics и jaeger. Отдельного профиля у них нет, а их пароли и файлы TLS обязательны — то есть стек не опционален. Резервное копирование, наоборот, включается явно профилем:

docker compose -f docker-compose.onprem.yml --env-file .env --profile backup up -d

Порядок запуска задан зависимостями и не требует вашего вмешательства: сначала хранилища до состояния healthy, затем оба одноразовых контейнера, затем query-api и ingest-api, после того как query-api стал healthych-writer и worker, и последним caddy.

Сети разделены: app с исходящим доступом (SMTP, webhook’и, телеметрия по согласию), внутренние data и observability без маршрута наружу, и edge, где у Caddy статический адрес. Именно этому адресу и только ему доверяются заголовки с адресом клиента.

Журналы, остановка и удаление

Журналы сервисов читаются штатно:

docker compose -f docker-compose.onprem.yml --env-file .env logs -f query-api
docker compose -f docker-compose.onprem.yml --env-file .env logs -f ch-writer

Метрики приложения слушают порт 9090 и наружу не публикуются, поэтому смотреть их нужно изнутри контейнера:

docker compose -f docker-compose.onprem.yml --env-file .env exec query-api \
  wget -qO- http://localhost:9090/metrics | grep pp_

Логи всех контейнеров собираются в Elasticsearch и доступны через Kibana, метрики и дашборды — через Grafana; публикуемого порта нет ни у одного из этих интерфейсов.

Остановка и удаление:

# Остановить сервисы, оставив контейнеры и тома
docker compose -f docker-compose.onprem.yml --env-file .env stop

# Удалить контейнеры и сети; именованные тома остаются на месте
docker compose -f docker-compose.onprem.yml --env-file .env down

down без дополнительных флагов данные не удаляет. Всё состояние лежит в именованных томах проекта actionpulse-onprem: pg-data, ch-data, redis-data, nats-data, caddy-data, caddy-config, import-uploads, prometheus-data, alertmanager-data, grafana-data, es-data, kibana-data, filebeat-data, backup-metrics, backup-state. После down установку поднимают снова тем же up -d с тем же .env.

Что проверить и частые ошибки

  • preflight.sh отказал — читайте его сообщение буквально. Чаще всего это оставленное значение-заглушка, отсутствующие права 0600 у .env или у приватного ключа, дублирующаяся строка переменной либо ссылка на образ без @sha256.
  • ingest-api не стартует — скорее всего не завершился query-api-nats-bootstrap. Проверьте его журнал и PP_NATS_BOOTSTRAP_URL: эту одноразовую учётную запись нельзя выдавать долгоживущим сервисам, и проверка окружения такое отклоняет.
  • 403 license_invalid на запись — лицензия не загружена или истекла, проверьте GET /api/v1/license. 401 — истёк access-токен (15 минут), обновите его через /api/v1/auth/refresh.
  • События не доходят до отчётов — смотрите журнал ch-writer и метрику pp_writer_lag.
  • Контейнер перезапускается по кругу — начинайте с logs этого сервиса. У Filebeat это обычно недоступный Elasticsearch, неверный CA или неверные учётные данные: проверка доставки специально сделана так, чтобы поломка была видна как unhealthy, а не как тишина в Kibana.
  • Веб-интерфейс не отдаётся с этого адреса — статика кабинета в комплект не входит, обратный прокси проксирует остальные пути на query-api. Всё, что описано выше, выполняется через API; способ раздачи статики согласуйте с поставщиком.

Дальше: проверки состояния и наблюдаемость и конфигурация целиком.