Je souhaite exécuter un travail cron python) à l'intérieur d'un conteneur de menu fixe en mode détaché. Ma configuration est la suivante:
Mon script python est test.py
#!/usr/bin/env python
import datetime
print "Cron job has run at %s" %datetime.datetime.now()
Mon fichier cron est my-crontab
* * * * * /test.py > /dev/console
et mon Dockerfile est
FROM ubuntu:latest
RUN apt-get update && apt-get install -y software-properties-common python-software-properties && apt-get update
RUN apt-get install -y python cron
ADD my-crontab /
ADD test.py /
RUN chmod a+x test.py
RUN crontab /my-crontab
ENTRYPOINT cron -f
Quels sont les problèmes potentiels avec cette approche? Existe-t-il d'autres approches et quels sont leurs avantages et inconvénients?
Plusieurs problèmes que j'ai rencontrés lors de la tentative d'exécution d'un travail cron dans un conteneur Docker étaient les suivants:
Il y a des problèmes spécifiques à cron et des problèmes spécifiques à docker dans la liste, mais dans tous les cas, ils doivent être résolus pour que cron fonctionne.
À cette fin, ma solution de travail actuelle au problème posé dans la question est la suivante:
Créez un volume de menu fixe dans lequel tous les scripts exécutés sous cron écriront:
# Dockerfile for test-logs
# BUILD-USING: docker build -t test-logs .
# RUN-USING: docker run -d -v /t-logs --name t-logs test-logs
# INSPECT-USING: docker run -t -i --volumes-from t-logs ubuntu:latest /bin/bash
FROM stackbrew/busybox:latest
# Create logs volume
VOLUME /var/log
CMD ["true"]
Le script qui s'exécutera sous cron est test.py
:
#!/usr/bin/env python
# python script which needs an environment variable and runs as a cron job
import datetime
import os
test_environ = os.environ["TEST_ENV"]
print "Cron job has run at %s with environment variable '%s'" %(datetime.datetime.now(), test_environ)
Afin de transmettre la variable d'environnement au script que je veux exécuter sous cron, suivez la suggestion de Thomas et mettez un fragment de crontab pour chaque script (ou groupe de scripts) nécessitant une variable d'environnement docker dans /etc/cron.d
avec un espace réservé XXXXXXX
qui doit être défini.
# placed in /etc/cron.d
# TEST_ENV is an docker environment variable that the script test.py need
TEST_ENV=XXXXXXX
#
* * * * * root python /test.py >> /var/log/test.log
Au lieu d'appeler directement cron, enveloppez-le dans un python) script qui fait les choses suivantes: 1. lit la variable d'environnement à partir de la variable d'environnement docker et la définit dans un fragment de crontab.
#!/usr/bin/env python
# run-cron.py
# sets environment variable crontab fragments and runs cron
import os
from subprocess import call
import fileinput
# read docker environment variables and set them in the appropriate crontab fragment
environment_variable = os.environ["TEST_ENV"]
for line in fileinput.input("/etc/cron.d/cron-python",inplace=1):
print line.replace("XXXXXXX", environment_variable)
args = ["cron","-f", "-L 15"]
call(args)
La Dockerfile
celle du conteneur dans lequel les tâches cron sont exécutées est la suivante:
# BUILD-USING: docker build -t test-cron .
# RUN-USING docker run --detach=true --volumes-from t-logs --name t-cron test-cron
FROM debian:wheezy
#
# Set correct environment variables.
ENV HOME /root
ENV TEST_ENV test-value
RUN apt-get update && apt-get install -y software-properties-common python-software-properties && apt-get update
# Install Python Setuptools
RUN apt-get install -y python cron
RUN apt-get purge -y python-software-properties software-properties-common && apt-get clean -y && apt-get autoclean -y && apt-get autoremove -y && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
ADD cron-python /etc/cron.d/
ADD test.py /
ADD run-cron.py /
RUN chmod a+x test.py run-cron.py
# Set the time zone to the local time zone
RUN echo "America/New_York" > /etc/timezone && dpkg-reconfigure --frontend noninteractive tzdata
CMD ["/run-cron.py"]
Enfin, créez les conteneurs et exécutez-les:
docker build -t test-logs .
docker run -d -v /t-logs --name t-logs test-logs
docker build -t test-cron .
docker run --detach=true --volumes-from t-logs --name t-cron test-cron
docker run -t -i --volumes-from t-logs ubuntu:latest /bin/bash
. Les fichiers de log sont en /var/log
.Voici un complément sur la réponse de Rosksw.
Il n'est pas nécessaire de remplacer une chaîne dans le fichier crontab pour transmettre des variables d'environnement aux tâches cron.
Il est plus simple de stocker les variables d’environnement dans un fichier lors de l’exécution du conteneur, puis de les charger à partir de ce fichier à chaque exécution cron. J'ai trouvé le conseil ici .
Dans le fichier de docker:
CMD mkdir -p /data/log && env > /root/env.txt && crond -n
Dans le fichier crontab:
* * * * * root env - `cat /root/env.txt` my-script.sh
Ajout de fragments de crontab dans /etc/cron.d/
au lieu d'utiliser crontab
de root pourrait être préférable.
Cela:
Observez que le format de ces fichiers est un peu différent d'une entrée crontab. Voici un exemple du paquet php Debian:
# /etc/cron.d/php5: crontab fragment for php5
# This purges session files older than X, where X is defined in seconds
# as the largest value of session.gc_maxlifetime from all your php.ini
# files, or 24 minutes if not defined. See /usr/lib/php5/maxlifetime
# Look for and purge old sessions every 30 minutes
09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -x /usr/lib/php5/sessionclean ] && [ -d /var/lib/php5 ] && /usr/lib/php5/sessionclean /var/lib/php5 $(/usr/lib/php5/maxlifetime)
Globalement, par expérience, exécuter cron dans un conteneur fonctionne très bien (à part la journalisation cron qui laisse beaucoup à désirer).
Voici une solution alternative.
dans Dockerfile
ADD docker/cron/my-cron /etc/cron.d/my-cron
RUN chmod 0644 /etc/cron.d/my-cron
ADD docker/cron/entrypoint.sh /etc/entrypoint.sh
ENTRYPOINT ["/bin/sh", "/etc/entrypoint.sh"]
dans entrypoint.sh
#!/usr/bin/env bash
printenv | cat - /etc/cron.d/my-cron > ~/my-cron.tmp \
&& mv ~/my-cron.tmp /etc/cron.d/my-cron
cron -f
Nous utilisons la solution ci-dessous. Il supporte à la fois docker logs
fonctionnalité et possibilité de suspendre le processus cron dans le conteneur sur le PID 1 (si vous utilisez tail -f
solutions de contournement fournies ci-dessus - en cas de blocage de Cron, le menu fixe ne suivra pas la stratégie de redémarrage):
cron.sh:
#!/usr/bin/env bash
printenv | cat - /etc/cron.d/cron-jobs > ~/crontab.tmp \
&& mv ~/crontab.tmp /etc/cron.d/cron-jobs
chmod 644 /etc/cron.d/cron-jobs
tail -f /var/log/cron.log &
cron -f
Dockerfile:
RUN apt-get install --no-install-recommends -y -q cron
ADD cron.sh /usr/bin/cron.sh
RUN chmod +x /usr/bin/cron.sh
ADD ./crontab /etc/cron.d/cron-jobs
RUN chmod 0644 /etc/cron.d/cron-jobs
RUN touch /var/log/cron.log
ENTRYPOINT ["/bin/sh", "/usr/bin/cron.sh"]
crontab:
* * * * * root <cmd> >> /var/log/cron.log 2>&1
Et s'il vous plaît n'oubliez pas d'ajouter la nouvelle ligne effrayant dans votre crontab
Ne mélangez pas crond et votre image de base. Préférez utiliser une solution native pour votre langue (programmez ou crython, comme le dit Anton), ou découplez-la. En découplant cela, je veux dire, gardez les choses séparées, de sorte que vous n’ayez pas à maintenir une image juste pour être la fusion entre python et crond.
Vous pouvez utiliser Tasker , un exécuteur de tâches doté de la prise en charge de cron (un planificateur), pour le résoudre, si vous souhaitez que les choses restent découplées.
Voici un docker-compose.yml
fichier, 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: helloFromPython
tasks:
docker:
- name: helloFromPython
image: python:3-slim
script:
- python -c 'print("Hello world from python")'
Il suffit de courir docker-compose up
et le voir fonctionner. Voici le rapport Tasker avec la documentation complète:
Vous pouvez exécuter crond
dans le même conteneur que faisant quelque chose de étroitement lié en utilisant une image de base qui gère bien le PID 0, comme phusion/baseimage .
Peut-être plus propre serait d'avoir un autre conteneur lié à celui-ci qui ne fait que lancer crond
. Par exemple:
Dockerfile
FROM busybox
ADD crontab /var/spool/cron/crontabs/www-data
CMD crond -f
crontab
* * * * * echo $USER
Puis lancez:
$ docker build -t cron .
$ docker run --rm --link something cron
Remarque: dans ce cas, le travail sera exécuté en tant que www-data
. Impossible de monter simplement le fichier crontab
en tant que volume, car il doit appartenir à root
avec un accès en écriture uniquement pour root
, sinon crond
n'exécutera rien. De plus, vous devrez exécuter crond
comme root
.
Une autre possibilité consiste à utiliser Crython . Crython vous permet de planifier régulièrement une fonction python à partir d'un seul script python /.). Elle comprend même la syntaxe cron:
@crython.job(expr='0 0 0 * * 0 *')
def job():
print "Hello world"
L'utilisation de crython évite les divers problèmes liés à l'exécution de crond dans un conteneur de menu fixe: votre travail est désormais un processus unique qui se réveille à tout moment, ce qui convient mieux au modèle d'exécution du menu fixe. Mais cela présente l'inconvénient d'inscrire la planification dans votre programme, ce qui n'est pas toujours souhaitable. Néanmoins, cela pourrait être utile dans certains cas d'utilisation.