AWS Elastic Beanstalk, работает cronjob

Я хотел бы знать, есть ли способ настроить cronjob/ задачу для выполнения каждую минуту. В настоящее время любой из моих экземпляров должен быть в состоянии выполнить эту задачу.

Вот что я пытался сделать в файлах конфигурации без успеха:

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

Я не совсем уверен, что это правильный способ сделать это

Есть идеи?

19 ответов

Решение

Вот как я добавил работу cron в Elastic Beanstalk:

Создайте папку в корне вашего приложения с именем.ebextensions, если она еще не существует. Затем создайте файл конфигурации в папке.ebextensions. Я буду использовать example.config для иллюстрации. Затем добавьте это в 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

Это файл конфигурации 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 в этом файле.

Так, например:

# 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

Так что это задание 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 октября '16 в 9:23

Это официальный способ сделать это сейчас (2015+). Пожалуйста, попробуйте сначала, это самый простой из доступных на данный момент и самый надежный метод.

Согласно текущим документам, можно выполнять периодические задачи на их так называемом рабочем уровне.

Ссылаясь на документацию:

AWS Elastic Beanstalk поддерживает периодические задачи для уровней рабочей среды в средах, в которых выполняется предварительно заданная конфигурация со стеком решений, который содержит "v1.2.0" в имени контейнера. Вы должны создать новую среду.

Также интересна часть о cron.yaml:

Чтобы вызывать периодические задачи, ваш исходный комплект приложения должен включать файл cron.yaml на корневом уровне. Файл должен содержать информацию о периодических задачах, которые вы хотите запланировать. Укажите эту информацию, используя стандартный синтаксис crontab.

Обновление: мы смогли получить эту работу. Вот несколько важных моментов из нашего опыта (платформа Node.js):

  • При использовании файла cron.yaml убедитесь, что у вас установлена ​​последняя версия awsebcli, поскольку более старые версии не будут работать должным образом.
  • Также важно создать новую среду (по крайней мере, в нашем случае), а не просто клонировать старую.
  • Если вы хотите убедиться, что CRON поддерживается в вашем экземпляре рабочего уровня EC2, вставьте в него ssh (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:

Вы можете использовать leader_only. Один экземпляр выбран в качестве лидера в группе автоматического масштабирования. Если для значения leader_only задано значение true, команда выполняется только для экземпляра, помеченного как лидер.

Я пытаюсь добиться подобной вещи на моем eb, поэтому обновлю свой пост, если я решу это.

ОБНОВИТЬ:

Хорошо, теперь у меня есть рабочие cronjobs, использующие следующую конфигурацию eb:

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

По сути, я создаю временный файл с помощью cronjobs, затем устанавливаю crontab для чтения из временного файла, а затем удаляю временный файл. Надеюсь это поможет.

Я поговорил с агентом поддержки AWS, и именно так мы сработали для меня. Решение 2015:

Создайте файл в каталоге.ebextensions с вашим_файлом.config. В файле конфигурации введите:

 файлы:
  "/Etc/cron.d/cron_example":
    режим: "000644"
    владелец: root
    группа: корень
    содержание: |
      * * * * * root /usr/local/bin/cron_example.sh

  "/Usr/local/bin/cron_example.sh":
    режим: "000755"
    владелец: root
    группа: корень
    содержание: |
      #!/ Bin / Баш

      /usr/local/bin/test_cron.sh || выход
      echo "Cron работает в" `date` >> /tmp/cron_example.log
      # Теперь выполняйте задачи, которые должны выполняться только на 1 экземпляре...

  "/Usr/local/bin/test_cron.sh":
    режим: "000755"
    владелец: root
    группа: корень
    содержание: |
      #!/ Bin / Баш

      METADATA=/ Opt / AWS /bin/ec2-метаданные
      INSTANCE_ID = `$ METADATA -i | awk '{print $ 2}' `
      REGION = `$ METADATA -z | awk '{print substr ($ 2, 0, length ($ 2) -1)}' `

      # Найдите название нашей группы автоматического масштабирования.
      ASG=`aws ec2 description-tags --filters" Имя = идентификатор ресурса, Значения =$INSTANCE_ID" \
        --region $REGION - выходной текст | awk '/aws: автоматическое масштабирование: groupName / {print $ 5}' `

      # Найти первый экземпляр в группе
      FIRST=`aws-авто-масштабирование description-auto-scaling-groups --auto-scaling-group-names $ASG \
        --region $REGION - выходной текст | awk '/InService$/ {print $4}' | сортировать | голова -1`

      # Проверьте, если они одинаковы.
      [ "$FIRST" = "$INSTANCE_ID" ]

команды:
  rm_old_cron:
    команда: "рм *.бак"
    cwd: "/etc/cron.d"
    ignoreErrors: true

Это решение имеет 2 недостатка:

  1. При последующих развертываниях Beanstalk переименовывает существующий скрипт cron в.bak, но cron все равно будет его запускать. Ваш Cron теперь выполняется дважды на одной машине.
  2. Если ваша среда расширяется, вы получаете несколько экземпляров, каждый из которых запускает ваш скрипт cron. Это означает, что ваши почтовые снимки повторяются, или ваши архивы базы данных дублируются

Временное решение:

  1. Убедитесь, что любой сценарий.ebextensions, который создает cron, также удаляет файлы.bak при последующих развертываниях.
  2. Есть вспомогательный скрипт, который выполняет следующие действия: - получает текущий идентификатор экземпляра из метаданных; - получает текущее имя группы автоматического масштабирования из тегов EC2; - получает список экземпляров EC2 в этой группе, отсортированных по алфавиту. - Принимает первый экземпляр из этого списка. - Сравнивает идентификатор экземпляра из шага 1 с первым идентификатором экземпляра из шага 4. Затем ваши сценарии cron могут использовать этот вспомогательный сценарий, чтобы определить, должны ли они выполняться.

Предостережение:

  • Роль IAM, используемая для экземпляров Beanstalk, требует ec2:DescribeTags и autoscale: разрешения DescribeAutoScalingGroups
  • Экземпляры, выбранные из, показаны как InService при автоматическом масштабировании. Это не обязательно означает, что они полностью загружены и готовы к запуску вашего cron.

Вам не нужно устанавливать роли IAM, если вы используете роль beanstalk по умолчанию.

Как упоминалось выше, основной недостаток при установке любой конфигурации crontab заключается в том, что это происходит только при развертывании. Поскольку кластер автоматически масштабируется, а затем возвращается обратно, рекомендуется также быть первым выключенным сервером. Кроме того, не было бы переключения при сбое, что для меня было критическим.

Я провел некоторое исследование, а затем поговорил с нашим специалистом по учетным записям AWS, чтобы поделиться идеями и проверить решение, которое я придумал. Вы можете сделать это с OpsWorks, хотя это немного похоже на использование дома, чтобы убить муху. Также возможно использовать Data Pipeline с Task Runner, но это ограничивает возможности сценариев, которые он может выполнять, и мне нужно было иметь возможность запускать сценарии PHP с доступом ко всей базе кода. Вы также можете выделить экземпляр EC2 вне кластера ElasticBeanstalk, но тогда у вас снова не будет аварийного переключения.

Итак, вот то, что я придумал, что, по-видимому, является нетрадиционным (как прокомментировал представитель AWS) и может считаться хаком, но оно работает и устойчиво при сбое. Я выбрал решение для кодирования с использованием SDK, который я покажу в PHP, хотя вы можете сделать тот же метод на любом языке, который вы предпочитаете.

// 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");
}

