Elido
13 хв читанняІнженерія
Ключова

Self-hosting Elido на k3s - повний посібник

Покроковий посібник із розгортання повного стека Elido у кластері k3s: налаштування Helm, 14 сервісів, шар даних у вигляді StatefulSets, TLS за запитом від Caddy, резервне копіювання та стратегія оновлення.

Marius Voß
DevRel · edge infra
Architecture diagram of Elido on k3s: edge-redirect and api-core Deployments in front, StatefulSets for Postgres, Redis, ClickHouse, Redpanda, MinIO, and Meilisearch behind, Caddy Ingress handling on-demand TLS at the top

Керована версія Elido працює на інфраструктурі в регіоні ЄС із конфігурацією, що передбачає приватність за замовчуванням. Для більшості користувачів цього цілком достатньо. Але для декого - ні.

Якщо ваша команда безпеки вимагає, щоб дані про призначення коротких посилань і події кліків ніколи не залишали певного дата-центру, якщо ваша політика аудиту вимагає повного контролю над сервером бази даних, або якщо ви вбудовуєте Elido у внутрішню платформу, яка має працювати ізольовано, шлях self-hosting існує саме для цього.

Цей посібник проведе вас через процес розгортання Elido на k3s, дистрибутиві Kubernetes промислового рівня, який комфортно працює на одній віртуальній машині або невеликому HA-кластері. До кінця ви матимете всі запущені сервіси, налаштований TLS, підключене резервне копіювання та відпрацьований шлях оновлення. Посібник передбачає, що вам потрібна працююча система, а не підручник з концепцій Kubernetes - пояснення стислі, а кроки - конкретні.

Навіщо хостити самостійно#

Перш ніж перейти до інструкцій, варто точно визначити компроміси. Self-hosting не стає автоматично кращим - він переносить операційні ризики з постачальника на вашу власну команду.

Резиденція даних і комплаєнс. Якщо ваша структура комплаєнсу (сфера ISO 27001, внутрішня політика класифікації даних або пункт договору про резиденцію даних) вимагає, щоб метадані посилань і події аналітики залишалися на інфраструктурі, яку ви безпосередньо контролюєте, керований SaaS не може задовольнити цю вимогу незалежно від того, де він фізично працює. Self-hosted розгортання на віртуальній машині в регіоні ЄС, якою ви володієте, вирішує це питання. Дивіться огляд комплаєнсу для отримання детальної інформації про те, як архітектура Elido узгоджується з поширеними стандартами.

Прогнозованість витрат при масштабуванні. Ціна керованих планів залежить від кількості активних посилань і обсягу кліків. Вище певного порогу - зазвичай понад кілька мільйонів кліків на місяць - вартість одного івенту в керованих планах перевищує витрати на інфраструктуру для самостійної підтримки робочого навантаження StatefulSet. Точка перетину залежить від структури вашого трафіку, але вона існує.

Контроль аудиту. Деяким організаціям потрібен доступ до сирих таблиць Postgres і даних про кліки у ClickHouse для підготовки доказів, юридичного утримання або інтеграції з SIEM. API Elido надає кінцеві точки журналів аудиту та експорт доказів, але прямий доступ до бази даних доступний лише в self-hosted розгортаннях.

Чим ви жертвуєте. k3s вимагає, щоб хтось відповідав за оновлення, стан вузлів, перевірку резервних копій та реагування на інциденти. Якщо ваша команда не має досвіду експлуатації Kubernetes, керована версія у відповідному хостинг-локації майже напевно буде правильним вибором. Дивіться останній розділ цього допису для більш детального розгляду того, коли self-hosting є невдалим рішенням.

Попередні вимоги#

  • Кластер k3s. Один вузол із 4 vCPU та 8 ГБ RAM впорається з легкими навантаженнями. Для HA рекомендується мінімум три вузли керування (control-plane) та два або більше робочих вузлів. Вбудований etcd у k3s забезпечує HA контрольного рівня; HA рівня даних забезпечується Patroni (Postgres) всередині Helm-чарту.
  • kubectl, налаштований на ваш кластер.
  • Helm 3.14 або новішої версії.
  • Домен під вашим контролем із можливістю створення DNS-записів. IP-адреса ingress k3s повинна бути доступна з інтернету через порти 80 і 443 для успішного проходження перевірок Let's Encrypt ACME.
  • Якщо ви керуєтеся вимогами комплаєнсу - віртуальна машина в регіоні ЄС. Hetzner (Фалькенштайн, Гельсінкі, Нюрнберг) та OVH (Гравлін, Рубе) - це два провайдери, яких використовує власна крайова інфраструктура 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 блокує виконання до того часу, поки всі Deployments та StatefulSets не перейдуть у стан готовності. На новому вузлі без кешованих образів очікуйте 5–8 хвилин при каналі 1 Гбіт/с.

Перевірте, чи все запустилося:

