AWS Elastic Beanstalk, працює cronjob


89

Я хотів би знати, чи є спосіб налаштувати cronjob / завдання для виконання щохвилини. На даний момент будь-який з моїх екземплярів повинен мати змогу запустити це завдання.

Це те, що я намагався зробити в конфігураційних файлах без успіху:

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

Я не дуже впевнений, що це правильний спосіб зробити це

Будь-які ідеї?


1
Чи правильна команда? Я маю на увазі ... це може бути: command: echo "* / 1 * * * * root php /etc/httpd/myscript.php"> /etc/cron.d/something У будь-якому випадку, я б запропонував вам використовувати flag_only прапор, інакше всі машини одразу запускатимуть цю роботу cron
aldrinleal

Так! безумовно, використовуючи прапор leader_only, я спробую змінити команду.
Onema

Відповіді:


97

Ось як я додав завдання 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 буде виконуватися щохвилини кожної години кожного дня як кореневий користувач і відкидатиме вихідні дані до / 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 жовтня 2016 року о 9:23


12
Я використовував вашу пропозицію деякий час, і нещодавно зіткнувся з проблемою, коли якимось чином лідер змінився, в результаті чого кілька випадків запускали cron. Щоб вирішити цю проблему, я змінив 01_some_cron_jobдо 02_some_cron_jobі додав 01_remove_cron_jobsз наступним: command: "rm /etc/cron.d/cron_jobs || exit 0". Таким чином, після кожного розгортання cron_jobsфайл матиме лише керівник . Якщо лідери зміняться, ви можете просто передислокуватися, і крони будуть виправлені ще раз.
Віллем Рензема,

4
Я б радив не покладатися на leader_onlyвласність. Він використовується лише під час розгортання, і якщо зменшити масштаб або ваш екземпляр "лідера" не вдається, ви обов'язково матимете посилання на
арнаслу

2
Не роби цього. Це занадто ненадійно. Єдиний спосіб, як я змусив це працювати, - це запустити мікро екземпляр і запустити звідти завдання cron за допомогою CURL. Це гарантує, що його запускає лише один екземпляр, а лідер, у якому встановлені крони, не припиняється.
Бен Сінклер,

1
Я спробував це виправити за допомогою невеликого рубінового сценарію, ви можете знайти його тут: github.com/SocialbitGmbH/AWSBeanstalkLeaderManager
Томас Кекейзен

8
Тепер AWS має захист від автоматичного припинення екземпляра. Просто увімкніть його у своєму екземплярі лідера, і все готово.
Ніколас Аревало

58

Це офіційний спосіб зробити це зараз (2015+). Спробуйте спершу це, це на сьогоднішній день найпростіший доступний спосіб, а також найнадійніший.

Згідно з поточними документами, можна виконувати періодичні завдання на їх так званому робочому рівні .

Посилання на документацію:

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

Також цікава частина про cron.yaml :

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

Оновлення: нам вдалося отримати цю роботу. Ось декілька важливих помилок з нашого досвіду (платформа Node.js):

  • Використовуючи файл cron.yaml , переконайтесь, що у вас найновіші awsebcli , оскільки старіші версії працюватимуть неправильно.
  • Також життєво важливо створити нове середовище (принаймні в нашому випадку це було), а не просто клонувати старе.
  • Якщо ви хочете переконатися, що CRON підтримується на вашому екземплярі рівня EC2 Worker Tier, ssh у нього ( eb ssh) та запустіть cat /var/log/aws-sqsd/default.log. Він повинен повідомляти як aws-sqsd 2.0 (2015-02-18). Якщо у вас немає версії 2.0, при створенні вашого середовища щось пішло не так, і вам потрібно створити нову, як зазначено вище.

2
Про cron.yaml є чудовий допис у блозі: Запуск завдань cron на Amazon Web Services (AWS) Elastic Beanstalk - Medium
jwako

5
Дякую за це - питання новачка - мені потрібен мій cron, щоб двічі на годину перевіряти базу даних мого веб-додатку щодо майбутніх подій календаря та надсилати електронне повідомлення із нагадуванням, коли це відбуватиметься. Яке тут найкраще налаштувати, чи слід вказати URL-адресу cron.yaml на маршрут у моєму веб-додатку? Або я повинен надати моєму робочому додатку доступ до бази даних? Так мало там про це!
Крістіан

