web-dev-qa-db-fra.com

AWS Elastic Beanstalk, exécution d'un cronjob

J'aimerais savoir s'il existe un moyen de configurer une tâche/tâche cron pour qu'elle s'exécute chaque minute. Actuellement, n'importe laquelle de mes instances devrait pouvoir exécuter cette tâche.

C'est ce que j'ai essayé de faire dans les fichiers de configuration sans succès:

container_commands:
  01cronjobs:
    command: echo "*/1 * * * * root php /etc/httpd/myscript.php"

Je ne suis pas vraiment sûr si c'est la bonne façon de le faire

Des idées?

80
Onema

Voici comment j'ai ajouté un travail cron à Elastic Beanstalk:

Créez un dossier à la racine de votre application appelé .ebextensions s'il n'existe pas déjà. Créez ensuite un fichier de configuration dans le dossier .ebextensions. Je vais utiliser example.config à des fins d'illustration. Ajoutez ensuite ceci à example.config

container_commands:
  01_some_cron_job:
    command: "cat .ebextensions/some_cron_job.txt > /etc/cron.d/some_cron_job && chmod 644 /etc/cron.d/some_cron_job"
    leader_only: true

Ceci est un fichier de configuration YAML pour Elastic Beanstalk. Lorsque vous copiez ceci dans votre éditeur de texte, assurez-vous que celui-ci utilise des espaces plutôt que des tabulations. Sinon, vous obtiendrez une erreur YAML lorsque vous transmettez ceci à EB.

Donc, cela crée une commande appelée 01_some_cron_job. Les commandes étant exécutées dans l'ordre alphabétique, le 01 s'assure qu'il est exécuté en tant que première commande.

La commande récupère ensuite le contenu d'un fichier appelé some_cron_job.txt et l'ajoute à un fichier appelé some_cron_job dans /etc/cron.d.

La commande modifie ensuite les autorisations sur le fichier /etc/cron.d/some_cron_job.

La clé leader_only garantit que la commande n'est exécutée que sur l'instance ec2 considérée comme le leader. Plutôt que de fonctionner sur toutes les instances ec2 en cours d'exécution.

Créez ensuite un fichier appelé some_cron_job.txt dans le dossier .ebextensions. Vous allez placer vos tâches cron dans ce fichier.

Donc par exemple:

# The newline at the end of this file is extremely important.  Cron won't run without it.
* * * * * root /usr/bin/php some-php-script-here > /dev/null

Ainsi, cette tâche cron sera exécutée toutes les minutes, toutes les heures, en tant qu'utilisateur root et supprimera la sortie dans/dev/null./usr/bin/php est le chemin d'accès à php. Puis remplacez some-php-script-here par le chemin d'accès à votre fichier php. Ceci suppose évidemment que votre travail cron doit exécuter un fichier PHP.

Assurez-vous également que le fichier some_cron_job.txt comporte une nouvelle ligne à la fin du fichier, comme le dit le commentaire. Sinon, cron ne fonctionnera pas.

Mise à jour: Il existe un problème avec cette solution lorsque Elastic Beanstalk met à niveau vos instances. Par exemple, supposons qu'une seule instance exécute le travail cron. Vous obtenez une augmentation du trafic et Elastic Beanstalk vous permet de passer à deux instances. Leader_only s'assurera que vous ne disposez que d'un seul travail cron entre les deux instances. Votre trafic diminue et Elastic Beanstalk vous réduit à une instance. Mais au lieu de mettre fin à la deuxième instance, Elastic Beanstalk met fin à la première instance qui était le leader. Vous n'avez maintenant plus de tâches cron en cours d'exécution, car elles ne fonctionnaient que sur la première instance terminée. Voir les commentaires ci-dessous.

Mise à jour 2: Cela ressort clairement des commentaires ci-dessous: AWS dispose désormais d'une protection contre la terminaison automatique d'instances. Activez-le simplement sur votre instance de leader et vous êtes prêt à partir. - Nicolás Arévalo Le 28 Octobre '16 à 9:23

82
Anarchtica

