AWS Elastic Beanstalk, bir cronjob çalıştırma


89

Her dakika yürütmek için bir cronjob / görev kurmanın bir yolu olup olmadığını bilmek istiyorum. Şu anda örneklerimden herhangi biri bu görevi çalıştırabilmelidir.

Yapılandırma dosyalarında başarılı olmadan yapmaya çalıştığım şey bu:

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

Bunu yapmanın doğru yolu olup olmadığından gerçekten emin değilim

Herhangi bir fikir?


1
Komut doğru mu? Yani ... şu olabilir: command: echo "* / 1 * * * * root php /etc/httpd/myscript.php"> /etc/cron.d/something Her iki durumda da, şunu kullanmanızı öneririm: lider_only işaretleyin, aksi takdirde tüm makineler bu cron işini bir kerede çalıştırır
aldrinleal

Evet! kesinlikle leader_only bayrağını kullanarak komutu değiştirmeyi deneyeceğim.
Onema

Yanıtlar:


98

Elastic Beanstalk'a şu şekilde bir cron işi ekledim:

Zaten yoksa, uygulamanızın kök dizininde .ebextensions adlı bir klasör oluşturun. Sonra, .ebextensions klasörünün içinde bir yapılandırma dosyası oluşturun. Örnekleme amacıyla example.config kullanacağım. Ardından bunu example.config dosyasına ekleyin

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

Bu, Elastic Beanstalk için bir YAML yapılandırma dosyasıdır. Bunu metin düzenleyicinize kopyaladığınızda, metin düzenleyicinizin sekmeler yerine boşluklar kullandığından emin olun. Aksi takdirde, bunu EB'ye ittiğinizde YAML hatası alırsınız.

Yani bunun yaptığı 01_some_cron_job adlı bir komut oluşturmaktır. Komutlar alfabetik sıraya göre çalıştırılır, böylece 01 ilk komut olarak çalıştırılmasını sağlar.

Komut daha sonra some_cron_job.txt adlı bir dosyanın içeriğini alır ve bunu /etc/cron.d'deki some_cron_job adlı bir dosyaya ekler.

Komut daha sonra /etc/cron.d/some_cron_job dosyasındaki izinleri değiştirir.

Leader_only anahtarı, komutun yalnızca lider olarak kabul edilen ec2 örneğinde çalıştırılmasını sağlar. Çalıştırdığınız her ec2 örneğinde çalıştırmak yerine.

Sonra, .ebextensions klasörünün içinde some_cron_job.txt adlı bir dosya oluşturun. Cron işlerinizi bu dosyaya yerleştireceksiniz.

Yani mesela:

# 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

Yani bu cron işi, kök kullanıcı olarak her günün her saatinde her dakika çalışacak ve çıktıyı / dev / null'a atacaktır. / usr / bin / php, php'ye giden yoldur. Sonra bazı-php-script-here yerine php dosyanızın yolunu yazın. Bu açıkça cron işinizin bir PHP dosyası çalıştırması gerektiğini varsayar.

Ayrıca, some_cron_job.txt dosyasının, yorumun söylediği gibi dosyanın sonunda bir satırsonu satırına sahip olduğundan emin olun. Aksi takdirde cron çalışmayacaktır.

Güncelleme: Elastic Beanstalk örneklerinizi ölçeklendirdiğinde bu çözümle ilgili bir sorun vardır. Örneğin, cron işi çalışan bir örneğiniz olduğunu varsayalım. Trafikte artış elde edersiniz, böylece Elastic Beanstalk sizi iki örneğe kadar ölçeklendirir. Leader_only, iki örnek arasında yalnızca bir cron işi çalıştırmanızı sağlar. Trafiğiniz azalır ve Elastic Beanstalk sizi bir örneğe ölçeklendirir. Ancak, ikinci örneği sonlandırmak yerine Elastic Beanstalk, lider olan ilk örneği sonlandırır. Yalnızca sonlandırılan ilk örnekte çalıştıkları için şu anda çalışan herhangi bir cron işiniz yok. Aşağıdaki yorumlara bakın.

