web-dev-qa-db-fra.com

Le script de démarrage Apache garde le fichier verrou ouvert

J'écris un script bash pour une tâche de sauvegarde. Entre autres choses (gel du système de fichiers, verrouillage de la base de données, instantanés, ...), il faut arrêter Apache et le redémarrer après la sauvegarde. Comme ce script doit être exécuté comme une tâche cronjob et que les temps d'exécution peuvent varier considérablement (notamment parce qu'il attend un "bon moment" pour effectuer la sauvegarde), j'ai essayé de le protéger des exécutions multiples à l'aide de flock.

Cependant, flock conserve le verrou même après la fermeture du script de sauvegarde. Ce comportement est indépendant de la façon dont j'utilise flock (utilisez un répertoire, un fichier ou un descripteur de fichier ouvert à partir du script).

J'ai traqué le problème jusqu'au redémarrage d'Apache2 et je peux le voir dans le one-liner suivant

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

Voir la session interactive suivante pour illustrer le problème:

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

En effet, le script Apache démarré maintient mes descripteurs de fichier de verrouillage ouverts:

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

Une fois Apache arrêté, le verrou est libéré:

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  

C'est pourquoi ma question (s): Comment le processus Apache "hérite-t-il" de ces descripteurs de fichier? Pourquoi le même comportement ne se produit-il pas avec d'autres scripts de démarrage (par exemple, "service mysql start")? Et y a-t-il un moyen de l'éviter?

3
mastov

Puisque personne n'a écrit de réponse (malgré la prime), je vais m'expliquer comment j'ai finalement résolu le problème.

Le commentaire de muru (merci beaucoup!) était juste et m'a amené sur la bonne voie: dans Ubuntu 14.04, le démarrage de Apache est géré par un sysv script init, et étant un script shell, il hérite de tout (variables d’environnement, descripteurs de fichiers ouverts, etc.), et le processus Apache qu’il a démarré héritera également de tous ces éléments. Mysql, à son tour, est géré par Upstart, ce qui garantit un environnement propre pour les services démarrés. Ceci explique le comportement différent de Mysql et Apache.

Armé de cette connaissance, je savais mieux quoi chercher et j'ai trouvé cette réponse sur unix.stackexchange.com . Il suggère de fermer le descripteur de fichier dans le sous-shell qui appelle des scripts qui ne devraient pas en hériter, tout en maintenant le descripteur ouvert dans le shell externe, afin de s'assurer que le verrouillage reste effectif.

Malheureusement, cela signifie que j'ai dû déplacer les appels flock dans le script Shell, sinon mon script ne saurait pas quel descripteur de fichier fermer. Ainsi, mon nouveau script Shell (qui tire également son inspiration de ce billet de blog sur flock ) a la structure suivante:

#!/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>-"
3
mastov