C'est la manière officielle de le faire maintenant (2015+). Veuillez essayer ceci d’abord, c’est de loin la méthode la plus simple et la plus fiable actuellement.

Selon les documents actuels, est capable d'exécuter des tâches périodiques sur leur soi-disant niveau de travail .

Citant la documentation:

AWS Elastic Beanstalk prend en charge des tâches périodiques pour les niveaux d'environnement de travail dans des environnements exécutant une configuration prédéfinie avec une pile de solution contenant "v1.2.0" dans le nom du conteneur. Vous devez créer un nouvel environnement.

La partie sur cron.yaml est également intéressante.

Pour appeler des tâches périodiques, votre groupe de sources d’application doit inclure un fichier cron.yaml au niveau de la racine. Le fichier doit contenir des informations sur les tâches périodiques que vous souhaitez planifier. Spécifiez cette information en utilisant la syntaxe standard de la crontab.

Mise à jour: Nous avons pu obtenir ce travail. Voici quelques pièges importants de notre expérience (plate-forme Node.js):

  • Lorsque vous utilisez le fichier cron.yaml, assurez-vous de disposer du dernier awsebcli , car les anciennes versions ne fonctionneront pas correctement.
  • Il est également essentiel de créer un nouvel environnement (du moins dans notre cas, c’était le cas), et pas seulement de répliquer l’ancien.
  • Si vous voulez vous assurer que CRON est pris en charge sur votre instance EC2 Worker Tier, connectez-vous ssh (eb ssh) et exécutez cat /var/log/aws-sqsd/default.log. Il devrait signaler comme aws-sqsd 2.0 (2015-02-18). Si vous n'avez pas la version 2.0, quelque chose s'est mal passé lors de la création de votre environnement et vous devez en créer un comme indiqué ci-dessus.
54
xaralis

En ce qui concerne la réponse de jamieb, et comme le mentionne alrdinleal, vous pouvez utiliser la propriété 'leader_only' pour vous assurer qu'une seule instance EC2 exécute le travail cron.

Citation extraite de http://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html :

vous pouvez utiliser leader_only. Une instance est choisie pour être le leader dans un groupe Auto Scaling. Si la valeur leader_only est définie sur true, la commande ne s'exécute que sur l'instance marquée en tant que leader.

J'essaie de faire quelque chose de similaire sur mon compte-rendu électronique, je mettrai donc mon message à jour si je le résous.

METTRE À JOUR:

Ok, j'ai maintenant des tâches cron qui utilisent la configuration eb suivante:

files:
  "/tmp/cronjob" :
    mode: "000777"
    owner: ec2-user
    group: ec2-user
    content: |
      # clear expired baskets
      */10 * * * * /usr/bin/wget -o /dev/null http://blah.elasticbeanstalk.com/basket/purge > $HOME/basket_purge.log 2>&1
      # clean up files created by above cronjob
      30 23 * * * rm $HOME/purge*
    encoding: plain 
container_commands:
  purge_basket: 
    command: crontab /tmp/cronjob
    leader_only: true
commands:
  delete_cronjob_file: 
    command: rm /tmp/cronjob

Essentiellement, je crée un fichier temporaire avec les tâches cron, puis je configure la crontab pour qu'elle lise à partir du fichier temporaire, puis le supprime par la suite. J'espère que cela t'aides.

31
beterthanlife

Comme mentionné ci-dessus, le défaut fondamental de toute configuration de crontab est que cela ne se produit que lors du déploiement. Au fur et à mesure que le cluster est automatiquement mis à l'échelle, puis redescendu, il est également recommandé d'être le premier serveur désactivé. De plus, il n'y aurait pas de basculement, ce qui était essentiel pour moi.

J'ai fait des recherches, puis discuté avec notre spécialiste des comptes AWS pour échanger des idées et valider la solution que j'ai proposée. Vous pouvez accomplir cela avec OpsWorks , bien que ce soit un peu comme utiliser une maison pour tuer une mouche. Il est également possible d'utiliser Data Pipeline avec Task Runner , mais les scripts qu'il peut exécuter sont limités, et j'avais besoin de pouvoir exécuter des scripts PHP, avec un accès au code complet. base. Vous pouvez également dédier une instance EC2 en dehors du cluster ElasticBeanstalk, mais vous n'avez alors plus aucun basculement.