Güncelleme 2: Bunu aşağıdaki yorumlardan açıkça belirtmek isteriz: AWS artık otomatik bulut sunucusu sonlandırmasına karşı korumaya sahiptir. Sadece lider örneğinizde etkinleştirin ve gitmeniz iyi olur. - Nicolás Arévalo 28 Eki 2016, 21:23


12
Önerinizi bir süredir kullanıyorum ve son zamanlarda liderin bir şekilde geçiş yaptığı bir sorunla karşılaştım ve sonuçta cron'u birden fazla örnek çalıştırdı. Bu sorunu çözmek için, ben değiştim 01_some_cron_jobetmek 02_some_cron_jobve eklenen 01_remove_cron_jobsaşağıdaki ile: command: "rm /etc/cron.d/cron_jobs || exit 0". Bu şekilde, her konuşlandırmadan sonra yalnızca lider cron_jobsdosyaya sahip olur. Liderler değişirse, yeniden konuşlandırabilirsiniz ve cron'lar bir kez daha çalışacak şekilde sabitlenir.
Willem Renzema

4
leader_onlyMülkiyete güvenmeye karşı öneririm . Yalnızca dağıtım sırasında kullanılır ve ölçeği küçültürseniz veya "lider" örneğiniz başarısız olursa, sorun başvurusu
yapmanız kaçınılmazdır

2
Bunu yapma. Fazla güvenilmez. Bunu çalıştırmamın tek yolu bir mikro örnek çalıştırmak ve oradan CURL kullanarak cron işleri çalıştırmak. Bu, onu yalnızca bir örneğin çalıştırmasını ve kurulu cronlara sahip liderin sonlandırılmamasını garanti eder.
Ben Sinclair

1
Bunu küçük bir yakut komut dosyasıyla düzeltmeye çalıştım, burada bulabilirsiniz: github.com/SocialbitGmbH/AWSBeanstalkLeaderManager
Thomas Kekeisen

8
AWS artık otomatik bulut sunucusu sonlandırmasına karşı korumaya sahiptir. Sadece lider örneğinizde etkinleştirin ve gitmeniz iyi olur.
Nicolás Arévalo

58

Şimdi bunu yapmanın resmi yolu budur (2015+). Lütfen önce bunu deneyin, şu anda mevcut olan en kolay ve en güvenilir yöntemdir.

Mevcut belgelere göre, kişi sözde çalışan katmanında periyodik görevler çalıştırabilir .

Belgelerden alıntı yapmak:

AWS Elastic Beanstalk, kapsayıcı adında "v1.2.0" içeren bir çözüm yığınıyla önceden tanımlanmış bir yapılandırma çalıştıran ortamlarda çalışan ortamı katmanları için periyodik görevleri destekler. Yeni bir ortam yaratmalısınız.

Ayrıca ilginç olan cron.yaml ile ilgili kısım :

Periyodik görevleri çağırmak için, uygulama kaynak paketinizin kök düzeyinde bir cron.yaml dosyası içermesi gerekir. Dosya, planlamak istediğiniz periyodik görevler hakkında bilgi içermelidir. Bu bilgiyi standart crontab sözdizimini kullanarak belirtin.

Güncelleme: Bu işi alabildik. Deneyimlerimizden bazı önemli bilgiler (Node.js platformu):

  • Cron.yaml dosyasını kullanırken , en son awsebcli'ye sahip olduğunuzdan emin olun , çünkü eski sürümler düzgün çalışmayacaktır.
  • Sadece eskisini klonlamak değil, yeni bir ortam yaratmak da (en azından bizim durumumuzda öyleydi) hayati önem taşır.
  • EC2 Worker Tier bulut sunucunuzda CRON'un desteklendiğinden emin olmak istiyorsanız, içine ssh ( eb ssh) ve çalıştırın cat /var/log/aws-sqsd/default.log. Olarak rapor etmelidir aws-sqsd 2.0 (2015-02-18). 2.0 sürümüne sahip değilseniz, ortamınızı oluştururken bir şeyler ters gitti ve yukarıda belirtildiği gibi yeni bir tane oluşturmanız gerekir.