Итак, пройдемся по всему этому и узнаем, как он работает... Вы вызываете скрипты из crontab, как обычно, на каждом экземпляре EC2. Каждый сценарий включает это в начале (или включает один файл для каждого, как я его использую), который устанавливает объект ElasticBeanstalk и извлекает список всех экземпляров. Он использует только первый сервер в списке и проверяет, соответствует ли он самому себе, который, если он это делает, продолжает, в противном случае он умирает и закрывается. Я проверил, и возвращаемый список кажется непротиворечивым, который технически должен быть непротиворечивым только в течение минуты или около того, поскольку каждый экземпляр выполняет запланированный cron. Если это действительно изменится, это не будет иметь значения, так как опять-таки это относится только к этому маленькому окну.

Это ни в коем случае не элегантно, но соответствует нашим особым потребностям - которые не должны были увеличивать стоимость с дополнительным обслуживанием или иметь отдельный экземпляр EC2, и имели бы отказ при сбое в случае любого отказа. Наши сценарии cron запускают сценарии обслуживания, которые помещаются в SQS, и каждый сервер в кластере помогает выполнять их. По крайней мере, это может дать вам альтернативный вариант, если он соответствует вашим потребностям.

-Davey

Если вы используете Rails, вы можете использовать гем всякий раз, когда вы используете эластичный боб. Это позволяет вам запускать задания cron на всех экземплярах или только на одном. Он проверяет каждую минуту, чтобы убедиться, что существует только один экземпляр "лидера", и автоматически переводит один сервер в "лидер", если его нет. Это необходимо, поскольку Elastic Beanstalk имеет концепцию лидера только во время развертывания и может закрыть любой экземпляр в любое время при масштабировании.

ОБНОВЛЕНИЕ Я перешел на использование AWS OpsWorks и больше не поддерживаю этот драгоценный камень. Если вам нужно больше функциональности, чем доступно в основах Elastic Beanstalk, я настоятельно рекомендую перейти на OpsWorks.

