417 votes

Comment exécuter une tâche cron à l'intérieur d'un conteneur docker ?

J'essaie d'exécuter un cronjob à l'intérieur d'un conteneur docker qui invoque un shell script.

Hier, j'ai cherché partout sur le web et sur stack overflow, mais je n'ai pas vraiment trouvé de solution qui fonctionne.
Comment puis-je le faire ?

EDIT :

J'ai créé un Dépôt github (commenté) avec un conteneur docker cron fonctionnel qui invoque un shell script à un intervalle donné.

512voto

VonC Points 414372

Vous pouvez copier votre crontab dans une image, afin que le conteneur lancé à partir de cette image exécute la tâche.

Voir " Exécuter une tâche cron avec Docker " de Julien Boulay dans son Ekito/docker-cron :

Créons un nouveau fichier appelé " hello-cron " 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.

Si vous vous demandez ce qu'est 2>&1, Ayman Hourieh explique .

Le fichier Docker suivant décrit toutes les étapes de la construction de votre image.

FROM ubuntu:latest
MAINTAINER docker@ekito.fr

RUN apt-get update && apt-get -y install cron

# Copy hello-cron file to the cron.d directory
COPY hello-cron /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 faire apt-get installer moins bruyant ? :
apt-get -y install -qq --force-yes cron peut aussi fonctionner)

Comme l'a noté Nathan Lloyd sur les commentaires :

Note rapide à propos d'un gotcha :
Si vous ajoutez un fichier script et que vous demandez à cron de l'exécuter, n'oubliez pas de
RUN chmod 0744 /the_script
Cron échoue silencieusement si vous oubliez .


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 réponse :

 * * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2

Remplacez la dernière ligne de Dockerfile par

CMD ["cron", "-f"]

