O script de inicialização do Apache mantém o arquivo de bloqueio aberto

3

Estou escrevendo um script bash para uma tarefa de backup. Entre outras coisas (congelamento do sistema de arquivos, bloqueio de banco de dados, snapshots, ...), isso envolve desligar o Apache e reiniciá-lo após o Backup. Como esse script deve ser executado como um cronjob e os tempos de execução podem variar muito (especialmente porque o script espera por um "momento bom" para fazer o Backup), tentei protegê-lo de várias execuções usando flock .

No entanto, flock mantém o bloqueio mesmo após o script de backup sair. Esse comportamento é independente da maneira como eu uso flock (use um diretório, um arquivo ou um descritor de arquivo aberto a partir do script).

Eu procurei o problema até o reinício do apache2 e posso vê-lo no seguinte one-liner

flock -n /var/lock/startapache service apache2 start

Veja a seguinte sessão interativa para ilustrar o problema:

root@fermat:/home/ubuntu# service apache2 stop
 * Stopping web server apache2                                                                                                                  ... waiting .                                                                                                                          [ OK ]
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service apache2 start || echo failed
 * Starting web server apache2                                                                                                          [ OK ] 
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service apache2 start || echo failed
failed

Isso porque o script Apache iniciado mantém meus descritores de arquivos de bloqueio abertos:

root@fermat:/home/ubuntu# lsof /var/lock/startapache 
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
apache2 23651 root    3u   REG   0,17        0 6673997 /run/lock/startapache
apache2 23656 trac    3u   REG   0,17        0 6673997 /run/lock/startapache
apache2 23674 trac    3u   REG   0,17        0 6673997 /run/lock/startapache
apache2 23675 root    3u   REG   0,17        0 6673997 /run/lock/startapache
apache2 23676 root    3u   REG   0,17        0 6673997 /run/lock/startapache
apache2 23677 root    3u   REG   0,17        0 6673997 /run/lock/startapache
apache2 23694 root    3u   REG   0,17        0 6673997 /run/lock/startapache
apache2 23696 root    3u   REG   0,17        0 6673997 /run/lock/startapache

Depois de encerrar o Apache, o bloqueio é liberado novamente:

root@fermat:/home/ubuntu# service apache2 stop
 * Stopping web server apache2                                                                                                                  ... waiting                                                                                                                            [ OK ]
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service apache2 start || echo failed
 * Starting web server apache2  

Portanto, minha (s) pergunta (s): Como o processo do Apache "herda" esses descritores de arquivos? Por que o mesmo comportamento não ocorre com outros scripts de inicialização (por exemplo, "service mysql start")? E existe uma maneira de evitar isso?

    
por mastov 29.04.2016 / 12:43

1 resposta

3

Como ninguém escreveu uma resposta (apesar da generosidade), vou me explicar como resolvi isso finalmente.

O comentário de

muru (muito obrigado!) estava certo e me trouxe no caminho certo: No Ubuntu 14.04, a inicialização do Apache é gerenciada por um script init sysv, e sendo um shell script Ele herda tudo (variáveis de ambiente, descritores de arquivos abertos, etc.), e o processo do apache iniciado por ele também herdará todos eles. O Mysql, por sua vez, é gerenciado pelo Upstart, o que garante um ambiente limpo para os serviços iniciados. Isso explica o comportamento diferente do Mysql e do Apache.

Armado com esse conhecimento, eu sabia melhor o que procurar e encontrei esta resposta em unix.stackexchange.com . Ele sugere fechar o descritor de arquivo no sub-shell que chama scripts que não devem herdá-lo, enquanto mantém o descritor aberto no shell externo, para garantir que o bloqueio permaneça efetivo.

Infelizmente, isso significa que eu tive que mover as chamadas de flock para dentro do script de shell, caso contrário, meu script não saberia qual descritor de arquivo deve ser fechado. Então, meu novo shell script (que também se inspirou no este post no blog sobre flock ) tem a seguinte estrutura:

#!/bin/bash

FLOCK_FILE="/var/lock/backup-lock"
FLOCK_FD=20

# Locking
eval "exec $FLOCK_FD>'$FLOCK_FILE'"
if ! flock -n $FLOCK_FD
then
  echo "FAILED! There is a backup script already running."
  exit 1
fi

(
  # Unlock in sub-shell, so daemons with bad startup scripts
  # (like Apache) don't inherit the look.
  # Note that the lock is still alive in general because it's
  # held by the outer shell.
  eval "exec $FLOCK_FD>-"


  # ... normal backup stuff from the original script ...

  # Among other stuff the mentioned vicious line:
  service apache2 start

  # ... normal backup stuff from the original script ...
)

# Unlock in outer shell because we're done.
eval "exec $FLOCK_FD>-"
    
por mastov 10.05.2016 / 13:20