2017: Если вы используете Laravel5+

Вам просто нужно 2 минуты, чтобы настроить его:

  • создать рабочий уровень
  • установить laravel-aws-worker

    composer require dusterio/laravel-aws-worker

  • добавьте cron.yaml в корневую папку:

Добавьте cron.yaml в корневую папку вашего приложения (это может быть частью вашего репозитория или вы можете добавить этот файл непосредственно перед развертыванием в EB - важно, чтобы этот файл присутствовал во время развертывания):

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

Это оно!

Вся ваша задача в App\Console\Kernel теперь будет выполнен

Подробные инструкции и объяснения: https://github.com/dusterio/laravel-aws-worker

Как писать задачи внутри Laravel: https://laravel.com/docs/5.4/scheduling

Вы действительно не хотите работать cron на Elastic Beanstalk. Поскольку у вас будет несколько экземпляров приложения, это может вызвать состояние гонки и другие странные проблемы. Я на самом деле недавно писал об этом (4-й или 5-й отзыв вниз страницы). Краткая версия: в зависимости от приложения, используйте очередь заданий, такую ​​как SQS, или стороннее решение, такое как iron.io.

Более читаемое решение с использованием files вместо container_commands:

файлы:
  "/Etc/cron.d/my_cron":
    режим: "000644"
    владелец: root
    группа: корень
    содержание: |
      # переопределить адрес электронной почты по умолчанию
      MAILTO="example@gmail.com"
      # запускать команду Symfony каждые пять минут (как пользователь ec2)
      */10 * * * * ec2-пользователь /usr/bin/php /var/app/current/app/console делает: что-то
    кодировка: обычная
команды:
  # удалить файл резервной копии, созданный Elastic Beanstalk
  clear_cron_backup:
    команда: rm -f /etc/cron.d/watson.bak

Обратите внимание, что формат отличается от обычного формата crontab тем, что в нем указывается пользователь, который будет запускать команду.

Мой 1 цент вклада за 2018 год

Вот правильный способ сделать это (используя django/python а также django_crontab приложение):

внутри .ebextensions папка создать файл, как это 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

Это должно быть container_commands вместо commands

Последний пример от 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) он работал отлично.

Кто-то задавался вопросом о проблемах автоматического масштабирования leader_only, когда появляются новые лидеры. Я не могу понять, как ответить на их комментарии, но посмотрите эту ссылку: http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling-environment/

Лучший способ сделать это — использовать Elastic Beanstalk Worker Environment (см. «Вариант 1» ниже). Однако это увеличит расходы на сервер. Если вы не хотите этого делать, см. «Вариант 2» ниже, чтобы узнать, как настроить сам cron.

Вариант 1. Используйте рабочие среды Elastic Beanstalk Worker.

Amazon поддерживает Elastic Beanstalk Worker Environments. Это управляемые среды Elastic Beanstalk, которые поставляются с очередью SQS, в которую вы можете ставить задачи. Вы также можете предоставить им конфигурацию cron, которая будет автоматически ставить задачу в очередь по повторяющемуся расписанию. Затем, вместо получения запросов от балансировщика нагрузки, каждый сервер в рабочей среде имеет демона (управляемого Elastic Beanstalk), который опрашивает очередь на наличие задач и вызывает соответствующую веб-конечную точку при получении сообщения в очереди. Рабочие среды имеют несколько преимуществ по сравнению с запуском cron самостоятельно:

  1. Производительность. Теперь ваши задачи выполняются на выделенных серверах, а не конкурируют за ЦП и память с веб-запросами. У вас также могут быть разные спецификации для рабочих серверов (например, вы можете иметь больше памяти только на рабочих серверах).
  2. Масштабируемость. Вы также можете увеличить количество рабочих серверов до более чем 1, чтобы справляться с большими нагрузками.
  3. Специальные задачи. Ваш код может ставить в очередь специальные задачи, а также запланированные.
  4. Стандартизация. Вы пишете задачи как веб-конечные точки, а не настраиваете собственную структуру задач, что позволяет стандартизировать код и инструменты.

Если вы просто хотите заменить cron, все, что вам нужно сделать, это создать файл с именемcron.yamlна верхнем уровне вашего проекта с такой конфигурацией:

cron.yaml

      version: 1
cron:
 - name: "hourly"
   url: "/tasks/hourly"
   schedule: "0 */1 * * *"

Это будет вызывать URL-адрес /tasks/hourly один раз в час.

Если вы развертываете одну и ту же кодовую базу в веб-среде и рабочей среде, URL-адреса задач должны требовать переменной среды, которую вы устанавливаете в рабочих средах, а не в веб-средах. Таким образом, ваши конечные точки задач не видны миру (серверы задач по умолчанию не принимают входящие HTTP-запросы, поскольку единственное, что вызывает их, — это демон на сервере).