Voici donc ce que j'ai proposé, qui est apparemment non conventionnel (comme l'a commenté le représentant AWS) et peut être considéré comme un piratage, mais cela fonctionne et est solide avec le basculement. J'ai choisi une solution de codage utilisant le SDK, que je montrerai en PHP, bien que vous puissiez utiliser la même méthode dans n'importe quelle langue de votre choix.

// contains the values for variables used (key, secret, env)
require_once('cron_config.inc'); 

// Load the AWS PHP SDK to connection to ElasticBeanstalk
use Aws\ElasticBeanstalk\ElasticBeanstalkClient;

$client = ElasticBeanstalkClient::factory(array(
    'key' => AWS_KEY,
    'secret' => AWS_SECRET,
    'profile' => 'your_profile',
    'region'  => 'us-east-1'
));

$result = $client->describeEnvironmentResources(array(
    'EnvironmentName' => AWS_ENV
));

if (php_uname('n') != $result['EnvironmentResources']['Instances'][0]['Id']) {
    die("Not the primary EC2 instance\n");
}

Alors, parcourez ceci et son fonctionnement ... Vous appelez les scripts de crontab comme vous le feriez normalement sur chaque instance EC2. Chaque script inclut cela au début (ou inclut un seul fichier pour chacun, comme je l’utilise), qui établit un objet ElasticBeanstalk et récupère une liste de toutes les instances. Il utilise uniquement le premier serveur de la liste et vérifie s'il correspond, ce qui se produira s'il continue, sinon il meurt et se ferme. J'ai vérifié et la liste renvoyée semble être cohérente. Techniquement, elle ne doit être cohérente que pendant une minute environ, chaque instance exécutant le cron planifié. Si cela change, cela n’aura pas d’importance, car là encore, il n’est pertinent que pour cette petite fenêtre.

Cela n’était pas élégant du tout, mais répondait à nos besoins spécifiques: ne pas augmenter les coûts avec un service supplémentaire ou avoir une instance EC2 dédiée, et avoir un basculement en cas de défaillance. Nos scripts cron exécutent des scripts de maintenance qui sont placés dans SQS et chaque serveur de la grappe facilite l'exécution. Au moins, cela peut vous donner une alternative si cela répond à vos besoins. 

-Davey

11
user1599237

J'ai parlé à un agent de support AWS et c'est comme cela que nous avons obtenu que cela fonctionne pour moi. Solution 2015:

Créez un fichier dans votre répertoire .ebextensions avec votre nom de fichier.config . Dans l'entrée du fichier de configuration:

des dossiers:
 "/etc/cron.d/cron_example":
 mode: "000644" 
 propriétaire: root 
 groupe: racine 
 contenu: | 
 * * * * * root /usr/local/bin/cron_example.sh

 "/usr/local/bin/cron_example.sh":
 mode: "000755" 
 propriétaire: root 
 groupe: racine 
 contenu: | 
 #!/bin/bash 

 /usr/local/bin/test_cron.sh || sortie
 echo "Cron en cours d'exécution à" `date` >> /tmp/cron_example.log
 # Faites maintenant des tâches qui ne devraient s’exécuter que sur 1 instance ...

 "/usr/local/bin/test_cron.sh":
 mode: "000755" 
 propriétaire: root 
 groupe: racine 
 contenu: | 
 #!/bin/bash 

 METADATA =/opt/aws/bin/ec2-metadata 
 INSTANCE_ID = `$ METADATA -i | awk '{print $ 2}' `
 REGION = `$ METADATA -z | awk '{print substr ($ 2, 0, length ($ 2) -1)}' `

 # Trouver le nom de notre groupe Auto Scaling .
 ASG = `aws ec2 describe-tags --filters" Nom = id-ressource, Valeurs = $ INSTANCE_ID "\ 
 --region $ REGION --output text | awk '/ aws: autoscaling: groupName/{print $ 5}' `

 # Trouver la première instance du groupe 
 FIRST = `aws autoscaling décrire-auto-mettre à l'échelle-groupes --auto-mettre à l'échelle-noms-groupes $ ASG\
 --region $ REGION --output text | awk '/ InService $/{print $ 4}' | trier | tête -1` 

 # Testez si elles sont identiques .
 ["$ FIRST" = "$ INSTANCE_ID"] 

 Commandes: 
 rm_old_cron: 
 commande: "rm * .bak" 
 cwd: "/etc/cron.d"
 ignoreErrors: true 

