web-dev-qa-db-fra.com

Comment exécuter une tâche cron dans un conteneur Docker?

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é.

138
C Heyer

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

207
VonC

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

65
hugoShaka

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
22
Youness

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 contenant vos cronjobs, sous cette forme:

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

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:

http://github.com/opsxcq/tasker

11
OPSXCQ

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 :)

8
vanugrah

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]

/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
4
Gaafar

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"
4
Jakob Eriksson

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.

Voici les docs , voici le référentiel images .

4
funky-future

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
3
Sagar Ghuge

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.

0
Pedro

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.

0
Jesus Segnini

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

0
Senica Gonzalez