Managed-версия Elido работает на инфраструктуре в регионе ЕС с конфигурацией «privacy-by-default». Для большинства пользователей этого достаточно. Для некоторых - нет.
Если ваша команда безопасности требует, чтобы данные о назначении коротких ссылок и события кликов никогда не покидали определенный дата-центр, если ваша политика аудита требует полного контроля над сервером базы данных или если вы встраиваете Elido во внутреннюю платформу, которая должна работать в изолированной сети (air-gapped), путь self-hosting существует именно для этих целей.
Это руководство описывает процесс развертывания Elido на k3s - минималистичном дистрибутиве Kubernetes промышленного уровня, который отлично работает на одной VM или в небольшом HA-кластере. К концу этого руководства у вас будут запущены все сервисы, настроен TLS, подключено резервное копирование и определен повторяемый путь обновления. Руководство предполагает, что вам нужна работающая система, а не учебник по концепциям Kubernetes - пояснения будут краткими, а операционные шаги - четкими.
Почему стоит выбрать self-hosting#
Прежде чем переходить к плейбуку, стоит точно определить все «за» и «против». Self-hosting не является автоматически лучшим вариантом - он перекладывает операционные риски с вендора на вашу собственную команду.
Резидентность данных и комплаенс. Если ваши рамки соответствия (ISO 27001, внутренняя политика классификации данных или договорное обязательство о резидентности данных) требуют, чтобы метаданные ссылок и события аналитики оставались на инфраструктуре, которую вы контролируете напрямую, managed SaaS не сможет удовлетворить эти требования независимо от того, где он физически запущен. Self-hosted развертывание на VM в регионе ЕС, которой вы владеете, решает эту задачу. Подробности того, как архитектура Elido соотносится с общими стандартами, см. в обзоре комплаенса.
Предсказуемость затрат при масштабировании. Тарифные планы managed-версии зависят от количества активных ссылок и объема кликов. Выше определенного порога - обычно это более нескольких миллионов кликов в месяц - стоимость события в managed-планах превышает затраты на инфраструктуру для самостоятельного запуска аналогичной нагрузки в StatefulSet. Точка пересечения зависит от профиля вашего трафика, но она существует.
Контроль аудита. Некоторым организациям требуется доступ к сырым таблицам Postgres и данным событий кликов в ClickHouse для сбора доказательств, юридического удержания (legal hold) или интеграции с SIEM. API Elido предоставляет эндпоинты логов аудита и экспорт доказательств, но прямой доступ к базе данных возможен только в self-hosted развертываниях.
Чем вы жертвуете. k3s требует, чтобы кто-то отвечал за обновления, состояние узлов, проверку резервных копий и реагирование на инциденты. Если у вашей команды нет опыта эксплуатации Kubernetes, managed-версия в хостинг-локации, соответствующей требованиям комплаенса, почти наверняка будет правильным выбором. В последнем разделе этой статьи приведено более прямое сравнение того, когда self-hosting - неудачное решение.
Предварительные требования#
- Кластер k3s. Одного узла с 4 vCPU и 8 ГБ ОЗУ достаточно для легких нагрузок. Для HA рекомендуется топология минимум из трех узлов control-plane и двух или более рабочих узлов. Встроенный etcd в k3s обеспечивает HA для control-plane; HA для уровня данных обрабатывается Patroni (Postgres) внутри Helm-чарта.
kubectl, настроенный на ваш кластер.- Helm 3.14 или новее.
- Домен, которым вы управляете, с возможностью создания DNS-записей. IP-адрес ингресса k3s должен быть доступен по портам 80 и 443 из интернета для успешного прохождения ACME-вызовов Let's Encrypt.
- Если важен комплаенс - VM в регионе ЕС. Hetzner (Фалькенштайн, Хельсинки, Нюрнберг) и OVH (Гравелин, Рубе) - два провайдера, которых использует собственная edge-инфраструктура Elido.
На чистой Hetzner CX32 (4 vCPU / 8 ГБ) под управлением Ubuntu 24.04 k3s устанавливается примерно за 30 секунд:
curl -sfL https://get.k3s.io | sh -
# Скопируйте kubeconfig на локальную машину:
scp root@<your-vm>:/etc/rancher/k3s/k3s.yaml ~/.kube/elido-self-host.yaml
export KUBECONFIG=~/.kube/elido-self-host.yaml
kubectl get nodes # должен показать Ready
Клонируйте репозиторий Elido, чтобы получить Helm-чарт (чарт находится в deploy/helm/elido/ - отдельного публичного Helm-репозитория нет):
git clone https://github.com/elidoapp/elido.git
cd elido
Быстрый старт#
Пресет для self-hosting (deploy/helm/elido/values-selfhost.yaml) - рекомендуемая отправная точка для одноузлового развертывания k3s. Сначала создайте секреты, затем установите чарт:
# 1. Создайте все необходимые Kubernetes Secrets за один раз (идемпотентно)
./scripts/bootstrap-secrets.sh elido
# 2. Установите, используя пресет self-host
helm -n elido upgrade --install elido ./deploy/helm/elido \
-f ./deploy/helm/elido/values-selfhost.yaml \
--set ingress.hosts.redirect[0]=r.example.com \
--set ingress.hosts.api=api.example.com \
--set ingress.hosts.dashboard=app.example.com \
--set image.tag=$(git rev-parse --short HEAD) \
--create-namespace \
--wait --timeout 10m
Для пользовательских переопределений (количество реплик для продакшена, внешние управляемые сервисы и т. д.) скопируйте values-selfhost.yaml в локальный файл my-values.yaml, отредактируйте его и передайте через флаг -f. Ключевые разделы верхнего уровня: ingress.hosts.* (ваши хосты), image.registry/image.tag и блоки resources для каждого сервиса. Не используйте global.domain - чарт использует ingress.hosts.* для конфигурации доменов.
Флаг --wait блокирует выполнение, пока все Deployment и StatefulSet не перейдут в состояние готовности. На свежем узле без кэшированных образов это займет 5–8 минут при канале 1 Гбит/с.
Проверьте, что всё запустилось:
kubectl -n elido get pods
kubectl -n elido get svc
Полный список параметров см. в справочнике значений чарта: helm show values ./deploy/helm/elido. Файл deploy/helm/elido/README.md в репозитории является авторитетным источником информации по конкретным именам полей и использованию скрипта bootstrap.
Архитектура, которую вы развертываете#
Архитектура Elido разделена по бюджету задержки. Понимание этого разделения поможет вам принимать обоснованные решения по распределению ресурсов.
Горячий путь: edge-redirect#
edge-redirect - единственный сервис на синхронном пути запроса перенаправления. Он написан на Go с использованием fasthttp и имеет жесткий бюджет задержки: p50 5 мс, p95 15 мс при попадании в кэш. Сервис поддерживает двухуровневый кэш: внутрипроцессный LRU (L1), за которым стоит Redis Cluster (L2). При промахе кэша он выполняет gRPC-вызов к api-core. События кликов отправляются в Redpanda по принципу «выстрелил и забыл» - ответ о перенаправлении никогда не задерживается в ожидании завершения записи события.
Helm-чарт развертывает edge-redirect как Deployment с HorizontalPodAutoscaler. В однорегиональной self-hosted конфигурации две реплики - разумная отправная точка. L2 кэш Redis является общим для всех реплик, поэтому прогрев кэша после rolling update происходит быстро.
Теплый путь: API#
Пять сервисов обрабатывают синхронные запросы API и бизнес-логику:
api-core- Go + chi, REST и gRPC. Источник истины для ссылок, рабочих пространств, членства, кастомных доменов, событий аудита.api-bff- слой BFF на Node/Hono для веб-дашборда и мобильных клиентов. Агрегирует данные изapi-coreиanalytics-api.analytics-api- Go, запросы к ClickHouse. Обслуживает дашборды аналитики.billing- Go + chi + sqlc. Движок НДС ЕС, интеграция платежей LiqPay, генерация инвойсов.search- Go, проксирует поисковые запросы ссылок в Meilisearch.
Аутентификация обрабатывается Ory Kratos (личности, сессии, верификация email) и Ory Hydra (токены OAuth2/OIDC для сторонних интеграций и расширения браузера). Оба развернуты в чарте как Deployments.
Холодный путь: асинхронные воркеры#
Шесть сервисов потребляют события из топиков Redpanda и не имеют ограничений по времени ответа:
click-ingester- потребляет события кликов, записывает в ClickHouse.webhook-dispatcher- рассылает подписанные полезные нагрузки вебхуков на эндпоинты клиентов.notification- уведомления по email и внутри приложения (события аккаунта, алерты ссылок).url-scanner- сканирует целевые URL через Google Safe Browsing, PhishTank, SURBL.metadata-fetcher- извлекает метаданные Open Graph для превью ссылок.domain-manager- верификация DNS и настройка on-demand TLS в Caddy для кастомных доменов.
Уровень данных: StatefulSets#
Шесть систем хранения данных поддерживают работу вышеуказанных сервисов:
| Система | Роль | Ресурс чарта |
|---|---|---|
| Postgres (Patroni) | Источник истины для ссылок, пользователей, биллинга | StatefulSet, 3 реплики в режиме HA |
| Redis Cluster | Горячий кэш ссылок | StatefulSet |
| ClickHouse | Хранилище событий кликов и аналитические запросы | StatefulSet |
| Redpanda | Шина событий между сервисами | StatefulSet |
| MinIO | Пользовательские ассеты (QR-коды, экспорты) | StatefulSet |
| Meilisearch | Поиск по ссылкам внутри приложения | StatefulSet |
В одноузловом развертывании каждый StatefulSet запускает одну реплику. В режиме HA (включается установкой ha.enabled: true в values) Postgres масштабируется до трех реплик под управлением Patroni, Redis - до шести (три мастера, три реплики), а Redpanda - до трех брокеров.
Caddy ingress#
Caddy отвечает за терминацию TLS и on-demand TLS для кастомных доменов клиентов. Когда оператор рабочего пространства добавляет кастомный домен через дашборд (или API), domain-manager проверяет владение DNS, регистрирует хост как разрешенный, а Caddy выпускает сертификат Let's Encrypt при первом HTTP-запросе к этому хосту. Чарт развертывает Caddy как DaemonSet на узлах с меткой elido.app/ingress=true или как Deployment в стандартной одноузловой конфигурации.
Процесс для кастомного домена клиента go.company.com:
- Оператор создает CNAME:
go.company.com → <your-k3s-ingress-ip>(или A-запись, указывающую напрямую). - Оператор вызывает
POST /v1/workspaces/{id}/domainsили нажимает «Add domain» в дашборде. domain-managerзапрашивает DNS, чтобы подтвердить, что CNAME указывает на IP ингресса.- Caddy получает первый HTTPS-запрос, проверяет разрешенный список, выданный Elido, и запрашивает сертификат у Let's Encrypt через ACME HTTP-01.
- Сертификат сохраняется в томе состояния Caddy и обновляется автоматически.
Начальная загрузка (bootstrap)#
После завершения helm install API администрирования станет доступен, но учетной записи пользователя еще не будет.
Создание администратора#
# Пробросьте порт к админ-интерфейсу api-core (по умолчанию не экспонируется)
kubectl -n elido port-forward svc/api-core 8081:8081 &
# Создайте первую учетную запись администратора
curl -X POST http://localhost:8081/internal/admin/bootstrap \
-H 'Content-Type: application/json' \
-d '{
"email": "[email protected]",
"password": "your-secure-password"
}'
Эндпоинт bootstrap можно вызвать только из внутрикластерной сети, и он отключается после первого успешного вызова. Как только учетная запись администратора будет создана, войдите через дашборд на вашем настроенном домене.
Создание первого рабочего пространства#
После входа дашборд предложит создать рабочее пространство при первом использовании. Также это можно сделать через API:
curl -X POST https://links.example.com/v1/workspaces \
-H "Authorization: Bearer <your-session-token>" \
-H 'Content-Type: application/json' \
-d '{"name": "My Workspace", "slug": "my-workspace"}'
Добавление кастомного домена#
В настройках рабочего пространства перейдите в раздел Domains и добавьте ваш домен для коротких ссылок. Настройте DNS-запись (CNAME или A) перед нажатием кнопки «Verify» - domain-manager немедленно проверяет DNS и возвращает ошибку, если запись еще не распространилась. После успешной верификации Caddy выпустит сертификат при первом запросе на редирект на этот домен. Выпуск сертификата обычно занимает 10–30 секунд при первом запросе.
Проверьте, что TLS работает:
curl -I https://go.company.com/healthz
# Ожидается: HTTP/2 200
Эксплуатационные вопросы#
Резервное копирование#
Postgres. Чарт включает CronJob, который запускает pg_dump по настраиваемому расписанию (по умолчанию: ежедневно в 02:00 UTC) и загружает сжатый дамп в бакет MinIO, указанный в values. Для HA-развертываний pg_dump запускается на реплике, чтобы не нагружать основной узел. Включите его в values:
backups:
postgres:
enabled: true
schedule: "0 2 * * *"
retentionDays: 30
s3Bucket: "elido-backups"
Для восстановления на определенный момент времени (PITR) включите архивацию WAL (postgres.walArchive.enabled: true), которая непрерывно отправляет сегменты WAL в MinIO. Сочетание ежедневных pg_dump и архивации WAL обеспечивает RPO менее 5 минут.
ClickHouse. ClickHouse хранит события кликов, которые добавляются только в конец и могут быть повторно воспроизведены из Redpanda, если позволяет срок хранения. Чарт включает CronJob, который выполняет SQL-команду BACKUP TABLE в MinIO, используя нативный интерфейс бэкапов ClickHouse. Включите с помощью backups.clickhouse.enabled: true.
Redpanda. Redpanda - это потоковая шина, а не база данных. Срок хранения (retention) основан на времени (по умолчанию: 7 дней) и настраивается в values в разделе redpanda.retention. Если ваш click-ingester отстанет больше чем на окно хранения, события будут потеряны. Мониторьте отставание (lag) групп потребителей - чарт включает алерт Prometheus (ElidoRedpandaConsumerLag), который срабатывает, если любая группа потребителей отстает более чем на 100 000 сообщений.
Проверка бэкапов. Бэкап, который никогда не тестировался - это бэкап, на который нельзя полагаться. Проводите тренировочное восстановление в отдельный namespace хотя бы раз в квартал:
kubectl -n elido-restore create ns elido-restore || true
helm install elido-restore ./deploy/helm/elido \
--namespace elido-restore \
--values ./my-values.yaml \
--set restore.fromBackup=true \
--set restore.backupDate="2026-05-01"
Мониторинг#
Чарт включает ServiceMonitor для каждого сервиса, если в кластере присутствует Prometheus Operator CRD. Ключевые метрики для алертинга:
monitoring:
prometheus:
enabled: true # требуется prometheus-operator в кластере
grafana:
enabled: true # развертывает встроенные дашборды
adminPassword: "change-me"
Встроенные дашборды Grafana охватывают:
- Задержка p50/p95
edge-redirect, коэффициент попадания в кэш, объем кликов - Частота запросов
api-core, частота ошибок, задержка gRPC - Отставание (lag) потребителей
click-ingesterпо каждому разделу Redpanda - Отставание репликации Postgres master/replica (Patroni)
- Частота вытеснения (eviction) и давление на память в Redis
Если у вас уже есть стек Prometheus в кластере, установите monitoring.prometheus.install: false и направьте ваш стек на ServiceMonitors.
Patroni HA для Postgres#
В режиме HA Patroni управляет выбором лидера и переключением при сбое (failover). Чарт настраивает Patroni на использование распределенного хранилища конфигураций kubernetes (использует Kubernetes ConfigMaps, отдельный etcd не требуется). Переключение обычно завершается за 15–30 секунд. Во время failover в api-core и billing могут возникать кратковременные ошибки записи; оба сервиса повторяют попытки при ошибках 5xx с экспоненциальной задержкой.
Для проверки состояния кластера Patroni:
kubectl -n elido exec -it postgres-0 -- patronictl -c /etc/patroni/config.yml list
Процесс обновления#
Elido следует семантическому версионированию. Патч-релизы содержат исправления ошибок и не требуют ручных шагов миграции. Минорные и мажорные релизы могут включать миграции схемы базы данных, которые встроены в бинарные файлы сервисов и запускаются автоматически при старте пода через раннер миграций.
Рекомендуемый путь обновления:
# 1. Получите последнюю версию чарта (git pull в клонированном репозитории)
git pull origin main
# 2. Ознакомьтесь с примечаниями к обновлению
helm show chart ./deploy/helm/elido | grep -A10 'version\|appVersion'
# 3. Проверьте изменения в values (требуется плагин helm-diff)
helm diff upgrade elido ./deploy/helm/elido \
--namespace elido \
--values ./my-values.yaml
# 4. Обновите
helm upgrade elido ./deploy/helm/elido \
--namespace elido \
--values ./my-values.yaml \
--wait --timeout 10m
Обработка миграций. Каждый бинарный файл сервиса на Go содержит свои миграции схемы (используя golang-migrate с встроенной директорией миграций). При запуске пода бинарный файл выполняет migrate up перед началом обработки запросов. При rolling update новый под применяет все ожидающие миграции до того, как старый под будет завершен. Миграции обязаны быть обратно совместимыми с предыдущей минорной версией - новая колонка, добавленная в v1.5, должна допускать значение NULL или иметь значение по умолчанию, чтобы бинарный файл v1.4, работающий параллельно во время обновления, не выдавал ошибку.
Blue/green для мажорных обновлений. Для обновлений мажорных версий, где изменения схемы не являются обратно совместимыми, используйте стратегию blue/green: установите новую версию с отдельным именем релиза Helm в промежуточном (staging) namespace, перенесите туда снапшот рабочей базы данных, проведите smoke-тестирование, а затем выполните переключение DNS на уровне ингресса. После проверки «зеленого» стека удалите «синий» релиз.
# Установка зеленого стека
helm install elido-green ./deploy/helm/elido \
--namespace elido-green \
--create-namespace \
--values ./green-values.yaml
# После верификации переключите DNS у вашего регистратора
# Затем выведите синий стек из эксплуатации
helm uninstall elido --namespace elido
Когда не стоит выбирать self-hosting#
Self-hosting - правильный выбор в довольно узком ряду обстоятельств. Чаще, чем вы могли бы ожидать, это ошибочное решение.
Ваша нагрузка невелика. Если вы создаете менее 100 000 ссылок в месяц и у вас нет строгих требований к резидентности данных, managed Elido обойдется дешевле и по деньгам, и по времени на эксплуатацию, чем запуск k3s. Хостинг включает бэкапы, обновления и дежурства для инфраструктуры, которой иначе владели бы вы.
У вас нет опыта эксплуатации Kubernetes. k3s минималистичен, но Kubernetes сложен. Если в вашей команде никто не эксплуатировал StatefulSets, не занимался бэкапом/восстановлением etcd или не отлаживал CrashLoopBackOff в Patroni в 2 часа ночи, self-hosting добавляет категорию операционных рисков, которую экономия на инфраструктуре не покроет.
Ваше требование комплаенса - резидентность в ЕС, а не отдельный инстанс. Резидентность данных в ЕС означает, что данные хранятся и обрабатываются в ЕС. Managed-инфраструктура Elido работает в Hetzner FRA и OVH GRA, что удовлетворяет требованиям статьи 44 GDPR без трансграничной передачи данных. Если фактическое требование вашего отдела комплаенса - «данные в ЕС», managed-продукт уже удовлетворяет его без self-hosted развертывания - см. страницу с ценами, чтобы узнать о функциях комплаенса, доступных в каждом плане.
Вам нужна производительность multi-region edge. Managed-сервис запускает edge-узлы (POPs) во Франкфурте, Эшберне и Сингапуре, причем горячий путь edge-redirect развернут в каждом из них. Однорегиональный self-hosted кластер k3s ограничен географическим расстоянием от VM до конечного пользователя. Multi-region self-hosting возможен, но он значительно увеличивает операционную нагрузку - это совсем другая задача, отличная от того, что описано в этом руководстве.
Вопросы по значениям Helm-чарта, требованиям к ресурсам StatefulSet для конкретных объемов трафика или путям обновления для конкретной версии следует задавать на доске обсуждений GitHub для self-hosted редакции. По вопросам комплаенса - в частности, что дает self-hosted конфигурация помимо резидентности в ЕС - страница комплаенса подробно описывает лог аудита, экспорт доказательств и средства контроля RBAC.
Похожие статьи в блоге#
- Достижение p95 < 15 мс для редиректов из FRA, ASH и SGP
- Почему мы используем ClickHouse для аналитики кликов (а не Postgres)
- Резидентность данных в ЕС для маркетинговых инструментов: что на самом деле спрашивает ваш DPO
- SCIM и SSO для маркетинговых инструментов: что на самом деле спрашивает корпоративный ИТ-отдел
Попробуйте Elido
Вставьте URL - получите короткую ссылку
Без регистрации. Ссылка живёт 30 дней. Зарегистрируйтесь, чтобы оставить её навсегда.
Бесплатно, без регистрации · 2 в день