Migração de container autônomo para containers web e de dados separados

Esse processo é seguro? Consigo executar uma configuração multicontêiner sem problemas em um ambiente de desenvolvimento, mas, se começar a usá-lo em produção, enquanto as pessoas acessam o contêiner antigo e o novo contêiner faz o bootstrap e executa a etapa de migração do banco de dados, as requisições ao contêiner antigo ainda usarão a lógica de backend antiga e salvarão dados conforme definido na versão anterior, mesmo após o término da etapa de migração do banco de dados (mas antes que todo o processo de bootstrap seja concluído).

Embora eu saiba que isso não seja um problema específico do Discourse (um ambiente com várias réplicas poderia ter esse problema se uma réplica for atualizada antes da outra, a menos que todas sejam paradas antes da atualização, o que provavelmente não será o caso se você quiser alta disponibilidade), o processo que você descreveu ainda é seguro, de forma geral?

Uma coisa que posso pensar é garantir sempre manter o Discourse atualizado para ter a migração mínima do banco de dados entre reconstruções. Mas, em qualquer caso, isso ainda não é ideal, e problemas podem surgir mesmo nesse cenário.

A configuração multicontêiner parece ser uma das abordagens recomendadas (embora não seja a padrão com apenas 1 contêiner), então acho que deve ser segura, e estou apenas pensando demais.

Você sabe se funciona bem em sites de produção (fazendo o bootstrap em um contêiner mesmo enquanto outro está em execução)? Estou apenas perguntando para saber sobre pessoas que já fizeram isso em sites de produção, para ter algum feedback e saber se funciona bem, mesmo após várias reconstruções, se há algumas armadilhas, etc… Como disse, em um ambiente de desenvolvimento funciona bem.

Se você deseja zero tempo de inatividade, há algumas etapas adicionais que precisam ser realizadas: desative as “migrações pós-implementação” nos novos contêineres, implemente totalmente o novo contêiner, ative as migrações pós-implementação e, em seguida, execute a implementação novamente. Isso impedirá que migrações que removem colunas sejam executadas até que o código antigo deixe de estar em execução.

Ainda não há um guia passo a passo (howto) sobre isso; a documentação está disponível apenas aqui:

No entanto, na maioria dos casos, a maioria dos fóruns pode suportar de um a três minutos de tempo de inatividade por mês.

4 curtidas

Sim. Na maioria das vezes, não há migrações que quebrem o container em execução.

Você também pode configurá-lo para ter as migrações desativadas no web_only.yml. Depois que o novo container estiver em execução, faça algo como:

 cd /var/www/discourse; SKIP_POST_DEPLOYMENT_MIGRATIONS=0 rake db:migrate

dentro do container em execução.

3 curtidas

Sim, isso funciona perfeitamente em produção com nginx como um servidor proxy reverso front-end.

Veja o que eu faço;

Três containers:

  • Data (data.yml)
  • Socket1 (socket1.yml)
  • Socket2 (socket2.yml)

Especificamos um único socket de domínio Unix na configuração do nginx, por exemplo:

location / {
       proxy_pass http://unix:/var/run/nginx.http.sock;
       proxy_set_header Host $http_host;
       proxy_http_version 1.1;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto https;
       proxy_set_header X-Real-IP $remote_addr;
}

Em seguida, selecionamos o container que queremos que esteja ativo por meio de um link simbólico para o socket real, assim:

Digamos que queremos que o container socket2 esteja ativo:

ls -sf /var/discourse/shared/socket2/nginx.http.sock /var/run/nginx.http.sock

Digamos que queremos fazer uma alteração no socket1 e deixar o socket1 ativo:

cd /var/discourse
./launcher rebuild socket1
ls -sf /var/discourse/shared/socket1/nginx.http.sock /var/run/nginx.http.sock

Note que não há motivo para fazer o bootstrap apenas do container socket1, porque o container é exposto via um socket de domínio Unix em seu próprio diretório compartilhado / volume, então ambos esses containers de “aplicação web” podem rodar ao mesmo tempo:

  • Socket1: /var/discourse/shared/socket1/nginx.http.sock
  • Socket2: /var/discourse/shared/socket2/nginx.http.sock