5
Bunun için teşekkürler - çaylak soru - yaklaşan takvim etkinlikleri için web uygulamamın veritabanını saatte iki kez kontrol etmek ve geldiğinde bir hatırlatma e-postası göndermek için cron'uma ihtiyacım var. Buradaki en iyi kurulum nedir, cron.yaml URL'sinin Web uygulamamdaki bir rotaya işaret etmesini sağlamalı mıyım? Veya çalışan env uygulamama veritabanına erişim izni vermeli miyim? Bu konuda çok az şey var!
christian

5
@christian Bunu yapma şeklimiz, iki farklı ortamda çalışan aynı uygulamaya sahibiz (bu nedenle özel bir yapılandırma gerekmez) - çalışan ve ortak web sunucusu bir. Çalışan ortamı, uygulamamızın aradığı bir ENV değişkeni ayarlayarak etkinleştirilen bazı özel rotalara sahiptir. Bu şekilde, normal uygulama ile paylaşılan kod tabanı lüksüne sahip olurken cron.yaml dosyanızda yalnızca çalışanlara özel rotalar ayarlayabilirsiniz. Çalışan uygulamanız, web sunucusu bir ile aynı kaynaklara kolayca erişebilir: veritabanı, modeller vb.
xaralis

1
@JaquelinePassos v1.2.0, çözüm yığını sürümüdür. Yeni ortam oluştururken hangi çözüm yığını sürümünü oluşturmak istediğinizi seçmenize izin vermelidir. V1.2.0'dan daha yeni bir şey yapmalıdır. URL ile ilgili olarak, bir dosya yolu değil, uygulamanızın dinlediği URL olmalıdır. Django yönetim komutlarını çalıştırmak mümkün değildir, sadece HTTP istekleri yapar.
xaralis

4
Benim için net olmayan bir şey, cron.yaml aracılığıyla cron işlerini çalıştırmak için fazladan bir EC2 makinesi ayırmak zorunda kalmamak için bir yol olup olmadığıdır. İdeal olarak, HTTP isteklerine hizmet veren makineyle aynı makinede çalışır (yani web katmanı).
Wenzel Jakob

32

Jamieb'in yanıtıyla ilgili olarak ve genellikle bahsedildiği gibi, cron işini yalnızca bir EC2 bulut sunucusunun çalıştırmasını sağlamak için 'leader_only' özelliğini kullanabilirsiniz.

Alıntı http://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html adresinden alınmıştır :

Leader_only kullanabilirsiniz. Bir örnek, Otomatik Ölçeklendirme grubunda lider olarak seçilir. Leader_only değeri true olarak ayarlanırsa, komut yalnızca lider olarak işaretlenen örnekte çalışır.

EB'mde benzer bir şey elde etmeye çalışıyorum, bu yüzden çözersem yazımı güncelleyeceğim.

GÜNCELLEME:

Tamam, şimdi aşağıdaki eb yapılandırmasını kullanarak çalışan cronjobs'um var:

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

Esasen, cronjobs ile bir geçici dosya oluşturuyorum ve ardından crontab'ı geçici dosyadan okuyacak şekilde ayarlıyorum, ardından geçici dosyayı siliyorum. Bu yardımcı olur umarım.


3
Bu crontab'ı çalıştıran örneğin otomatik ölçeklendirme tarafından sonlandırılmamasını nasıl sağlarsınız? Varsayılan olarak, en eski örneği sonlandırır.
Sebastien

1
Bu henüz çözemediğim bir sorun. Amazon'un işlevselliğindeki bir kusur olarak, lider_only komutlarının mevcut lider EB tarafından sonlandırıldığında yeni bir lidere uygulanmaması bana çarpıcı geliyor. Eğer bir şey bulursanız lütfen paylaşın!
beterthanlife

7
Bu yüzden (sonunda) liderin otomatik ölçeklendirme ile işten çıkarılmasını nasıl önleyeceğimi keşfettim - özel otomatik ölçeklendirme sonlandırma ilkeleri. Bkz. Docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/…
beterthanlife

1
@Nate Muhtemelen bunu şimdiye kadar anlamışsınızdır, ancak bunların çalıştığı sırayı okumama dayanarak, "komutlar" "container_commands" dan önce çalışır, böylece dosyayı oluşturursunuz, sonra siler ve sonra crontab'ı çalıştırırsınız .
2014, 17:50

