angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Exécuter une commande Docker dans un conteneur Cron Docker

Le Alpine Docker image permet de construire très facilement un conteneur Cron.

18 avril 2023
Dans Docker
post main image
https://unsplash.com/@unsplash

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

En savoir plus...

Alpine Cron Docker

Laissez un commentaire

Commentez anonymement ou connectez-vous pour commenter.

Commentaires

Laissez une réponse

Répondez de manière anonyme ou connectez-vous pour répondre.