Une méthode pour remplacer un serveur qui exécute un traitement par lots avec cron sans temps d'arrêt

Contexte

C'est une question soudaine, mais comment gérez-vous les Jobs que vous exécutez régulièrement? Même en 2018, lorsque le serveur sans serveur est populaire, il se peut que certaines personnes utilisent toujours l'ancien cron.

Dans cet article, je voudrais suggérer un moyen de remplacer un serveur exécutant le traitement par lots par cron sans temps d'arrêt. Aussi, je voudrais expliquer pourquoi c'est possible en jetant un coup d'œil au code source de cron. (Pour des raisons de temps, nous ne gérons cronie que sur CentOS7.)

En fonctionnement réel, vous devez concevoir un système de gestion des tâches approprié, mais cet article présentera une méthode trouble.

TL; DR

Qu'est-ce que cron?

cron est un planificateur de travaux basé sur le temps utilisé dans les systèmes d'exploitation UNIX et convient à la planification de tâches exécutées régulièrement. Il est également utilisé pour la gestion du système et pour l'exécution de tâches de services réels.

Par exemple, si vous enregistrez un travail comme celui-ci avec crontab -e, le lot d'agrégation sera exécuté à 0 minute toutes les heures.

0 * * * * /bin/bash -l -c '/home/vagrant/bin/execute_hourly_aggregation'

Pour plus d'informations, consultez les articles Wikipédia ci-dessous et les différents documents qui servent de références.

https://ja.wikipedia.org/wiki/Crontab

Conditions préalables

Dans cette méthode proposée, nous visons à remplacer par zéro temps d'arrêt dans un environnement où un lot est toujours exécuté, comme un lot qui prend beaucoup de temps ou un grand nombre de lots.

Cependant, sachez que la méthode proposée ne peut pas atteindre un temps d'arrêt strict sur un serveur où de nouvelles tâches sont lancées toutes les minutes.

Informations environnementales

J'expliquerai dans l'environnement suivant, qui a été mis en place dans Vagrant. La version est légèrement différente du code source à expliquer, mais il n'y a pas de changements majeurs, donc j'espère que vous comprenez le mécanisme.

[vagrant@localhost ~]$ cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
[vagrant@localhost ~]$ yum list installed | grep cron
Failed to set locale, defaulting to C
cronie.x86_64                   1.4.11-19.el7                   @anaconda
cronie-anacron.x86_64           1.4.11-19.el7                   @anaconda
crontabs.noarch                 1.11-6.20121102git.el7          @anaconda

procédure

1. Préparation d'un nouveau serveur avec crond arrêté

Tout d'abord, préparons un nouveau serveur. En ce qui concerne la structure des fichiers, il n'y a pas de problème avec la même structure de fichiers que l'ancien serveur, mais faites-le de manière à satisfaire les deux suivants.

Par exemple, dans la série CentOS7, utilisez la commande service pour l'arrêter.

[vagrant@localhost ~]$ sudo service crond stop
Redirecting to /bin/systemctl stop crond.service
[vagrant@localhost ~]$ service crond status
Redirecting to /bin/systemctl status crond.service
● crond.service - Command Scheduler
   Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled)
   Active: inactive (dead) since Tue 2018-09-11 17:44:33 UTC; 11s ago
  Process: 3406 ExecStart=/usr/sbin/crond -n $CRONDARGS (code=exited, status=0/SUCCESS)
 Main PID: 3406 (code=exited, status=0/SUCCESS)

2. Attendez que le travail s'arrête

Laissez le temps aux tâches cron de s'arrêter. L'important ici n'est pas lorsque le travail est terminé, mais lorsque le travail n'est plus expulsé. Regardez le processus ci-dessous crond avec ps aux f etc. et assurez-vous que le dernier traitement par lots sur l'ancien serveur a été lancé.

root      3492  0.0  0.3  26096  1704 ?        Ss   17:45   0:00 /usr/sbin/crond -n
root      3921  0.0  0.4  82144  2488 ?        S    18:05   0:00  \_ /usr/sbin/CROND -n
vagrant   3924  0.0  0.3  12992  1568 ?        Ss   18:05   0:00  |   \_ /bin/bash -l -c echo "start at $(date)" && sleep 120 && echo "end at $(date)"
vagrant   3937  0.0  0.0   7764   352 ?        S    18:05   0:00  |   |   \_ sleep 120

3. Arrêtez crond sur l'ancien serveur et démarrez crond sur le nouveau serveur

Enfin, nous passerons aux travaux de remplacement. Tout d'abord, arrêtez crond sur l'ancien serveur et démarrez crond sur le nouveau serveur. À ce stade, l'ancien serveur ne lancera plus de nouvelles opérations par lots.

4. Observez la fin du traitement par lots sur l'ancien serveur