kubectl -n elido get pods
kubectl -n elido get svc

Перегляньте довідку щодо значень чарту за допомогою helm show values ./deploy/helm/elido. Файл deploy/helm/elido/README.md у репозиторії є авторитетним джерелом для конкретних назв полів та використання скрипта ініціалізації.

Процес розгортання Helm для Elido: bootstrap-secrets створює Kubernetes Secrets, helm upgrade install рендерить чарт із значеннями для self-host, маніфести застосовуються до API-сервера k3s, кластер узгоджує 14 Deployments та 6 StatefulSets, кожен pod запускає migrate up при старті, а helm wait блокується до готовності всіх podів

Архітектура, яку ви розгортаєте#

Архітектура Elido розділена за бюджетом затримки. Розуміння цього поділу допоможе вам приймати обґрунтовані рішення щодо розподілу ресурсів.

Топологія кластера k3s для Elido: Caddy ingress зверху, Deployment гарячого шляху edge-redirect та Deployments API теплого шляху під ним, Deployments воркерів холодного шляху збоку, і шар даних у вигляді StatefulSets (Postgres, Redis, ClickHouse, Redpanda, MinIO, Meilisearch) знизу

Гарячий шлях: 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 є спільним для всіх реплік, тому прогрів кешу відбувається швидко після оновлення.

Теплий шлях: 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 (ідентичність, сесії, перевірка пошти) та Ory Hydra (токени OAuth2/OIDC для сторонніх інтеграцій та розширення браузера). Обидва розгортаються як Deployments у чарті.

Холодний шлях: асинхронні воркери#

Шість сервісів споживають події з топіків Redpanda і не мають обмежень за часом відповіді:

  • click-ingester - споживає події кліків, записує у ClickHouse.
  • webhook-dispatcher - розсилає підписані дані вебхуків на кінцеві точки клієнтів.
  • notification - електронна пошта та сповіщення в додатку (події облікового запису, оповіщення про посилання).
  • url-scanner - сканує URL-адреси призначення через Google Safe Browsing, PhishTank, SURBL.
  • metadata-fetcher - отримує метадані Open Graph для прев'ю посилань.
  • domain-manager - перевірка DNS та надання 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 у значеннях) Postgres масштабується до трьох реплік під управлінням Patroni, Redis - до шести (три основні, три репліки), а Redpanda - до трьох брокерів.

Підключення шару даних для self-hosted Elido: edge-redirect зчитує кеш L2 з Redis, api-core використовує Postgres як джерело істини та відправляє події в Redpanda, click-ingester споживає Redpanda та записує в ClickHouse, analytics-api запитує ClickHouse, search запитує Meilisearch, а api-core зберігає ресурси в MinIO

Caddy ingress#

Caddy відповідає за термінацію TLS та надання TLS за запитом для власних доменів орендарів. Коли оператор робочого простору додає власний домен через панель керування (або API), domain-manager перевіряє право власності через DNS, реєструє хост як дозволений, а Caddy створює сертифікат Let's Encrypt при першому HTTP-запиті до цього хоста. Чарт розгортає Caddy як DaemonSet на вузлах із міткою elido.app/ingress=true, або як Deployment у стандартному однонодовому налаштуванні.

Процес для власного домену орендаря go.company.com:

  1. Оператор створює CNAME: go.company.com → <ваш-ip-ingress-k3s> (або A-запис, що вказує безпосередньо).
  2. Оператор викликає POST /v1/workspaces/{id}/domains або натискає «Додати домен» у панелі.
  3. domain-manager перевіряє DNS, щоб підтвердити, що CNAME вказує на IP-адресу ingress.
  4. Caddy отримує перший HTTPS-запит, перевіряє список дозволених доменів, виданий Elido, і запитує сертифікат у Let's Encrypt через ACME HTTP-01.
  5. Сертифікат зберігається в томі стану Caddy і оновлюється автоматично.

Перший запуск та ініціалізація#

Після завершення 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 <ваш-сесійний-токен>" \
  -H 'Content-Type: application/json' \
  -d '{"name": "My Workspace", "slug": "my-workspace"}'

Додавання власного домену#

У налаштуваннях робочого простору перейдіть до розділу «Домени» та додайте свій домен для коротких посилань. Встановіть DNS-запис (CNAME або A) перед тим, як натиснути «Перевірити» - 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, вказаний у значеннях. У HA-розгортаннях pg_dump запускається на репліці, щоб не впливати на основну базу. Активуйте це у values:

backups:
  postgres:
    enabled: true
    schedule: "0 2 * * *"
    retentionDays: 30
    s3Bucket: "elido-backups"

Для відновлення на певний момент часу активуйте архівування 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 - це шина потокової передачі, а не база даних. Зберігання базується на часі (типово: 7 днів) і налаштовується у значеннях у розділі redpanda.retention. Якщо ваш click-ingester відстає більше ніж на вікно зберігання, події втрачаються. Моніторте затримку груп споживачів (consumer group lag) - чарт містить сповіщення Prometheus (ElidoRedpandaConsumerLag), яке спрацьовує, коли будь-яка група споживачів відстає на понад 100 тисяч повідомлень.