1
@Sebastien en eski girişimi korumak için işte yaptığım şey: 1 - girişin sonlandırma korumasını ENBABLE olarak değiştirin. 2 - Auto Scale Group'a gidin ve EBS Ortam Kimliğinizi bulun, DÜZENLE'ye tıklayın ve Fesih Politikalarını "En Yeni Durum" olarak değiştirin
Ronaldo Bahia

12

Yukarıda belirtildiği gibi, herhangi bir crontab yapılandırmasının oluşturulmasındaki temel kusur, bunun yalnızca dağıtımda gerçekleşmesidir. Küme otomatik olarak büyütüldükçe ve sonra küçüldükçe, aynı zamanda kapatılan ilk sunucu olması da tercih edilir. Ek olarak, benim için kritik olan yük devretme olmayacaktı.

Biraz araştırma yaptım, ardından fikirleri yansıtmak ve bulduğum çözümü doğrulamak için AWS hesap uzmanımızla konuştum. Bunu OpsWorks ile başarabilirsiniz , ancak bu biraz sinek öldürmek için bir ev kullanmak gibi. Data Pipeline'ı Task Runner ile kullanmak da mümkündür , ancak bu, yürütebileceği betiklerde sınırlı yeteneğe sahiptir ve tüm kod tabanına erişimle PHP betiklerini çalıştırabilmem gerekiyordu. Ayrıca ElasticBeanstalk kümesinin dışında bir EC2 bulut sunucusunu da tahsis edebilirsiniz, ancak bu durumda tekrar yük devretme olmaz.

İşte bulduğum şey, görünüşe göre alışılmadık (AWS temsilcisinin yorumladığı gibi) ve bir hack olarak kabul edilebilir, ancak işe yarıyor ve yük devretme ile sağlam. Aynı yöntemi tercih ettiğiniz herhangi bir dilde de uygulayabilmenize rağmen, PHP'de göstereceğim SDK'yı kullanarak bir kodlama çözümü seçtim.

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

Yani bunun üzerinden yürümek ve nasıl çalıştığını ... Normalde her EC2 bulut sunucusunda yaptığınız gibi crontab'dan komut dosyalarını çağırırsınız. Her komut dosyası, bir ElasticBeanstalk nesnesi oluşturan ve tüm örneklerin bir listesini alan başlangıçta bunu içerir (veya her biri için tek bir dosya içerir). Listedeki yalnızca ilk sunucuyu kullanır ve kendisiyle eşleşip eşleşmediğini kontrol eder, eğer yaparsa devam eder, aksi takdirde ölür ve kapanır. Kontrol ettim ve döndürülen liste tutarlı görünüyor, bu da teknik olarak yalnızca bir dakika kadar tutarlı olması gerekiyor, çünkü her bir örnek planlanmış cron'u çalıştırıyor. Değişirse, önemli olmaz, çünkü yine sadece o küçük pencere için önemlidir.

Bu hiçbir şekilde zarif değil, ancak özel ihtiyaçlarımıza uyuyordu - bu, ek bir hizmetle maliyeti artırmak veya özel bir EC2 bulut sunucusuna sahip olmak zorunda değildi ve herhangi bir arıza durumunda yük devretmeye neden olacaktı. Cron betiklerimiz, SQS'ye yerleştirilen bakım betikleri çalıştırır ve kümedeki her sunucu yürütülmesine yardımcı olur. En azından ihtiyaçlarınıza uyuyorsa bu size alternatif bir seçenek sunabilir.

-Davey


Php_uname ('n') özel DNS adını (örneğin ip-172.24.55.66) döndürdüğünü buldum, bu aradığınız örnek kimliği değil. Php_uname () kullanmak yerine şunu kullandım: $instanceId = file_get_contents("http://instance-data/latest/meta-data/instance-id"); O zaman karşılaştırmayı yapmak için sadece o $ instanceId varlığını kullanın.
Valorum

1
Örnekler dizisinin her bir Describe çağrısında aynı sıralamayı sunacağına dair herhangi bir garanti var mı? Sıralanan ilk girdinin sizin geçerli örnek kimliğiniz olup olmadığını kontrol etmeden önce, her girdinin ['Id'] alanını bir diziye çıkarmanızı ve bunları PHP'de sıralamanızı öneririm.
Gabriel