Cette solution présente 2 inconvénients:

  1. Lors de déploiements ultérieurs, Beanstalk renomme le script cron existant en .bak, mais cron l'exécutera toujours. Votre Cron s'exécute maintenant deux fois sur le même ordinateur.
  2. Si votre environnement évolue, vous obtenez plusieurs instances, toutes exécutant votre script cron. Cela signifie que vos mails sont répétés ou que vos archives de base de données sont dupliquées.

Solution de contournement:

  1. Assurez-vous que tout script .ebextensions créant un cron supprime également les fichiers .bak lors de déploiements ultérieurs.
  2. Ayez un script d'assistance qui fait ce qui suit: - Obtient l'ID d'instance actuel à partir des métadonnées - Obtient le fichier Auto .__ actuel. Nom du groupe de mise à l'échelle à partir des balises EC2 - Obtient la liste des EC2 Instances de ce groupe, triées par ordre alphabétique. - Prend le premier exemple de cette liste. - Compare l'ID d'instance de l'étape 1 avec le premier ID d'instance de l'étape 4 . Vos scripts cron peuvent ensuite utiliser ce script d'assistance pour déterminer s'ils doivent être exécutés.

Caveat: 

  • Le rôle IAM utilisé pour les instances Beanstalk nécessite les autorisations ec2: DescribeTags et autoscaling: DescribeAutoScalingGroups.
  • Les instances choisies sont celles indiquées comme InService par Auto Scaling. Cela ne signifie pas nécessairement qu'ils sont complètement démarrés et prêts à exécuter votre cron.

Vous n'auriez pas à définir les rôles IAM si vous utilisez le rôle beanstalk par défaut.

8
Ken

Si vous utilisez Rails, vous pouvez utiliser le joyau every-elasticbeanstalk . Il vous permet d'exécuter des tâches cron sur toutes les instances ou sur une seule. Il vérifie toutes les minutes pour s'assurer qu'il n'y a qu'une seule instance "leader" et promouvra automatiquement un serveur en "leader" s'il n'y en a pas. Cela est nécessaire car Elastic Beanstalk n'a que le concept de leader lors du déploiement et peut fermer n'importe quelle instance à tout moment lors de la mise à l'échelle.

UPDATE Je suis passé à AWS OpsWorks et je ne maintiens plus ce bijou. Si vous avez besoin de plus de fonctionnalités que celles disponibles dans les bases d'Elastic Beanstalk, je vous recommande fortement de passer à OpsWorks.

7
dignoe

Vous ne voulez vraiment pas exécuter de tâches cron sur Elastic Beanstalk. Étant donné que vous aurez plusieurs instances d'application, cela peut entraîner des problèmes de concurrence et d'autres problèmes étranges. En fait, j'ai récemment blogué à propos de cela (4ème ou 5ème conseil sur la page). La version courte: Selon l'application, utilisez une file d'attente de travaux telle que SQS ou une solution tierce telle que iron.io .

6
jamieb

Une solution plus lisible utilisant files au lieu de container_commands:

des dossiers:
 "/etc/cron.d/my_cron":
 mode: "000644" 
 propriétaire: root 
 groupe: racine 
 contenu: | 
 # remplace l'adresse électronique par défaut 
 MAILTO = "[email protected]" 
 # lance une commande Symfony toutes les cinq minutes (en tant qu'utilisateur ec2) 
 */10 * * * * utilisateur ec2/usr/bin/php/var/app/current/app/console do: quelque chose 
 codage: simple 
 commandes: 
 # supprime le fichier de sauvegarde créé par Elastic Beanstalk 
 clear_cron_backup: 
 commande: rm -f /etc/cron.d/watson.bak

