A versão gerida do Elido funciona em infraestrutura de região europeia com uma configuração de privacidade por defeito. Para a maioria dos utilizadores, isso é suficiente. Para alguns, não é.
Se a sua equipa de segurança exige que os dados de destino de hiperligações curtas e os eventos de clique nunca saiam de um datacenter específico, se a sua política de auditoria exige controlo total sobre o servidor de base de dados, ou se está a integrar o Elido numa plataforma interna que precisa de funcionar em air-gap, o caminho de self-hosting existe exatamente para esse fim.
Este guia mostra como implementar o Elido em k3s, uma distribuição Kubernetes mínima e pronta para produção que funciona confortavelmente numa única VM ou num pequeno cluster de HA. No final terá todos os serviços a funcionar, TLS provisionado, backups configurados e um caminho de upgrade repetível. O guia pressupõe que quer um sistema funcional, não um tutorial sobre conceitos de Kubernetes - as explicações são curtas e os passos operacionais são explícitos.
Por que fazer self-hosting#
Antes de entrar no playbook, vale a pena ser preciso sobre os trade-offs. O self-hosting não é automaticamente melhor - transfere o risco operacional de um fornecedor para a sua própria equipa.
Residência de dados e conformidade. Se o seu framework de conformidade (âmbito ISO 27001, política interna de classificação de dados ou cláusula contratual de residência de dados) exige que os metadados de hiperligações e os eventos de analytics permaneçam em infraestrutura que controla diretamente, o SaaS gerido não pode satisfazer esse requisito independentemente do local onde corre fisicamente. Uma implementação self-hosted numa VM de região europeia que possui pode. Consulte a visão geral de conformidade para detalhes específicos sobre como a arquitetura do Elido se mapeia nos frameworks comuns.
Previsibilidade de custo a escala. Os planos geridos têm preços baseados em hiperligações ativas e volume de cliques. Acima de um determinado limiar - tipicamente em torno de alguns milhões de cliques por mês - o custo por evento nos planos geridos supera o custo de infraestrutura de executar a carga de trabalho equivalente de StatefulSet. O ponto de cruzamento depende da forma do seu tráfego, mas existe.
Controlo de auditoria. Algumas organizações requerem acesso às tabelas Postgres brutas e aos dados de eventos de clique do ClickHouse para empacotamento de evidências, retenção legal ou integração com SIEM. A API do Elido expõe endpoints de registo de auditoria e exportações de evidências, mas o acesso direto à base de dados só está disponível em implementações self-hosted.
O que está a ceder. O k3s requer que alguém se responsabilize pelos upgrades, pela saúde dos nós, pela verificação de backups e pela resposta a incidentes. Se a sua equipa não tem experiência operacional com Kubernetes, a versão gerida numa localização de alojamento conforme é quase certamente a resposta certa. Consulte a última secção deste artigo para uma abordagem mais direta de quando o self-hosting é a escolha errada.
Pré-requisitos#
- Um cluster k3s. Um único nó com 4 vCPU e 8 GB de RAM suporta cargas de trabalho leves. Para HA, três nós de plano de controlo mais dois ou mais nós worker é a topologia mínima recomendada. O etcd integrado no k3s cobre a HA do plano de controlo; a HA do data plane é tratada pelo Patroni (Postgres) dentro do Helm chart.
kubectlconfigurado com um contexto a apontar para o seu cluster.- Helm 3.14 ou posterior.
- Um domínio que controla com capacidade de criar registos DNS. O IP de ingresso do k3s precisa de ser alcançável nas portas 80 e 443 a partir da internet para que os desafios ACME do Let's Encrypt sejam bem-sucedidos.
- Se for por razões de conformidade, uma VM de região europeia. Hetzner (Falkenstein, Helsínquia, Nuremberga) e OVH (Gravelines, Roubaix) são os dois fornecedores que a própria infraestrutura de edge do Elido utiliza.
Num Hetzner CX32 fresco (4 vCPU / 8 GB) a executar Ubuntu 24.04, o k3s instala em cerca de 30 segundos:
curl -sfL https://get.k3s.io | sh -
# Copy the kubeconfig to your local machine:
scp root@<your-vm>:/etc/rancher/k3s/k3s.yaml ~/.kube/elido-self-host.yaml
export KUBECONFIG=~/.kube/elido-self-host.yaml
kubectl get nodes # should show Ready
Clone o repositório do Elido para obter o Helm chart (o chart está em deploy/helm/elido/ no repositório - não existe um repositório Helm público separado):
git clone https://github.com/elidoapp/elido.git
cd elido
Início rápido#
O preset de self-host (deploy/helm/elido/values-selfhost.yaml) é o ponto de partida recomendado para uma implementação k3s de nó único. Faça primeiro o bootstrap dos secrets e depois instale:
# 1. Mint all required Kubernetes Secrets in one shot (idempotent)
./scripts/bootstrap-secrets.sh elido
# 2. Install using the self-host preset
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
Para substituições personalizadas (contagens de réplicas de produção, serviços externos geridos, etc.), copie values-selfhost.yaml para um my-values.yaml local, edite-o e passe-o com -f. As principais secções de valores de nível superior são ingress.hosts.* (os seus hostnames), image.registry/image.tag, e blocos resources por serviço. Não use global.domain - o chart usa ingress.hosts.* para a configuração de domínio.
A flag --wait bloqueia até que todos os Deployments e StatefulSets atinjam o estado pronto. Num nó novo sem imagens em cache, espere 5–8 minutos numa ligação de 1 Gbps.
Verifique que tudo subiu:
kubectl -n elido get pods
kubectl -n elido get svc
Consulte a referência de valores do chart com helm show values ./deploy/helm/elido para a superfície completa de parâmetros. O deploy/helm/elido/README.md no repositório é a fonte autoritativa para nomes de campos específicos e utilização do script de bootstrap.
Arquitetura que está a implementar#
A arquitetura do Elido é dividida por orçamento de latência. Compreender essa divisão ajuda a tomar decisões informadas de alocação de recursos.
Hot path: edge-redirect#
edge-redirect é o único serviço no caminho síncrono de um pedido de redirecionamento. É escrito em Go com fasthttp e tem um orçamento de latência rígido: p50 5 ms, p95 15 ms num acerto de cache. O serviço mantém uma cache de dois níveis: uma LRU em processo (L1) suportada por Redis Cluster (L2). Num miss de cache, cai para uma chamada gRPC ao api-core. Os eventos de clique são emitidos fire-and-forget para o Redpanda - a resposta de redirecionamento nunca fica à espera que a escrita do evento esteja concluída.
O Helm chart implementa o edge-redirect como um Deployment com um HorizontalPodAutoscaler. Numa configuração self-hosted de região única, duas réplicas é um bom ponto de partida. A cache Redis L2 é partilhada entre réplicas, pelo que o aquecimento da cache é rápido após uma atualização gradual.
Warm path: superfície de API#
Cinco serviços tratam do trabalho síncrono de API e lógica de negócio:
api-core- Go + chi, REST e gRPC. Fonte de verdade para hiperligações, workspaces, memberships, domínios personalizados, eventos de auditoria.api-bff- Camada BFF Node/Hono para o painel web e clientes móveis. Agrega oapi-coree oanalytics-api.analytics-api- Go, consultas ClickHouse. Serve os painéis de analytics.billing- Go + chi + sqlc. Motor de IVA da UE, integração de pagamentos LiqPay, geração de faturas.search- Go, faz proxy de consultas de pesquisa de hiperligações para o Meilisearch.
A autenticação é tratada pelo Ory Kratos (identidade, sessões, verificação de email) e Ory Hydra (tokens OAuth2/OIDC para integrações de terceiros e a extensão de browser). Ambos são implementados como Deployments no chart.
Cold path: workers assíncronos#
Seis serviços consomem eventos de tópicos Redpanda e não têm orçamento de tempo de resposta:
click-ingester- consome eventos de clique, escreve para ClickHouse.webhook-dispatcher- distribui payloads de webhook assinados para endpoints dos clientes.notification- notificações por email e in-app (eventos de conta, alertas de hiperligações).url-scanner- executa análise de URL de destino contra Google Safe Browsing, PhishTank, SURBL.metadata-fetcher- obtém metadados Open Graph para pré-visualizações de hiperligações.domain-manager- verificação de DNS e provisionamento de TLS on-demand do Caddy para domínios personalizados.
Data plane: StatefulSets#
Seis sistemas stateful suportam os serviços acima:
| Sistema | Papel | Recurso do chart |
|---|---|---|
| Postgres (Patroni) | Fonte de verdade para hiperligações, utilizadores, faturação | StatefulSet, 3 réplicas em modo HA |
| Redis Cluster | Cache de hiperligações no hot path | StatefulSet |
| ClickHouse | Armazenamento de eventos de clique e consultas de analytics | StatefulSet |
| Redpanda | Bus de eventos entre serviços | StatefulSet |
| MinIO | Assets carregados pelos utilizadores (imagens QR, exportações) | StatefulSet |
| Meilisearch | Pesquisa de hiperligações in-app | StatefulSet |
Numa implementação de nó único, cada StatefulSet executa uma réplica. Em modo HA (ativado com ha.enabled: true nos values), o Postgres escala para três réplicas com Patroni, o Redis escala para seis (três primários, três réplicas), e o Redpanda escala para três brokers.
Ingresso Caddy#
O Caddy trata da terminação TLS e do TLS on-demand para domínios personalizados de tenants. Quando um operador de workspace adiciona um domínio personalizado através do painel (ou da API), o domain-manager verifica a propriedade do DNS, regista o hostname como permitido, e o Caddy provisiona o certificado Let's Encrypt no primeiro pedido HTTP a esse hostname. O chart implementa o Caddy como um DaemonSet em nós com a label elido.app/ingress=true, ou como um Deployment na configuração padrão de nó único.
O fluxo para um domínio personalizado de tenant go.company.com:
- O operador cria um CNAME:
go.company.com → <your-k3s-ingress-ip>(ou um registo A a apontar diretamente). - O operador chama
POST /v1/workspaces/{id}/domainsou clica em "Add domain" no painel. - O
domain-managerconsulta DNS para confirmar que o CNAME resolve para o IP de ingresso. - O Caddy recebe o primeiro pedido HTTPS, verifica a lista de permissões emitida pelo Elido, e solicita um certificado ao Let's Encrypt via ACME HTTP-01.
- O certificado é armazenado no volume de estado do Caddy e renovado automaticamente.
Bootstrap do primeiro arranque#
Após a conclusão do helm install, a API de administração está disponível mas não existe nenhuma conta de utilizador.
Bootstrap do utilizador administrador#
# Port-forward to the api-core admin interface (not exposed by default)
kubectl -n elido port-forward svc/api-core 8081:8081 &
# Create the first admin account
curl -X POST http://localhost:8081/internal/admin/bootstrap \
-H 'Content-Type: application/json' \
-d '{
"email": "[email protected]",
"password": "your-secure-password"
}'
O endpoint de bootstrap só pode ser chamado de dentro da rede do cluster e é desativado após a primeira chamada bem-sucedida. Uma vez criada a conta de administrador, faça login através do painel no seu domínio configurado.
Criar o primeiro workspace#
Após o login, o painel solicita a criação de um workspace na primeira utilização. Em alternativa, via 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"}'
Adicionar um domínio personalizado#
Nas definições do workspace, vá a Domains e adicione o seu domínio de hiperligações curtas. Defina o registo DNS (CNAME ou A) antes de clicar em "Verify" - o domain-manager verifica o DNS imediatamente e devolve um erro se o registo ainda não se tiver propagado. Após passar a verificação, o Caddy provisiona o certificado no primeiro pedido de redirecionamento para esse domínio. A emissão do certificado demora tipicamente 10–30 segundos no primeiro pedido.
Verifique que o TLS está a funcionar:
curl -I https://go.company.com/healthz
# Expect: HTTP/2 200
Preocupações operacionais#
Backups#
Postgres. O chart inclui um CronJob que executa pg_dump num horário configurável (padrão: diariamente às 02:00 UTC) e carrega o dump comprimido para o bucket MinIO configurado nos values. Para implementações de HA, o pg_dump é executado contra uma réplica para evitar impacto no primário. Ative nos values:
backups:
postgres:
enabled: true
schedule: "0 2 * * *"
retentionDays: 30
s3Bucket: "elido-backups"
Para recuperação point-in-time, ative o arquivamento de WAL (postgres.walArchive.enabled: true nos values), que envia segmentos WAL para o MinIO continuamente. Combine pg_dump diário com arquivamento de WAL para um RPO abaixo de 5 minutos.
ClickHouse. O ClickHouse armazena eventos de clique, que são append-only e podem ser reproduzidos a partir do Redpanda se a retenção o permitir. O chart inclui um CronJob que executa SQL BACKUP TABLE para MinIO usando a interface de backup nativa do ClickHouse. Ative com backups.clickhouse.enabled: true.
Redpanda. O Redpanda é um bus de streaming, não uma base de dados. A retenção é baseada no tempo (padrão: 7 dias) e configurada nos values em redpanda.retention. Se o seu click-ingester ficar atrasado mais do que a janela de retenção, os eventos são perdidos. Monitorize o lag do consumer group - o chart inclui um alerta Prometheus (ElidoRedpandaConsumerLag) que dispara quando qualquer consumer group está mais de 100K mensagens atrasado.
Verificação de backups. Um backup que nunca foi testado é um backup em que não pode confiar. Execute um exercício de restauro num namespace separado pelo menos trimestralmente:
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"
Monitorização#
O chart inclui um ServiceMonitor para cada serviço se o CRD do Prometheus Operator estiver presente no cluster. Métricas principais para alertar:
monitoring:
prometheus:
enabled: true # requires prometheus-operator in cluster
grafana:
enabled: true # deploys bundled dashboards
adminPassword: "change-me"
Os painéis Grafana incluídos cobrem:
- Latência p50/p95 do
edge-redirect, taxa de acertos de cache, volume de cliques - Taxa de pedidos do
api-core, taxa de erros, latência gRPC - Lag do consumidor do
click-ingesterpor partição Redpanda - Lag de replicação primário/réplica do Postgres (Patroni)
- Taxa de eviction e pressão de memória do Redis
Se já tem um stack Prometheus no cluster, defina monitoring.prometheus.install: false e aponte o seu stack existente para os ServiceMonitors.
Patroni HA para Postgres#
Em modo HA, o Patroni gere a eleição de líder e o failover. O chart configura o Patroni com o armazenamento de configuração distribuída kubernetes (usa ConfigMaps do Kubernetes, sem etcd separado). O failover normalmente conclui em 15–30 segundos. Durante o failover, o api-core e o billing experimentam erros de escrita breves; ambos os serviços fazem retry em 5xx com backoff exponencial.
Para inspecionar o estado do cluster Patroni:
kubectl -n elido exec -it postgres-0 -- patronictl -c /etc/patroni/config.yml list
Fluxo de upgrade#
O Elido segue versionamento semântico. Os releases de patch contêm correções de bugs e não requerem passos de migração manuais. Os releases minor e major podem incluir migrações de esquema de base de dados, que são incorporadas nos binários dos serviços e executadas automaticamente no arranque do pod através do executor de migração.
O caminho de upgrade recomendado:
# 1. Pull the latest chart (git pull in your cloned repo)
git pull origin main
# 2. Review the upgrade notes
helm show chart ./deploy/helm/elido | grep -A10 'version\|appVersion'
# 3. Diff the values changes (requires helm-diff plugin)
helm diff upgrade elido ./deploy/helm/elido \
--namespace elido \
--values ./my-values.yaml
# 4. Upgrade
helm upgrade elido ./deploy/helm/elido \
--namespace elido \
--values ./my-values.yaml \
--wait --timeout 10m
Tratamento de migrações. Cada binário de serviço Go contém as suas migrações de esquema (usando golang-migrate contra um diretório de migrações incorporado). No arranque do pod, o binário executa migrate up antes de servir pedidos. Numa atualização gradual, o novo pod aplica quaisquer migrações pendentes antes de o pod antigo terminar. As migrações têm de ser retrocompatíveis com a versão minor anterior - uma nova coluna adicionada na v1.5 tem de ser nullable ou ter um valor padrão para que o binário v1.4 a correr ao lado durante a janela de rollout não dê erro.
Blue/green para upgrades major. Para upgrades de versão major onde as alterações de esquema não são retrocompatíveis, use a estratégia blue/green: instale a nova versão com um nome de release Helm separado num namespace de staging, migre um snapshot da base de dados de produção para ele, faça smoke-test, depois execute o cutover de DNS no nível de ingresso. Após validar o stack green, elimine o release blue.
# Install green stack
helm install elido-green ./deploy/helm/elido \
--namespace elido-green \
--create-namespace \
--values ./green-values.yaml
# After validation, cut over DNS at your registrar
# Then decommission blue
helm uninstall elido --namespace elido
Quando não fazer self-hosting#
O self-hosting é a escolha certa num conjunto restrito de circunstâncias. Em mais casos do que poderia esperar, é a escolha errada.
A sua carga de trabalho é pequena. Se cria menos de 100K hiperligações por mês e não tem requisitos rigorosos de residência de dados, o Elido gerido custa menos em dinheiro e tempo operacional do que executar k3s. O tier alojado inclui backups, upgrades e on-call para a infraestrutura que de outra forma seria sua.
Não tem experiência operacional com Kubernetes. O k3s é mínimo, mas o Kubernetes não é simples. Se ninguém na sua equipa operou StatefulSets, tratou de backup/restauro de etcd, ou depurou um CrashLoopBackOff no Patroni às 2 da manhã, o self-hosting adiciona uma categoria de risco operacional que a poupança em custos de infraestrutura não compensa.
O seu requisito de conformidade é residência de dados na UE, não inquilino específico. Residência de dados na UE significa dados armazenados e processados na UE. A infraestrutura gerida do Elido funciona em Hetzner FRA e OVH GRA, ambos satisfazendo os requisitos do Artigo 44 do RGPD sem transferência transfronteiriça. Se o requisito real da sua equipa de conformidade é "dados na UE", o produto gerido já o satisfaz sem uma implementação self-hosted - consulte a página de preços para as funcionalidades de conformidade disponíveis em cada plano.
Quer desempenho de edge multi-região. O serviço gerido executa POPs de edge em Frankfurt, Ashburn e Singapura, com o edge-redirect do hot path implementado em cada um. Um único cluster k3s self-hosted de região única tem latência de redirecionamento limitada pela distância geográfica da VM ao utilizador final. O self-hosting multi-região é possível, mas multiplica significativamente a superfície operacional - é uma empreitada diferente do que este guia cobre.
Questões sobre os values do Helm chart, requisitos de recursos de StatefulSet para volumes de tráfego específicos, ou caminhos de upgrade para uma versão específica devem ser colocadas no GitHub Discussions para a edição self-hosted. Para questões relacionadas com conformidade - especificamente o que a configuração self-hosted fornece além da residência na UE - a página de conformidade cobre o registo de auditoria, a exportação de evidências e os controlos RBAC em detalhe.
Relacionados no blog#
Experimente Elido
Cole uma URL, obtenha um link curto
Sem cadastro. O link vive 30 dias. Cadastre-se para mantê-lo para sempre.
Grátis, sem necessidade de registo · 2 por dia