Bu cevaba dayanarak şu çözümü yaptım: stackoverflow.com/questions/14077095/… - çok benzer ancak iki kez çalıştırma şansı YOK.
TheStoryCoder

11

Bir AWS destek temsilcisiyle konuştum ve bunun benim için çalışmasını sağladık. 2015 çözümü:

.Ebextensions dizininizde dosya_adı.config ile bir dosya oluşturun. Yapılandırma dosyası girişinde:

Dosyalar:
  "/etc/cron.d/cron_example":
    mod: "000644"
    sahip: kök
    grup: kök
    içerik: |
      * * * * * root /usr/local/bin/cron_example.sh

  "/usr/local/bin/cron_example.sh":
    mod: "000755"
    sahip: kök
    grup: kök
    içerik: |
      #! / bin / bash

      /usr/local/bin/test_cron.sh || çıkış
      echo "Cron" "tarih`de çalışıyor >> /tmp/cron_example.log
      # Şimdi yalnızca 1 örnekte çalışması gereken görevleri yapın ...

  "/usr/local/bin/test_cron.sh":
    mod: "000755"
    sahip: kök
    grup: kök
    içerik: |
      #! / bin / bash

      META VERİ = / opt / aws / bin / ec2-metadata
      INSTANCE_ID = `$ METADATA -i | awk '{baskı $ 2}' '
      BÖLGE = `$ METADATA -z | awk '{baskı substr ($ 2, 0, uzunluk (2 $) -1)}' '

      # Otomatik Ölçeklendirme Grubumuzun adını bulun.
      ASG = "aws ec2 açıklama etiketleri - filtreler" Ad = kaynak kimliği, Değerler = $ INSTANCE_ID "\
        --region $ REGION - çıktı metni | awk '/ aws: autoscaling: groupName / {print $ 5}' '

      # Gruptaki ilk örneği bulun
      İLK = `aws otomatik ölçeklendirme, otomatik ölçeklendirme gruplarını tanımla - otomatik ölçeklendirme grup adları $ ASG \
        --region $ REGION - çıktı metni | awk '/ InService $ / {print $ 4}' | sırala | kafa -1`

      # Aynı olup olmadıklarını test edin.
      ["$ İLK" = "$ INSTANCE_ID"]

komutlar:
  rm_old_cron:
    komut: "rm * .bak"
    cwd: "/etc/cron.d"
    ignoreErrors: true

Bu çözümün 2 dezavantajı vardır:

  1. Sonraki dağıtımlarda, Beanstalk mevcut cron betiğini .bak olarak yeniden adlandırır, ancak cron yine de çalıştıracaktır. Cron'unuz artık aynı makinede iki kez çalışıyor.
  2. Ortamınız ölçeklenirse, tümü cron betiğinizi çalıştıran birkaç örnek elde edersiniz. Bu, posta çekimlerinizin tekrarlandığı veya veritabanı arşivlerinizin çoğaltıldığı anlamına gelir

Çözüm:

  1. Bir cron oluşturan herhangi bir .ebextensions komut dosyasının sonraki dağıtımlarda .bak dosyalarını da kaldırdığından emin olun.
  2. Aşağıdakileri yapan bir yardımcı komut dosyasına sahip olun: - Meta Veriden geçerli Örnek Kimliğini alır - Geçerli Otomatik Ölçeklendirme Grubu adını EC2 Etiketlerinden alır - Bu Gruptaki EC2 Bulut Sunucularının alfabetik olarak sıralanmış listesini alır. - Bu listeden ilk örneği alır. - 1. adımdaki Örnek Kimliğini 4. adımdaki ilk Örnek Kimliği ile karşılaştırır. Daha sonra, cron komut dosyalarınız çalıştırılıp çalıştırılmayacağını belirlemek için bu yardımcı komut dosyasını kullanabilir.

Uyarı:

  • Beanstalk örnekleri için kullanılan IAM Rolü için ec2 gerekir: DescribeTags ve autoscaling: DescribeAutoScalingGroups izinleri
  • Otomatik Ölçeklendirme ile Hizmet İçi olarak gösterilen örnekler arasından seçilen örneklerdir. Bu, onların tamamen başlatıldıkları ve cron'unuzu çalıştırmaya hazır oldukları anlamına gelmez.

