Guide Complet des Expressions Cron | Des Bases aux Techniques Avancées
Guide complet de la syntaxe des expressions Cron avec 10 exemples réels, techniques de débogage, meilleures pratiques et FAQ. Maîtrisez les tâches planifiées Linux.
Learn by doing
Practice with the while reading - free, no registration required
Real-World Examples
Case Study 1: Sauvegarde de base de données automatisée
Problem
Une startup avait besoin de sauvegardes PostgreSQL quotidiennes fiables. Les premières tentatives ont échoué, les journaux cron montraient des erreurs "command not found".
Solution
Le problème était que pg_dump nécessitait des variables d'environnement et que le script utilisait des chemins relatifs. La solution consistait à définir les variables d'environnement au début du script, utiliser des chemins absolus, ajouter la journalisation et la gestion des erreurs :
#!/bin/bash
# Script de sauvegarde de base de données
# Variables d'environnement
export PGPASSWORD='your_password'
export PATH=/usr/local/bin:/usr/bin:/bin
# Configuration
DB_NAME="mydb"
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_${DATE}.sql"
# Créer la sauvegarde
/usr/bin/pg_dump -h localhost -U postgres "$DB_NAME" > "$BACKUP_FILE" 2>&1
# Vérifier le succès
if [ $? -eq 0 ]; then
echo "Sauvegarde réussie: $BACKUP_FILE" >> /var/log/db_backup.log
# Conserver les sauvegardes des 7 derniers jours
find "$BACKUP_DIR" -name "${DB_NAME}_*.sql" -mtime +7 -delete
else
echo "Sauvegarde échouée, vérifiez les messages d'erreur" >> /var/log/db_backup.log
exit 1
fi
Case Study 2: Vérification de santé des microservices
Problem
Un microservice déployé sur Kubernetes nécessitait des vérifications de santé régulières. Le service se bloquait parfois et nécessitait un redémarrage automatique.
Solution
Créer une ressource Kubernetes CronJob qui appelle régulièrement le endpoint de santé :
apiVersion: batch/v1
kind: CronJob
metadata:
name: health-check
spec:
schedule: "*/5 * * * *" # Toutes les 5 minutes
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
containers:
- name: health-check
image: curlimages/curl:latest
imagePullPolicy: Always
command:
- /bin/sh
- -c
- |-
response=$(curl -s -o /dev/null -w "%{http_code}" http://service:8080/health)
if [ $response -ne 200 ]; then
echo "Vérification de santé échouée, HTTP Status: $response"
# Déclencher la logique de redémarrage
kubectl rollout restart deployment/my-service
fi
restartPolicy: OnFailure
Case Study 3: Rotation et archivage des journaux
Problem
Les fichiers journaux du serveur web augmentaient rapidement, l'espace disque était souvent insuffisant. Une rotation, compression et nettoyage automatiques des journaux étaient nécessaires.
Solution
Utiliser logrotate avec cron pour la gestion des journaux :
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
lastaction
# Déplacer les journaux compressés de plus de 7 jours vers l'archive
find /var/log/nginx -name "*.gz" -mtime +7 -exec mv {} /archive/nginx/ \;
endaction
}
# Tâche cron (logrotate crée automatiquement)
0 0 * * * /usr/sbin/logrotate -f /etc/logrotate.conf
Case Study 4: Rapports par email planifiés
Problem
Le service financier devait recevoir un rapport de revenus de la veille par email chaque matin à 8h.
Solution
Créer un script qui génère le rapport et l'envoie par email :
#!/usr/bin/env python3
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import subprocess
from datetime import datetime, timedelta
def generate_report():
"""Générer le rapport de revenus"""
yesterday = datetime.now() - timedelta(days=1)
date_str = yesterday.strftime('%Y-%m-%d')
# Exécuter la requête SQL
cmd = f'''
mysql -u reporter -ppassword -h db.internal -e "
SELECT product, SUM(amount) as revenue
FROM sales
WHERE DATE(created_at) = '{date_str}'
GROUP BY product
"
'''
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
return result.stdout
def send_email(report):
"""Envoyer l'email"""
msg = MIMEMultipart()
msg['From'] = '[email protected]'
msg['To'] = '[email protected]'
msg['Subject'] = f'Rapport de revenus - {datetime.now().strftime("%Y-%m-%d")}'
body = f'''
Rapport de revenus
{'='*40}
{report}
{'='*40}
Cet email est généré automatiquement, ne pas répondre.
'''
msg.attach(MIMEText(body, 'plain'))
with smtplib.SMTP('smtp.company.com', 587) as server:
server.starttls()
server.login('[email protected]', 'password')
server.send_message(msg)
if __name__ == '__main__':
report = generate_report()
send_email(report)
Case Study 5: Synchronisation des tâches planifiées distribuées
Problem
Les tâches planifiées s'exécutant sur plusieurs serveurs devaient s'assurer qu'un seul serveur s'exécute à la fois pour éviter le traitement en double.
Solution
Utiliser un verrou distribué pour assurer l'exclusion mutuelle :
#!/bin/bash
# Tâche de traitement de données distribué
LOCK_FILE="/tmp/data_processing.lock"
LOCK_TIMEOUT=3600 # 1 heure de timeout
REDIS_HOST="redis.internal"
# Acquérir le verrou
acquire_lock() {
redis-cli -h "$REDIS_HOST" SETNX "$LOCK_FILE" "$HOSTNAME:$$" > /dev/null
return $?
}
# Libérer le verrou
release_lock() {
redis-cli -h "$REDIS_HOST" DEL "$LOCK_FILE" > /dev/null
}
# Logique principale
main() {
echo "Démarrage du traitement des données..."
# Vérifier si le verrou a expiré
lock_time=$(redis-cli -h "$REDIS_HOST" GET "$LOCK_FILE.ttl")
if [ -n "$lock_time" ]; then
current_time=$(date +%s)
if [ $((current_time - lock_time)) -gt $LOCK_TIMEOUT ]; then
echo "Verrou expiré, acquisition forcée"
release_lock
fi
fi
# Tenter d'acquérir le verrou
if ! acquire_lock; then
owner=$(redis-cli -h "$REDIS_HOST" GET "$LOCK_FILE")
echo "Tâche déjà en cours: $owner"
exit 0
fi
# Définir le timeout du verrou
redis-cli -h "$REDIS_HOST" SETEX "$LOCK_FILE.ttl" "$LOCK_TIMEOUT" "$(date +%s)" > /dev/null
# Exécuter la tâche
python3 /scripts/process_data.py
# Libérer le verrou
release_lock
echo "Traitement des données terminé"
}
# Exécution
main
Case Study 6: Déploiement progressif
Problem
Tous les serveurs devaient être mis à jour progressivement sans interruption de service, 5 serveurs à la fois, à 2 minutes d'intervalle.
Solution
Système de déploiement progressif déclenché par Cron :
#!/bin/bash
# Script de déploiement progressif
DEPLOYMENT_CONFIG="/etc/deployment/config.json"
CURRENT_BATCH=$(cat /var/run/deployment_batch 2>/dev/null || echo 0)
BATCH_SIZE=5
BATCH_DELAY=120 # 2 minutes
# Obtenir la liste des serveurs
servers=$(jq -r '.servers[]' "$DEPLOYMENT_CONFIG")
total_servers=$(echo "$servers" | wc -l)
# Calculer le batch actuel
start=$((CURRENT_BATCH * BATCH_SIZE))
end=$((start + BATCH_SIZE))
echo "Début du déploiement batch $((CURRENT_BATCH + 1)), serveurs $start-$end"
# Déployer le batch actuel
echo "$servers" | sed -n "${start},${end}p" | while read server; do
echo "Déploiement sur $server ..."
ssh "$server" 'bash -s' < /scripts/update_service.sh
done
# Enregistrer la progression
if [ $end -lt $total_servers ]; then
echo $((CURRENT_BATCH + 1)) > /var/run/deployment_batch
echo "Batch terminé, attente du prochain..."
else
echo "Tous les batches déployés"
rm -f /var/run/deployment_batch
fi
Case Study 7: Renouvellement automatique de certificats
Problem
Les certificats SSL devaient être renouvelés tous les 90 jours, les procédures manuelles étaient oubliées et causaient des interruptions de service.
Solution
Utiliser le renouvellement automatique Let's Encrypt :
#!/bin/bash
# Renouvellement automatique de certificat SSL
DOMAINS="example.com www.example.com api.example.com"
EMAIL="[email protected]"
CERT_DIR="/etc/letsencrypt/live"
LOG_FILE="/var/log/cert_renewal.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
# Vérifier l'expiration du certificat
check_cert_expiry() {
local domain=$1
local cert_file="$CERT_DIR/$domain/cert.pem"
if [ ! -f "$cert_file" ]; then
log "Certificat non trouvé: $domain"
return 1
fi
# Calculer les jours restants
expiry_date=$(openssl x509 -enddate -noout -in "$cert_file" | cut -d= -f2)
expiry_epoch=$(date -d "$expiry_date" +%s)
current_epoch=$(date +%s)
days_left=$(( (expiry_epoch - current_epoch) / 86400 ))
echo $days_left
}
# Renouveler le certificat
renew_cert() {
log "Début du renouvellement du certificat: $DOMAINS"
certbot certonly --non-interactive --agree-tos --email "$EMAIL" --webroot -w /var/www/html -d $DOMAINS >> "$LOG_FILE" 2>&1
if [ $? -eq 0 ]; then
log "Certificat renouvelé avec succès"
# Recharger nginx
systemctl reload nginx
# Envoyer une notification
echo "Certificat renouvelé avec succès" | mail -s "Renouvellement de certificat" "$EMAIL"
else
log "Renouvellement du certificat échoué"
echo "Renouvellement du certificat échoué, vérifiez les journaux" | mail -s "Renouvellement échoué" "$EMAIL"
exit 1
fi
}
# Logique principale
for domain in $DOMAINS; do
days_left=$(check_cert_expiry "$domain")
if [ $days_left -lt 30 ]; then
log "Le certificat expire dans $days_left jours, début du renouvellement"
renew_cert
else
log "Le certificat a encore $days_left jours, pas de renouvellement nécessaire"
fi
done
Case Study 8: Préchauffage du cache
Problem
Le site e-commerce avait un taux de réussite du cache faible pendant les périodes de pointe, nécessitant un préchauffage avant la pointe.
Solution
Préchauffer automatiquement le cache des produits populaires avant la pointe :
#!/usr/bin/env python3
import requests
import json
from datetime import datetime
API_BASE = "https://api.example.com"
CACHE_ENDPOINT = f"{API_BASE}/cache/warmup"
def get_popular_products():
"""Obtenir les produits populaires"""
response = requests.get(f"{API_BASE}/analytics/popular", params={
'limit': 1000,
'time_range': '24h'
})
return response.json()['products']
def warmup_cache(product_ids):
"""Préchauffer le cache"""
chunks = [product_ids[i:i+50] for i in range(0, len(product_ids), 50)]
for chunk in chunks:
try:
response = requests.post(CACHE_ENDPOINT, json={
'product_ids': chunk,
'ttl': 3600
}, timeout=30)
if response.status_code == 200:
print(f"Préchauffage réussi: {len(chunk)} produits")
else:
print(f"Préchauffage échoué: {response.text}")
except Exception as e:
print(f"Erreur de préchauffage: {e}")
def main():
print(f"[{datetime.now()}] Début du préchauffage du cache")
try:
# Obtenir les produits populaires
products = get_popular_products()
product_ids = [p['id'] for p in products]
print(f"{len(product_ids)} produits populaires obtenus")
# Préchauffer le cache
warmup_cache(product_ids)
print(f"[{datetime.now()}] Préchauffage du cache terminé")
except Exception as e:
print(f"Préchauffage échoué: {e}")
exit 1
if __name__ == '__main__':
main()
Case Study 9: Synchronisation et nettoyage des données
Problem
Les données de production anonymisées devaient être synchronisées avec l'environnement de test, en ne conservant que les 30 derniers jours.
Solution
Créer un script de synchronisation et de nettoyage des données :
#!/bin/bash
# Synchronisation de base de données vers l'environnement de test
PROD_DB="production"
TEST_DB="test_staging"
RETENTION_DAYS=30
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# Exporter les données de production (anonymisées)
dump_data() {
log "Début de l'export des données de production..."
mysqldump -h prod-db.internal -u exporter -ppassword \
--single-transaction \
--quick \
--lock-tables=false \
--where="DATE(created_at) >= DATE(NOW()) - INTERVAL $RETENTION_DAYS DAY" \
"$PROD_DB" \
users orders products \
| gzip > /tmp/dump.sql.gz
# Anonymisation
zcat /tmp/dump.sql.gz | sed \
-e 's/"\(email\|phone\|ssn\)":"[^"]*"/"\1":"***"/g' \
-e 's/"\(credit_card\|password\)":"[^"]*"/"\1":"***"/g' \
| gzip > /tmp/dump_anon.sql.gz
log "Export et anonymisation des données terminés"
}
# Importer dans l'environnement de test
import_data() {
log "Début de l'import dans l'environnement de test..."
gunzip < /tmp/dump_anon.sql.gz | mysql -h test-db.internal -u importer -ppassword "$TEST_DB"
log "Import des données terminé"
}
# Nettoyer les fichiers temporaires
cleanup() {
log "Nettoyage des fichiers temporaires..."
rm -f /tmp/dump*.sql.gz
}
# Nettoyer les anciennes données de test
cleanup_old_data() {
log "Nettoyage des anciennes données de test..."
mysql -h test-db.internal -u admin -ppassword "$TEST_DB" <<EOF
DELETE FROM orders WHERE DATE(created_at) < DATE(NOW()) - INTERVAL $RETENTION_DAYS DAY;
DELETE FROM audit_logs WHERE DATE(created_at) < DATE(NOW()) - INTERVAL $RETENTION_DAYS DAY;
OPTIMIZE TABLE users, orders, products;
EOF
log "Nettoyage des anciennes données terminé"
}
# Processus principal
main() {
log "===== Début de la synchronisation des données ====="
dump_data
import_data
cleanup_old_data
cleanup
log "===== Synchronisation des données terminée ====="
}
main
Case Study 10: Intégration de la surveillance et des alertes
Problem
Lors de l'échec des tâches planifiées, l'équipe des opérations devait être notifiée immédiatement, une intégration avec le système de surveillance existant était nécessaire.
Solution
Utiliser Prometheus et Alertmanager pour l'intégration des alertes :
#!/bin/bash
# Wrapper de tâche avec intégration de surveillance
TASK_NAME="data_import"
TASK_COMMAND="/usr/bin/python3 /scripts/import.py"
LOG_FILE="/var/log/tasks/${TASK_NAME}.log"
METRICS_FILE="/var/lib/node_exporter/textfile_collector/${TASK_NAME}.prom"
# Initialisation
START_TIME=$(date +%s)
echo "# HELP task_last_success_timestamp Dernière heure de succès" > "$METRICS_FILE"
echo "# TYPE task_last_success_timestamp gauge" >> "$METRICS_FILE"
# Exécuter la tâche
run_task() {
echo "[$(date)] Début de l'exécution de la tâche: $TASK_NAME" >> "$LOG_FILE"
$TASK_COMMAND >> "$LOG_FILE" 2>&1
EXIT_CODE=$?
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
if [ $EXIT_CODE -eq 0 ]; then
echo "[$(date)] Tâche réussie, durée: ${DURATION}s" >> "$LOG_FILE"
# Enregistrer la métrique de succès
echo "task_last_success_timestamp{task="$TASK_NAME"} $END_TIME" >> "$METRICS_FILE"
echo "task_duration_seconds{task="$TASK_NAME",status="success"} $DURATION" >> "$METRICS_FILE"
else
echo "[$(date)] Tâche échouée, code de sortie: $EXIT_CODE" >> "$LOG_FILE"
# Enregistrer la métrique d'échec
echo "task_last_success_timestamp{task="$TASK_NAME"} 0" >> "$METRICS_FILE"
echo "task_duration_seconds{task="$TASK_NAME",status="failed"} $DURATION" >> "$METRICS_FILE"
# Envoyer une alerte
curl -X POST http://alertmanager:9093/api/v1/alerts -d '[
{
"labels": {
"alertname": "TaskFailed",
"task": "'$TASK_NAME'",
"severity": "critical"
},
"annotations": {
"summary": "Tâche échouée: '$TASK_NAME'",
"description": "Code de sortie: '$EXIT_CODE', journal: '$LOG_FILE'"
}
}
]'
exit 1
fi
}
run_task