Planifier une action via cron dans un container Docker

closeCet article a été publié il y a 6 ans 3 mois 24 jours, il est donc possible qu’il ne soit plus à jour. Les informations proposées sont donc peut-être expirées.

Lors de la transformation de l’installation de ma forge personnelle pour la passer sous Docker Swarm, j’ai développé un petit script python pour gérer la mise à jour du sous-domaine de connexion SSH. Mon besoin était simple, donc l’exécution régulière est directement gérée dans le script, mais cette solution pratique manque cruellement de souplesse quand on veut passer à quelque chose de plus fin et complexe. Voyons comment faire plus puissant, sans que ça ne soit horriblement compliqué.

Dans le monde traditionnel de l’informatique, on imagine sans mal aussi bien sous Windows (tâches planifiées) que Linux (cron mon amour), les solutions pour planifier l’exécution de tâches à intervalles plus ou moins régulier (du style, du lundi au vendredi entre 8h et 18h). Mais quand votre service tourne sous Docker, à fortiori sous Docker Swarm, c’est fini, et l’exécution doit passer par d’autres moyens.

Rapide, simple, limité : inclus dans le service

Mon service consiste en un script Python unique, qui tourne en boucle infinie, avec une pause d’une minute à chaque fois. C’est bien, mais pour appliquer la plage d’horaire d’exécution que j’ai mis en exemple juste au dessus, c’est violent à écrire en Python et ne se résume plus dès lors à un sleep(60).

Mais techniquement, vous pouvez appliquer cette méthode à tous les langages qui vous permettent de faire des boucles, donc ça reste valable selon moi pour un besoin très basique.

Cron, mais pas avec la méthode habituelle

Je remercie à Djerfy d’avoir utilisé cette astuce qui a piqué ma curiosité. Pour un bot qui nous envoie régulièrement des citations du site Chuck Norris Facts, c’est via cron que le script qui concerne la connexion à l’API et l’envoi de la citation sur le channel Telegram est exécuté. Cela demande un peu plus de boulot par contre. Il faut plusieurs informations et commandes dans plusieurs fichiers, à commencer par la définition de la stack :

On retrouve le format habituel des tâches cron (minutes, heures, jour, mois, jour de la semaine), ce qui nous donnera des possibilités bien plus nombreuses. Le Dockerfile rajoute ensuite deux fichiers au lieu d’un seul :

Ce n’est donc plus le fichier python qui est appelé directement, mais init.sh. Que contient ce fichier ? Plusieurs choses, mais pour ce qui nous intéresse, voici les morceaux de choix :

Mais qu’est-ce que ça fait ce morceau de code ? Pour rappel, contrairement à un système Linux classique, le processus avec le PID 1 dans un conteneur Docker est le processus lancé par la dernière commande CMD du Dockerfile, ici init.sh du coup. Dans ce script, au delà d’une vérification de base, les deux premières lignes intéressantes concernent la définition de variables concernant les descripteurs de fichier pour le fameux processus 1; en gros, on peut rediriger les sorties standards et d’erreurs vers ces descripteurs, ce qui permet de visualiser les messages de notre application réelle via les commandes Docker sur l’hôte.

Ensuite, on définit un nouveau fichier de crontab avec les informations de l’environnement (c’est ce que fait le  echo |crontab -), et enfin on lance crond. Mais que fait donc le -f ? Au début, en fouillant « bêtement » le manuel de crond, je ne trouve pas trace du -f en question, mais c’est le cas dans le manuel de cron. Le switch -f indique à cron de rester en premier plan (foreground, pour les anglophiles qui se poseraient la question), et non de passer en mode démon, ce qui est une condition sine qua non sur le PID 1 pour qu’un container Docker reste en vie.

La puissance de Cron, avec ses faiblesses

J’imagine qu’il faudra un peu plus de boulot si vous devez intégrer plusieurs tâches planifiées dans le même container. On sera probablement plus avisé de séparer les tâches dans des containers séparés si ça concerne des scripts différents.

Cron n’est pas non plus la solution la plus parfaite, et j’ai déjà buté ou été obligé de bricoler (parfois dans le script lancé lui-même), pour obtenir le résultat que voulait le client. En gros, ça ne fait rien de plus ni de moins qu’un service cron sur un OS classique, mais ça permet déjà bien plus de possibilités que d’écrire et de conditionner en dur les exécutions de votre tâche.

1 Commentaire
Le plus ancien
Le plus récent
Commentaires en ligne
Afficher tous les commentaires
gileri
05/05/2018 22:05

crond devant être lancé en root (ce qui est bien lourd), si l’on souhaite lancer le script en utilisateur autre que root, il devient impossible d’écrire depuis le script dans /proc/1/fd à cause des permissions POSIX. La solution que j’ai utilisée est d’avoir comme commande RUN

crond && tail -F --pid=$(pidof crond)