Перевірка резервних копій. Бекап, який ніколи не перевірявся - це бекап, на який не можна покластися. Принаймні раз на квартал проводьте тренування з відновлення в окремий простір імен:

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 охоплюють:

  • edge-redirect: затримка p50/p95, коефіцієнт попадання в кеш, обсяг кліків
  • api-core: частота запитів, рівень помилок, затримка gRPC
  • click-ingester: затримка споживача на кожну секцію Redpanda
  • Postgres: затримка реплікації між основною базою та репліками (Patroni)
  • Redis: рівень видалення даних (eviction rate) та тиск на пам'ять

Якщо у вас уже є стек Prometheus у кластері, встановіть monitoring.prometheus.install: false і спрямуйте ваш існуючий стек на ServiceMonitors.

Patroni HA для Postgres#

У режимі HA Patroni керує вибором лідера та перемиканням у разі збою (failover). Чарт налаштовує Patroni з розподіленим сховищем конфігурацій kubernetes (використовує Kubernetes ConfigMaps, окремий etcd не потрібен). Перемикання зазвичай триває 15–30 секунд. Під час перемикання 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. Перевірте різницю у значеннях (потрібен плагін 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), перенесіть туди знімок продакшн-бази, проведіть смоук-тести, а потім перемкніть DNS на рівні ingress. Після перевірки нового стека видаліть старий реліз.

# Встановіть новий (green) стек
helm install elido-green ./deploy/helm/elido \
  --namespace elido-green \
  --create-namespace \
  --values ./green-values.yaml

# Після перевірки перемкніть DNS у вашого реєстратора
# Потім видаліть старий (blue) стек
helm uninstall elido --namespace elido

Коли не варто обирати self-hosting#

Self-hosting - це правильне рішення лише за певних обставин. У більшій кількості випадків, ніж ви могли б очікувати, воно є помилковим.

Ваше навантаження невелике. Якщо ви створюєте менше 100 тисяч посилань на місяць і не маєте суворих вимог до резиденції даних, керований Elido коштує менше (і в грошах, і в операційному часі), ніж запуск k3s. Платний тариф включає бекапи, оновлення та підтримку інфраструктури, якою інакше володіли б ви.

Ви не маєте досвіду експлуатації Kubernetes. k3s мінімалістичний, але Kubernetes не є простим. Якщо ніхто у вашій команді не працював зі StatefulSets, не виконував резервне копіювання/відновлення etcd або не відлагожував CrashLoopBackOff у Patroni о другій годині ночі, self-hosting додає категорію операційних ризиків, яку економія на інфраструктурі не компенсує.

Ваша вимога комплаєнсу - це резиденція даних у ЄС, а не специфічна оренда (tenancy). Резиденція даних у ЄС означає зберігання та обробку даних у межах ЄС. Керована інфраструктура Elido працює в Hetzner FRA та OVH GRA, обидва з яких відповідають вимогам Статті 44 GDPR без транскордонної передачі даних. Якщо фактична вимога вашої команди з комплаєнсу - «дані в ЄС», керований продукт уже задовольняє її без необхідності self-hosted розгортання - дивіться сторінку цін щодо функцій комплаєнсу на кожному плані.

Вам потрібна продуктивність edge у декількох регіонах. Керований сервіс запускає edge POP у Франкфурті, Ешберні та Сінгапурі, де розгорнуто edge-redirect. У поодинокому self-hosted кластері k3s затримка перенаправлення обмежена географічною відстанню віртуальної машини від кінцевого користувача. Мультирегіональний self-hosting можливий, але він значно збільшує операційну складність - це зовсім інше завдання порівняно з тим, що описано в цьому посібнику.


Питання щодо значень Helm-чарту, вимог до ресурсів StatefulSet для певних обсягів трафіку або шляхів оновлення для конкретної версії слід ставити на форумі GitHub Discussions для self-hosted видання. Щодо питань комплаєнсу - зокрема, що дає self-hosted конфігурація крім резиденції в ЄС - сторінка комплаєнсу детально описує журнали аудиту, експорт доказів та контроль RBAC.

Схоже в блозі#

Спробуйте Elido

Вставте URL - отримайте коротке посилання

Без реєстрації. Посилання живе 30 днів. Зареєструйтесь, щоб зберегти назавжди.

Безкоштовно, без реєстрації · 2 на день

Спробуйте Elido

URL-скорочувач із хостингом у ЄС: власні домени, глибока аналітика, відкритий API. Безкоштовний тариф - без кредитної картки.

Теги
self hosted url shortener
k3s
kubernetes url shortener
helm
self host elido
url shortener kubernetes
data residency
eu compliance

Читати далі