Isso não causa “colisão de vínculo de porta” exposta como quando uma porta de container TCP/IP é exposta. Por esse motivo, eu exponho apenas um socket de domínio Unix em produção (não uma porta TCP/IP)

Claro, você pode fazer o bootstrap se quiser:

cd /var/discourse
./launcher bootstrap socket1
./launcher start socket1
ls -sf /var/discourse/shared/socket1/nginx.http.sock /var/run/nginx.http.sock

Fica a seu critério, mas lembre-se de que, se você rodar ambos os containers ao mesmo tempo, ambos executarão o sidekiq e executarão tarefas agendadas, conforme nossa experiência; então, ocasionalmente, sincronizamos nossos uploads em ambos os containers.

Isso funciona perfeitamente para nós e podemos reconstruir um container de aplicação web e torná-lo ativo com basicamente zero tempo de inatividade. Levamos o tempo de inatividade muito a sério em produção e evitamos sempre que possível.

Nota:

Este método (acima) foi projetado para a parte de aplicação web da solução, não para o container de dados. Eu não criei uma solução semelhante para dados; mas quem sabe, talvez um dia, eu gastarei algum tempo e construirei algo semelhante (mas diferente, claro) para o container de dados (algum tipo de “dois containers de dados, sincronizar os bancos de dados”, método totalmente a definir no meu pensamento neste momento).

Então, eu estou na verdade fazendo o oposto disso:

Como eu disse, em um ambiente de desenvolvimento funciona bem.

Geralmente não configuro isso em dev porque leva mais tempo para configurar e não é necessário em dev porque um pouco de tempo de inatividade é aceitável, já que é apenas “eu e o código” (não está ao vivo com usuários e bots acessando o site) e além disso eu não uso docker em desenvolvimento** (no desktop).

Espero que isso ajude.


**Por “desenvolvimento”, eu me refiro ao desenvolvimento de software (por exemplo, plugins); não apenas simplesmente “estágio” de uma instalação do Discourse, a que me refiro como “estágio” e não “desenvolvimento” (apenas para ficar bem claro).

2 curtidas

Obrigado, eu não sabia disso. É um ótimo recurso e consigo ver como ele pode evitar problemas como o que mencionei antes (embora isso não evite mudanças de lógica que utilizem colunas já existentes, mas esse deve ser um caso mais raro).

Obrigado pela resposta. O SKIP_POST_DEPLOYMENT_MIGRATIONS parece ser o que @riking mencionou e parece ser o que eu procurava, para evitar que migrações quebrem coisas feitas no contêiner em execução.

Obrigado pela sua explicação. Isso parece ser uma boa abordagem para mim, alternando entre 2 contêineres (para poder ter 1 contêiner em execução enquanto o outro é inicializado).

Quando disse “ambiente de desenvolvimento”, quis dizer que era um ambiente de desenvolvimento remoto que eu usava para testar a configuração multicontêiner (tanto na mesma máquina quanto com os contêineres em máquinas diferentes).

Eu disse desenvolvimento e não staging porque, em um ambiente de staging, eu usaria os arquivos yml com os mesmos plugins e um banco de dados de produção de backup para testar. Mas é verdade que, se eu apenas quiser configurar um ambiente de desenvolvimento, na maioria dos casos, eu usaria a abordagem de 1 contêiner.

2 curtidas

Pelo que pude perceber, este guia é apenas muita conversa em torno de:

  • fazer backup
  • criar uma nova instância do Discourse completamente nova, com mais palavras, mas com os mesmos resultados de apenas executar discourse-setup 2container
  • restaurar

