Conquistando a implementação de inatividade zero

40

Estou tentando obter implantações com tempo de inatividade zero para que eu possa implantar menos durante as horas de folga e mais durante as horas "mais lentas" - ou a qualquer momento, em teoria.

Minha configuração atual, um pouco simplificada:

  • Servidor Web A (aplicativo .NET)
  • Servidor da Web B (.NET App)
  • Servidor de banco de dados (SQL Server)

Meu processo de implantação atual:

  1. "Pare" os sites no Web Server A e B
  2. Atualize o esquema do banco de dados para a versão do aplicativo que está sendo implementado
  3. Atualizar o servidor da Web
  4. Atualizar servidor da Web B
  5. Coloque tudo de volta online

Problema atual

Isso leva a uma pequena quantidade de tempo de inatividade todo mês - cerca de 30 minutos. Eu faço isso durante as horas de folga, então não é um grande problema - mas é algo que eu gostaria de evitar.

Além disso, não há como voltar atrás. Eu geralmente não faço scripts DB de reversão - apenas atualizo scripts.

Aproveitando o balanceador de carga

Adoraria poder atualizar um servidor da Web de cada vez. Retire o Web Server A do balanceador de carga, atualize-o, coloque-o de volta on-line e repita para o Servidor da Web B.

O problema é o banco de dados. Cada versão do meu software precisará ser executada em uma versão diferente do banco de dados - então estou meio que "preso".

Solução possível

Uma solução atual que estou considerando é a adoção das seguintes regras:

  • Nunca exclua uma tabela de banco de dados.
  • Nunca exclua uma coluna do banco de dados.
  • Nunca renomeie uma coluna de banco de dados.
  • Nunca reordene uma coluna.
  • Todo procedimento armazenado deve ter versão.
    • Significado - 'spFindAllThings' se tornará 'spFindAllThings_2' quando for editado.
    • Em seguida, ele se torna "spFindAllThings_3" quando editado novamente.
    • A mesma regra se aplica a visualizações.

Enquanto isso, parece um pouco extremo - acho que resolve o problema. Cada versão do aplicativo estará atingindo o banco de dados de maneira não violenta. O código espera certos resultados das visualizações / procedimentos armazenados - e isso mantém esse 'contrato' válido. O problema é - apenas escoa desleixado. Eu sei que posso limpar antigos procedimentos armazenados depois que o aplicativo é implantado por algum tempo, mas parece sujo. Também - isso depende de todos os desenvolvedores que seguem essas regras, o que acontecerá principalmente, mas imagino que alguém cometerá um erro.

Finalmente - minha pergunta

  • Isso é desleixado ou hacky?
  • Alguém mais está fazendo isso dessa maneira?
  • Como outras pessoas estão resolvendo esse problema?
por MattW 24.06.2013 / 14:46
fonte

4 respostas

14

Esta é uma abordagem muito pragmática para atualizações de software apoiadas por banco de dados. Foi descrito por Martin Fowler e Pramod Sadalage em 2003 e posteriormente escrito em Refactoring Databases: Evolutionary Database Design .

Eu posso ver o que você quer dizer quando diz que parece desleixado, mas quando feito intencionalmente e com premeditação, e aproveitando para refazer as estruturas não utilizadas da base de código e banco de dados quando elas demonstravelmente não são mais usadas, é muito mais robusto do que soluções mais simples baseadas em scripts de atualização e reversão.

    
por 24.06.2013 / 14:57
fonte
5

"Tempo de inatividade zero" é apenas uma das muitas possíveis razões para esse tipo de abordagem. Manter um modelo de dados compatível com versões anteriores ajuda você a lidar com muitos problemas diferentes:

  • se você tiver muitos pacotes de software acessando seu banco de dados, não precisará verificar todos eles se uma alteração de esquema os afetar (em organizações maiores com várias equipes criando programas acessando o mesmo banco de dados, alterações de esquema pode se tornar muito difícil)

  • Se precisar, você pode verificar uma versão mais antiga de um de seus programas e provavelmente executará novamente um banco de dados mais novo (contanto que você não espere que o programa antigo manipule as colunas mais recentes corretamente )

  • A importação / exportação de dados arquivados para a versão atual do banco de dados é muito mais fácil