Voir aussi (à propos de cron -f (c'est-à-dire cron "avant-plan") " docker ubuntu cron -f ne fonctionne pas "


Construisez et exécutez-la :

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 s'afficher :

Hello world
Hello world

Eric ajoute dans les commentaires :

Notez bien que tail peut ne pas afficher le bon fichier s'il est créé pendant la construction de l'image.
Si c'est le cas, vous devez créer ou toucher le fichier pendant l'exécution du conteneur pour que tail récupère le bon fichier.

Voir " Sortie de tail -f à la fin d'un docker CMD ne montre pas ".


Voir plus dans " Exécution de Cron dans Docker " (avril 2021) de Jason Kulatunga comme il commenté ci-dessous

Voir l'image de Jason AnalogJ/docker-cron basé sur :

  • Installation du fichier Docker cronie / crond selon la distribution.

  • un point d'entrée initialisant /etc/environment et ensuite appeler

    cron -f -l 2

1 votes

J'ai d'abord dû installer cron, car il n'est pas inclus. Mais en ajoutant ceci au Dockerfile, cela fonctionne. Merci ! RUN apt-get update && apt-get install cron

0 votes

@CHeyer OK, j'ai mis à jour la réponse pour y inclure les éléments manquants RUN commandement.

3 votes

Vous devriez probablement ajouter -y pour installer cron afin d'éviter que la construction de docker ne s'arrête

210voto

hugoShaka Points 957

La réponse acceptée peut être dangereux 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 a bifurqué et est passé en arrière-plan n'est pas surveillé et peut s'arrêter sans que vous le sachiez.

Lorsque vous utilisez CMD cron && tail -f /var/log/cron.log le processus cron bifurque essentiellement afin d'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 en arrière-plan pourrait s'arrêter ou échouer, vous ne le remarquerez pas, votre conteneur continuera de fonctionner silencieusement et votre outil d'orchestration ne le redémarrera pas.

Vous pouvez éviter cela en redirigeant directement la sortie des commandes du cron dans votre docker. stdout et stderr qui sont situés respectivement dans /proc/1/fd/1 et /proc/1/fd/2 .

En utilisant des redirections shell de base, vous pouvez faire quelque chose comme ceci :

* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2

Et votre CMD sera : CMD ["cron", "-f"]

30 votes

Joli : cron -f est pour "cron foreground". J'ai inclus votre réponse dans la mienne ci-dessus, pour plus de visibilité. +1

0 votes

Disons que mon programme ne produit rien. Puis-je quand même utiliser cette méthode et être sûr que mon processus ne va pas s'arrêter en arrière-plan ?

2 votes

@Arcsector cette méthode évite de mettre un processus en arrière plan, c'est pourquoi elle n'échoue pas silencieusement. Avoir un processus en arrière-plan dans un conteneur docker n'est pas simple. Si vous voulez avoir un processus d'arrière-plan en cours d'exécution, vous pouvez utiliser un processus init pour surveiller les multiples processus que vous exécutez dans le conteneur. Une autre solution consiste à lancer le processus dans un autre conteneur à côté du conteneur principal, appelé "sidecar". La meilleure solution consiste souvent à éviter d'avoir plusieurs processus dans le conteneur.

176voto

Oscar Fanelli Points 751

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"]

cronjobs est le fichier qui contient vos cronjobs, sous cette forme :

* * * * * echo "hello stackoverflow" >> /test_file 2>&1
# remember to end this file with an empty new line

17 votes

Simple, léger et standard basé sur l'image. Cela devrait être la réponse acceptée. Utilisez également le > /proc/1/fd/1 2> /proc/1/fd/2 pour accéder à la sortie des cronjobs directement à partir des journaux de docker.

5 votes

Pour les personnes n'utilisant pas l'alpin : Le crond supportant le -d 8 n'est pas le cron standard, c'est la commande crond de busybox. Par exemple, sous ubuntu, vous pouvez exécuter cette commande comme suit busybox crond -f -d 8 . Pour les anciennes versions, vous devez utiliser -L /dev/stdout/ .

0 votes

Pour mon besoin - émuler un ensemble de tâches cron fonctionnant indépendamment dans un environnement de production où j'ai besoin de voir ce qui se passe - c'est parfait.

54voto

Youness Points 589

Ce que @VonC a suggéré est bien mais je préfère faire toute la configuration des tâches cron en une seule ligne. Cela permet d'éviter les problèmes liés aux plates-formes croisées, comme la localisation des cronjobs, 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 lancé votre conteneur docker, vous pouvez vous assurer que 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 avoir ENTRYPOINT au lieu de CMD, alors vous pouvez remplacer le CMD ci-dessus par

ENTRYPOINT cron start && tail -f /var/log/cron.log

5 votes

RUN apt-get update && apt-get -y install cron sinon il ne sera pas en mesure de trouver le paquet cron

3 votes

Merci Youness, vous m'avez donné l'idée de faire ce qui suit, qui a fonctionné dans mon cas où chaque cron est spécifié dans un fichier différent : RUN cat $APP_HOME/crons/* | crontab Comme un charme :)

0 votes

En ajoutant cron à un point d'entrée script semble être la meilleure option : ENTRYPOINT ["entrypoint.sh"]

24voto

OPSXCQ Points 246

Il y a un autre moyen de le faire, c'est d'utiliser Tasker un gestionnaire de tâches qui prend en charge le cron (un planificateur).

Pourquoi ? Parfois, pour exécuter une tâche cron, vous devez mélanger votre image de base (python, java, nodejs, ruby) avec le cron. Cela signifie une autre image à maintenir. Tasker évite cela en découplant le crond et votre conteneur. Vous pouvez vous concentrer sur l'image que vous souhaitez utiliser pour exécuter vos commandes, et configurer Tasker pour qu'il l'utilise.

Ici, un 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 ici, toutes s'exécuteront toutes les minutes ( every: minute ), et chacun d'entre eux exécutera la commande script à l'intérieur de l'image définie dans image section.

Il suffit de courir docker-compose up et voir qu'il fonctionne. Voici le dépôt de Tasker avec la documentation complète :

http://github.com/opsxcq/tasker

0 votes

Dockerception (exécuter des conteneurs docker à partir d'un autre conteneur) est une mauvaise pratique et devrait être limitée à l'intégration continue. Une solution de contournement serait d'utiliser docker exec sur les conteneurs spécifiés.

1 votes

Tasker n'utilise pas docker dans docker (Dind/Dockerception), notez que le socket docker est passé comme un mapping, tous les conteneurs créés sont créés dans le daemon que tasker exécute. Et si vous ne voulez pas exécuter tasker dans docker, vous pouvez simplement le déployer comme n'importe quelle autre application.

2 votes

Je ne vois pas l'intérêt d'utiliser Tasker. Cela me semble vraiment exagéré d'utiliser Java et des trucs juste pour exécuter une tâche cron.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X