Полная документация находится здесь: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features-managing-env-tiers.html .

Вариант 2: настроить Cron

Если вы хотите запустить cron, вам нужно убедиться, что он работает только на одном сервере. Флага в конфиге .ebextensions недостаточно, потому что серверы не всегда остаются лидером. Это можно исправить, удалив конфигурацию cron, если она присутствует на любом сервере, в качестве первого шага развертывания, а затем установив ее только на один сервер с помощьюleader_only. Вот пример.ebextensionsконфигурационный файл, который выполняет это:

.ebextensions/cron.config

      container_commands:
    01_remove_cron_jobs:
        command: "rm /etc/cron.d/cronjobs || exit 0"
    02_set_up_cron:
        command: "cat .ebextensions/cronjobs.txt > /etc/cron.d/cronjobs && chmod 644 /etc/cron.d/cronjobs"
        leader_only: true

Этот файл конфигурации предполагает существование файла.ebextensions/cronjobs.txt. Этот файл содержит вашу фактическую конфигурацию cron. Обратите внимание, что для того, чтобы иметь загруженные переменные среды и ваш код в области видимости, вам нужно иметь код, который делает это запеченным в каждой команде. Ниже приведен пример конфигурации cron, которая работает в среде Python на базе Amazon Linux 2:

.ebextensions/cronjobs.txt

      SHELL=/bin/bash
PROJECT_PATH=/var/app/current
ENV_PATH=/opt/elasticbeanstalk/deployment/env

# m h dom mon dow user command
0 * * * * ec2-user set -a; source <(sudo cat $ENV_PATH) && cd $PROJECT_PATH && python HOURLY_COMMAND > /dev/null
# Cron requires a newline at the end of the file

Так что мы боролись с этим некоторое время, и после некоторого обсуждения с представителем AWS я наконец-то нашел то, что я считаю лучшим решением.

Использование рабочего уровня с cron.yaml - определенно самое простое решение. Однако, что не ясно из документации, так это то, что это поместит задание в конец очереди SQS, которую вы используете для фактического запуска заданий. Если ваши задания cron чувствительны ко времени (как и многие), это неприемлемо, так как это будет зависеть от размера очереди. Одним из вариантов является использование совершенно отдельной среды только для запуска заданий cron, но я думаю, что это излишне.

Некоторые из других опций, например, проверка того, являетесь ли вы первым экземпляром в списке, также не идеальны. Что если текущий первый экземпляр находится в процессе выключения?

Защита экземпляра также может сопровождаться проблемами - что если этот экземпляр заблокирован / заблокирован?

Важно понимать, как AWS сам управляет функциональностью cron.yaml. Существует демон SQS, который использует таблицу "Динамо" для обработки "выбора лидера". Он часто пишет в эту таблицу, и если текущий лидер не написал в течение короткого времени, следующий экземпляр вступит во владение как лидер. Вот как демон решает, какой экземпляр запустить работу в очередь SQS.

Мы можем изменить существующую функциональность вместо того, чтобы пытаться переписать нашу собственную. Вы можете увидеть полное решение здесь: https://gist.github.com/dorner/4517fe2b8c79ccb3971084ec28267f27

Это в Ruby, но вы можете легко адаптировать его к любому другому языку с AWS SDK. По сути, он проверяет текущего лидера, а затем проверяет состояние, чтобы убедиться, что он в хорошем состоянии. Он будет зацикливаться до тех пор, пока текущий лидер не окажется в хорошем состоянии, и, если текущий экземпляр является лидером, выполнить задание.

Основываясь на принципах ответа от пользователя 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`)
)

Вот исправление, если вы хотите сделать это на PHP. Вам просто нужен cronjob.config в вашей папке.ebextensions, чтобы он работал следующим образом.

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 получает переменные окружения для файлов. Вы можете отладить вывод в tmp/sendemail.log, как указано выше.

Надеюсь, это поможет кому-то, так же, как и нам!

У меня было другое решение, если php-файл нужно запускать через cron, и если вы установили какие-либо экземпляры NAT, вы можете поместить cronjob в экземпляр NAT и запустить php-файл через wget.

Чтобы контролировать, может ли автоматическое масштабирование завершать конкретный экземпляр при масштабировании, используйте защиту экземпляров. Вы можете включить настройку защиты экземпляра в группе автоматического масштабирования или в отдельном экземпляре автоматического масштабирования. Когда автоматическое масштабирование запускает экземпляр, экземпляр наследует настройку защиты экземпляра группы автоматического масштабирования. Вы можете изменить настройку защиты экземпляра для группы автоматического масштабирования или экземпляра автоматического масштабирования в любое время.

http://docs.aws.amazon.com/autoscaling/latest/userguide/as-instance-termination.html

Другие вопросы по тегам