Cópia atômica de vários diretórios

3

Esta questão está relacionada à implantação de aplicativos da Web.

Introdução (você pode pular)

Estou usando o django, e o modo como meu provedor de hospedagem configurou o suporte ao django acaba fazendo com que o webapp seja espalhado em pelo menos três locais:

  • Código de aplicativo real em /something/<my_apps>/
  • Configurações do site / URLs / modelos em /something/<my_site>/
  • css, javascript e outras "mídias" em /something_else/media/

Portanto, quando eu implantar / atualizar o site, preciso atualizar vários diretórios de uma só vez.

Pergunta real:

Existe uma maneira de fazer uma cópia atômica de arquivos? Eu não sou "especialista" em sistemas Linux, por favor, perdoe minha ignorância.

A operação de cópia envolveu várias árvores de diretórios, duas ou três, basicamente:

copy _tree1 to tree1
copy _tree2 to tree2

Por atômica, quero dizer:

  • É totalmente copiado ou não é copiado. Nunca deve estar em um estado de alguma cópia, mas alguma falha.
  • É feito no menor tempo possível. O ideal é que não haja nenhum ponto no tempo em que o sistema veja a cópia em andamento, ou veja a versão antiga dos arquivos ou a nova versão; em nenhum momento poderá ver a versão antiga do arquivo A, mas a nova versão do arquivo B. Se isso não for inteiramente possível, então não deve demorar mais do que alguns milésimos de segundo.

Minha idéia é ter algo como double buffering: Eu preparo tudo em uma área de preparação, por exemplo, _tree_x e, em seguida, copie mova para tree_x com deve ser uma operação atômica que simplesmente muda de ponteiros no disco.

Eu acho que uma única operação de mover copy é atômica no linux, (não é?), mas eu preciso que várias dessas operações sejam atômicas também; Eu quero que eles sejam tratados como se fossem uma única operação de movimento.

    
por hasen 29.07.2009 / 15:12

6 respostas

6

Acho que você está no caminho certo com uma área de teste. Eu não estou ciente de nenhum comando atômico, mas se você organizar seus arquivos e então usar um script para remover o primeiro diretório e mover (não copiar) o segundo, e fazê-lo para todos os três diretórios, ele deve ser muito rápido. / p>

Como alternativa, você pode querer usar links simbólicos. Dessa forma, você poderia ter aproximadamente:

/version/22/<my-apps>
/version/22/<my-site>
/version/22/<my-media>

e implantar um diretório /version/23 com os mesmos subdiretórios. Então, onde o arquivo real iria (e novamente, para velocidade, você precisará de um script), você pode usar um link simbólico para que quando alguém for para a página mais recente, eles obtenham qualquer versão atual (e tudo acontece transparentemente e eles não têm idéia). O benefício disso é que o seu trabalho antigo ainda está por aí até que você decida excluí-lo. [Embora, concedido, um sistema de controle de versão seja a melhor opção para manter o trabalho mais antigo.]

Você teria que verificar que 1) você pode executar scripts (e de tal forma que os usuários da web não podem!), e 2) que você pode usar links simbólicos (como alguns servidores web estão configurados para não segui-los. )

    
por 29.07.2009 / 15:22
2

Talvez eu não esteja pensando totalmente nisso, mas por que não fazer sua operação de cópia em um novo diretório? Quando é feito "mv" o diretório antigo para outro nome e "mv" o novo diretório para o nome desejado.

Isso não é tecnicamente atômico, já que há um período em que o diretório antigo será movido e o novo diretório ainda não estará no lugar, mas pode ser bom o suficiente.

    
por 29.07.2009 / 15:19
0

Para sites grandes, a atualização de um site pode ser feita com vários servidores lidando com solicitações. Você pode então desligar um servidor, atualizá-lo e depois colocá-lo novamente online, repetindo para outros servidores no cluster.

Para um único site hospedado, talvez faça sentido encerrar o site colocando uma página fechada do site em index.html na pasta raiz e, em seguida, fazendo as alterações.

Se você realmente precisa manter o site funcionando o máximo de tempo possível, posso sugerir o seguinte:

  1. Cópias atômicas não existem, no entanto, renomear uma única pasta acontece atomicamente. Ao colocar as renomeações em um script e executar o script, você pode ter uma série de renomeações muito rápidas, uma após a outra. Você precisará do dobro do espaço em disco do site para ter as pastas antes e depois no servidor.

  2. Isso não resolve seus problemas - apenas reduz a exposição. As versões before e after podem precisar de campos de dados do banco de dados diferentes, portanto, a consulta SQL também precisará ser executada. Uma pessoa pode estar carregando uma página ao mesmo tempo em que sua atualização ocorre. O início do carregamento da página da Web pode carregar páginas antes da alteração e as últimas partes do carregamento da página podem usar arquivos após a cópia.

por 29.07.2009 / 17:03
0