Aqui está uma regra adicional para sua lista

  • cada nova coluna deve ser NULÁVEL ou fornecer um valor padrão significativo

(isso garante que até mesmo programas mais antigos que não conhecem as novas colunas não quebrarão nada quando criarem novos registros em seu banco de dados).

Naturalmente, essa abordagem tem uma desvantagem real: a qualidade do seu modelo de dados pode diminuir com o tempo. E se você tiver controle total sobre todos os aplicativos que acessam seu banco de dados e puder refatorá-los facilmente quando, por exemplo, for renomear uma coluna, talvez considere refatorar as coisas de maneira mais limpa.

    
por 24.06.2013 / 15:21
fonte
3

Ele varia de uma implantação para outra.

Claro, você nunca poderá excluir uma tabela ou coluna. Você nunca poderia mudar nada que quebrasse a compatibilidade da interface. Você sempre pode adicionar uma camada de abstração. Mas então você tem que versão essa abstração e a versão do versionamento.

A pergunta que você precisa fazer é: cada release altera o esquema de tal forma que ele não é compatível com versões anteriores?

Se muito poucas versões alterarem o esquema dessa forma, o problema do banco de dados será mudo. Basta fazer uma implantação contínua dos servidores de aplicativos.

As duas coisas que eu vi mais ajudar com a implantação mínima de tempo de inatividade são:

  1. Esforce-se por compatibilidade com versões anteriores - pelo menos em um único lançamento. Você nem sempre conseguirá, mas posso apostar que você pode conseguir 90% ou mais dos seus lançamentos, especialmente se cada lançamento for pequeno.
  2. Tenha um script de banco de dados de pré-lançamento e pós-lançamento. Isso permite que você gerencie renomeações ou alterações de interface criando o novo objeto antes da implementação do código do aplicativo e, em seguida, descartando o antigo após a implementação do código do aplicativo. Se você adicionar uma nova coluna não anulável, poderá adicioná-la como anulável em seu script de pré-lançamento com um acionador que preenche um valor padrão. Então, no seu pós-lançamento, você pode soltar o gatilho.

Espero que o restante de suas implantações possam ser salvas nas janelas de manutenção.

Outras ideias que podem ajudar a lidar com as poucas implantações que exigem tempo de inatividade:

  • Você pode criar compatibilidade com versões anteriores em seu código? Por exemplo, existe alguma maneira de o seu código suportar vários tipos de conjuntos de resultados? Se você precisar alterar uma coluna de um int para um double, o código do aplicativo poderá lê-la como uma string e analisá-la. Tipo de hacky, mas se é um código temporário para passar pelo processo de lançamento, pode não ser o fim do mundo.
  • Os procedimentos armazenados podem ajudar a isolar o código do seu aplicativo das alterações no esquema. Isso só pode ir tão longe, mas ajuda um pouco.
por 25.06.2013 / 03:58
fonte
2

Você poderia fazer isso desse jeito para um pouco de esforço extra.

  1. Fazer backup do banco de dados fazendo uma exportação
  2. Importe o backup, mas renomeie-o com uma versão de lançamento, por exemplo, myDb_2_1
  3. Execute o lançamento do banco de dados no myDB_2_1
  4. "Interromper" o pool de aplicativos no servidor Web A ou retirá-lo do balanceador de carga
  5. Atualize o Servidor da Web A, execute os testes de pós-implementação e reverta, se necessário
  6. Sessione o servidor Web B e coloque o servidor Web A de volta em loop
  7. Atualize o servidor da Web B e, em seguida, retorne ao balanceador de carga

Naturalmente, as atualizações da Web precisariam de novas entradas de configuração para apontar para o novo esquema Db. A coisa é se você está fazendo lançamentos uma vez por mês e é uma pequena equipe quantas mudanças de banco de dados você realmente está fazendo que não são compatíveis com versões anteriores? Se você puder controlar isso testando, você pode obter uma implantação automatizada sem tempo de inatividade ou, no máximo, apenas 5 minutos de inatividade.

    
por 25.06.2013 / 00:39
fonte