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

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

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

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

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

Любые идеи?

Ответ 1

Вот как я добавил задание 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 -здесь путь к вашему php файлу. Очевидно, это предполагает, что ваше задание cron должно запускать файл PHP.

Кроме того, убедитесь, что файл some_cron_job.txt имеет новую строку в конце файла, как говорит комментарий. В противном случае cron не будет работать.

Update: Существует проблема с этим решением, когда Elastic Beanstalk расширяет ваши экземпляры. Например, скажем, у вас есть один экземпляр с запущенным заданием cron. Вы получаете увеличение трафика, так что эластичный beanstalk весит вас до двух экземпляров. Lead_only гарантирует, что у вас есть только одно задание cron между двумя экземплярами. Ваш трафик уменьшается, а эластичный бобовый штанги сводит вас к одному экземпляру. Но вместо того, чтобы завершать второй экземпляр, Elastic Beanstalk завершает первый экземпляр, который был лидером. Теперь у вас нет заданий cron, поскольку они выполнялись только в первом экземпляре, который был завершен. См. комментарии ниже.

Обновление 2: Просто сделав это ясно из комментариев ниже: AWS теперь имеет защиту от автоматического завершения экземпляра. Просто включите его на примере вашего лидера, и вы хорошо пойдете. - Nicolás Arévalo Oct 28 '16 at 9:23

Ответ 2

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

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

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

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

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

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

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

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

Ответ 3

Что касается ответа на jamieb, и, как упоминается в alrdinleal, вы можете использовать свойство "leader_only", чтобы гарантировать, что только один экземпляр EC2 запускает задание cron.

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

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

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

UPDATE:

Хорошо, теперь у меня есть рабочие 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 для чтения из временного файла, а затем удаляю временный файл. Надеюсь, это поможет.

Ответ 4

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

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

Итак, вот что я придумал, который, по-видимому, нетрадиционен (как комментировал AWS rep) и может считаться взломом, но он работает и прочен с ошибкой. Я выбрал решение для кодирования с использованием 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. Каждый script включает это в начале (или включает в себя один файл для каждого, как я его использую), который устанавливает объект ElasticBeanstalk и извлекает список всех экземпляров. Он использует только первый сервер в списке и проверяет, совпадает ли он с самим собой, и если он его продолжает, в противном случае он умирает и закрывается. Я проверил, и возвращаемый список кажется последовательным, и технически он должен быть согласован в течение минуты или около того, поскольку каждый экземпляр выполняет запланированный cron. Если он изменится, это не имеет значения, поскольку снова это относится только к этому маленькому окну.

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

-Davey

Ответ 5

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

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

files:
  "/etc/cron.d/cron_example":
    mode: "000644"
    owner: root
    group: root
    content: |
      * * * * * root /usr/local/bin/cron_example.sh

  "/usr/local/bin/cron_example.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/bin/bash

      /usr/local/bin/test_cron.sh || exit
      echo "Cron running at " `date` >> /tmp/cron_example.log
      # Now do tasks that should only run on 1 instance ...

  "/usr/local/bin/test_cron.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/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)}'`

      # Find our Auto Scaling Group name.
      ASG=`aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" \
        --region $REGION --output text | awk '/aws:autoscaling:groupName/ {print $5}'`

      # Find the first instance in the Group
      FIRST=`aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $ASG \
        --region $REGION --output text | awk '/InService$/ {print $4}' | sort | head -1`

      # Test if they're the same.
      [ "$FIRST" = "$INSTANCE_ID" ]

commands:
  rm_old_cron:
    command: "rm *.bak"
    cwd: "/etc/cron.d"
    ignoreErrors: true

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

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

Обход проблемы:

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

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

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

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

files:
  "/etc/cron.d/my_cron":
    mode: "000644"
    owner: root
    group: root
    content: |
      # override default email address
      MAILTO="[email protected]"
      # run a Symfony command every five minutes (as ec2-user)
      */10 * * * * ec2-user /usr/bin/php /var/app/current/app/console do:something
    encoding: plain
commands:
  # delete backup file created by Elastic Beanstalk
  clear_cron_backup:
    command: rm -f /etc/cron.d/watson.bak

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

Ответ 11

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

Ответ 12

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

Ответ 13

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

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

Ответ 14

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

Ответ 15

Здесь вы можете исправить ошибку, которую вы хотите сделать в 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, как указано выше.

Надеюсь, это поможет кому-то, поскольку это, безусловно, помогло нам!

Ответ 16

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

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

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

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

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

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

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

Ответ 17

Основываясь на принципах ответа от пользователя 1599237, где вы позволяете заданиям cron запускаться на всех экземплярах, но затем вместо этого в начале заданий определяете, следует ли им разрешить запускать, я нашел другое решение.

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

У него нет минусов, только плюсы:

  • без дополнительных экземпляров или расходов
  • надежное решение - нет шансов на двойное исполнение
  • масштабируемый - автоматически работает, когда ваши экземпляры масштабируются вверх и вниз
  • аварийное переключение - автоматически работает в случае сбоя экземпляра

В качестве альтернативы вы можете использовать вместо общей базы данных общую файловую систему (например, AWS EFS через протокол NFS).

Следующее решение создано в рамках фреймворка PHP Yii, но вы можете легко адаптировать его для другого фреймворка и языка. Также обработчик исключений Yii::$app->system является моим собственным модулем. Замените его тем, что вы используете.

/**
 * Obtain a lock for doing a given job or quit if unable
 *
 * Examples:
 *
 * 'php /var/app/current/yii lock/obtain-lock 60 empty-trash php /var/app/current/yii maintenance/empty-trash'
 * 'php /var/app/current/yii lock/obtain-lock 60 empty-trash php /var/app/current/yii maintenance/empty-trash StdOUT./test.log'
 * 'php /var/app/current/yii lock/obtain-lock 60 "empty trash" php /var/app/current/yii maintenance/empty-trash StdOUT./test.log StdERR.ditto'
 * 'php /var/app/current/yii lock/obtain-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 actionObtainLock() {
    $argsAll = $args = func_get_args();
    if (!is_numeric($args[0])) {
        \Yii::$app->system->error('Duration for obtaining lock is not numeric.', ['Args' => $argsAll]);
    }
    if (!$args[1]) {
        \Yii::$app->system->error('Job name for obtaining 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 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')
)

Ответ 18

интересно, будет ли следующая работа

container_commands:
 01_some_cron_job:
  command: "echo '* * * * * ls' | crontab"
  leader_only: true