5
@christian Як ми це робимо, у нас однаковий додаток працює у двох різних середовищах (отже, не потрібна спеціальна конфігурація) - робоча та загальний веб-сервер. У робочому середовищі активовано деякі спеціальні маршрути, встановивши змінну ENV, яку шукає наш додаток. Таким чином, ви можете встановити спеціальні маршрути лише для робітників у своєму cron.yaml, маючи розкіш спільної кодової бази із звичайним додатком. Ваш робочий додаток може легко отримати доступ до тих самих ресурсів, що й веб-сервер: база даних, моделі тощо
xaralis

1
@JaquelinePassos v1.2.0 - це версія стеку рішень. Це має дозволити вам вибрати, яку версію стеку рішень ви хочете створити під час створення нового середовища. Будь-що новіше, ніж v1.2.0, має робити. Щодо URL-адреси, це має бути URL-адреса, яку слухає ваша програма, а не шлях до файлу. Неможливо запустити команди управління Django, він виконує лише запити HTTP.
xaralis

4
Мені незрозуміло одне, якщо є спосіб уникнути необхідності виділяти додаткову машину EC2 лише для запуску завдань cron через cron.yaml. В ідеалі він працював би на тій самій машині, що і та, яка обслуговує запити HTTP (тобто веб-рівень).
Венцель Якоб,

31

Щодо відповіді jamieb, і як згадує alrdinleal, ви можете використовувати властивість 'leader_only', щоб переконатися, що тільки один екземпляр EC2 запускає роботу cron.

Цитата взята з http://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html :

Ви можете використовувати leader_only. Один екземпляр обраний лідером у групі автоматичного масштабування. Якщо для значення value_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 для читання з тимчасового файлу, а потім видаляю тимчасовий файл. Сподіваюся, це допомагає.


3
Як би ви гарантували, що екземпляр, що запускає цей crontab, не припиняється автоматично масштабуванням? За замовчуванням він завершує найстаріший екземпляр.
Себастьян,

1
Це питання, яке мені ще не вдалося вирішити. Мені здається недоліком у функціональності Amazon, що команди leader_only не застосовуються до нового лідера, коли поточний припиняється EB. Якщо ви щось придумали, будь ласка, поділіться!
beterthanlife

7
Тож я (нарешті) виявив, як запобігти припиненню лідера за допомогою автоматичного масштабування - власні політики припинення автоматичного масштабування. Подивитися Docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/…
beterthanlife

1
@Nate Ви напевно вже зрозуміли це, але виходячи з мого прочитання порядку, в якому вони виконуються, "команди" запускаються перед "container_commands", щоб створити файл, потім видалити його, а потім спробувати запустити crontab .
clearf

1
@Sebastien, щоб зберегти найстарішу інстанцію, ось що я роблю: 1 - змінити захист припинення інстанції на ENBABLE. 2 - Перейдіть до Auto Scale Group і знайдіть свій ідентифікатор середовища EBS, натисніть EDIT і змініть Політику припинення на "NewestInstance"
Роналду Байя

12

Як згадувалося вище, основним недоліком встановлення будь-якої конфігурації 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, і кожен сервер кластера допомагає виконувати. Принаймні це може дати вам альтернативний варіант, якщо він відповідає вашим потребам.

-Дейві


Я виявив, що php_uname ('n') повертає приватне ім'я DNS (наприклад, ip-172.24.55.66), яке не є ідентифікатором екземпляра, який ви шукаєте. Замість того, щоб використовувати php_uname (), я в кінцевому підсумку використав це: $instanceId = file_get_contents("http://instance-data/latest/meta-data/instance-id"); Тоді просто використовуйте той var instance instance для порівняння.
Valorum

1
Чи є якась гарантія того, що масив Примірників подає однаковий порядок для кожного виклику Опишіть? Я б запропонував витягти поле ['Id'] кожного запису в масив та відсортувати їх у PHP, перш ніж перевірити, чи є перший відсортований запис вашим поточним instanceId.
Габріель

На основі цієї відповіді я зробив таке рішення: stackoverflow.com/questions/14077095/… - це дуже схоже, але НЕ має шансів подвійного виконання.
TheStoryCoder