Notez que le format diffère du format habituel de crontab en ce qu'il spécifie que l'utilisateur doit exécuter la commande en tant que.

4
Tamlyn

Quelqu'un s'interrogeait sur les problèmes de redimensionnement automatique de leader_only lorsque de nouveaux dirigeants se présentent. Je n'arrive pas à comprendre comment répondre à leurs commentaires, mais voyez ce lien: http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto- environnement de mise à l'échelle/

2
Random5000
1
poiati

Mon 1 cent de cotisation pour 2018

Voici la bonne façon de le faire (en utilisant Django/python et Django_crontab app): 

dans le dossier .ebextensions, créez un fichier comme celui-ci 98_cron.config:

files:
  "/tmp/98_create_cron.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/bin/sh
      cd /
      Sudo /opt/python/run/venv/bin/python /opt/python/current/app/manage.py crontab remove > /home/ec2-user/remove11.txt
      Sudo /opt/python/run/venv/bin/python /opt/python/current/app/manage.py crontab add > /home/ec2-user/add11.txt 

container_commands:
    98crontab:
        command: "mv /tmp/98_create_cron.sh /opt/elasticbeanstalk/hooks/appdeploy/post && chmod 774 /opt/elasticbeanstalk/hooks/appdeploy/post/98_create_cron.sh"
        leader_only: true

Il doit être container_commands au lieu de commands

1
Ronaldo Bahia

voici un correctif si vous voulez le faire en PHP. Vous avez juste besoin de cronjob.config dans votre dossier .ebextensions pour que cela fonctionne comme ceci.

files:
  "/etc/cron.d/my_cron":
    mode: "000644"
    owner: root
    group: root
    content: |
        empty stuff
    encoding: plain
commands:
  01_clear_cron_backup:
    command: "rm -f /etc/cron.d/*.bak"
  02_remove_content:
    command: "Sudo sed -i 's/empty stuff//g' /etc/cron.d/my_cron"
container_commands:
  adding_cron:
    command: "echo '* * * * * ec2-user . /opt/elasticbeanstalk/support/envvars && /usr/bin/php /var/app/current/index.php cron sendemail > /tmp/sendemail.log 2>&1' > /etc/cron.d/my_cron"
    leader_only: true

envvars obtient les variables d’environnement pour les fichiers. Vous pouvez déboguer la sortie sur le fichier tmp/sendemail.log comme ci-dessus.

J'espère que cela aide quelqu'un car cela nous a sûrement aidé!

0
foxybagga

Pour contrôler si Auto Scaling peut mettre fin à une instance particulière lors de la mise à l'échelle, utilisez la protection d'instance. Vous pouvez activer le paramètre de protection d'instance sur un groupe Auto Scaling ou sur une instance Auto Scaling individuelle. Lorsque Auto Scaling lance une instance, celle-ci hérite du paramètre de protection d'instance du groupe Auto Scaling. Vous pouvez modifier le paramètre de protection d'instance d'un groupe Auto Scaling ou d'une instance Auto Scaling à tout moment.

http://docs.aws.Amazon.com/autoscaling/latest/userguide/as-instance-termination.html#instance-protection

0
Dele

2017: Si vous utilisez Laravel5 +

Vous avez juste besoin de 2 minutes pour le configurer:

  • créer un niveau de travailleur
  • installer laravel-aws-worker

    composer require dusterio/laravel-aws-worker

  • ajoutez un fichier cron.yaml au dossier racine:

