O script é executado em 50 servidores. Como posso garantir que apenas um execute uma etapa específica?

1

Eu tenho algum trabalho que precisa ser feito em mais de 50 servidores. A primeira etapa é verificar uma versão atualizada de algum código-fonte em um diretório compartilhado (suponha que todos tenham a unidade compartilhada montada). A segunda é realizar algum trabalho em cada um dos servidores.

Eu prefiro que esses dois scripts sejam executados em cada um dos servidores. Todos os mais de 50 servidores são clonados a partir de uma única imagem de disco e não é prático personalizar nenhum deles.

Quando os 50 servidores executam o primeiro script, eu quero apenas o primeiro que tenta executá-lo para executá-lo. Os outros que eu quero simplesmente sair. O servidor que realmente executa o script deve atualizar um diretório compartilhado e, em seguida, sair. Então, mais tarde, o segundo script será executado e executará o trabalho em todos os servidores com base no código atualizado obtido pelo primeiro servidor.

Qual é a melhor maneira de fazer isso? Posso confiar que o primeiro script é executado em um servidor e criar um arquivo ou algo que atue como um 'semáforo' ou 'bloqueio' de algum tipo que mantenha os outros servidores longe?

Tornar isso mais complicado é que estou pensando em fazer com que os scripts sejam executados a partir de arquivos cron idênticos em cada um dos servidores - o que significa que todos os scripts poderiam tentar executá-lo ao mesmo tempo, assumindo que todos os relógios estão configurados de forma idêntica. / p>

Espero que estes sejam executados a partir de scripts bash. Isso faz sentido como uma abordagem?

EDIT: Atualizado com base em perguntas:

Não queremos que todos os servidores tentem fazer o checkout de sua própria cópia desses arquivos - eles estão em um repositório de código-fonte de vários GB e ter 50+ checkouts simultâneos desse código seria difícil para nosso servidor de controle de origem ( e não escalável para mais de 100 servidores).

Adicionar um cronjob aos 50+ servidores não é um grande problema, mas é mais difícil adicionar outro servidor personalizado com sua própria configuração. Já estamos clonando os 50 servidores - manter um servidor separado apenas para finalizar um código-fonte mais recente para os mais de 50 servidores acessarem parece um desperdício e irá adicionar mais sobrecarga do que apenas adicionar um script aos nossos servidores atuais.

    
por Kevin Bedell 06.05.2013 / 19:58

4 respostas

2

Três soluções.

  1. Execute a etapa "checkout" manualmente ou em um script separado em apenas um dos servidores. Esta parece ser a melhor abordagem - caso contrário, você pode se deparar com uma condição de corrida.
  2. Se você estiver disposto a aceitar uma chance de encontrar uma condição de corrida, certamente poderá tentar criar um arquivo com data e hora específico quando o primeiro script for executado. Ou, se as datas forem confiáveis o suficiente, você pode tentar verificar a data da última modificação dos arquivos com check-out.
  3. Se a personalização for realmente proibida, faça com que cada VM faça sua própria cópia dos arquivos para trabalhar, em vez de tentar usar um volume compartilhado.

Cada uma delas tem trocas, mas você não deixou claro por que deseja projetar a solução dessa maneira.

    
por 06.05.2013 / 20:18
1

Não há uma verdadeira atomicidade na rede sem muita engenharia para fornecê-la, e quanto mais engenharia for necessária, mais complicada ela será.

Existem compromissos sérios a serem considerados. Essa resposta não oferece uma visão sobre o que fazer quando o trabalho está concluído pela metade.

O NFSv3 suporta um mecanismo de bloqueio atômico em novos kernels (bem, muito antigo para ser franco) link . Assim, algum mecanismo para um semáforo, em teoria, pode ser alcançado da seguinte maneira.

  1. Um arquivo 'done' já existe no host. (este é um sinal apenas para o script 2)
  2. Abra um arquivo 'acquire' no host usando O_EXCL .
  3. Renomeie "concluído" para "pronto.privado".
  4. Faça seu trabalho especial aqui.
  5. Abra um arquivo "feito no host usando O_EXCL .
  6. Desvincular 'done.old'.
  7. Desvincular 'adquirir'

