Skip to content

Um grupo do central em servidores separados

Redundância explica de que é feita uma instalação com reserva e o que a segunda instância muda. Esta página trata de montá-la: quais ajustes cada máquina recebe, o que se diz aos streamers e o que verificar quando tudo estiver de pé.

O que se descreve aqui é um parque de servidores independentes — uma máquina por instância. Para experimentar o mesmo num único notebook, sem alugar servidores, siga o laboratório no Docker: o mesmo grupo sobe a partir de contêineres em alguns minutos.

O que exatamente é implantado

Uma instalação com reserva tem quatro partes, e apenas uma delas é o Catena:

  • Um cluster PostgreSQL, acessível por todas as instâncias num mesmo endereço. É o único depósito de estado e a única parte cuja tolerância a falhas o Catena não assume.
  • Duas ou mais instâncias do central — máquinas separadas com a mesma configuração, diferindo nos papéis e no endereço anunciado.
  • Streamers — máquinas separadas às quais se dá não o endereço de uma instância, e sim a lista de endereços do grupo.
  • Um ponto de entrada do operador — um único endereço com uma instância viva atrás: um nome DNS com comutação ou um balanceador.

A regra com que começa uma instalação assim: o central e a transmissão vivem em máquinas diferentes. Um streamer local dentro do processo do central não serve aqui — ele compartilha a sorte do processo, e seus streams morrem junto com o plano de controle.

Quantos de cada um

Parte Quantos Observação
PostgreSQL Um cluster lógico Dois servidores de banco independentes são duas instalações distintas, não uma reserva
Instâncias do central Duas ou mais Uma terceira entra com a mesma configuração; as existentes não precisam ser reescritas
Streamers Quantos o ar exigir A redundância deles vem da alocação, não daqui
Ponto de entrada do operador Um endereço Os streamers não passam por ele

A configuração de uma instância do central

As instâncias de um grupo diferem em exatamente duas linhas: o endereço anunciado e o conjunto de papéis. Todo o resto é idêntico, inclusive a string de conexão com o banco.

A primeira instância:

listeners:
  http:
    - port: 80

central:
  database_url: postgres://central:senha@db.example.com:5432/central
  admin_keys:
    - chave-de-gestao
  roles: [run, migrate, layouter, stats]
  advertise_url: http://central-a.example.com

api_auth:
  login: admin
  password: senha-do-operador

A segunda é a mesma, sem o papel migrate e com seu próprio endereço:

central:
  database_url: postgres://central:senha@db.example.com:5432/central
  admin_keys:
    - chave-de-gestao
  roles: [run, layouter, stats]
  advertise_url: http://central-b.example.com

Papéis são declarações, não atribuições

Um papel na configuração significa «esta máquina está pronta para fazer este trabalho», não «esta máquina o faz». Qual instância realmente faz o trabalho é decidido pelo PostgreSQL e não pela configuração — por isso layouter e stats são declarados pelas duas, e isso não é erro nem partida dupla.

  • run — servir requisições: o console, a API de gestão, a recepção da sincronização dos streamers, a exportação de métricas. Cada instância precisa dele.
  • migrate — aplicar as migrações do esquema na partida.
  • layouter — executar as passagens da alocação.
  • stats — manter a imagem do «agora mesmo» de todo o cluster.

O único papel que não se pode deixar em todas é migrate. Ele não é protegido por um mandato: dois processos que partam ao mesmo tempo lançarão as migrações em disputa. Por isso o esquema é aplicado por uma instância e as demais partem depois dela.

É também a razão para não deixar roles vazio na segunda instância: uma lista vazia não significa «nenhum papel», e sim os quatro, migrate incluído.

O endereço anunciado

advertise_url é o endereço pelo qual os vizinhos do grupo alcançam esta máquina. Ele é necessário porque a imagem do «agora mesmo» é mantida por uma instância enquanto as demais chegam a ela pela rede: o titular anota seu endereço e os vizinhos o leem de lá.

O endereço de um balanceador não serve aqui — levaria o vizinho de volta a si mesmo. O endereço tem de ser o de uma máquina específica.

O ajuste pode ser omitido: uma instância sem endereço anunciado funciona por completo e até pode manter a imagem, mas os vizinhos não chegarão a ela e dirão isso no log. No console uma linha assim fica marcada; ver a verificação após a implantação.

Licença

A licença é necessária a cada processo do central, não uma por grupo: sem ela o processo nem parte. A chave e o produto declarado são os mesmos em todas as instâncias.

Quem faz o quê: os mandatos

O trabalho cujo duplicado seria nocivo é feito por uma única instância do grupo: a que tomou o mandato correspondente. O mandato vive no PostgreSQL e é emitido numa conexão própria, de modo que a morte do processo o libera sozinha, sem timeouts nem análise de executores travados: se a sessão morreu, o mandato está livre.

Os mandatos são vários e são independentes. O central não tem um líder único: layouter e stats podem acabar em máquinas diferentes, e isso é um estado normal, não um desequilíbrio.

