J'essaie d'exécuter une tâche cron dans un conteneur de menu fixe qui appelle un script Shell.
Hier, j'ai effectué des recherches sur le Web et le débordement de pile, mais je ne pouvais pas vraiment trouver une solution qui fonctionne.
Comment puis-je faire ceci?
MODIFIER:
J'ai créé un référentiel github (commenté) avec un conteneur cron docker actif qui appelle un script Shell à un intervalle donné.
Vous pouvez copier votre crontab dans une image afin que le conteneur lancé à partir de cette image exécute le travail.
Voir " Exécuter un travail cron avec Docker " de Julien Boulay in his Ekito/docker-cron
:
Créons un nouveau fichier appelé "
crontab
" pour décrire notre travail.
* * * * * echo "Hello world" >> /var/log/cron.log 2>&1
# An empty line is required at the end of this file for a valid cron file.
Le fichier DockerFile suivant décrit toutes les étapes pour créer votre image.
FROM ubuntu:latest
MAINTAINER [email protected]
RUN apt-get update && apt-get -y install cron
# Add crontab file in the cron directory
ADD crontab /etc/cron.d/hello-cron
# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/hello-cron
# Apply cron job
RUN crontab /etc/cron.d/hello-cron
# Create the log file to be able to run tail
RUN touch /var/log/cron.log
# Run the command on container startup
CMD cron && tail -f /var/log/cron.log
(voir Gaafar 's commentaire et Comment puis-je installer apt-get
moins bruyant? :apt-get -y install -qq --force-yes cron
peut aussi fonctionner)
OU, assurez-vous que votre travail lui-même redirige directement vers stdout/stderr au lieu d'un fichier journal, comme décrit dans hugoShaka 's answer :
* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2
Remplacez la dernière ligne Dockerfile par
CMD ["cron", "-f"]
Voir aussi (à propos de cron -f
, c'est-à-dire que cron "foreground") " docker ubuntu cron -f
ne fonctionne pas "
Construisez et lancez:
Sudo docker build --rm -t ekito/cron-example .
Sudo docker run -t -i ekito/cron-example
Soyez patient, attendez 2 minutes et votre ligne de commande devrait afficher:
Hello world
Hello world
Eric ajoute dans les commentaires :
Notez que
tail
peut ne pas afficher le fichier correct s’il est créé lors de la création de l’image.
Si tel est le cas, vous devez créer ou toucher le fichier lors de l'exécution du conteneur pour que tail puisse récupérer le fichier correct.
Voir " La sortie de tail -f
à la fin d'un menu fixe CMD
n'est pas affichée ".
La solution adoptée peut être dangereuse dans un environnement de production.
Dans docker, vous ne devez exécuter qu'un seul processus par conteneur car, si vous ne le faites pas, le processus qui se divise en arrière-plan n'est pas surveillé par docker et peut s'arrêter à votre insu.
Lorsque vous utilisez CMD cron && tail -f /var/log/cron.log
, le processus cron fork essentiellement pour exécuter cron
en arrière-plan, le processus principal se termine et vous permet d'exécuter tailf
en avant-plan. Le processus cron peut s'arrêter ou échouer, vous ne le remarquerez pas, votre conteneur continuera à fonctionner en mode silencieux et votre outil d'orchestration ne le redémarrera pas.
Vous pouvez éviter une telle chose en redirigeant directement la sortie des commandes de cron dans votre menu fixe
stdout
etstderr
qui se trouvent respectivement dans/proc/1/fd/1
et/proc/1/fd/2
.
En utilisant des commandes bash basiques, vous voudrez peut-être faire quelque chose comme ceci:
* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2
Et votre CMD sera: CMD ["cron", "-f"]
Ce que @VonC a suggéré, c’est bien, mais je préfère effectuer toute la configuration des tâches cron sur une seule ligne. Cela éviterait les problèmes de plates-formes croisées comme l'emplacement de cronjob et vous n'avez pas besoin d'un fichier cron séparé.
FROM ubuntu:latest
# Install cron
RUN apt-get -y install cron
# Create the log file to be able to run tail
RUN touch /var/log/cron.log
# Setup cron job
RUN (crontab -l ; echo "* * * * * echo "Hello world" >> /var/log/cron.log") | crontab
# Run the command on container startup
CMD cron && tail -f /var/log/cron.log
Après avoir exécuté votre conteneur Docker, vous pouvez vérifier si le service cron fonctionne:
# To check if the job is scheduled
docker exec -ti <your-container-id> bash -c "crontab -l"
# To check if the cron service is running
docker exec -ti <your-container-id> bash -c "pgrep cron"
Si vous préférez utiliser ENTRYPOINT au lieu de CMD, vous pouvez remplacer le CMD ci-dessus par
ENTRYPOINT cron start && tail -f /var/log/cron.log
Pour ceux qui veulent utiliser une image simple et légère:
FROM Alpine:3.6
# copy crontabs for root user
COPY config/cronjobs /etc/crontabs/root
# start crond with log level 8 in foreground, output to stderr
CMD ["crond", "-f", "-d", "8"]
Où cronjobs est le fichier contenant vos cronjobs, sous cette forme:
* * * * * echo "hello stackoverflow" >> /test_file 2>&1
# remember to end this file with an empty new line
Il existe un autre moyen de le faire, consiste à utiliser Tasker , un exécuteur de tâches prenant en charge cron (un planificateur).
Pourquoi ? Parfois, pour exécuter un travail cron, vous devez mélanger votre image de base (python, Java, nodejs, Ruby) avec le fichier crond. Cela signifie une autre image à maintenir. Tasker éviter cela en découplant le crond et votre conteneur. Vous pouvez simplement vous concentrer sur l'image sur laquelle vous voulez exécuter vos commandes et configurer Tasker pour l'utiliser.
Voici un fichier docker-compose.yml
, qui exécutera certaines tâches pour vous
version: "2"
services:
tasker:
image: strm/tasker
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
environment:
configuration: |
logging:
level:
ROOT: WARN
org.springframework.web: WARN
sh.strm: DEBUG
schedule:
- every: minute
task: hello
- every: minute
task: helloFromPython
- every: minute
task: helloFromNode
tasks:
docker:
- name: hello
image: debian:jessie
script:
- echo Hello world from Tasker
- name: helloFromPython
image: python:3-slim
script:
- python -c 'print("Hello world from python")'
- name: helloFromNode
image: node:8
script:
- node -e 'console.log("Hello from node")'
Il y a 3 tâches, toutes s'exécutent toutes les minutes (every: minute
) et chacune exécutera le code script
, dans l'image définie dans la section image
.
Il suffit d'exécuter docker-compose up
et de le voir fonctionner. Voici le rapport Tasker avec la documentation complète:
La réponse de VonC est assez approfondie. De plus, j'aimerais ajouter une chose qui m'a aidé. Si vous souhaitez simplement exécuter un travail cron sans modifier le fichier, tentez simplement de supprimer le && tail -f /var/log/cron.log
de la commande cron. Cependant, cela entraînera la fermeture du conteneur de menu fixe peu de temps après son exécution car, une fois la commande cron terminée, docker pense que la dernière commande s'est terminée et donc tue le conteneur. Cela peut être évité en exécutant cron au premier plan via cron -f
.
J'écrirais cela comme un commentaire, mais je n'ai pas encore assez de points :)
J'ai créé une image Docker basée sur les autres réponses, qui peuvent être utilisées comme
docker run -v "/path/to/cron:/etc/cron.d/crontab" [gaafar/cron][1]
où /path/to/cron
: chemin absolu du fichier crontab
ou utiliser comme base dans un fichier Dockerfile
FROM gaafar/cron
# COPY crontab file in the cron directory
COPY crontab /etc/cron.d/crontab
# Add your commands here
Définissez le travail cron dans un conteneur dédié qui exécute la commande via docker exec sur votre service.
C'est une cohésion supérieure et le script en cours aura accès aux variables d'environnement que vous avez définies pour votre service.
#docker-compose.yml
version: "3.3"
services:
myservice:
environment:
MSG: i'm being cronjobbed, every minute!
image: Alpine
container_name: myservice
command: tail -f /dev/null
cronjobber:
image: docker:Edge
volumes:
- /var/run/docker.sock:/var/run/docker.sock
container_name: cronjobber
command: >
sh -c "
echo '* * * * * docker exec myservice printenv | grep MSG' > /etc/crontabs/root
&& crond -f"
Bien que cela ait pour but d’exécuter des tâches à côté d’un processus en cours dans un conteneur via l’interface exec
de Docker, cela peut vous intéresser.
J'ai écrit un démon qui observe les conteneurs et planifie les travaux définis dans leurs métadonnées. Exemple:
version: '2'
services:
wordpress:
image: wordpress
mysql:
image: mariadb
volumes:
- ./database_dumps:/dumps
labels:
deck-chores.dump.command: sh -c "mysqldump --all-databases > /dumps/dump-$$(date -Idate)"
deck-chores.dump.interval: daily
La configuration «classique», semblable à celle du cron, est également possible.
Lorsque vous déployez votre conteneur sur un autre hôte, notez qu'il ne lancera aucun processus automatiquement. Vous devez vous assurer que le service 'cron' fonctionne dans votre conteneur . Dans notre cas, j'utilise Supervisord avec d'autres services pour démarrer le service cron.
[program:misc]
command=/etc/init.d/cron restart
user=root
autostart=true
autorestart=true
stderr_logfile=/var/log/misc-cron.err.log
stdout_logfile=/var/log/misc-cron.out.log
priority=998
Donc, mon problème était le même. Le correctif consistait à modifier la section de commande dans le docker-compose.yml
.
De
commande: crontab/etc/crontab && tail -f/etc/crontab
À
commande: crontab/etc/crontab
commande: tail -f/etc/crontab
Le problème était le '&&' entre les commandes. Après avoir supprimé cela, tout allait bien.
Les tâches Cron sont stockées dans/var/spool/cron/crontabs (lieu commun dans toutes les distributions que je connais). BTW, vous pouvez créer un onglet cron dans bash en utilisant quelque chose comme ça:
crontab -l > cronexample
echo "00 09 * * 1-5 echo hello" >> cronexample
crontab cronexample
rm cronexample
Cela va créer un fichier temporaire avec une tâche cron, puis le programmer en utilisant crontab. Dernière ligne supprime le fichier temporaire.
Lors de l'exécution sur des images réduites qui restreignent l'accès root, je devais ajouter mon utilisateur aux utilisateurs sudoers et l'exécuter en tant que Sudo cron
FROM node:8.6.0
RUN apt-get update && apt-get install -y cron Sudo
COPY crontab /etc/cron.d/my-cron
RUN chmod 0644 /etc/cron.d/my-cron
RUN touch /var/log/cron.log
# Allow node user to start cron daemon with Sudo
RUN echo 'node ALL=NOPASSWD: /usr/sbin/cron' >>/etc/sudoers
ENTRYPOINT Sudo cron && tail -f /var/log/cron.log
Peut-être que cela aide quelqu'un