Por que não mover ou copiar /var/discourse/shared/standalone/{postgres,redis}* para /var/discourse/shared/data após uma parada limpa e antes de iniciar dois novos contêineres a partir de arquivos containers/*.yml separados? Um ciclo de backup/restauração parece ser uma maneira muito pesada de mover todos esses dados, adicionando horas desnecessariamente ao processo. Estou ignorando algo óbvio aqui?

Acabei de testar esse processo no meu Discourse de teste e também separei o Redis, já que estava fazendo isso, só para garantir que cobrisse todos os aspectos. Edição: Movi a descrição para um novo tópico:

O site parece estar funcionando perfeitamente sem um ciclo de backup/restauração. Há algo não óbvio que eu deveria verificar?

Fiz o mesmo processo para um Discourse relativamente grande e está funcionando bem. Decidi que, em produção, nomearia meu novo contêiner web_only como app, para que meus dedos continuem fazendo a coisa certa de forma natural. Depois de escrever os novos arquivos containers/*.yml, o tempo de inatividade para toda a migração foi de 12 minutos, muito mais rápido do que seria para um ciclo de backup/restauração.

É apenas que isso exige um pouco mais de trabalho e compreensão de como as coisas funcionam. A restauração de backup é mais à prova de falhas.

1 curtida

Acho que, então, só concordamos em discordar. Eu acharia que, se alguém é competente para rodar múltiplos containers, pode executar alguns comandos, e não acho que esses comandos sejam mais difíceis de digitar do que qualquer bin/rails c e começar a digitar comandos Ruby por aqui, ou que exijam habilidades significativamente mais complexas ou diferentes das necessárias para usar múltiplos containers de qualquer forma. :smiling_face: Mas vou migrar o conteúdo para um novo post separado, em vez de deixá-lo enterrado em um comentário aqui.

1 curtida

Essa é um argumento razoável. Talvez fazer o procedimento parecer mais assustador evite que pessoas sem as habilidades necessárias tentem fazê-lo.

…E, se cometerem um erro, não há substituto para um backup antes de uma migração! Espero ter deixado isso claro no meu artigo, agora linkado acima! :smiling_face:

1 curtida

Aí está o erro do seu argumento. Adicionar 2container ao ./discourse-setup não inclui nenhuma medida de competência. Há muitas pessoas que rodam dois containers simplesmente porque veem tópicos como este e assumem que há algum segredo ou que é a “coisa a se fazer”.

O tópico sobre o PostgreSQL 12 deve servir como um aviso quando se trata da complexidade adicional. Usar um backup como etapa entre os estados permite que o usuário reverta para um único container renomeando um único arquivo; uma vez que você começa a mover pastas, essa simplicidade se perde.

2 curtidas

@Stephen Há uma falha no seu argumento: a descrição de múltiplos containers está cheia de avisos de que você precisa assumir a responsabilidade pelas atualizações e entender como funciona, e a descrição longa acima é tão obscura que provavelmente qualquer pessoa que a lesse desistiria de qualquer maneira. Leia meu Migrate quickly to separate web and data containers e me diga que isso não vai assustar as pessoas que terão dificuldade em segui-lo, ou que falha em enfatizar a necessidade de backup e a capacidade de retornar a um backup se algo der errado!

Fiquei profundamente infeliz quando executei ./launcher rebuild app pouco depois de migrar para um servidor mais capaz (para uma correção de segurança) e ter meu site fora do ar por um tempo excessivamente longo, grande parte do qual foi gasto recriando as partes do postgres do container. Foi quando encontrei a documentação de 2 containers e esta documentação, e realmente não quis passar por outra queda de 4 horas para migrar, então continuei aceitando longos períodos de inatividade para ./launcher rebuild app para evitar as 4 horas de queda que uma restauração levaria. Como uma pessoa vagamente competente, fiquei muito irritado por um longo tempo por essa configuração estar efetivamente oculta.

O tópico sobre o postgres 12 é uma ótima referência, porque as pessoas acabam com mais tempo de inatividade porque precisam recriar o inteiro aplicativo várias vezes, quando poderiam estar recriando apenas o container do postgres duas vezes. Não posso dizer que li todo o tópico devido à exclusão automática de 6 dias, mas não me parece óbvio que implantações de múltiplos containers incompetentes sejam o, ou mesmo um, grande problema lá.

(Desculpe, às vezes fico um pouco cansado do “todos os usuários são incompetentes” por aqui.)

2 curtidas

Isso pode não fazer sentido para você, mas para aqueles de nós que estão aqui no meta há 6 ou 7 anos prestando assistência às pessoas no Support, sempre fará sentido ter uma estratégia de rollback.

Bugs realmente acabam chegando aos testes aprovados; ocasionalmente, limitações de taxa do rubygems afetam reconstruções e até mesmo o GitHub já teve seus raros tropeços. Por esse motivo sozinho, não vejo nenhum valor em fazer uma alteração de estado que dificulte simplesmente renomear um arquivo e executar ./launcher start app.

Sua tolerância ao risco pode ser diferente, caso em que você pode seguir um caminho diferente. Para aqueles que regularmente ajudam a resolver os problemas, o guia atual funciona bem.

3 curtidas

Não sinto que você realmente leu o processo que escrevi, já que você escreve como se eu não tivesse enfatizado a necessidade da capacidade de realizar a restauração. Por favor, vá ler e, em seguida, volte e considere editar o que você escreveu para que seja uma declaração verdadeira sobre minhas instruções. Como está, sinto que você está me “esplaining” sem me dar a cortesia de se dar ao trabalho de ler o que escrevi.

No entanto, adicionei muitas outras advertências adicionais, incluindo uma no topo, além das advertências neste post, que existe há cinco anos e continua sendo o local canônico para instruções sobre esse processo de migração.

2 curtidas

Primeiramente, obrigado por tentar limpar essa “selva” para nós, aventureiros :sweat_smile:, provavelmente estarei no seu encalço em alguns dias…
O que há no arquivo multisite.yml?

Olá.

Recentemente configurei um fórum Discourse auto-hospedado em um VPS. Os uploads estão sendo armazenados no Wasabi. O mesmo vale para os backups. Tudo está hospedado na Linode.

Usei o modelo standalone e achei a configuração uma brisa de ar fresco em comparação a outros softwares. Foi sublime! Uma pura alegria. Desejo que todos os projetos de código aberto dedicassem tanta atenção à instalação e configuração quanto o Discourse.

Agora, o problema. Tenho um PostgreSQL dedicado rodando em um servidor separado, acessível apenas via um endereço RFC-1918 que não é acessível pela Internet, e gostaria que o Discourse o utilizasse. Não sou muito fã de servidores de banco de dados rodando no mesmo servidor que o servidor web/aplicação.

Então, existe alguma maneira de separar o banco de dados standalone e movê-lo para meu cluster de banco de dados dedicado?

Estou assumindo que tudo o que precisarei fazer é fazer um pgdump do banco de dados do Discourse, movê-lo para meu banco de dados dedicado e restaurá-lo, seguido por um postgres vacuum analyze em todas as tabelas após a restauração/importação e, em seguida, apenas apontar o aplicativo Discourse para o novo banco de dados através da rede?

Mas não consigo encontrar onde as credenciais do banco de dados estão armazenadas. Olhei no app.yml, mas não parecem haver entradas de banco de dados, e quando olhei na pasta ../templates/, nenhum dos arquivos yml parecia ter credenciais de banco de dados.

O que estou tentando fazer é mesmo possível?"

1 curtida

Você pode seguir este guia que aborda a configuração das credenciais:

3 curtidas

Excelente! Isso é exatamente o que eu estava procurando. Muito obrigado!

Agora, para fazer o nginx ver o IP real atrás do CloudFlare… :wink:

Se normalmente não há credenciais para o banco de dados PostgreSQL integrado, existe alguma maneira de executar um pg_dump e exportar o banco de dados a partir do contêiner standalone?

1 curtida

O backup padrão do Discourse é um pg_dump em um arquivo tar, então você pode usá-lo.

Se quiser fazer algo personalizado, como encaminhar o dump ou usar um formato diferente, você sempre pode:

ssh root@forum
cd /var/discourse
./launcher enter app
su postgres
psql # ou pg_dump
2 curtidas