Varsayılan beanstalk rolünü kullanıyorsanız, IAM Rollerini ayarlamanız gerekmez.


7

Rails kullanıyorsanız, her zaman elastikbeanstalk cevherini kullanabilirsiniz . Ya tüm örneklerde ya da yalnızca birinde cron işleri çalıştırmanıza izin verir. Yalnızca bir "lider" örnek olduğundan emin olmak için her dakika kontrol eder ve yoksa bir sunucuyu otomatik olarak "lider" konumuna yükseltir. Elastic Beanstalk dağıtım sırasında yalnızca lider kavramına sahip olduğundan ve ölçeklendirme sırasında herhangi bir anda herhangi bir örneği kapatabileceğinden bu gereklidir.

GÜNCELLEME AWS OpsWorks kullanmaya geçtim ve artık bu cevherin bakımını yapmıyorum. Elastic Beanstalk'ın temellerinde bulunandan daha fazla işleve ihtiyacınız varsa, OpsWorks'e geçmenizi şiddetle tavsiye ederim.


OpsWorks kullanarak bunu nasıl çözdüğünüzü bize anlatır mısınız? Cron işlerini yapan özel katmanlar mı çalıştırıyorsunuz?
Tommie

Evet, sadece bir sunucuda çalışan bir admin / cron katmanım var. Tüm cron işlerimi içeren özel bir yemek kitabı hazırladım. AWS'nin docs.aws.amazon.com/opsworks/latest/userguide/… adresinde bir kılavuzu vardır .
dignoe

@dignoe, OpsWorks kullanarak cron işleri çalıştırmak için bir sunucu atarsanız, aynı şey Elastic Beanstalk kullanırsa, cron işlerini çalıştırmak için tek sunuculu bir ortam kullanabilirim. Yük Dengeleyici ile bile, en azından her zaman bir sunucu örneğini korumak için maksimum ve minimum örnekler bire ayarlanmıştır.
Jose Nobile

6

Elastic Beanstalk'ta cron işleri yürütmek istemezsiniz. Birden fazla uygulama örneğiniz olduğu için bu, yarış koşullarına ve diğer garip sorunlara neden olabilir. Aslında yakın zamanda bununla ilgili blog yazdım (sayfanın 4. veya 5. ipucu). Kısa versiyon: Uygulamaya bağlı olarak, SQS gibi bir iş kuyruğu veya iron.io gibi bir üçüncü taraf çözümü kullanın .


SQS, kodun yalnızca bir kez çalıştırılacağını garanti etmez. İron.io sitesini beğendim, kontrol edeceğim.
Nathan H

Ayrıca blog gönderinizde RDS'de InnoDB kullanmanızı tavsiye ediyorsunuz. Görevlerimi depolamak için RDS üzerinde bir tablo kullanıyorum ve bu görevleri yalnızca bir sunucunun çalıştırdığından emin olmak için InnoDB "GÜNCELLEME İÇİN SEÇ ..." özelliğini kullanıyorum. Uygulamanız bir cron işi veya kullanıcı etkileşimi olmadan SQS ile nasıl iletişim kurar?
James Alday

1
@JamesAlday Bu SO sorusu oldukça eski. Yukarıdaki yorumu yazdığımdan beri AWS, çalışan sunuculardan birini yönetici olarak seçerek Elastic Beanstalk üzerinde cron işlerini gerçekleştirmenin zarif bir yolunu tanıttı. Bunu söyledikten sonra, cron + MySQL'i bir iş kuyruğu olarak kötüye kullanıyor gibisiniz. Yine de somut öneriler sunabilmem için uygulamanız hakkında çok şey bilmem gerekiyor.
jamieb

Çalıştırılacak işler için bir tabloyu kontrol eden cron aracılığıyla çalışan bir komut dosyam var. İşlemlerin kullanılması, birden çok sunucunun aynı işi çalıştırmasını engeller. SQS'ye baktım, ancak dağıtmak yerine tüm komut dosyalarını çalıştıran bir ana sunucuya ihtiyacınız var ve aynı betiği birden çok kez çalıştırmadığınızdan emin olmak için yine de mantık yazmanız gerekiyor. Ancak, görevleri kullanıcı etkileşimi veya cron olmadan nasıl çalıştıracağınız konusunda hala kafam karıştı - uygulamanızı görevleri kuyrukta çalıştırmak için ne tetikler?
James Alday