Primeiro, a melhor maneira de fazer isso seria alterar a configuração do httpd para apontar para os novos diretórios e, em seguida, reiniciar o httpd. Eu suponho que isso não é possível.

Eu tenho uma idéia que assume que os dados em seus três diretórios não estão mudando o tempo todo, pois requer movimentos não-atômicos desses três diretórios originais para cópias desses diretórios. Não tenho 100% de certeza de que isso funcionaria, mas você poderia testá-lo. É mais fácil escrever isso como um roteiro do que explicá-lo em inglês. Deixe-me saber se preciso explicar mais alguma coisa.

Suponha que os três caminhos nominais sejam: / pathA / dir1, / pathA / dir2, / pathB / dir3

mkdir /pathC
ln -s pathC /linkI
cd /pathA
tar pcf - dir1 | (cd /pathC; tar pxf -)
tar pcf - dir2 | (cd /pathC; tar pxf -)
cd /pathB
tar pcf - dir3 | (cd /pathC; tar pxf -)
cd /pathA
mv dir1 dir1.orig && ln -s /linkI/dir1 dir1
mv dir2 dir2.orig && ln -s /linkI/dir2 dir2
cd /pathB
mv dir3 dir3.orig && ln -s /linkI/dir3 dir3
mkdir /pathD
cd /pathD
mkdir dir1
mkdir dir2
mkdir dir3
(cd dir1 && )
(cd dir2 && )
(cd dir3 && )
cd /
ln -sf pathD /linkI

(Edit: Hm, de alguma forma eu perdi a resposta de Clinton Blackmore acima, que é basicamente idêntica ao que eu estou sugerindo. Então, não importa.)

    
por 29.07.2009 / 19:10
0

Para aqueles que lêem esta velha questão, é possível, mas requer muitos links simbólicos.

Minha resposta é baseada na de Clinton Blackmore (a resposta atualmente aceita).

Vários diretórios (ou vários arquivos, para esse assunto) não podem ser alterados atomicamente. Portanto, não podemos usar diretórios diretamente. Arquivos únicos podem ser atualizados atomicamente usando a chamada de sistema rename () substituindo o arquivo antigo pelo novo. Os links simbólicos podem ser atualizados da mesma maneira, usando mv -T para permitir que mv não desreferencie o destino.

Então, se tivermos esses diretórios:

/srv/dirA/
/srv/dirB/
/srv/dirC/

Podemos fazer com que todos sejam links simbólicos para três outros diretórios:

/srv/dirA -> /version/current/dirA
/srv/dirB -> /version/current/dirB
/srv/dirC -> /version/current/dirC

/version/current , por sua vez, é apenas um link simbólico para o diretório com a versão atual:

/version/current -> /version/22/

Toda a aplicação web pode então ser atualizada com dois comandos simples, dos quais o último 'substitui' todos os três diretórios de uma só vez (ele não substitui os diretórios, apenas substitui onde esses diretórios apontam):

$ ln -s /version/23/ /version/next
$ mv -T /version/next /version/current

Eu não testei isso, mas deve funcionar. O sinalizador -T pode ser um sinalizador não padrão. Como alternativa, python -c "import os; os.rename('/version/next', '/version/current')" pode ser usado no lugar (também não testado).

Eu não sei qual será o impacto no desempenho, mas duvido que seja significativo se você já estiver executando o Django. Eu acho que pode ser importante se você está servindo grandes quantidades de arquivos estáticos com a menor latência possível (como CDNs), e mesmo assim, provavelmente, será apenas um pequeno impacto no desempenho. Em suma, você não precisa se preocupar com o desempenho.

Note que existem algumas armadilhas: o Django é um servidor e não será reiniciado exatamente ao mesmo tempo. Para que isso seja verdadeiramente atômico, o Django teria que ser configurado de uma maneira que pularia completamente a noção de 'versão atual'. Em vez disso, você iniciaria o Django a partir da versão atual para uso em produção. Para atualizar, a próxima versão seria iniciada, então o servidor seria reiniciado (suponho que a maioria dos servidores da Web tenha alguma forma de reiniciar sem ficar offline), e todo o processo deve ser atômico. Mas eu não sou especialista nessa área.

Outra pegadinha (como mencionado por Ptolemy) é que em um servidor ocupado, serão algumas pessoas que vêem páginas parcialmente de uma versão e parcialmente da outra, devido ao cache e ao fato de que vários recursos são solicitados em momentos diferentes durante o pageload (pode haver alguns segundos entre as cargas de recursos). Penso nos dois, o caching será o mais importante, mas também o mais fácil de se trabalhar. Eu duvido que isso seja um grande problema na prática.

    
por 26.02.2014 / 23:03
0

Você já tentou usar o atomic-rsync ? Ele usa uma combinação de comandos rsync e mv conforme sugerido em outras respostas.

    
por 20.11.2015 / 07:54