crond a cessé, mais il y a encore des emplois qui crond ont été lancés. Attendons la fin du traitement par lots. À ce moment, le travail lancé par crond a perdu son parent principal et est maintenant suspendu sous / usr / sbin / CROND.

root      4199  0.0  0.4  82144  2488 ?        S    18:18   0:00 /usr/sbin/CROND -n
vagrant   4201  0.0  0.3  12992  1564 ?        Ss   18:18   0:00  \_ /bin/bash -l -c echo "start at $(date)" && sleep 120 && echo "end at $(date)"
vagrant   4214  0.0  0.0   7764   352 ?        S    18:18   0:00  |   \_ sleep 120

5. Arrêtez complètement l'ancien serveur

Attendez que le dernier traitement par lots soit terminé, puis arrêtez poliment.

Révéler

Pourquoi est-ce possible? Revérifions l'état du processus à l'exécution tout en lisant le code cron pendant un moment.

Tout d'abord, "cron" boucle le processus suivant "while" jusqu'à ce qu'il reçoive un signal de "SIGINT" ou "SIGTERM".

	while (!got_sigintterm) {
		int timeDiff;
		enum timejump wakeupKind;

		/* ... wait for the time (in minutes) to change ... */
		do {
			cron_sleep(timeRunning + 1, &database);
			set_time(FALSE);
		} while (!got_sigintterm && clockTime == timeRunning);
		if (got_sigintterm)
			break;
		timeRunning = clockTime;
// ~Omission~
		handle_signals(&database);
	}

https://github.com/cronie-crond/cronie/blob/40b7164227a17058afb4f3d837ebb3263943e2e6/src/cron.c#L354-L481

En d'autres termes, vous pouvez voir que le nouveau lot s'exécute toutes les (environ) toutes les minutes de cette vérification.

Le nouveau lot est découvert par find_jobs () et fonctionne comme un descendant du crond original via job_runqueue (), do_command (), child_process ().

root      3492  0.0  0.3  26096  1704 ?        Ss   17:45   0:00 /usr/sbin/crond -n
root      3921  0.0  0.4  82144  2488 ?        S    18:05   0:00  \_ /usr/sbin/CROND -n
vagrant   3924  0.0  0.3  12992  1568 ?        Ss   18:05   0:00  |   \_ /bin/bash -l -c echo "start at $(date)" && sleep 120 && echo "end at $(date)"
vagrant   3937  0.0  0.0   7764   352 ?        S    18:05   0:00  |   |   \_ sleep 120

Alors que se passe-t-il si vous vous arrêtez ici avec SIGTERM? Jetons un coup d'œil au code de la dernière partie de nettoyage.


#if defined WITH_INOTIFY
	if (inotify_enabled && (EnableClustering != 1))
		set_cron_unwatched(fd);

	if (fd >= 0 && close(fd) < 0)
		log_it("CRON", pid, "INFO", "Inotify close failed", errno);
#endif

	log_it("CRON", pid, "INFO", "Shutting down", 0);

	(void) unlink(_PATH_CRON_PID);

	return 0;

https://github.com/cronie-crond/cronie/blob/40b7164227a17058afb4f3d837ebb3263943e2e6/src/cron.c#L482-L495

As-tu remarqué? Au lieu d '«attendre» ou «tuer» le processus enfant ou petit-enfant, le parent est tranquillement mort, confiant à l'enfant le traitement du petit-enfant.

root      4199  0.0  0.4  82144  2488 ?        S    18:18   0:00 /usr/sbin/CROND -n
vagrant   4201  0.0  0.3  12992  1564 ?        Ss   18:18   0:00  \_ /bin/bash -l -c echo "start at $(date)" && sleep 120 && echo "end at $(date)"
vagrant   4214  0.0  0.0   7764   352 ?        S    18:18   0:00  |   \_ sleep 120

Par conséquent, les nouveaux travaux ne sont pas supprimés, tandis que les travaux déjà supprimés peuvent être terminés jusqu'à la fin. (C'est un peu douloureux)

Résumé

Puisque crond est implémenté très simplement, il est possible de le remplacer par zéro temps d'arrêt même avec une méthode brute telle que la méthode proposée. Bien sûr, en fonctionnement réel, c'est un domaine qui devrait être systématisé correctement, mais j'espère que vous serez intéressé par le «cron» familier.

référence

Recommended Posts

Une méthode pour remplacer un serveur qui exécute un traitement par lots avec cron sans temps d'arrêt
Générez un mot de passe facile à retenir avec apg
Serveur de jeu avec deux PC
[Mac] Je souhaite créer un serveur HTTP simple qui exécute CGI avec Python
Un serveur qui fait écho aux données POSTées avec flask / python
[1 hour challenge] J'ai essayé de créer un site de bonne aventure qui soit trop adapté à Python
Une version simple des statistiques gouvernementales (contrôle de l'immigration) facile à gérer avec jupyter