AWS Elastic Beanstalk, eseguendo un cronjob

Vorrei sapere se esiste un modo per configurare un cronjob / task da eseguire ogni minuto. Attualmente qualsiasi mia istanza dovrebbe essere in grado di eseguire questa attività.

Questo è quello che ho cercato di fare nei file di configurazione senza successo:

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

Non sono davvero sicuro se questo è il modo corretto per farlo

Qualche idea?

    Ecco come ho aggiunto un job cron a Elastic Beanstalk:

    Crea una cartella nella root della tua applicazione chiamata .ebextensions se non esiste già. Quindi creare un file di configurazione nella cartella .ebextensions. Userò example.config a scopo illustrativo. Quindi aggiungere questo ad 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 

    Questo è un file di configurazione YAML per Elastic Beanstalk. Assicurati quando lo copi nel tuo editor di testo che il tuo editor di testo usa spazi invece di tabulazioni. Altrimenti si otterrà un errore YAML quando si spinge questo in EB.

    Quindi, ciò che fa è creare un comando chiamato 01_some_cron_job. I comandi vengono eseguiti in ordine alfabetico in modo che 01 si assicuri che venga eseguito come primo comando.

    Il comando prende quindi il contenuto di un file chiamato some_cron_job.txt e lo aggiunge a un file chiamato some_cron_job in /etc/cron.d.

    Il comando modifica quindi le autorizzazioni sul file /etc/cron.d/some_cron_job.

    La chiave leader_only garantisce che il comando venga eseguito solo sull’istanza di ec2 considerata leader. Anziché eseguire su ogni istanza di ec2, è ansible che tu stia funzionando.

    Quindi creare un file chiamato some_cron_job.txt all’interno della cartella .ebextensions. Inserirai i tuoi lavori cron in questo file.

    Quindi per esempio:

     # 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 

    Quindi questo cron job verrà eseguito ogni minuto di ogni ora di ogni giorno come utente root e scarterà l’output su / dev / null. / usr / bin / php è il percorso di php. Quindi sostituisci some-php-script-qui con il percorso del tuo file php. Questo ovviamente sta assumendo che il tuo cron job abbia bisogno di eseguire un file PHP.

    Inoltre, assicurati che il file some_cron_job.txt abbia una nuova riga alla fine del file, proprio come dice il commento. Altrimenti cron non verrà eseguito.

    Aggiornamento: c’è un problema con questa soluzione quando Elastic Beanstalk ridimensiona le tue istanze. Ad esempio, diciamo che hai un’istanza con il processo cron in esecuzione. Si ottiene un aumento del traffico in modo che Elastic Beanstalk riduca fino a due istanze. Il leader_only garantirà che solo un cron job venga eseguito tra le due istanze. Il tuo traffico diminuisce e Elastic Beanstalk ti ridimensiona fino a un’istanza. Ma invece di terminare la seconda istanza, Elastic Beanstalk termina la prima istanza che era il leader. Ora non hai nessun processo cron in esecuzione poiché erano in esecuzione solo sulla prima istanza che è stata terminata. Vedi i commenti qui sotto.

    Aggiornamento 2: chiarendo questo dai commenti seguenti: AWS ora protegge contro la chiusura automatica delle istanze. Basta abilitarlo nella tua istanza leader e sei a posto. – Nicolás Arévalo 28 ottobre 16 alle 9:23

    Questo è il modo ufficiale per farlo ora (2015+). Si prega di provare prima questo, è di gran lunga il metodo più semplice attualmente disponibile e più affidabile pure.

    Secondo i documenti attuali, uno è in grado di eseguire attività periodiche sul loro cosiddetto livello di lavoro .

    Citando la documentazione:

    AWS Elastic Beanstalk supporta attività periodiche per i livelli dell’ambiente di lavoro in ambienti che eseguono una configurazione predefinita con uno stack di soluzioni che contiene “v1.2.0” nel nome del contenitore. Devi creare un nuovo ambiente.

    Interessante è anche la parte su cron.yaml :

    Per richiamare attività periodiche, il pacchetto sorgente dell’applicazione deve includere un file cron.yaml al livello principale. Il file deve contenere informazioni sulle attività periodiche che si desidera pianificare. Specificare queste informazioni usando la syntax crontab standard.

    Aggiornamento: siamo riusciti a ottenere questo lavoro. Ecco alcuni trucchi importanti tratti dalla nostra esperienza (piattaforma Node.js):

    • Quando usi il file cron.yaml , assicurati di avere l’ultimo awsebcli , perché le versioni precedenti non funzioneranno correttamente.
    • È anche vitale creare un nuovo ambiente (almeno nel nostro caso lo fosse), non solo clonare quello vecchio.
    • Se vuoi assicurarti che CRON sia supportato nell’istanza di EC2 Worker Tier, esegui ssh in esso ( eb ssh ) ed esegui cat /var/log/aws-sqsd/default.log . Dovrebbe essere segnalato come aws-sqsd 2.0 (2015-02-18) . Se non si dispone della versione 2.0, qualcosa è andato storto durante la creazione del proprio ambiente ed è necessario crearne uno nuovo come sopra indicato.

    Per quanto riguarda la risposta di jamieb, e come menzioni alrdinleal, è ansible utilizzare la proprietà ‘leader_only’ per garantire che solo una istanza EC2 esegua il processo cron.

    Citazione tratta da http://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html :

    puoi usare leader_only. Un’istanza viene scelta come leader in un gruppo di ridimensionamento automatico. Se il valore leader_only è impostato su true, il comando viene eseguito solo sull’istanza contrassegnata come leader.

    Sto cercando di ottenere una cosa simile sul mio eb, quindi aggiornerò il mio post se lo risolvo.

    AGGIORNARE:

    Ok, ora sto lavorando con cronjobs usando la seguente configurazione di 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 

    Essenzialmente, creo un file temporaneo con i cronjobs e poi setto il crontab per leggere dal file temporaneo, quindi cancellerò il file temporaneo in seguito. Spero che questo ti aiuti.

    Come accennato in precedenza, il difetto fondamentale nello stabilire qualsiasi configurazione di crontab è che si verifica solo durante l’implementazione. Poiché il cluster viene ridimensionato automaticamente e quindi retrocesso, viene preferito anche il primo server distriggersto. Inoltre, non ci sarebbe stato un fail-over, che per me è stato fondamentale.

    Ho fatto qualche ricerca, poi ho parlato con il nostro specialista di account AWS per far rimbalzare idee e validare la soluzione che ho trovato. Puoi farlo con OpsWorks , anche se è un po ‘come usare una casa per uccidere una mosca. È anche ansible utilizzare Data Pipeline con Task Runner , ma questo ha un’abilità limitata negli script che è in grado di eseguire e ho bisogno di essere in grado di eseguire script PHP, con accesso all’intero codebase. È anche ansible dedicare un’istanza EC2 al di fuori del cluster ElasticBeanstalk, ma non si ha più alcun failover.

    Quindi ecco cosa mi è venuto in mente, che a quanto pare non è convenzionale (come ha commentato la rappresentante di AWS) e può essere considerato un hack, ma funziona ed è solido con il failover. Ho scelto una soluzione di codifica utilizzando l’SDK, che mostrerò in PHP, sebbene tu possa fare lo stesso metodo in qualsiasi lingua tu preferisca.

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

    Percorrendo questo e come funziona … Chiami script da crontab come faresti normalmente in ogni istanza EC2. Ogni script include questo all’inizio (o include un singolo file per ognuno, mentre lo utilizzo), che stabilisce un object ElasticBeanstalk e recupera un elenco di tutte le istanze. Usa solo il primo server nell’elenco e controlla se corrisponde a se stesso, che se continua, altrimenti muore e si chiude. Ho controllato e l’elenco restituito sembra essere coerente, che tecnicamente deve essere coerente solo per un minuto o così, poiché ogni istanza esegue il cron pianificato. Se cambia, non importa, dal momento che è rilevante solo per quella piccola finestra.

    Questo non è elegante in alcun modo, ma si adatta alle nostre esigenze specifiche – che non è stato quello di aumentare i costi con un servizio aggiuntivo o di avere un’istanza EC2 dedicata, e avremmo un failover in caso di errore. I nostri script cron eseguono script di manutenzione che vengono inseriti in SQS e ciascun server nel cluster consente l’esecuzione. Almeno questo può darti un’opzione alternativa se soddisfa le tue esigenze.

    -Davey

    Se stai usando Rails, puoi usare la gem when-elasticbeanstalk . Ti permette di eseguire lavori cron su tutte le istanze o su una sola. Controlla ogni minuto per garantire che ci sia una sola istanza “leader” e automaticamente promuoverà un server in “leader” se non ce ne sono. Ciò è necessario dal momento che Elastic Beanstalk ha solo il concetto di leader durante la distribuzione e può arrestare qualsiasi istanza in qualsiasi momento durante il ridimensionamento.

    AGGIORNAMENTO Sono passato a utilizzare OpsWorks di AWS e non sto più mantenendo questo gioiello. Se hai bisogno di più funzionalità di quelle disponibili nelle basi di Elastic Beanstalk, ti ​​consiglio vivamente di passare a OpsWorks.

    Ho parlato con un agente di supporto AWS e questo è il modo in cui abbiamo ottenuto questo per me. Soluzione del 2015:

    Crea un file nella tua directory .ebextensions con nome_file.config. Nell’input del file di configurazione:

      File:
       "/etc/cron.d/cron_example":
         modalità: "000644"
         proprietario: root
         gruppo: radice
         contenuto: |
           * * * * * root /usr/local/bin/cron_example.sh
    
       "/usr/local/bin/cron_example.sh":
         modalità: "000755"
         proprietario: root
         gruppo: radice
         contenuto: |
           #! / Bin / bash
    
           /usr/local/bin/test_cron.sh ||  Uscita
           echo "Cron in esecuzione a" `date` >> /tmp/cron_example.log
           # Ora esegui attività che dovrebbero essere eseguite solo su 1 istanza ...
    
       "/usr/local/bin/test_cron.sh":
         modalità: "000755"
         proprietario: root
         gruppo: radice
         contenuto: |
           #! / Bin / bash
    
           METADATA = / opt / AWS / bin / EC2-metadati
           INSTANCE_ID = `$ METADATA -i |  awk '{print $ 2}' `
           REGIONE = `$ METADATA -z |  awk '{print substr ($ 2, 0, length ($ 2) -1)}' `
    
           # Trova il nostro nome del gruppo di ridimensionamento automatico.
           ASG = `aws ec2 Descrive-tags - filtri" Name = resource-id, Valori = $ INSTANCE_ID "\
             --region $ REGION --output text |  awk '/ aws: autoscaling: groupName / {print $ 5}' `
    
           # Trova la prima istanza nel gruppo
           PRIMO = `aws autoscaling Descrive-auto-scaling-groups --auto-scaling-group-names $ ASG \
             --region $ REGION --output text |  awk '/ InService $ / {print $ 4}' |  ordinare |  testa -1`
    
           # Verifica se sono uguali.
           ["$ FIRST" = "$ INSTANCE_ID"]
    
     comandi:
       rm_old_cron:
         comando: "rm * .bak"
         cwd: "/etc/cron.d"
         ignoreErrors: true
    

    Questa soluzione ha 2 svantaggi:

    1. Nelle distribuzioni successive, Beanstalk rinomina lo script cron esistente come .bak, ma cron lo eseguirà comunque. Il tuo Cron ora esegue due volte sullo stesso computer.
    2. Se il tuo ambiente si ridimensiona, ottieni diverse istanze, tutte con lo script cron. Ciò significa che le tue mailing vengono ripetute o gli archivi del tuo database duplicati

    Soluzione:

    1. Garantire che qualsiasi script .ebextensions che crea un cron rimuove anche i file .bak nelle distribuzioni successive.
    2. Avere uno script di aiuto che effettua le seguenti operazioni: – Ottiene l’ID istanza corrente dai metadati – Ottiene il nome corrente del gruppo di ridimensionamento automatico dai tag EC2: Ottiene l’elenco delle istanze EC2 in quel gruppo, in ordine alfabetico. – Prende la prima istanza da quella lista. – Confronta l’ID istanza del passaggio 1 con il primo ID istanza del passaggio 4. Gli script cron possono quindi utilizzare questo script di supporto per determinare se devono essere eseguiti.

    Avvertimento:

    • Il ruolo IAM utilizzato per le istanze Beanstalk ha bisogno di ec2: DescribeTags e autoscaling: DescribeAutoScalingGroups permessi
    • Le istanze scelte sono quelle mostrate come InService in Auto Scaling. Questo non significa necessariamente che siano completamente avviati e pronti per eseguire il cron.

    Non si dovrebbe impostare i ruoli IAM se si utilizza il ruolo di beanstalk predefinito.

    Davvero non vuoi eseguire processi cron su Elastic Beanstalk. Dal momento che avrai più istanze di applicazione, questo può causare condizioni di gara e altri problemi strani. In realtà, di recente ho parlato di questo (4 ° o 5 ° suggerimento in fondo alla pagina). La versione breve: a seconda dell’applicazione, utilizzare una coda di lavoro come SQS o una soluzione di terze parti come iron.io.

    Una soluzione più leggibile che utilizza i files anziché i container_commands :

     File:
       "/etc/cron.d/my_cron":
         modalità: "000644"
         proprietario: root
         gruppo: radice
         contenuto: |
           # sostituisce l'indirizzo email predefinito
           MAILTO = "[email protected]"
           # esegue un comando Symfony ogni cinque minuti (come ec2-user)
           * / 10 * * * * ec2-user / usr / bin / php / var / app / current / app / console do: qualcosa
         codifica: semplice
     comandi:
       # cancella il file di backup creato da Elastic Beanstalk
       clear_cron_backup:
         comando: rm -f /etc/cron.d/watson.bak
    

    Nota che il formato è diverso dal solito formato crontab in quanto specifica all’utente di eseguire il comando come.

    Qualcuno si chiedeva quali fossero i primi problemi di ridimensionamento automatico quando sorgessero nuovi leader. Non riesco a capire come rispondere ai loro commenti, ma vedere questo link: http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling- ambiente/

    Per controllare se il ridimensionamento automatico può terminare una particolare istanza durante il ridimensionamento, utilizzare la protezione dell’istanza. È ansible abilitare l’impostazione di protezione dell’istanza su un gruppo di ridimensionamento automatico o su un’istanza di ridimensionamento automatico individuale. Quando Auto Scaling avvia un’istanza, l’istanza eredita l’impostazione di protezione dell’istanza del gruppo Auto Scaling. È ansible modificare l’impostazione di protezione dell’istanza per un gruppo di ridimensionamento automatico o un’istanza di ridimensionamento automatico in qualsiasi momento.

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

    Ho avuto un’altra soluzione a questo se un file php deve essere eseguito attraverso cron e se hai impostato qualsiasi istanza NAT allora puoi mettere cronjob su istanza NAT ed eseguire file php attraverso wget.

    2017: se stai usando Laravel5 +

    Hai solo bisogno di 2 minuti per configurarlo:

    • creare un livello di lavoratore
    • installa laravel-aws-worker

      composer require dusterio/laravel-aws-worker

    • aggiungi un cron.yaml alla cartella principale:

    Aggiungi cron.yaml alla cartella principale dell’applicazione (questa può essere una parte del repository o potresti aggiungere questo file subito prima della distribuzione in EB – l’importante è che questo file sia presente al momento della distribuzione):

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

    Questo è tutto!

    Ora tutto il tuo compito in App\Console\Kernel verrà eseguito

    Istruzioni e spiegazioni dettagliate: https://github.com/dusterio/laravel-aws-worker

    Come scrivere attività all’interno di Laravel: https://laravel.com/docs/5.4/scheduling

    Quindi abbiamo lottato con questo per un po ‘e dopo qualche discussione con un rappresentante AWS ho finalmente trovato quello che penso sia la soluzione migliore.

    Utilizzare un livello di lavoro con cron.yaml è sicuramente la soluzione più semplice. Tuttavia, ciò che la documentazione non chiarisce è che questo metterà il lavoro alla fine della coda SQS che stai usando per eseguire effettivamente i tuoi lavori. Se i tuoi lavori cron sono sensibili al tempo (come molti lo sono), questo non è accettabile, poiché dipenderebbe dalla dimensione della coda. Un’opzione è di usare un ambiente completamente separato solo per eseguire i cron job, ma penso che sia eccessivo.

    Alcune delle altre opzioni, come il controllo per vedere se sei la prima istanza nell’elenco, non sono nemmeno l’ideale. Cosa succede se la prima istanza corrente è in fase di arresto?

    La protezione delle istanze può anche avere problemi: cosa succede se quell’istanza viene bloccata / bloccata?

    Quello che è importante capire è come AWS stesso gestisce la funzionalità di cron.yaml. C’è un demone SQS che usa una tabella Dynamo per gestire “l’elezione dei leader”. Scrive spesso su questo tavolo e se il leader attuale non ha scritto tra poco, l’istanza successiva assumerà il ruolo di leader. Questo è il modo in cui il daemon decide quale istanza triggersre il lavoro nella coda SQS.

    Possiamo riutilizzare la funzionalità esistente piuttosto che provare a riscrivere la nostra. Puoi vedere la soluzione completa qui: https://gist.github.com/dorner/4517fe2b8c79ccb3971084ec28267f27

    Questo è in Ruby, ma puoi facilmente adattarlo a qualsiasi altro linguaggio che abbia l’SDK AWS. In sostanza, controlla il leader corrente, quindi controlla lo stato per assicurarsi che sia in buono stato. Andrà in loop finché non ci sarà un leader attuale in buono stato e, se l’istanza corrente è il leader, eseguirà il lavoro.

    ecco una soluzione in caso tu voglia farlo in PHP. Hai solo bisogno di cronjob.config nella tua cartella .ebextensions per farlo funzionare così.

     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 

    l’ambiente ottiene le variabili d’ambiente per i file. È ansible eseguire il debug dell’output su tmp / sendemail.log come sopra.

    Spero che questo aiuti qualcuno come sicuramente ci ha aiutato!

    Il mio 1 centesimo di contributo per il 2018

    Ecco il modo giusto per farlo (usando l’applicazione django/python e django_crontab ):

    all’interno .ebextensions cartella .ebextensions crea un file come questo 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 

    Ha bisogno di essere container_commands invece di commands