Ajoutez cron.yaml au dossier racine de votre application (il peut s'agir d'une partie De votre référentiel ou vous pouvez ajouter ce fichier juste avant le déploiement sur EB - l'important est que ce fichier soit présent à ce moment de déploiement):

version: 1
cron:
 - name: "schedule"
   url: "/worker/schedule"
   schedule: "* * * * *"

C'est tout!

Toute votre tâche dans App\Console\Kernel sera maintenant exécutée

Instructions détaillées et explications: https://github.com/dusterio/laravel-aws-worker

Comment écrire des tâches dans Laravel: https://laravel.com/docs/5.4/scheduling

0
Sebastien Horin

Nous sommes donc aux prises avec ce problème depuis un certain temps et après quelques discussions avec un représentant AWS, j'ai finalement trouvé ce que je pense être la meilleure solution.

L'utilisation d'un niveau de travail avec cron.yaml est certainement la solution la plus simple. Cependant, la documentation n'indique pas clairement que cela placera le travail au fin de la file d'attente SQS que vous utilisez pour exécuter vos travaux. Si vos tâches cron sont sensibles au temps (comme beaucoup le sont), cela n'est pas acceptable, car cela dépend de la taille de la file d'attente. Une option consiste à utiliser un environnement complètement séparé uniquement pour exécuter des tâches cron, mais je pense que c'est exagéré.

Certaines des autres options, telles que vérifier si vous êtes la première instance de la liste, ne sont pas idéales non plus. Que se passe-t-il si la première instance actuelle est en cours de fermeture?

La protection d'instance peut également être problématique: que se passe-t-il si cette instance est verrouillée/gelée?

Ce qui est important à comprendre, c'est comment AWS gère lui-même la fonctionnalité cron.yaml. Il existe un démon SQS qui utilise une table Dynamo pour gérer "l'élection du chef". Il écrit souvent à cette table et si le leader actuel ne l'a pas écrit dans peu de temps, l'instance suivante le remplacera. Voici comment le démon décide quelle instance déclencher le travail dans la file d'attente SQS.

Nous pouvons réutiliser les fonctionnalités existantes plutôt que d'essayer de réécrire les nôtres. Vous pouvez voir la solution complète ici: https://Gist.github.com/dorner/4517fe2b8c79ccb3971084ec28267f27

C'est en Ruby, mais vous pouvez facilement l'adapter à toute autre langue utilisant le kit SDK AWS. Essentiellement, il vérifie le leader actuel, puis vérifie l'état pour s'assurer qu'il est en bon état. La boucle est bouclée jusqu'à ce qu'un leader actuel soit en bon état et, si l'instance actuelle est le leader, exécutez le travail.

0
Cidolfas

Sur la base des principes de la réponse de user1599237 , où vous laissez les tâches cron s'exécuter sur toutes les instances, puis déterminez si elles doivent être autorisées à s'exécuter au début des tâches, j'ai créé une autre solution.

Au lieu de regarder les instances en cours d'exécution (et d'avoir à stocker votre clé et votre secret AWS), j'utilise la base de données MySQL à laquelle je me connecte déjà à partir de toutes les instances.

Il n'y a pas d'inconvénients, seulement des points positifs:

  • pas d'instance supplémentaire ni de frais
  • solution solide comme le roc - aucune chance de double exécution
  • évolutif - fonctionne automatiquement lorsque vos instances sont agrandies et redimensionnées
  • failover - fonctionne automatiquement en cas de défaillance d'une instance

Vous pouvez également utiliser un système de fichiers commun (tel que AWS EFS via le protocole NFS) à la place d'une base de données.

La solution suivante est créée dans PHP framework Yii mais vous pouvez facilement l'adapter à un autre framework et à un autre langage. De plus, le gestionnaire d'exceptions Yii::$app->system est un module de ma part. Remplacez-le par ce que vous utilisez.

/**
 * Obtain an exclusive lock to ensure only one instance or worker executes a job
 *
 * Examples:
 *
 * `php /var/app/current/yii process/lock 60 empty-trash php /var/app/current/yii maintenance/empty-trash`
 * `php /var/app/current/yii process/lock 60 empty-trash php /var/app/current/yii maintenance/empty-trash StdOUT./test.log`
 * `php /var/app/current/yii process/lock 60 "empty trash" php /var/app/current/yii maintenance/empty-trash StdOUT./test.log StdERR.ditto`
 * `php /var/app/current/yii process/lock 60 "empty trash" php /var/app/current/yii maintenance/empty-trash StdOUT./output.log StdERR./error.log`
 *
 * Arguments are understood as follows:
 * - First: Duration of the lock in minutes
 * - Second: Job name (surround with quotes if it contains spaces)
 * - The rest: Command to execute. Instead of writing `>` and `2>` for redirecting output you need to write `StdOUT` and `StdERR` respectively. To redirect stderr to stdout write `StdERR.ditto`.
 *
 * Command will be executed in the background. If determined that it should not be executed the script will terminate silently.
 */
