docker - hub - ¿Cómo ejecutar un trabajo cron dentro de un contenedor acoplable?
docker-compose (15)
Estoy tratando de ejecutar un cronjob dentro de un contenedor docker que invoca un script de shell.
Ayer estuve buscando en toda la web y el desbordamiento de pila, pero realmente no pude encontrar una solución que funcione.
¿Cómo puedo hacer esto?
EDITAR:
He creado un repositorio github (comentado) con un contenedor cron de Docker que funciona que invoca un script de shell en un intervalo determinado.
Al ejecutar algunas imágenes recortadas que restringen el acceso de root, tuve que agregar mi usuario a los sudoers y ejecutar como
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
Tal vez eso ayude a alguien
Aunque esto tiene como objetivo ejecutar trabajos junto a un proceso en ejecución en un contenedor a través de la interfaz
exec
de Docker, esto puede ser de su interés.
He escrito un demonio que observa contenedores y programa trabajos, definidos en sus metadatos, en ellos. Ejemplo:
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
''Clásico'', la configuración de tipo cron también es posible.
Aquí están los docs , aquí está el repositorio de imágenes .
Creé una imagen de Docker basada en las otras respuestas, que se puede usar como
docker run -v "/path/to/cron:/etc/cron.d/crontab" gaafar/cron
where
/path/to/cron
: ruta absoluta al archivo crontab, o puede usarlo como base en un Dockerfile:
FROM gaafar/cron
# COPY crontab file in the cron directory
COPY crontab /etc/cron.d/crontab
# Add your commands here
Como referencia, la imagen está aquí .
Cuando implemente su contenedor en otro host, tenga en cuenta que no iniciará ningún proceso automáticamente. Debe asegurarse de que el servicio ''cron'' se esté ejecutando dentro de su contenedor. En nuestro caso, estoy usando Supervisord con otros servicios para iniciar el servicio 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
Defina el cronjob en un contenedor dedicado que ejecuta el comando a través de docker exec a su servicio.
Esto es una mayor cohesión y el script en ejecución tendrá acceso a las variables de entorno que haya definido para su servicio.
#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"
Entonces, mi problema era el mismo.
La solución fue cambiar la sección de comandos en
docker-compose.yml
.
De
comando: crontab / etc / crontab && tail -f / etc / crontab
A
comando: crontab / etc / crontab
comando: tail -f / etc / crontab
El problema era el ''&&'' entre los comandos. Después de eliminar esto, todo estuvo bien.
Hay otra forma de hacerlo, es usar Tasker , un corredor de tareas que tiene soporte cron (un planificador).
Por qué ? A veces, para ejecutar un trabajo cron, debe mezclar su imagen base (python, java, nodejs, ruby) con el crond. Eso significa otra imagen para mantener. Tasker evita eso desacoplando el crond y tu contenedor. Puede enfocarse en la imagen que desea ejecutar sus comandos y configurar Tasker para usarla.
Aquí un archivo
docker-compose.yml
, que ejecutará algunas tareas por usted
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")''
Hay 3 tareas allí, todas se ejecutarán cada minuto (
every: minute
), y cada una de ellas ejecutará el código del
script
, dentro de la imagen definida en la sección de
image
.
Simplemente ejecute
docker-compose up
y vea que funciona.
Aquí está el repositorio de Tasker con la documentación completa:
Intente usar una gema mecánica para programar tareas. Siga los pasos provistos en este enlace.
Puede llamar a la tarea de rastrillo dentro del archivo lib / clock.rb como se muestra a continuación.
every(1.day, ''Import large data from csv files'', :at => ''5:00'') do |job|
`rake ''portal:import_data_from_csv''`
end
Cree un contenedor separado en el archivo docker-compose y ejecute el siguiente comando dentro del contenedor.
command: bundle exec clockwork lib/clock.rb
La forma más sólida que encontré hasta ahora es ejecutar un contenedor cron independiente: instale el cliente de Docker y monte el Docker Sock para que pueda hablar con el servidor de Docker en el host.
Luego, solo use env vars para cada trabajo cron y un script de punto de entrada para generar / etc / crontab
Aquí hay una imagen que creé usando este principio y usándola en producción durante los últimos 3-4 años.
https://www.vip-consult.solutions/post/better-docker-cron#content
La solución adoptada puede ser peligrosa en un entorno de producción .
En Docker, solo debe ejecutar un proceso por contenedor porque si no lo hace, el proceso que se bifurcó y pasó a segundo plano no se supervisa y puede detenerse sin que usted lo sepa.
Cuando usa
CMD cron && tail -f /var/log/cron.log
el proceso cron básicamente se bifurca para ejecutar
cron
en segundo plano, el proceso principal se cierra y le permite ejecutar
tailf
en primer plano.
El proceso cron en segundo plano podría detenerse o fallar, no lo notará, su contenedor seguirá ejecutándose en silencio y su herramienta de orquestación no lo reiniciará.
Puede evitar tal cosa al redirigir directamente la salida de comandos del cron a su docker
stdout
ystderr
que se encuentran respectivamente en/proc/1/fd/1
y/proc/1/fd/2
.
Usando redireccionamientos básicos de shell, puede hacer algo como esto:
* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2
Y su CMD será:
CMD ["cron", "-f"]
Lo que ha sugerido @VonC es bueno, pero prefiero hacer toda la configuración del trabajo cron en una sola línea. Esto evitaría problemas entre plataformas como la ubicación de cronjob y no necesita un archivo cron separado.
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
Después de ejecutar su contenedor docker, puede asegurarse de que el servicio cron esté funcionando:
# 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 prefiere tener ENTRYPOINT en lugar de CMD, puede sustituir el CMD anterior con
ENTRYPOINT cron start && tail -f /var/log/cron.log
Los trabajos de Cron se almacenan en / var / spool / cron / crontabs (lugar común en todas las distribuciones que conozco). Por cierto, puedes crear una pestaña cron en bash usando algo así:
crontab -l > cronexample
echo "00 09 * * 1-5 echo hello" >> cronexample
crontab cronexample
rm cronexample
Esto creará un archivo temporal con la tarea cron, luego lo programará usando crontab. Última línea eliminar archivo temporal.
Para aquellos que desean usar una imagen simple y liviana:
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"]
Donde cronjobs es el archivo que contiene sus cronjobs, de esta forma:
* * * * * echo "hello " >> /test_file 2>&1
# remember to end this file with an empty new line
Puede copiar su crontab en una imagen, para que el contenedor lanzado desde dicha imagen ejecute el trabajo.
Consulte "
Ejecutar un trabajo cron con Docker
" de
Julien Boulay
en su
Ekito/docker-cron
:
Creemos un nuevo archivo llamado "
hello-cron
" para describir nuestro trabajo.
* * * * * 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.
El siguiente Dockerfile describe todos los pasos para construir su imagen.
FROM ubuntu:latest
MAINTAINER [email protected]
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
(vea el
comment
Gaafar
y
¿Cómo hago que
apt-get
install sea menos ruidoso?
apt-get -y install -qq --force-yes cron
puede funcionar)
O, asegúrese de que su trabajo en sí mismo redirija directamente a stdout / stderr en lugar de un archivo de registro, como se describe en la answer hugoShaka :
* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2
Reemplace la última línea de Dockerfile con
CMD ["cron", "-f"]
Ver también (acerca de
cron -f
, es decir cron "primer plano") "
docker ubuntu
cron -f
no funciona
"
Construir y ejecutarlo:
sudo docker build --rm -t ekito/cron-example .
sudo docker run -t -i ekito/cron-example
Sea paciente, espere 2 minutos y su línea de comando debería mostrar:
Hello world
Hello world
Eric agrega en los comentarios :
Tenga en cuenta que la
tail
puede no mostrar el archivo correcto si se crea durante la creación de la imagen.
Si ese es el caso, debe crear o tocar el archivo durante el tiempo de ejecución del contenedor para que tail pueda recoger el archivo correcto.
Consulte "
No se muestra la salida de
tail -f
al final de una ventana acoplable
CMD
".
respuesta
es bastante exhaustiva.
Además, me gustaría agregar una cosa que me ayudó.
Si solo desea ejecutar un trabajo cron sin seguir un archivo, estaría tentado a eliminar el
&& tail -f /var/log/cron.log
del comando cron.
Sin embargo, esto hará que el contenedor Docker salga poco después de ejecutarse porque cuando se completa el comando cron, Docker cree que el último comando ha salido y, por lo tanto, mata el contenedor.
Esto se puede evitar ejecutando cron en primer plano a través de
cron -f
.