Cada emissão leva um número — a época. Ela cresce a cada troca de titular e viaja com cada escrita: uma instância que acorda de uma pausa já sem ser a titular recebe uma recusa na escrita, em vez de estragar a alocação depois do fato. É isso que torna a mudança de um mandato segura e sem confirmação manual.

A troca de titular leva segundos — o que o PostgreSQL precisa para notar a sessão morta e o vizinho para vir buscar o mandato liberado.

Uma instância que não tem mandato algum não é reserva nem está dormindo: ela serve por completo o console, a API e a sincronização dos streamers. O mandato diz respeito apenas ao trabalho de fundo.

A configuração de um streamer

O streamer se dirige ao grupo, não a um processo, então sua configuração leva uma lista de endereços:

managed_by:
  name: streamer-01
  url:
    - http://central-a.example.com
    - http://central-b.example.com
  join_token: token-de-adesao

A ordem da lista é uma preferência de partida, não uma vinculação permanente. O streamer fica no endereço atual enquanto ele responder e passa ao seguinte após uma requisição malsucedida. Ele não volta por conta própria: enquanto o novo endereço responder não há nada a ganhar trocando-o, e alternar por igualdade só espalharia a máquina pelo grupo.

O segundo endereço é obrigatório, não desejável. Uma interrupção do laço de sincronização é o único sinal de vida de um streamer que o central tem. Um streamer que fique com um único endereço morto consta como offline um minuto e meio depois, e então o layouter move honestamente seus streams para outra máquina — de uma viva e transmitindo. A morte de uma instância de gestão vira a mudança de metade do ar.

Convém dividir a lista pelas metades do parque: dar a uma metade a primeira instância como primeiro endereço e à outra a segunda. Em estado tranquilo a carga de sincronização fica distribuída e, ao morrer qualquer uma das instâncias, só a metade dela se muda.

O ponto de entrada do operador

O operador precisa de um único endereço que encontre sozinho uma instância viva: um nome DNS com comutação ou um balanceador diante do grupo. Um manipulador de requisições não guarda estado próprio, então qualquer instância pode responder à requisição de um operador.

Os streamers não devem passar por esse ponto de entrada: eles têm a própria lista de endereços, e um intermediário a mais no caminho da sincronização só acrescenta um ponto de falha.

Ordem de implantação

  1. Subir o PostgreSQL e criar o banco.
  2. Iniciar a primeira instância — a que tem o papel migrate. Esperar até que ela responda às requisições: o esquema é aplicado antes de ela ficar pronta.
  3. Iniciar as demais instâncias.
  4. Abrir a janela de adesão e subir os streamers, dando a cada um a lista de endereços do grupo.
  5. Apontar o ponto de entrada do operador para o grupo.

O pacote nas máquinas das instâncias é o catena-base, o mesmo que os streamers recebem: a pergunta de instalação sobre um PostgreSQL existente é respondida com o endereço do banco compartilhado, enquanto os papéis e o endereço anunciado vêm da configuração acima. O pacote catena não serve aqui — ele monta o banco na própria máquina, e um grupo precisa dele separado das instâncias.

A ordem só importa no primeiro passo: as demais instâncias não subirão antes do esquema, e os streamers não aderirão antes de haver um central vivo.

O que verificar após a implantação

Tudo o que vale olhar está no console, na seção Cluster → Central.

  • A tabela tem tantas linhas quantas instâncias. Uma linha aparece segundos após uma partida. Se não aparecer, a instância não chegou ao banco.
  • Cada linha tem o «Endereço anunciado» preenchido. A marca «não anunciado» significa que os vizinhos não chegarão a essa instância: falta-lhe advertise_url.
  • A coluna «Papéis reivindicados» mostra os papéis declarados. Garanta que migrate esteja declarado em exatamente uma.
  • A coluna «Mandatos detidos» não está vazia em pelo menos uma instância. Vazia em todas significa que nenhuma declarou os papéis layouter e stats, e o grupo não tem nem alocação nem imagem do «agora mesmo».
  • A coluna «Compilação» é a mesma em todas as linhas. Versões diferentes num grupo são um estado normal apenas enquanto durar uma atualização.
  • A seção «Streamers» mostra todo o parque a partir de qualquer instância. Metade do parque visível num endereço e a outra metade em outro significa que as instâncias não veem a imagem uma da outra: revise advertise_url.

Não é preciso testar a comutação à mão, e não convém fazê-lo numa instalação em serviço — para isso existe o laboratório, sobre um banco descartável.

O que este arranjo não faz

  • Não torna o banco redundante. A tolerância a falhas do PostgreSQL é tarefa do próprio banco: uma réplica com comutação ou uma instância gerenciada de um provedor.
  • Não substitui backups. A reserva protege da perda de uma máquina, não de uma exclusão por engano — ver Backup e restauração.
  • Não toca o ar. Os streams transmitem e gravam o arquivo sem o central; só a gestão é tornada redundante, ver Autonomia do streamer.