Heres algum material de script de shell de modelo que tenta isso.

#!/bin/bash
# WARNING: This is a cricital line! NEVER EDIT THIS
set -e -o noclobber

BASEPATH=/tmp
cd "${BASEPATH}"

# 1. A done file exists on the host already (this is a signal for script 2 only)
# 2. Open an 'acquire' file on the host using 'O_EXCL'.
echo > 'acquire'

# 3. Rename 'done' to 'done.old'.
mv 'done' 'done.old' 2>/dev/null || :

# 4. Do your special work here.
echo "How much wood could a woodchuck chuck if a woodchuck could chuck wood?"

# 5. Open a 'done' file using O_EXCL
echo > 'done'

# 6. Unlink 'done.old'.
unlink 'done.old' || :

# 7. Unlink 'acquire'.
unlink 'acquire'

A linha mais importante é o set -e -o noclobber , que serve a dois propósitos.

  • Garante que, se algum comando falhar, o script sairá.
  • O script não sobrescreve arquivos (faz com que ocorram no O_EXCL).

Dado o critério set , a parte funcional mais importante é echo > acquire , que abrirá atomicamente o arquivo de aquisição. Se isso falhar (porque outra pessoa o possui, mesmo que DOIS sejam abertos de uma vez, apenas um vencerá) a opção -e de set garante que o script seja encerrado.

Nunca deve haver dois desses scripts sendo executados em paralelo. Este script, no entanto, não oferece uma solução onde dois scripts são executados um após o outro (o que seria permitido em sua forma atual). Eu acho que a melhor maneira de fazer isso seria alterar o arquivo 'done' para ser um arquivo nomeado com timestamp que você procura pela existência antes que o processo comece. Assim, isso pressupõe que é "seguro" confiar no tempo como um meio para determinar a segurança da criticalidade do código.

Eu menciono que isso não é concreto. No momento, isso oferece a garantia de que dois processos não podem reivindicar o arquivo ao mesmo tempo. Como mencionado, mais modificações para permitir que ele não comece com a presença de um arquivo 'done' são necessárias.

Outras coisas não cobertas são:

  • E se o processo começar, mas não terminar?
  • Se o diretório compartilhado estiver indisponível antes ou na metade do caminho para lidar com isso.
  • Se o host está demorando muito para fazer o material "seguro" na etapa 4, como isso afeta a próxima vez em que deseja executar? Devemos usar a instância antiga uma vez que sua instância acabada ou nova?

Para cobrir esses problemas, é necessário ter um mecanismo de 'vedação' (muita mudança de infraestrutura) para garantir que a recuperação do bloqueio em outro host seja uma operação segura.

    
por 06.05.2013 / 21:06
1

Poderia sugerir o seguinte,

Nomeie um servidor como um repositório de código replicado. Você pode então cronografar as atualizações para esse repositório em qualquer intervalo. O restante dos servidores pode testar se existe um repositório local e, em seguida, rsync os arquivos do servidor nomeado. Essas informações podem ser armazenadas no espaço do servidor de arquivos compartilhados. Isso será muito fácil de automatizar e deverá ser bastante robusto.

Outra solução radical - > seria usar a sincronização bittorrent. O servidor de repositório seria lido / gravado enquanto os outros teriam um compartilhamento somente leitura. Pode ser mais rápido, pois a carga da rede será compartilhada entre os servidores. O btsync pode ser configurado através de um arquivo de configuração e o cliente linux funciona muito bem.

EDIT: você pode pular o servidor de repositório para a solução radical e ficar com o btsync.

Felicidades! :)

Danie

    
por 07.05.2013 / 09:26
0

Você terá que usar algum tipo de arquivo de bloqueio (antes de fazer qualquer coisa) que mostre o proprietário do primeiro script e o tempo de execução. Quando alguém tenta executar o script, ele deve procurar o arquivo de bloqueio e sair. No final do script (se ele foi executado), exclua o arquivo de bloqueio.

    
por 06.05.2013 / 20:27