public function actionLock() {
    $argsAll = $args = func_get_args();
    if (!is_numeric($args[0])) {
        \Yii::$app->system->error('Duration for obtaining process lock is not numeric.', ['Args' => $argsAll]);
    }
    if (!$args[1]) {
        \Yii::$app->system->error('Job name for obtaining process lock is missing.', ['Args' => $argsAll]);
    }

    $durationMins = $args[0];
    $jobName = $args[1];
    $instanceID = null;
    unset($args[0], $args[1]);

    $command = trim(implode(' ', $args));
    if (!$command) {
        \Yii::$app->system->error('Command to execute after obtaining process lock is missing.', ['Args' => $argsAll]);
    }

    // If using AWS Elastic Beanstalk retrieve the instance ID
    if (file_exists('/etc/elasticbeanstalk/.aws-eb-system-initialized')) {
        if ($awsEb = file_get_contents('/etc/elasticbeanstalk/.aws-eb-system-initialized')) {
            $awsEb = json_decode($awsEb);
            if (is_object($awsEb) && $awsEb->instance_id) {
                $instanceID = $awsEb->instance_id;
            }
        }
    }

    // Obtain lock
    $updateColumns = false;  //do nothing if record already exists
    $affectedRows = \Yii::$app->db->createCommand()->upsert('system_job_locks', [
        'job_name' => $jobName,
        'locked' => gmdate('Y-m-d H:i:s'),
        'duration' => $durationMins,
        'source' => $instanceID,
    ], $updateColumns)->execute();
    // The SQL generated: INSERT INTO system_job_locks (job_name, locked, duration, source) VALUES ('some-name', '2019-04-22 17:24:39', 60, 'i-HmkDAZ9S5G5G') ON DUPLICATE KEY UPDATE job_name = job_name

    if ($affectedRows == 0) {
        // record already exists, check if lock has expired
        $affectedRows = \Yii::$app->db->createCommand()->update('system_job_locks', [
                'locked' => gmdate('Y-m-d H:i:s'),
                'duration' => $durationMins,
                'source' => $instanceID,
            ],
            'job_name = :jobName AND DATE_ADD(locked, INTERVAL duration MINUTE) < NOW()', ['jobName' => $jobName]
        )->execute();
        // The SQL generated: UPDATE system_job_locks SET locked = '2019-04-22 17:24:39', duration = 60, source = 'i-HmkDAZ9S5G5G' WHERE job_name = 'clean-trash' AND DATE_ADD(locked, INTERVAL duration MINUTE) < NOW()

        if ($affectedRows == 0) {
            // We could not obtain a lock (since another process already has it) so do not execute the command
            exit;
        }
    }

    // Handle redirection of stdout and stderr
    $command = str_replace('StdOUT', '>', $command);
    $command = str_replace('StdERR.ditto', '2>&1', $command);
    $command = str_replace('StdERR', '2>', $command);

    // Execute the command as a background process so we can exit the current process
    $command .= ' &';

    $output = []; $exitcode = null;
    exec($command, $output, $exitcode);
    exit($exitcode);
}

Voici le schéma de base de données que j'utilise:

CREATE TABLE `system_job_locks` (
    `job_name` VARCHAR(50) NOT NULL,
    `locked` DATETIME NOT NULL COMMENT 'UTC',
    `duration` SMALLINT(5) UNSIGNED NOT NULL COMMENT 'Minutes',
    `source` VARCHAR(255) NULL DEFAULT NULL,
    PRIMARY KEY (`job_name`)
)
0
TheStoryCoder

J'avais une autre solution à cela si un fichier php doit être exécuté via cron et si vous aviez défini des instances NAT, vous pouvez alors mettre cronjob sur une instance NAT et exécuter le fichier php via wget.

0
prasoon