Chciałbym wiedzieć, czy istnieje sposób na skonfigurowanie cronjob / task do wykonywania co minutę. Obecnie każda z moich instancji powinna mieć możliwość uruchomienia tego zadania.
Oto, co próbowałem zrobić w plikach konfiguracyjnych bez powodzenia:
container_commands:
01cronjobs:
command: echo "*/1 * * * * root php /etc/httpd/myscript.php"
Nie jestem pewien, czy jest to właściwy sposób
Jakieś pomysły?
Odpowiedzi:
Oto jak dodałem zadanie crona do Elastic Beanstalk:
Utwórz folder w katalogu głównym aplikacji o nazwie .ebextensions, jeśli jeszcze nie istnieje. Następnie utwórz plik konfiguracyjny w folderze .ebextensions. W celach ilustracyjnych użyję example.config. Następnie dodaj to do example.config
To jest plik konfiguracyjny YAML dla Elastic Beanstalk. Podczas kopiowania tego do edytora tekstu upewnij się, że używa on spacji zamiast tabulatorów. W przeciwnym razie otrzymasz błąd YAML, gdy przekażesz to do EB.
Więc to, co to robi, to utworzenie polecenia o nazwie 01_some_cron_job. Polecenia są uruchamiane w kolejności alfabetycznej, więc 01 upewnia się, że jest uruchomione jako pierwsze polecenie.
Następnie polecenie pobiera zawartość pliku o nazwie some_cron_job.txt i dodaje ją do pliku o nazwie some_cron_job w /etc/cron.d.
Następnie polecenie zmienia uprawnienia w pliku /etc/cron.d/some_cron_job.
Klucz Leader_only zapewnia, że polecenie zostanie uruchomione tylko na instancji ec2, która jest uważana za lidera. Zamiast uruchamiać na każdej instancji ec2, którą możesz mieć uruchomioną.
Następnie utwórz plik o nazwie some_cron_job.txt w folderze .ebextensions. W tym pliku umieścisz swoje zadania cron.
Na przykład:
Więc to zadanie crona będzie uruchamiane co minutę o każdej godzinie każdego dnia jako użytkownik root i odrzuca dane wyjściowe do / dev / null. / usr / bin / php to ścieżka do pliku php. Następnie zamień część-php-script-here na ścieżkę do twojego pliku php. To oczywiście zakłada, że twoje zadanie cron musi uruchamiać plik PHP.
Upewnij się również, że plik some_cron_job.txt ma znak nowej linii na końcu pliku, tak jak mówi komentarz. W przeciwnym razie cron nie będzie działać.
Aktualizacja: występuje problem z tym rozwiązaniem, gdy Elastic Beanstalk skaluje Twoje instancje. Na przykład, powiedzmy, że masz jedno wystąpienie z uruchomionym zadaniem cron. Zyskujesz wzrost ruchu, więc Elastic Beanstalk skaluje Cię do dwóch instancji. Leader_only zapewni, że między dwiema instancjami będzie działać tylko jedno zadanie cron. Twój ruch spada, a Elastic Beanstalk skaluje Cię do jednego wystąpienia. Ale zamiast zakończyć drugą instancję, Elastic Beanstalk kończy pierwszą instancję, która była liderem. Nie masz teraz uruchomionych żadnych zadań cron, ponieważ działały one tylko w pierwszej instancji, która została zakończona. Zobacz komentarze poniżej.
Aktualizacja 2: Po prostu wyjaśniam to na podstawie poniższych komentarzy: AWS ma teraz ochronę przed automatycznym zakończeniem instancji. Po prostu włącz go na swojej instancji lidera i gotowe. - Nicolás Arévalo 28 października 2016 o 9:23
źródło
01_some_cron_job
się02_some_cron_job
i dodał01_remove_cron_jobs
z poniższych sytuacji:command: "rm /etc/cron.d/cron_jobs || exit 0"
. W ten sposób po każdym wdrożeniu tylko lider będzie miałcron_jobs
plik. Jeśli przywódcy się zmienią, możesz po prostu rozmieścić ponownie, a crony zostaną naprawione tak, aby działały jeszcze raz.leader_only
własności. Jest używany tylko podczas wdrażania i jeśliTo jest oficjalny sposób, aby to zrobić teraz (2015+). Spróbuj najpierw tego, jest to zdecydowanie najłatwiejsza obecnie dostępna metoda, a także najbardziej niezawodna.
Zgodnie z obecnymi dokumentami można uruchamiać okresowe zadania na tak zwanym poziomie pracownika .
Cytując dokumentację:
Interesująca jest również część dotycząca cron.yaml :
Aktualizacja: udało nam się uzyskać tę pracę. Oto kilka ważnych problemów z naszego doświadczenia (platforma Node.js):
eb ssh
) i uruchomcat /var/log/aws-sqsd/default.log
. Powinien zgłosić się jakoaws-sqsd 2.0 (2015-02-18)
. Jeśli nie masz wersji 2.0, coś poszło nie tak podczas tworzenia środowiska i musisz utworzyć nowe, jak opisano powyżej.źródło
Jeśli chodzi o odpowiedź jamieba i jak wspomina alrdinleal, możesz użyć właściwości „Leader_only”, aby upewnić się, że tylko jedna instancja EC2 uruchamia zadanie cron.
Cytat pochodzi z http://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html :
Próbuję osiągnąć podobną rzecz na moim eb, więc zaktualizuję mój post, jeśli go rozwiążę.
AKTUALIZACJA:
Ok, teraz mam działające cronjobs przy użyciu następującej konfiguracji eb:
Zasadniczo tworzę plik tymczasowy za pomocą cronjobs, a następnie ustawiam crontab na odczyt z pliku tymczasowego, a następnie usuwam plik tymczasowy. Mam nadzieję że to pomoże.
źródło
Jak wspomniano powyżej, podstawową wadą przy ustalaniu konfiguracji crontab jest to, że dzieje się to tylko podczas wdrażania. Ponieważ klaster jest automatycznie skalowany w górę, a następnie z powrotem w dół, preferowane jest, aby był również pierwszym wyłączonym serwerem. Ponadto nie byłoby awarii, co było dla mnie krytyczne.
Zrobiłem kilka badań, a następnie porozmawiałem z naszym specjalistą ds. Kont AWS, aby odrzucić pomysły i zweryfikować rozwiązanie, które wymyśliłem. Możesz to osiągnąć za pomocą OpsWorks , chociaż to trochę tak, jak użycie domu do zabicia muchy. Możliwe jest również użycie Data Pipeline z Task Runner , ale ma to ograniczone możliwości w skryptach, które może wykonywać, i musiałem być w stanie uruchamiać skrypty PHP, z dostępem do całej bazy kodu. Możesz również zadedykować instancję EC2 poza klastrem ElasticBeanstalk, ale wtedy nie ma ponownie przełączenia awaryjnego.
Oto, co wymyśliłem, co najwyraźniej jest niekonwencjonalne (jak skomentował przedstawiciel AWS) i można je uznać za hack, ale działa i jest solidne z przełączaniem awaryjnym. Wybrałem rozwiązanie do kodowania przy użyciu SDK, które pokażę w PHP, chociaż możesz zrobić tę samą metodę w dowolnym preferowanym języku.
A więc przechodząc przez to i jak to działa ... Skrypty wywołujesz z crontab tak jak zwykle na każdej instancji EC2. Każdy skrypt zawiera to na początku (lub zawiera pojedynczy plik dla każdego, ponieważ go używam), który ustanawia obiekt ElasticBeanstalk i pobiera listę wszystkich instancji. Używa tylko pierwszego serwera na liście i sprawdza, czy pasuje do siebie, a jeśli tak, kontynuuje, w przeciwnym razie umiera i zamyka się. Sprawdziłem i zwrócona lista wydaje się być spójna, co z technicznego punktu widzenia musi być spójne tylko przez około minutę, ponieważ każda instancja wykonuje zaplanowany cron. Jeśli to się zmieni, nie będzie to miało znaczenia, ponieważ znowu ma znaczenie tylko dla tego małego okna.
Nie jest to w żaden sposób eleganckie, ale odpowiadało naszym specyficznym potrzebom - które nie polegały na zwiększaniu kosztów za pomocą dodatkowej usługi lub konieczności posiadania dedykowanej instancji EC2, a także w przypadku awarii. Nasze skrypty cron uruchamiają skrypty konserwacyjne, które są umieszczane w SQS, a każdy serwer w klastrze pomaga w wykonywaniu. Przynajmniej może to dać ci alternatywną opcję, jeśli pasuje do twoich potrzeb.
-Davey
źródło
$instanceId = file_get_contents("http://instance-data/latest/meta-data/instance-id");
Następnie po prostu użyj tego $ instanceId var, aby zrobić porównanie.Rozmawiałem z agentem wsparcia AWS i tak to zadziałało. Rozwiązanie 2015:
Utwórz plik w swoim katalogu .ebextensions z nazwą twoja_nazwa_pliku.config. W pliku wejściowym pliku konfiguracyjnego:
To rozwiązanie ma 2 wady:
Obejście:
Ostrzeżenie:
Nie musisz ustawiać ról uprawnień, jeśli używasz domyślnej roli łodygi fasoli.
źródło
Jeśli używasz Railsów, możesz użyć klejnotu When-Elasticbeanstalk . Pozwala na uruchamianie zadań crona na wszystkich wystąpieniach lub tylko na jednym. Sprawdza co minutę, aby upewnić się, że jest tylko jedna instancja „lidera” i automatycznie awansuje jeden serwer na „lidera”, jeśli takiego nie ma. Jest to potrzebne, ponieważ Elastic Beanstalk ma koncepcję lidera tylko podczas wdrażania i może wyłączyć dowolną instancję w dowolnym momencie podczas skalowania.
AKTUALIZACJA Przerzuciłem się na AWS OpsWorks i nie zajmuję się już tym klejnotem. Jeśli potrzebujesz większej funkcjonalności niż ta dostępna w podstawach Elastic Beanstalk, bardzo polecam przejście na OpsWorks.
źródło
Naprawdę nie chcesz uruchamiać zadań cron na Elastic Beanstalk. Ponieważ będziesz mieć wiele instancji aplikacji, może to powodować warunki wyścigu i inne dziwne problemy. I rzeczywiście niedawno napisał o tym (4. lub 5. końcówki w dół strony). Krótka wersja: w zależności od aplikacji użyj kolejki zadań, takiej jak SQS lub rozwiązania innej firmy, np . Iron.io.
źródło
2017: Jeśli używasz Laravel5 +
Potrzebujesz tylko 2 minut, aby to skonfigurować:
zainstaluj laravel-aws-worker
composer require dusterio/laravel-aws-worker
dodaj plik cron.yaml do folderu głównego:
Otóż to!
Całe twoje zadanie
App\Console\Kernel
zostanie teraz wykonaneSzczegółowe instrukcje i wyjaśnienia: https://github.com/dusterio/laravel-aws-worker
Jak pisać zadania w Laravel: https://laravel.com/docs/5.4/scheduling
źródło
Bardziej czytelne rozwiązanie wykorzystujące
files
zamiastcontainer_commands
:Zwróć uwagę, że format różni się od zwykłego formatu tabeli crontab tym, że określa użytkownika, który ma uruchomić polecenie jako.
źródło
Mój 1 cent za 2018 rok
Oto właściwy sposób, aby to zrobić (za pomocą
django/python
idjango_crontab
aplikacji):wewnątrz
.ebextensions
folderu utwórz taki plik98_cron.config
:Musi być
container_commands
zamiastcommands
źródło
Ktoś zastanawiał się nad problemami z automatycznym skalowaniem Leader_only, gdy pojawiają się nowi liderzy. Nie wiem, jak odpowiedzieć na ich komentarze, ale zobacz ten link: http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling- środowisko/
źródło
Najnowszy przykład z Amazona jest najłatwiejszy i najbardziej wydajny (zadania okresowe):
https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features-managing-env-tiers.html
gdzie tworzysz oddzielną warstwę roboczą, aby wykonywać dowolne zadania cron. Utwórz plik cron.yaml i umieść go w folderze głównym. Jednym z problemów, które miałem (po tym, jak cron nie wydawał się wykonywać), było stwierdzenie, że mój CodePipeline nie ma uprawnień do wykonania modyfikacji dynamodb. Na tej podstawie po dodaniu dostępu FullDynamoDB pod IAM -> role -> yourpipeline i ponownym wdrożeniu (elastyczna łodyga fasoli) działało idealnie.
źródło
Oto pełne wyjaśnienie rozwiązania:
http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling-environment/
źródło
Więc walczyliśmy z tym przez jakiś czas i po krótkiej dyskusji z przedstawicielem AWS w końcu wymyśliłem to, co uważam za najlepsze rozwiązanie.
Korzystanie z warstwy roboczej z cron.yaml jest zdecydowanie najłatwiejszym rozwiązaniem. Jednak dokumentacja nie wyjaśnia, że spowoduje to umieszczenie zadania na końcu kolejki SQS, której używasz do rzeczywistego wykonywania zadań. Jeśli twoje zadania cron są wrażliwe na czas (jak wiele z nich), jest to niedopuszczalne, ponieważ zależałoby to od rozmiaru kolejki. Jedną z opcji jest użycie całkowicie oddzielnego środowiska tylko do uruchamiania zadań cron, ale myślę, że to przesada.
Niektóre inne opcje, takie jak sprawdzenie, czy jesteś na pierwszym miejscu na liście, też nie są idealne. Co się stanie, jeśli obecna pierwsza instancja jest w trakcie zamykania?
Ochrona instancji może również wiązać się z problemami - co, jeśli ta instancja zostanie zablokowana / zamrożona?
Ważne jest, aby zrozumieć, w jaki sposób sam AWS zarządza funkcjonalnością cron.yaml. Istnieje demon SQS, który używa tabeli Dynamo do obsługi „wyborów lidera”. Często pisze do tej tabeli, a jeśli obecny lider nie pisał przez krótką chwilę, następna instancja przejmie rolę lidera. W ten sposób demon decyduje, która instancja ma uruchomić zadanie w kolejce SQS.
Możemy zmienić przeznaczenie istniejącej funkcjonalności, zamiast próbować przepisać naszą własną. Pełne rozwiązanie można zobaczyć tutaj: https://gist.github.com/dorner/4517fe2b8c79ccb3971084ec28267f27
To jest w Rubim, ale możesz łatwo dostosować go do dowolnego innego języka, który ma AWS SDK. Zasadniczo sprawdza obecnego lidera, a następnie sprawdza stan, aby upewnić się, że jest w dobrym stanie. Będzie się zapętlać, dopóki aktualny lider nie będzie w dobrym stanie, a jeśli bieżąca instancja jest liderem, wykonaj zadanie.
źródło
http://docs.aws.amazon.com/autoscaling/latest/userguide/as-instance-termination.html#instance-protection
źródło
Miałem inne rozwiązanie tego problemu, jeśli plik php musi być uruchomiony przez cron i jeśli ustawiłeś jakieś instancje NAT, możesz umieścić cronjob na instancji NAT i uruchomić plik php przez wget.
źródło
tutaj jest poprawka na wypadek, gdybyś chciał to zrobić w PHP. Potrzebujesz tylko cronjob.config w swoim folderze .ebextensions, aby działał w ten sposób.
envvars pobiera zmienne środowiskowe dla plików. Możesz debugować dane wyjściowe w tmp / sendemail.log, jak powyżej.
Mam nadzieję, że to komuś pomoże, ponieważ z pewnością pomogło nam!
źródło
W oparciu o zasady odpowiedzi użytkownika1599237 , gdzie pozwalasz crona uruchamiać się na wszystkich instancjach, ale zamiast tego na początku zadań , czy powinny one być uruchamiane, stworzyłem inne rozwiązanie.
Zamiast patrzeć na uruchomione instancje (i muszę przechowywać klucz i tajny klucz AWS) używam bazy danych MySQL, z którą już się łączę ze wszystkich instancji.
Nie ma wad, tylko zalety:
Alternatywnie możesz również użyć powszechnie współdzielonego systemu plików (takiego jak AWS EFS przez protokół NFS) zamiast bazy danych.
Poniższe rozwiązanie zostało stworzone we frameworku PHP Yii, ale można je łatwo dostosować do innego frameworka i języka. Również
Yii::$app->system
moduł obsługi wyjątków jest moim własnym modułem. Zastąp go tym, czego używasz./** * 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); }
Oto schemat bazy danych, którego używam:
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`) )
źródło