11

Я поспілкувався з агентом підтримки AWS, і ось як ми домоглися, щоб це працювало для мене. Рішення 2015 року:

Створіть файл у своєму каталозі .ebextensions за допомогою імені_файла.config. У введеному файлі конфігурації:

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

  "/usr/local/bin/cron_example.sh":
    режим: "000755"
    власник: корінь
    група: корінь
    зміст: |
      #! / bin / bash

      /usr/local/bin/test_cron.sh || вихід
      echo "Cron працює на" `date` >> /tmp/cron_example.log
      # Тепер виконуйте завдання, які повинні виконуватися лише на 1 екземплярі ...

  "/usr/local/bin/test_cron.sh":
    режим: "000755"
    власник: корінь
    група: корінь
    зміст: |
      #! / bin / bash

      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 descri-tags --filters "Ім'я = ідентифікатор ресурсу, Значення = $ INSTANCE_ID" \
        --region $ REGION - вихідний текст | awk '/ aws: autoscaling: groupName / {print $ 5}' '

      # Знайдіть перший екземпляр у групі
      ПЕРШИЙ = ʻaws автомасштабування опис-авто-масштабування-груп --авто-масштабування-імен груп $ ASG \
        --region $ REGION - вихідний текст | awk '/ InService $ / {print $ 4}' | сортувати | голова -1`

      # Перевірте, чи однакові вони.
      ["$ FIRST" = "$ INSTANCE_ID"]

команди:
  rm_old_cron:
    команда: "rm * .bak"
    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 та автомасштабування: DescribeAutoScalingGroups дозволи
  • Вибрано такі екземпляри, які відображаються як InService за допомогою автоматичного масштабування. Це не обов'язково означає, що вони повністю завантажені та готові до запуску вашого cron.

Вам не доведеться встановлювати ролі IAM, якщо ви використовуєте роль beanstalk за замовчуванням.


7

Якщо ви використовуєте Rails, ви можете використовувати самоцвіт, що завжди є еластичним . Це дозволяє запускати завдання cron на всіх екземплярах або лише на одному. Він перевіряє щохвилини, щоб переконатися, що є лише один екземпляр "лідера", і автоматично підвищує один сервер до "лідера", якщо його немає. Це потрібно, оскільки Elastic Beanstalk має лише поняття лідера під час розгортання і може вимкнути будь-який екземпляр у будь-який час під час масштабування.

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


Не могли б ви розповісти нам, як ви це вирішили за допомогою OpsWorks? Ви запускаєте власні шари, які виконують завдання cron?
Tommie

Так, у мене є рівень адміністратора / cron, який працює лише на одному сервері. Я створив спеціальну кулінарну книгу, яка вміщує всі мої робочі місця. AWS має посібник на docs.aws.amazon.com/opsworks/latest/userguide/… .
dignoe

@dignoe, якщо ви призначите один сервер для запуску завдань cron за допомогою OpsWorks, те саме, що використовує Elastic Beanstalk, я можу використовувати середовище з одним сервером для запуску завдань cron. Навіть із Load Balancer, максимальний та мінімальний екземпляри встановлені на одиницю, щоб завжди зберігати принаймні екземпляр сервера.
Хосе Нобіле

6

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


SQS не гарантує, що код буде запущений лише один раз. Мені подобається сайт iron.io, я збираюся його перевірити.
Nathan H

Також у своєму дописі в блозі ви рекомендуєте використовувати InnoDB на RDS. Я використовую таблицю на RDS для зберігання своїх завдань і використовую функцію InnoDB "ВИБРАТИ ... ДЛЯ ОНОВЛЕННЯ", щоб переконатися, що ці завдання виконує лише один сервер. Як ваш додаток контактує з SQS без роботи cron або взаємодії користувача?
Джеймс Олдей,

1
@JamesAlday Це SO питання досить давнє. Оскільки я написав вищезазначений коментар, AWS представив елегантний спосіб обробки завдань cron на Elastic Beanstalk, обравши головним одним із запущених серверів. Сказавши це, схоже, ви неправильно використовуєте cron + MySQL як чергу роботи. Мені потрібно було б багато знати про ваш додаток, перш ніж я зможу запропонувати конкретні рекомендації.
jamieb

У мене є скрипт, який запускається через cron, який перевіряє таблицю для запуску завдань. Використання транзакцій перешкоджає виконанню однієї роботи кількома серверами. Я розглядав SQS, але вам потрібен головний сервер, який запускає всі сценарії, а не поширює їх, і вам все одно потрібно писати логіку, щоб переконатися, що ви не запускаєте один і той же сценарій кілька разів. Але мене все ще бентежить, як ви запускаєте завдання без взаємодії з користувачем або cron - що запускає ваш додаток для запуску завдань у черзі?
James Alday,

5

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


3

Більш читабельне рішення з використанням filesзамість container_commands:

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

Зверніть увагу, що формат відрізняється від звичайного формату crontab тим, що він вказує користувачеві для запуску команди як.


Одне питання полягає в тому, що в екземплярах Elastic Beanstalk EC2 за замовчуванням не налаштовані служби SMTP, тому опція MAILTO тут може не працювати.
Джастін Фінкельштейн

3

Мій 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


2

Хтось замислювався над проблемами автоматичного масштабування leader_only, коли виникають нові лідери. Здається, я не можу зрозуміти, як відповісти на їхні коментарі, але перегляньте це посилання: http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling- навколишнє середовище/


2

Останній приклад від Amazon є найпростішим та найефективнішим (періодичні завдання):

https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features-managing-env-tiers.html

де ви створюєте окремий робочий рівень для виконання будь-якого з ваших завдань cron. Створіть файл cron.yaml і помістіть його у свою кореневу папку. Одне з питань, яке я мав (після того, як cron, схоже, не виконувався), було виявлення того, що мій CodePipeline не мав повноважень виконувати модифікацію dynamodb. Виходячи з цього, після додавання доступу FullDynamoDB під IAM -> ролі -> ваш конвеєр і передислокація (еластична квасоля) він спрацював ідеально.


1

не використовуйте leader_only для створення унікального екземпляра в ASG. ASG ніколи не гарантує, що ви зможете зберегти цей конкретний екземпляр, але гарантує лише таку кількість екземплярів, що працюють. Екземпляр лідера може бути припинено через невдалу перевірку стану EB.
mst

1

Отже, ми боремося з цим деякий час, і після обговорення з представником AWS я нарешті придумав, що, на мою думку, є найкращим рішенням.

Використання робочого рівня з cron.yaml, безумовно, є найпростішим виправленням. Однак у документації не ясно, що це помістить завдання в кінець черги SQS, яку ви використовуєте для фактичного запуску своїх завдань. Якщо ваші завдання cron чутливі до часу (як і багато інших), це неприйнятно, оскільки це залежатиме від розміру черги. Один із варіантів - використовувати абсолютно окреме середовище лише для запуску завдань cron, але я думаю, що це надмірно.

Деякі інші варіанти, такі як перевірка, чи ви є першим екземпляром у списку, теж не є ідеальними. Що робити, якщо поточний перший екземпляр перебуває у стані завершення роботи?

Захист екземпляра також може мати проблеми: що, якщо цей екземпляр буде заблокований / заморожений?

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

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

Це в Ruby, але ви можете легко адаптувати його до будь-якої іншої мови, яка має AWS SDK. По суті, він перевіряє поточного лідера, а потім перевіряє стан, щоб переконатися, що він у хорошому стані. Він буде циклічно працювати, поки поточний лідер не буде в хорошому стані, і якщо поточний екземпляр є лідером, виконайте завдання.


0

Щоб контролювати, чи може автоматичне масштабування припинити певний екземпляр під час масштабування, використовуйте захист екземпляра. Ви можете ввімкнути параметр захисту екземпляра в групі автоматичного масштабування або окремому екземплярі автоматичного масштабування. Коли автомасштабування запускає екземпляр, екземпляр успадковує параметр захисту екземпляра групи автоматичного масштабування. Ви можете будь-коли змінити налаштування захисту примірника для групи автоматичного масштабування або екземпляра автоматичного масштабування.

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


0

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


0

ось виправлення, якщо ви хочете зробити це в 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, як зазначено вище.

Сподіваюся, це комусь допомагає, як це, безсумнівно, допомогло нам!


0

На основі принципів відповіді користувача user1599237 , де ви дозволяєте завданням 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`)
)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.