Я хотел бы знать, есть ли способ настроить задание / задачу cron для выполнения каждую минуту. В настоящее время любой из моих экземпляров может выполнять эту задачу.
Вот что я безуспешно пытался сделать в файлах конфигурации:
container_commands:
01cronjobs:
command: echo "*/1 * * * * root php /etc/httpd/myscript.php"
Я не совсем уверен, что это правильный способ сделать это
Любые идеи?
Ответы:
Вот как я добавил задание cron в Elastic Beanstalk:
Создайте папку в корне вашего приложения с именем .ebextensions, если она еще не существует. Затем создайте файл конфигурации в папке .ebextensions. Я буду использовать example.config в целях иллюстрации. Затем добавьте это в example.config
Это файл конфигурации YAML для Elastic Beanstalk. При копировании в текстовый редактор убедитесь, что в текстовом редакторе используются пробелы вместо табуляции. В противном случае вы получите ошибку YAML, когда нажмете это на EB.
Это создает команду 01_some_cron_job. Команды запускаются в алфавитном порядке, поэтому 01 гарантирует, что он будет запущен как первая команда.
Затем команда берет содержимое файла с именем some_cron_job.txt и добавляет его в файл с именем some_cron_job в /etc/cron.d.
Затем команда изменяет разрешения для файла /etc/cron.d/some_cron_job.
Ключ leader_only гарантирует, что команда будет запущена только на экземпляре ec2, который считается лидером. Вместо того, чтобы запускать каждый экземпляр ec2, который у вас может быть.
Затем создайте файл с именем some_cron_job.txt внутри папки .ebextensions. Вы разместите свои задания cron в этом файле.
Так например:
Таким образом, это задание cron будет запускаться каждую минуту каждого часа каждого дня от имени пользователя root и сбрасывать вывод в / dev / null. / usr / bin / php - это путь к php. Затем замените some-php-script-here на путь к вашему файлу php. Это, очевидно, предполагает, что ваше задание cron должно запускать файл PHP.
Также убедитесь, что файл some_cron_job.txt имеет новую строку в конце файла, как сказано в комментарии. В противном случае cron не запустится.
Обновление: существует проблема с этим решением, когда Elastic Beanstalk масштабирует ваши экземпляры. Например, допустим, у вас есть один экземпляр с запущенным заданием cron. Вы получаете увеличение трафика, поэтому Elastic Beanstalk масштабирует вас до двух экземпляров. Leader_only гарантирует, что между двумя экземплярами будет выполняться только одно задание cron. Ваш трафик уменьшается, а Elastic Beanstalk сокращает вас до одного экземпляра. Но вместо завершения второго экземпляра Elastic Beanstalk завершает работу первого экземпляра, который был лидером. Теперь у вас нет запущенных заданий cron, поскольку они выполнялись только на первом прерванном экземпляре. См. Комментарии ниже.
Обновление 2: просто поясняю это из комментариев ниже: теперь AWS имеет защиту от автоматического завершения инстанса. Просто включите его на своем экземпляре лидера, и все готово. - Николас Аревало 28 окт.
источник
01_some_cron_job
к02_some_cron_job
и добавил01_remove_cron_jobs
со следующим:command: "rm /etc/cron.d/cron_jobs || exit 0"
. Таким образом, после каждого развертыванияcron_jobs
файл будет только у лидера . Если лидеры меняются, вы можете просто передислоцироваться, и кроны будут исправлены, чтобы бежать еще раз.leader_only
собственность. Он используется только во время развертывания, и если вы уменьшите масштаб или ваш «ведущий» экземпляр выйдет из строя, у вас обязательноЭто официальный способ сделать это сейчас (2015+). Пожалуйста, сначала попробуйте этот способ, это, безусловно, самый простой из доступных в настоящее время и самый надежный метод.
Согласно текущим документам, можно запускать периодические задачи на так называемом рабочем уровне .
Ссылаясь на документацию:
Также интересна часть о cron.yaml :
Обновление: мы смогли получить эту работу. Вот несколько важных ошибок из нашего опыта (платформа Node.js):
eb ssh
) и запуститеcat /var/log/aws-sqsd/default.log
. Он должен сообщать какaws-sqsd 2.0 (2015-02-18)
. Если у вас нет версии 2.0, при создании среды что-то пошло не так, и вам нужно создать новую, как указано выше.источник
Что касается ответа jamieb, и, как упоминает alrdinleal, вы можете использовать свойство leader_only, чтобы гарантировать, что только один экземпляр EC2 выполняет задание cron.
Цитата взята из http://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html :
Я пытаюсь добиться того же на моем eb, поэтому обновлю свой пост, если я его решу.
ОБНОВИТЬ:
Хорошо, теперь у меня есть рабочие cronjobs со следующей конфигурацией eb:
По сути, я создаю временный файл с заданиями cron, а затем устанавливаю crontab для чтения из временного файла, а затем удаляю временный файл. Надеюсь это поможет.
источник
Как упоминалось выше, основной недостаток установки любой конфигурации crontab заключается в том, что это происходит только при развертывании. Поскольку кластер автоматически масштабируется вверх, а затем обратно, предпочтительно, чтобы он также был первым выключенным сервером. Вдобавок не будет переключения при отказе, что для меня было критично.
Я провел небольшое исследование, а затем поговорил с нашим специалистом по аккаунтам AWS, чтобы обсудить идеи и проверить решение, которое я придумал. Вы можете добиться этого с помощью OpsWorks , хотя это немного похоже на использование дома, чтобы убить муху. Также можно использовать Data Pipeline с Task Runner , но это имеет ограниченные возможности в сценариях, которые он может выполнять, и мне нужно было иметь возможность запускать сценарии PHP с доступом ко всей базе кода. Вы также можете выделить экземпляр EC2 вне кластера ElasticBeanstalk, но тогда у вас не будет повторного переключения при отказе.
Итак, вот что я придумал, что, по-видимому, нетрадиционно (как прокомментировал представитель AWS) и может считаться взломом, но он работает и надежен с отказом. Я выбрал решение для кодирования с использованием SDK, которое я покажу на PHP, хотя вы можете использовать тот же метод на любом языке, который вам больше нравится.
Итак, рассмотрим это и то, как это работает ... Вы вызываете скрипты из crontab, как обычно, на каждом экземпляре EC2. Каждый скрипт включает это в начале (или включает по одному файлу для каждого, как я его использую), который устанавливает объект ElasticBeanstalk и извлекает список всех экземпляров. Он использует только первый сервер в списке и проверяет, совпадает ли он с самим собой, что, если он это делает, продолжает, в противном случае он умирает и закрывается. Я проверил, и возвращенный список кажется согласованным, что технически должно быть согласованным только в течение минуты или около того, поскольку каждый экземпляр выполняет запланированный cron. Если это действительно изменится, это не имеет значения, поскольку опять же, это актуально только для этого маленького окна.
Это ни в коем случае не изящно, но соответствовало нашим конкретным потребностям, которые заключались не в увеличении стоимости за счет дополнительной услуги или необходимости иметь выделенный экземпляр EC2, а также в случае отказа в случае отказа. Наши сценарии cron запускают сценарии обслуживания, которые помещаются в SQS, и каждый сервер в кластере помогает выполнять. По крайней мере, это может дать вам альтернативный вариант, если он соответствует вашим потребностям.
-Дэйви
источник
$instanceId = file_get_contents("http://instance-data/latest/meta-data/instance-id");
Затем просто используйте этот $ instanceId var для сравнения.Я поговорил с агентом службы поддержки AWS, и вот как мы заставили меня работать. Решение 2015 года:
Создайте файл в каталоге .ebextensions с именем your_file_name.config. В ввод файла конфигурации:
У этого решения есть 2 недостатка:
Обходной путь:
Предостережение:
Вам не нужно будет устанавливать роли IAM, если вы используете роль beanstalk по умолчанию.
источник
Если вы используете Rails, вы можете использовать гем when-elasticbeanstalk . Он позволяет запускать задания cron на всех экземплярах или только на одном. Он ежеминутно проверяет наличие только одного экземпляра «лидера» и автоматически повышает один сервер до «лидера», если его нет. Это необходимо, поскольку Elastic Beanstalk имеет понятие лидера только во время развертывания и может закрыть любой экземпляр в любое время при масштабировании.
ОБНОВЛЕНИЕ Я перешел на использование AWS OpsWorks и больше не обслуживаю этот драгоценный камень. Если вам нужно больше функциональных возможностей, чем доступно в основах Elastic Beanstalk, я настоятельно рекомендую перейти на OpsWorks.
источник
Вы действительно не хотите запускать задания cron на Elastic Beanstalk. Поскольку у вас будет несколько экземпляров приложения, это может вызвать состояния гонки и другие странные проблемы. Я на самом деле недавно писал об этом в блоге (4-я или 5-я подсказка на странице). Короткая версия: В зависимости от приложения, использовать очереди заданий как SQS или решение третьей стороны , как iron.io .
источник
2017: Если вы используете Laravel5 +
На его настройку потребуется всего 2 минуты:
установить laravel-aws-worker
composer require dusterio/laravel-aws-worker
добавьте cron.yaml в корневую папку:
Это оно!
Вся ваша задача
App\Console\Kernel
теперь будет выполненаПодробные инструкции и объяснения: https://github.com/dusterio/laravel-aws-worker
Как писать задачи внутри Laravel: https://laravel.com/docs/5.4/scheduling
источник
Более читаемое решение, использующее
files
вместоcontainer_commands
:Обратите внимание, что формат отличается от обычного формата crontab тем, что в нем указывается, от имени какого пользователя следует запускать команду.
источник
Мой 1 цент взноса за 2018 год
Вот это правильный способ сделать это (используя
django/python
иdjango_crontab
приложение):внутри
.ebextensions
папки создайте такой файл98_cron.config
:Это должно быть
container_commands
вместоcommands
источник
Кто-то задумывался о проблемах автоматического масштабирования leader_only при появлении новых лидеров. Кажется, я не могу понять, как ответить на их комментарии, но посмотрите эту ссылку: http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling- Окружающая среда/
источник
Последний пример от Amazon - самый простой и эффективный (периодические задачи):
https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features-managing-env-tiers.html
где вы создаете отдельный рабочий уровень для выполнения любых ваших заданий cron. Создайте файл cron.yaml и поместите его в корневую папку. Одна из моих проблем (после того, как казалось, что cron не запускается) заключалась в том, что у моего CodePipeline не было полномочий для выполнения модификации Dynamodb. Исходя из этого, после добавления доступа к FullDynamoDB в разделе IAM -> роли -> yourpipeline и повторного развертывания (эластичный beanstalk) он работал отлично.
источник
Вот полное объяснение решения:
http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling-environment/
источник
Итак, мы долго боролись с этим, и после некоторого обсуждения с представителем AWS я, наконец, пришел к тому, что я считаю лучшим решением.
Использование рабочего уровня с cron.yaml - определенно самое простое решение. Однако документация не разъясняет, что это поместит задание в конец очереди SQS, которую вы используете для фактического выполнения своих заданий. Если ваши задания cron чувствительны ко времени (как и многие из них), это неприемлемо, так как это будет зависеть от размера очереди. Один из вариантов - использовать полностью отдельную среду только для запуска заданий cron, но я думаю, что это излишне.
Некоторые из других вариантов, например проверка, являетесь ли вы первым в списке, также не идеальны. Что, если текущий первый экземпляр находится в процессе отключения?
Защита экземпляра также может иметь проблемы - что, если этот экземпляр заблокируется / заморожен?
Важно понимать, как сама AWS управляет функциональностью cron.yaml. Существует демон SQS, который использует таблицу Dynamo для обработки «выбора лидера». Он часто записывает в эту таблицу, и если текущий лидер не записал в течение короткого времени, следующий экземпляр станет лидером. Таким образом демон решает, какой экземпляр запустить задание в очередь SQS.
Мы можем перепрофилировать существующую функциональность, а не пытаться переписать нашу собственную. Вы можете увидеть полное решение здесь: https://gist.github.com/dorner/4517fe2b8c79ccb3971084ec28267f27
Это в Ruby, но вы можете легко адаптировать его к любому другому языку, на котором есть AWS SDK. По сути, он проверяет текущего лидера, а затем проверяет состояние, чтобы убедиться, что оно в хорошем состоянии. Он будет зацикливаться до тех пор, пока текущий лидер не будет в хорошем состоянии, и если текущий экземпляр является лидером, выполнить задание.
источник
http://docs.aws.amazon.com/autoscaling/latest/userguide/as-instance-termination.html#instance-protection
источник
У меня было другое решение для этого, если файл php необходимо запустить через cron, и если вы установили какие-либо экземпляры NAT, вы можете поместить cronjob в экземпляр NAT и запустить файл php через wget.
источник
вот исправление, если вы хотите сделать это в PHP. Вам просто нужен cronjob.config в папке .ebextensions, чтобы он работал так.
envvars получает переменные среды для файлов. Вы можете отлаживать вывод в tmp / sendemail.log, как указано выше.
Надеюсь, это кому-то поможет, ведь это, безусловно, помогло нам!
источник
На основе принципов ответа от пользователя 1599237 , где вы позволяете заданиям cron запускаться во всех экземплярах, но вместо этого в начале заданий определяете, следует ли им разрешить запуск, я сделал другое решение.
Вместо того, чтобы смотреть на запущенные экземпляры (и хранить ваш ключ и секрет AWS), я использую базу данных MySQL, к которой я уже подключаюсь со всех экземпляров.
Минусов нет, только положительные:
В качестве альтернативы вы также можете использовать общую файловую систему (например, AWS EFS по протоколу NFS) вместо базы данных.
Следующее решение создано в PHP-фреймворке Yii, но вы можете легко адаптировать его для другого фреймворка и языка. Также обработчик исключений
Yii::$app->system
- это мой собственный модуль. Замените его тем, что вы используете./** * 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); }
Вот схема базы данных, которую я использую:
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`) )
источник