5

2017: Laravel5 + kullanıyorsanız

Yapılandırmak için sadece 2 dakikaya ihtiyacınız var:

  • bir Çalışan Katmanı oluşturun
  • laravel-aws-işçi kur

    composer require dusterio/laravel-aws-worker

  • kök klasöre bir cron.yaml ekleyin:

Uygulamanızın kök klasörüne cron.yaml ekleyin (bu, deponuzun bir parçası olabilir veya bu dosyayı EB'ye dağıtmadan hemen önce ekleyebilirsiniz - önemli olan, bu dosyanın dağıtım anında mevcut olmasıdır):

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

Bu kadar!

İçindeki tüm göreviniz App\Console\Kernelşimdi yürütülecek

Ayrıntılı talimatlar ve açıklamalar: https://github.com/dusterio/laravel-aws-worker

Laravel içinde görevler nasıl yazılır: https://laravel.com/docs/5.4/scheduling


3

Kullanmak filesyerine daha okunaklı bir çözüm container_commands:

Dosyalar:
  "/etc/cron.d/my_cron":
    mod: "000644"
    sahip: kök
    grup: kök
    içerik: |
      # varsayılan e-posta adresini geçersiz kıl
      MAILTO = "örnek@gmail.com"
      # Her beş dakikada bir Symfony komutunu çalıştırın (ec2 kullanıcısı olarak)
      * / 10 * * * * ec2-user / usr / bin / php / var / app / current / app / console do: bir şeyler
    kodlama: düz
komutlar:
  # Elastic Beanstalk tarafından oluşturulan yedekleme dosyasını sil
  clear_cron_backup:
    komut: rm -f /etc/cron.d/watson.bak

Biçimin, kullanıcının komutu çalıştıracağını belirlemesi açısından olağan crontab biçiminden farklı olduğunu unutmayın.


Buradaki bir sorun, Elastic Beanstalk EC2 bulut sunucularının varsayılan olarak SMTP hizmetlerine sahip olmamasıdır, bu nedenle buradaki MAILTO seçeneği çalışmayabilir.
Justin Finkelstein

3

2018 için 1 sentlik katkım

İşte bunu yapmanın doğru yolu (kullanma django/pythonve django_crontabuygulama):

.ebextensionsklasörün içinde bunun gibi bir dosya oluşturun 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

Olması gereken container_commandsyerinecommands



2

Bu yüzden bir süredir bununla uğraşıyoruz ve bir AWS temsilcisi ile biraz tartıştıktan sonra sonunda en iyi çözüm olduğunu düşündüğüm şeyi buldum.

Cron.yaml ile bir işçi katmanı kullanmak kesinlikle en kolay düzeltmedir. Ancak, belgelerin netleştirmediği şey, bunun işi, işlerinizi gerçekten çalıştırmak için kullandığınız SQS kuyruğunun sonuna yerleştireceğidir. Cron işleriniz zamana duyarlıysa (çoğu olduğu gibi), kuyruğun boyutuna bağlı olacağından bu kabul edilemez. Bir seçenek, sadece cron işlerini çalıştırmak için tamamen ayrı bir ortam kullanmaktır, ancak bence bu çok fazla.

Listedeki ilk örnek olup olmadığınızı kontrol etmek gibi diğer seçeneklerden bazıları da ideal değildir. Ya mevcut ilk örnek kapanma sürecindeyse?

Örnek koruması da sorunlarla gelebilir - bu örnek kilitlenirse / donarsa ne olur?

Anlaşılması gereken önemli olan, AWS'nin cron.yaml işlevselliğini nasıl yönettiğidir. "Lider seçimini" işlemek için bir Dinamo tablosu kullanan bir SQS arka plan programı vardır. Bu tabloya sık sık yazar ve mevcut lider kısa bir süre içinde yazmazsa, bir sonraki örnek lider olarak devralır. Bu, arka plan programının işi SQS kuyruğuna hangi örneğin başlatacağına karar vermesidir.

Kendi işlevimizi yeniden yazmaya çalışmak yerine mevcut işlevselliği yeniden kullanabiliriz. Tam çözümü burada görebilirsiniz: https://gist.github.com/dorner/4517fe2b8c79ccb3971084ec28267f27

Bu Ruby'de, ancak AWS SDK'ya sahip başka bir dile kolayca uyarlayabilirsiniz. Esasen mevcut lideri kontrol eder, ardından iyi durumda olduğundan emin olmak için durumu kontrol eder. İyi durumda mevcut bir lider olana kadar döngüye girer ve mevcut örnek lider ise, işi yürütün.


2

Amazon'un en son örneği, en kolay ve en verimli (periyodik görevler):

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

cron işlerinden herhangi birini yürütmek için ayrı bir çalışan katmanı oluşturduğunuz yer. Cron.yaml dosyasını oluşturun ve kök klasörünüze yerleştirin. Sahip olduğum bir sorun (cron yürütülüyor gibi görünmüyordu) CodePipeline'ımın bir dinamodb değişikliği yapma yetkisine sahip olmadığını bulmaktı. Buna dayanarak, IAM -> roller -> boru hattınız altında FullDynamoDB erişimi ekledikten ve yeniden konuşlandırdıktan sonra (elastik fasulye sapı) mükemmel çalıştı.



0

Otomatik Ölçeklemenin ölçeklenirken belirli bir örneği sonlandırıp sonlandırmayacağını kontrol etmek için örnek korumayı kullanın. Örnek koruma ayarını bir Otomatik Ölçeklendirme grubunda veya tek bir Otomatik Ölçeklendirme örneğinde etkinleştirebilirsiniz. Otomatik Ölçeklendirme bir örneği başlattığında, örnek Otomatik Ölçeklendirme grubunun örnek koruma ayarını devralır. Otomatik Ölçeklendirme grubu veya Otomatik Ölçeklendirme örneği için örnek koruma ayarını istediğiniz zaman değiştirebilirsiniz.

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


0

Bir php dosyasının cron üzerinden çalıştırılması gerekiyorsa ve herhangi bir NAT örneğini ayarladıysanız, o zaman cronjob'u NAT örneğine koyabilir ve php dosyasını wget aracılığıyla çalıştırabilirsiniz.


0

İşte bunu PHP'de yapmak isteyeceğiniz bir düzeltme. Bu şekilde çalışması için .ebextensions klasörünüzde cronjob.config'e ihtiyacınız var.

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 dosyalar için ortam değişkenlerini alır. Tmp / sendemail.log'daki çıktıda yukarıdaki gibi hata ayıklayabilirsiniz.

Umarım bu, bize kesinlikle yardımcı olduğu için birine yardımcı olur!


0

Cron işlerinin tüm örneklerde çalışmasına izin verdiğiniz , ancak bunun yerine işlerin başında çalıştırılmasına izin verilip verilmeyeceğini belirlediğiniz user1599237 yanıtının ilkelerine dayanarak, başka bir çözüm yaptım.

Çalışan örneklere bakmak (ve AWS anahtarınızı ve sırrınızı saklamak zorunda kalmak) yerine, tüm örneklerden zaten bağlandığım MySQL veritabanını kullanıyorum.

Olumsuz yanları yoktur, sadece olumlu yönleri vardır:

  • ekstra örnek veya masraf yok
  • kaya gibi sağlam çözüm - çifte uygulama şansı yok
  • ölçeklenebilir - örnekleriniz yukarı ve aşağı ölçeklendiğinde otomatik olarak çalışır
  • yük devretme - bir örneğin arızalanması durumunda otomatik olarak çalışır

Alternatif olarak, bir veritabanı yerine yaygın olarak paylaşılan bir dosya sistemini de ( NFS protokolü aracılığıyla AWS EFS gibi ) kullanabilirsiniz.

Aşağıdaki çözüm PHP çerçevesi Yii içinde oluşturulmuştur, ancak onu başka bir çerçeve ve dile kolayca uyarlayabilirsiniz. Ayrıca istisna işleyici Yii::$app->systembenim de bir modüldür. Kullandığınız şeyle değiştirin.

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

Bu, kullandığım veritabanı şeması:

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`)
)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.