Exécuter une commande Docker dans un conteneur Cron Docker
Le Alpine Docker image permet de construire très facilement un conteneur Cron.
Lorsque vous utilisez Docker, votre application se compose généralement de plusieurs conteneurs Docker . Souvent, vous souhaitez exécuter des scripts à l'intérieur de ces conteneurs à certains moments, par exemple, toutes les cinq minutes, une fois par heure, une fois par jour.
C'est là que le planificateur de tâches Cron entre en jeu, et il existe plusieurs options pour y parvenir. Dans cet article, je crée un conteneur Cron séparé et j'utilise la commande Docker Exec pour exécuter des commandes et des scripts dans un autre conteneur.
J'avais une solution qui fonctionnait en utilisant l'image de base Ubuntu mais je voulais une configuration plus simple et un encombrement plus faible. C'est pourquoi j'utilise ici l'image Alpine Docker image . Comme toujours, j'utilise Ubuntu 22.04.
Alpine et Cron
Alpine est déjà équipé de Cron. A l'intérieur du conteneur, il y a des répertoires où nous pouvons placer nos scripts :
docker run alpine:3.17 ls -l /etc/periodic
Résultat :
/etc/periodic:
total 20
drwxr-xr-x 2 root root 4096 Mar 29 14:45 15min
drwxr-xr-x 2 root root 4096 Mar 29 14:45 daily
drwxr-xr-x 2 root root 4096 Mar 29 14:45 hourly
drwxr-xr-x 2 root root 4096 Mar 29 14:45 monthly
drwxr-xr-x 2 root root 4096 Mar 29 14:45 weekly
Les scripts dans ces répertoires sont (périodiquement) exécutés par Cron comme suit :
docker run alpine:3.17 crontab -l
Résultat :
# do daily/weekly/monthly maintenance
# min hour day month weekday command
*/15 * * * * run-parts /etc/periodic/15min
0 * * * * run-parts /etc/periodic/hourly
0 2 * * * run-parts /etc/periodic/daily
0 3 * * 6 run-parts /etc/periodic/weekly
0 5 1 * * run-parts /etc/periodic/monthly
En utilisant les volumes Docker , nous pouvons mapper (monter) ces répertoires sur notre hôte. Vous voudrez probablement exécuter des commandes ou des scripts à d'autres moments, par exemple :
- Toutes les minutes.
- Toutes les heures entre 6 et 23.
- À minuit.
Cela signifie que nous devons ajouter des lignes à notre fichier crontab et créer de nouveaux répertoires à l'intérieur du conteneur. Nous pouvons le faire dans un fichier de démarrage ou dans le Dockerfile. Par exemple, pour ajouter une ligne pour un répertoire avec des scripts qui seront exécutés toutes les heures entre 6 et 23 dans le Dockerfile :
RUN echo "0 6-23 * * * run-parts /etc/periodic/hourly_0600_2300" >> /etc/crontabs/root
Nous voulons aussi utiliser Bash. Et pour pouvoir exécuter des commandes Docker dans notre conteneur qui peut référencer d'autres conteneurs Docker , nous ajoutons Docker à notre Dockerfile et un mappage de volume dans notre fichier docker-compose.yml :
RUN apk add --update --no-cache bash && \
apk add --update --no-cache docker && \
...
volumes:
# access docker containers outside this container
- /var/run/docker.sock:/var/run/docker.sock
Répertoires et fichiers
Voici une liste arborescente de tous les répertoires et fichiers du projet :
├── project
│ ├── cron
│ │ ├── jobs
│ │ │ ├── 15min
│ │ │ ├── 1min
│ │ │ │ └── run_every_minute
│ │ │ ├── 5min
│ │ │ │ └── run_every_5_minutes
│ │ │ ├── daily
│ │ │ ├── daily_0000
│ │ │ ├── hourly
│ │ │ │ └── run_every_hour
│ │ │ └── hourly_0600_2300
│ │ │ └── run_every_hour_0600_2300
│ │ └── Dockerfile
│ ├── docker-compose.yml
│ └── .env
Pour les tests, j'ai ajouté quelques scripts Bash. Important : Selon la documentation Alpine : "N'utilisez pas de points dans les noms de vos fichiers de scripts - cela les empêche de fonctionner".
- run_every_minute
- run_every_5_minutes
- run_every_hour
- run_every_hour_0600_2300
Le contenu de tous ces scripts est le même :
#!/bin/bash
script_name=`basename "$0"`
echo "Running ${script_name} ..."
Assurez-vous que tous ces scripts Bash sont exécutables, par exemple :
chmod a+x run_every_5_minutes
Les fichiers
Voici les autres fichiers du projet :
- Dockerfile
- docker-compose.yml
- .env
Fichier : Dockerfile
Notez que Cron doit être exécuté en tant que root, et non en tant que user.
# Dockerfile
FROM alpine:3.17
MAINTAINER Peter Mooring peterpm@xs4all.nl peter@petermooring.com
# Install required packages
RUN apk add --update --no-cache bash && \
apk add --update --no-cache docker && \
# every minute
echo "* * * * * run-parts /etc/periodic/1min" >> /etc/crontabs/root && \
# every 5 minutes
echo "*/5 * * * * run-parts /etc/periodic/5min" >> /etc/crontabs/root && \
# every hour between 06:00 and 23:00
echo "0 6-23 * * * run-parts /etc/periodic/hourly_0600_2300" >> /etc/crontabs/root && \
# every day at 00:00
echo "0 0 * * * run-parts /etc/periodic/daily_0000" >> /etc/crontabs/root
# show all run-parts
# RUN crontab -l
WORKDIR /
File: docker-compose.yml
Notez que nous avons défini le contexte à './cron'. Dans ce répertoire se trouvent tous les fichiers associés à ce conteneur.
# docker-compose.yml
version: "3.7"
x-service_defaults: &service_defaults
env_file:
- ./.env
restart: always
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
services:
cron:
<< : *service_defaults
build:
context: ./cron
dockerfile: Dockerfile
volumes:
- ./cron/jobs/1min:/etc/periodic/1min/:ro
- ./cron/jobs/5min:/etc/periodic/5min/:ro
- ./cron/jobs/15min:/etc/periodic/15min/:ro
- ./cron/jobs/hourly:/etc/periodic/hourly/:ro
- ./cron/jobs/hourly_0600_2300:/etc/periodic/hourly_0600_2300/:ro
- ./cron/jobs/daily:/etc/periodic/daily/:ro
- ./cron/jobs/daily_0000:/etc/periodic/daily_0000/:ro
# access docker containers outside this container
- /var/run/docker.sock:/var/run/docker.sock
command: crond -f -l 8
Fichier : .env
Dans le fichier .env nous spécifions seulement le nom du projet :
# .env
COMPOSE_PROJECT_NAME=myapp
Construction, démarrage et quelques tests
Pour (re)construire le conteneur :
docker-compose build --no-cache
Pour démarrer le conteneur Cron :
docker-compose up
La sortie sera quelque chose comme :
Attaching to myapp_cron_1
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_5_minutes ...
cron_1 | Running run_every_minute ...
...
Le nom du conteneur Cron est :
myapp_cron_1
Pour afficher les journaux du conteneur :
docker logs -t -f myapp_cron_1
Résultat :
2023-04-18T11:52:00.871554414Z Running run_every_minute ...
2023-04-18T11:53:00.872386069Z Running run_every_minute ...
2023-04-18T11:54:00.872337812Z Running run_every_minute ...
2023-04-18T11:55:00.872792007Z Running run_every_minute ...
2023-04-18T11:55:00.872825556Z Running run_every_5_minutes ...
2023-04-18T11:56:00.875379199Z Running run_every_minute ...
Pour vérifier quels scripts seront exécutés à partir du répertoire ./cron/jobs/hourly_0600_2300, exécutez la commande suivante dans un autre terminal :
docker exec myapp_cron_1 run-parts --test /etc/periodic/hourly_0600_2300
Cela n'exécutera pas les scripts ! Résultat :
/etc/periodic/hourly_0600_2300/run_every_hour_0600_2300
Exécuter un script ou une commande dans un autre conteneur Docker
Exécutons une commande dans un conteneur BusyBox. Dans un autre terminal, démarrez ce conteneur BusyBox et nommez-le 'my_busybox' :
docker run -it --name my_busybox --rm busybox
Créez un nouveau script 'run_busybox_env' dans le répertoire './cron/jobs/1min' et rendez-le exécutable :
#!/bin/bash
# run_busybox_env
DOCKER=/usr/bin/docker
${DOCKER} exec my_busybox env
Ce script exécute la commande 'env' dans le conteneur 'my_busybox' et liste les variables d'environnement du conteneur BusyBox.
Dans une minute, le résultat devrait apparaître dans notre conteneur Cron :
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_5_minutes ...
cron_1 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cron_1 | HOSTNAME=dc74e9704d49
cron_1 | HOME=/root
cron_1 | Running run_every_minute ...
HOSTNAME contient le ContainerId du conteneur BusyBox, qui sera différent dans votre cas. Vous pouvez obtenir le ContainerId du conteneur BusyBox en exécutant :
docker ps | grep my_busybox
Résumé
L'utilisation du Alpine Docker image pour notre conteneur Cron a été très facile. Tout a fonctionné en même temps. Le résultat est moins de configuration et une empreinte plus petite.
Liens / crédits
Alpine Linux:FAQ
https://wiki.alpinelinux.org/wiki/Alpine_Linux:FAQ
CRON Jobs with Alpine Linux and Docker
https://lucshelton.com/blog/cron-jobs-with-alpine-linux-and-docker
Docker Tip #40: Running Cron Jobs on the Host vs. in a Container
https://nickjanetakis.com/blog/docker-tip-40-running-cron-jobs-on-the-host-vs-in-a-container
Running cron jobs in a Docker Alpine container
https://devopscell.com/cron/docker/alpine/linux/2017/10/30/run-cron-docker-alpine.html
Récent
- Un commutateur de base de données avec HAProxy et HAProxy Runtime API
- Docker Swarm rolling updates
- Masquer les clés primaires de la base de données UUID de votre application web
- Don't Repeat Yourself (DRY) avec Jinja2
- SQLAlchemy, PostgreSQL, nombre maximal de lignes par user
- Afficher les valeurs des filtres dynamiques SQLAlchemy
Les plus consultés
- Utilisation des Python's pyOpenSSL pour vérifier les certificats SSL téléchargés d'un hôte
- Utiliser PyInstaller et Cython pour créer un exécutable Python
- Réduire les temps de réponse d'un Flask SQLAlchemy site web
- Connexion à un service sur un hôte Docker à partir d'un conteneur Docker
- Utiliser UUIDs au lieu de Integer Autoincrement Primary Keys avec SQLAlchemy et MariaDb
- SQLAlchemy : Utilisation de Cascade Deletes pour supprimer des objets connexes