AWS Elastic Beanstalk, menjalankan cronjob


89

Saya ingin tahu apakah ada cara untuk menyiapkan cronjob / tugas untuk dijalankan setiap menit. Saat ini salah satu contoh saya seharusnya dapat menjalankan tugas ini.

Inilah yang saya coba lakukan di file konfigurasi tanpa hasil:

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

Saya tidak begitu yakin apakah ini cara yang benar untuk melakukannya

Ada ide?


1
Apakah perintahnya benar? Maksud saya ... bisa jadi: command: echo "* / 1 * * * * root php /etc/httpd/myscript.php"> /etc/cron.d/something Apa pun itu, saya sarankan Anda menggunakan leader_only flag, jika tidak semua mesin akan menjalankan tugas cron ini sekaligus
aldrinleal

Iya! pasti menggunakan flag leader_only, saya akan mencoba mengubah perintah.
Onema

Jawaban:


97

Beginilah cara saya menambahkan tugas cron ke Elastic Beanstalk:

Buat folder di root aplikasi Anda dengan nama .ebextensions jika belum ada. Kemudian buat file konfigurasi di dalam folder .ebextensions. Saya akan menggunakan example.config untuk tujuan ilustrasi. Kemudian tambahkan ini ke 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

Ini adalah file konfigurasi YAML untuk Elastic Beanstalk. Pastikan saat Anda menyalin ini ke editor teks Anda bahwa editor teks Anda menggunakan spasi, bukan tab. Jika tidak, Anda akan mendapatkan kesalahan YAML saat Anda mendorong ini ke EB.

Jadi yang dilakukannya adalah membuat perintah bernama 01_some_cron_job. Perintah dijalankan dalam urutan abjad sehingga 01 memastikan itu dijalankan sebagai perintah pertama.

Perintah tersebut kemudian mengambil konten dari file bernama some_cron_job.txt dan menambahkannya ke file bernama some_cron_job di /etc/cron.d.

Perintah kemudian mengubah hak akses pada file /etc/cron.d/some_cron_job.

Kunci leader_only memastikan perintah hanya dijalankan pada turunan ec2 yang dianggap sebagai pemimpin. Daripada berjalan di setiap contoh ec2 yang mungkin Anda jalankan.

Kemudian buat file bernama some_cron_job.txt di dalam folder .ebextensions. Anda akan menempatkan pekerjaan cron Anda di file ini.

Jadi contohnya:

# 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

Jadi tugas cron ini akan berjalan setiap menit setiap jam setiap hari sebagai pengguna root dan membuang hasilnya ke / dev / null. / usr / bin / php adalah jalur ke php. Kemudian ganti some-php-script-here dengan path ke file php Anda. Ini jelas mengasumsikan pekerjaan cron Anda perlu menjalankan file PHP.

Juga, pastikan file some_cron_job.txt memiliki baris baru di akhir file seperti di komentar. Jika tidak, cron tidak akan berjalan.

Pembaruan: Ada masalah dengan solusi ini saat Elastic Beanstalk meningkatkan skala instans Anda. Misalnya, Anda memiliki satu instance dengan tugas cron sedang berjalan. Anda mendapatkan peningkatan lalu lintas sehingga Elastic Beanstalk menskalakan Anda hingga dua contoh. Leader_only akan memastikan Anda hanya memiliki satu tugas cron yang berjalan di antara dua contoh. Lalu lintas Anda menurun dan Elastic Beanstalk menurunkan Anda ke satu contoh. Namun alih-alih menghentikan instance kedua, Elastic Beanstalk menghentikan instance pertama yang menjadi pemimpin. Sekarang Anda tidak memiliki tugas cron apa pun yang berjalan karena tugas tersebut hanya berjalan pada instance pertama yang dihentikan. Lihat komentar di bawah.

Pembaruan 2: Hanya memperjelas ini dari komentar di bawah ini: AWS sekarang memiliki perlindungan terhadap penghentian instans otomatis. Aktifkan saja pada contoh pemimpin Anda dan Anda siap melakukannya. - Nicolás Arévalo 28 Oktober '16 pukul 9:23


12
Saya telah menggunakan saran Anda untuk beberapa waktu, dan baru-baru ini mengalami masalah di mana entah bagaimana pemimpinnya beralih, mengakibatkan banyak contoh menjalankan cron. Untuk mengatasi masalah itu, saya berubah 01_some_cron_jobuntuk 02_some_cron_jobdan ditambah 01_remove_cron_jobsdengan berikut ini: command: "rm /etc/cron.d/cron_jobs || exit 0". Dengan begitu, setelah setiap penerapan, hanya pemimpin yang akan memiliki cron_jobsfile tersebut. Jika pemimpin berubah, Anda hanya dapat menerapkan kembali dan crons akan diperbaiki untuk dijalankan sekali lagi.
Willem Renzema

4
Saya menyarankan agar tidak mengandalkan leader_onlyproperti. Ini hanya digunakan selama penerapan dan jika Anda menurunkan skala atau instans "pemimpin" Anda gagal, Anda pasti memiliki referensi
arnaslu

2
Jangan lakukan ini. Itu terlalu tidak bisa diandalkan. Satu-satunya cara saya membuat ini berfungsi adalah dengan menjalankan instance mikro dan menjalankan tugas cron dari sana menggunakan CURL. Ini menjamin bahwa hanya satu instance yang menjalankannya dan leader yang menginstal crons tidak akan dihentikan.
Ben Sinclair

1
Saya mencoba memperbaikinya dengan skrip ruby ​​kecil, Anda dapat menemukannya di sini: github.com/SocialbitGmbH/AWSBeanstalkLeaderManager
Thomas Kekeisen

8
AWS sekarang memiliki perlindungan terhadap penghentian instans otomatis. Aktifkan saja pada contoh pemimpin Anda dan Anda siap melakukannya.
Nicolás Arévalo

58

Ini adalah cara resmi untuk melakukannya sekarang (2015+). Silakan coba ini dulu, sejauh ini metode termudah yang tersedia saat ini dan paling dapat diandalkan juga.

Menurut dokumen saat ini, seseorang dapat menjalankan tugas berkala pada apa yang disebut tingkat pekerja .

Mengutip dokumentasi:

AWS Elastic Beanstalk mendukung tugas periodik untuk tingkatan lingkungan pekerja di lingkungan yang menjalankan konfigurasi yang telah ditentukan sebelumnya dengan tumpukan solusi yang berisi "v1.2.0" dalam nama penampung. Anda harus menciptakan lingkungan baru.

Yang juga menarik adalah bagian tentang cron.yaml :

Untuk menjalankan tugas periodik, paket sumber aplikasi Anda harus menyertakan file cron.yaml di tingkat root. File tersebut harus berisi informasi tentang tugas berkala yang ingin Anda jadwalkan. Tentukan informasi ini menggunakan sintaks crontab standar.

Pembaruan: Kami bisa mendapatkan pekerjaan ini. Berikut adalah beberapa hal penting dari pengalaman kami (platform Node.js):

  • Saat menggunakan file cron.yaml , pastikan Anda memiliki awsebcli terbaru , karena versi yang lebih lama tidak akan berfungsi dengan baik.
  • Penting juga untuk menciptakan lingkungan baru (setidaknya dalam kasus kami dulu), bukan hanya mengkloning yang lama.
  • Jika Anda ingin memastikan CRON didukung pada instans Tingkat Pekerja EC2 Anda, ssh ke dalamnya ( eb ssh), dan jalankan cat /var/log/aws-sqsd/default.log. Ini harus melaporkan sebagai aws-sqsd 2.0 (2015-02-18). Jika Anda tidak memiliki versi 2.0, ada yang salah saat membuat lingkungan Anda dan Anda perlu membuat yang baru seperti yang disebutkan di atas.


5
Terima kasih untuk ini - pertanyaan pemula - saya perlu cron saya untuk memeriksa database aplikasi web saya dua kali dalam satu jam untuk acara kalender yang akan datang, dan mengirim email pengingat ketika itu terjadi. Apa penyiapan terbaik di sini, haruskah saya memiliki URL cron.yaml yang mengarah ke rute di aplikasi Web saya? Atau haruskah saya memberikan akses aplikasi env pekerja saya ke database? Sedikit di luar sana tentang ini!
kristen

5
@christian Cara kami melakukannya, kami memiliki aplikasi yang sama yang berjalan di dua lingkungan berbeda (sehingga tidak diperlukan konfigurasi khusus) - satu pekerja dan server web umum. Lingkungan pekerja memiliki beberapa rute khusus yang diaktifkan dengan menyetel variabel ENV yang dicari aplikasi kita. Dengan cara ini, Anda dapat menyetel rute khusus pekerja khusus di cron.yaml Anda sambil menikmati kemewahan basis kode bersama dengan aplikasi normal. Aplikasi pekerja Anda dapat dengan mudah mengakses sumber daya yang sama seperti server web: database, model, dll.
xaralis

1
@JaquelinePassos v1.2.0 adalah versi stack solusi. Ini harus membiarkan Anda memilih versi tumpukan solusi mana yang ingin Anda buat saat membuat lingkungan baru. Apa pun yang lebih baru dari v1.2.0 harus dilakukan. Mengenai URL-nya, itu haruslah URL yang didengarkan aplikasi Anda, bukan jalur file. Tidak mungkin menjalankan perintah manajemen Django, ini hanya melakukan permintaan HTTP.
xaralis

4
Satu hal yang tidak jelas bagi saya adalah jika ada cara untuk menghindari keharusan mengalokasikan mesin EC2 tambahan hanya untuk menjalankan tugas cron melalui cron.yaml. Idealnya itu akan berjalan pada mesin yang sama dengan yang melayani permintaan HTTP (yaitu web tier).
Wenzel Jakob

31

Mengenai respons jamieb, dan seperti yang disebutkan alrdinleal, Anda dapat menggunakan properti 'leader_only' untuk memastikan bahwa hanya satu instans EC2 yang menjalankan tugas cron.

Kutipan diambil dari http://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html :

Anda dapat menggunakan leader_only. Satu instance dipilih untuk menjadi pemimpin dalam grup Auto Scaling. Jika nilai leader_only disetel ke true, perintah hanya berjalan pada instance yang ditandai sebagai pemimpin.

Saya mencoba untuk mencapai hal serupa di eb saya, jadi akan memperbarui posting saya jika saya menyelesaikannya.

MEMPERBARUI:

Oke, sekarang saya memiliki cronjobs yang berfungsi menggunakan konfigurasi eb berikut:

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

Pada dasarnya, saya membuat file temp dengan cronjobs dan kemudian mengatur crontab untuk membaca dari file temp, kemudian menghapus file temp sesudahnya. Semoga ini membantu.


3
Bagaimana Anda memastikan bahwa instance yang menjalankan crontab ini tidak dihentikan oleh penskalaan otomatis? Secara default, ini menghentikan instance terlama.
Sebastien

1
Itu adalah masalah yang belum bisa saya selesaikan. Menurut saya, ini merupakan cacat dalam fungsi amazon bahwa perintah leader_only tidak diterapkan ke pemimpin baru ketika perintah yang sekarang diakhiri oleh EB. Jika Anda menemukan sesuatu, silakan berbagilah!
beterthanlife

7
Jadi saya (akhirnya) menemukan cara mencegah pemimpin dihentikan oleh penskalaan otomatis - kebijakan penghentian penskalaan otomatis khusus. Lihat docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/…
beterthanlife

1
@Nate Anda mungkin sudah mengetahui hal ini sekarang, tetapi berdasarkan pembacaan saya tentang urutan yang dijalankannya, "perintah" dijalankan sebelum "container_commands" sehingga Anda akan membuat file, lalu menghapusnya, lalu mencoba menjalankan crontab .
hapus

1
@Sebastien untuk menjaga intance terlama, inilah yang saya lakukan: 1 - ubah perlindungan terminasi intance menjadi ENBABLE. 2 - Buka Auto Scale Group dan temukan ID Lingkungan EBS Anda, klik EDIT dan ubah Kebijakan Penghentian ke "LatestInstance"
Ronaldo Bahia

12

Seperti disebutkan di atas, kelemahan mendasar dalam menetapkan konfigurasi crontab adalah bahwa hal itu hanya terjadi saat penerapan. Saat cluster ditingkatkan secara otomatis, dan kemudian turun, ini disukai juga untuk menjadi server pertama yang dimatikan. Selain itu, tidak akan ada fail-over, yang bagi saya sangat penting.

Saya melakukan penelitian, lalu berbicara dengan spesialis akun AWS kami untuk menyampaikan ide dan memvalidasi solusi yang saya hasilkan. Anda dapat melakukannya dengan OpsWorks , meskipun ini seperti menggunakan rumah untuk membunuh lalat. Dimungkinkan juga untuk menggunakan Data Pipeline dengan Task Runner , tetapi ini memiliki kemampuan terbatas dalam skrip yang dapat dieksekusi, dan saya harus dapat menjalankan skrip PHP, dengan akses ke seluruh basis kode. Anda juga dapat mendedikasikan instans EC2 di luar cluster ElasticBeanstalk, tetapi Anda tidak akan mengalami fail-over lagi.

Jadi inilah yang saya temukan, yang tampaknya tidak konvensional (seperti komentar perwakilan AWS) dan dapat dianggap sebagai peretasan, tetapi berfungsi dan solid dengan fail-over. Saya memilih solusi pengkodean menggunakan SDK, yang akan saya tunjukkan dalam PHP, meskipun Anda dapat melakukan metode yang sama dalam bahasa apa pun yang Anda inginkan.

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

Jadi menelusuri ini dan bagaimana cara kerjanya ... Anda memanggil skrip dari crontab seperti yang biasa Anda lakukan pada setiap instans EC2. Setiap skrip menyertakan ini di awal (atau menyertakan satu file untuk masing-masing, saat saya menggunakannya), yang menetapkan objek ElasticBeanstalk dan mengambil daftar semua contoh. Ini hanya menggunakan server pertama dalam daftar, dan memeriksa apakah itu cocok dengan dirinya sendiri, yang jika terus berlanjut, jika tidak maka mati dan ditutup. Saya telah memeriksa dan daftar yang dikembalikan tampaknya konsisten, yang secara teknis hanya perlu konsisten selama satu menit atau lebih, karena setiap contoh menjalankan cron yang dijadwalkan. Jika memang berubah, itu tidak masalah, karena lagi itu hanya relevan untuk jendela kecil itu.

Ini tidak elegan dengan cara apa pun, tetapi sesuai dengan kebutuhan khusus kami - yang tidak meningkatkan biaya dengan layanan tambahan atau harus memiliki instans EC2 khusus, dan akan mengalami kegagalan jika terjadi kegagalan. Skrip cron kami menjalankan skrip pemeliharaan yang ditempatkan ke SQS dan setiap server dalam cluster membantu mengeksekusi. Setidaknya ini dapat memberi Anda pilihan alternatif jika sesuai dengan kebutuhan Anda.

-Davey


Saya menemukan bahwa php_uname ('n') mengembalikan nama DNS pribadi (misalnya ip-172.24.55.66), yang bukan ID instance yang Anda cari. Alih-alih menggunakan php_uname (), saya akhirnya menggunakan ini: $instanceId = file_get_contents("http://instance-data/latest/meta-data/instance-id"); Kemudian gunakan saja $ instanceId var itu untuk melakukan perbandingan.
Valorum

1
Apakah ada jaminan bahwa array Instances menyajikan urutan yang sama pada setiap panggilan Describe? Saya akan menyarankan untuk mengekstrak bidang ['Id'] dari setiap entri ke dalam array, dan mengurutkannya dalam PHP, sebelum Anda memeriksa apakah entri yang diurutkan pertama adalah instanceId Anda saat ini.
Gabriel

Berdasarkan jawaban ini saya membuat solusi ini: stackoverflow.com/questions/14077095/… - ini sangat mirip tetapi TIDAK memiliki peluang untuk eksekusi ganda.
TheStoryCoder

11

Saya berbicara dengan agen dukungan AWS dan inilah cara kami membuat ini berhasil untuk saya. Solusi 2015:

Buat file di direktori .ebextensions Anda dengan nama_file_anda.config. Di input file konfigurasi:

file:
  "/etc/cron.d/cron_example":
    mode: "000644"
    pemilik: root
    grup: root
    konten: |
      * * * * * root /usr/local/bin/cron_example.sh

  "/usr/local/bin/cron_example.sh":
    mode: "000755"
    pemilik: root
    grup: root
    konten: |
      #! / bin / bash

      /usr/local/bin/test_cron.sh || keluar
      echo "Cron berjalan pada" `date` >> /tmp/cron_example.log
      # Sekarang lakukan tugas yang seharusnya hanya berjalan pada 1 contoh ...

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

      # Temukan nama Grup Penskalaan Otomatis kami.
      ASG = ʻaws ec2 description-tags --filters "Name = resource-id, Values ​​= $ INSTANCE_ID" \
        --region $ REGION --teks keluaran | awk '/ aws: autoscaling: groupName / {print $ 5}' `

      # Temukan contoh pertama di Grup
      FIRST = ʻaw autoscaling menggambarkan-auto-scaling-groups --auto-scaling-group-names $ ASG \
        --region $ REGION --teks keluaran | awk '/ InService $ / {print $ 4}' | urutkan | kepala -1`

      # Uji apakah keduanya sama.
      ["$ FIRST" = "$ INSTANCE_ID"]

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

Solusi ini memiliki 2 kekurangan:

  1. Pada penerapan berikutnya, Pohon Kacang mengganti nama skrip cron yang ada menjadi .bak, tetapi cron akan tetap menjalankannya. Cron Anda sekarang dijalankan dua kali pada mesin yang sama.
  2. Jika lingkungan Anda meningkat, Anda mendapatkan beberapa contoh, semuanya menjalankan skrip cron Anda. Ini berarti jepretan email Anda diulang, atau arsip database Anda digandakan

Solusi:

  1. Pastikan skrip .ebextensions apa pun yang membuat cron juga menghapus file .bak pada penerapan selanjutnya.
  2. Memiliki skrip pembantu yang melakukan hal berikut: - Mendapatkan ID Instans saat ini dari Metadata - Mendapatkan nama Grup Penskalaan Otomatis saat ini dari Tag EC2 - Mendapatkan daftar Instans EC2 di Grup tersebut, diurutkan menurut abjad. - Mengambil contoh pertama dari daftar itu. - Bandingkan ID Instance dari langkah 1 dengan ID Instance pertama dari langkah 4. Skrip cron Anda kemudian dapat menggunakan skrip pembantu ini untuk menentukan apakah harus dijalankan.

Peringatan:

  • Peran IAM yang digunakan untuk instance Beanstalk membutuhkan ec2: DescribeTags dan autoscaling: izin DescribeAutoScalingGroups
  • Instans yang dipilih adalah yang ditampilkan sebagai InService oleh Auto Scaling. Ini tidak berarti mereka sepenuhnya di-boot dan siap untuk menjalankan cron Anda.

Anda tidak perlu menyetel Peran IAM jika Anda menggunakan peran pohon kacang default.


7

Jika Anda menggunakan Rails, Anda dapat menggunakan permata batang kacang yang elastis . Ini memungkinkan Anda menjalankan tugas cron pada semua instans atau hanya satu. Ia memeriksa setiap menit untuk memastikan bahwa hanya ada satu contoh "pemimpin", dan secara otomatis akan mempromosikan satu server menjadi "pemimpin" jika tidak ada. Ini diperlukan karena Elastic Beanstalk hanya memiliki konsep pemimpin selama penerapan dan dapat menutup instance apa pun kapan pun saat penskalaan.

UPDATE Saya beralih menggunakan AWS OpsWorks dan saya tidak lagi menjaga permata ini. Jika Anda membutuhkan lebih banyak fungsionalitas daripada yang tersedia di dasar-dasar Elastic Beanstalk, saya sangat menyarankan Anda untuk beralih ke OpsWorks.


Maukah Anda memberi tahu kami bagaimana Anda menyelesaikannya menggunakan OpsWorks? Apakah Anda menjalankan lapisan khusus yang melakukan tugas cron?
Tommie

Ya, saya memiliki lapisan admin / cron yang hanya berjalan di satu server. Saya menyiapkan buku masak khusus yang menampung semua tugas cron saya. AWS memiliki panduan di docs.aws.amazon.com/opsworks/latest/userguide/… .
dignoe

@dignoe jika Anda menetapkan satu server untuk menjalankan tugas cron menggunakan OpsWorks, hal yang sama menggunakan Elastic Beanstalk, saya dapat menggunakan lingkungan dengan satu server untuk menjalankan tugas cron. Bahkan dengan Load Balancer, instance maks dan min disetel ke satu, untuk selalu menyimpan instance server.
Jose Nobile

6

Anda benar-benar tidak ingin menjalankan tugas cron di Elastic Beanstalk. Karena Anda akan memiliki banyak contoh aplikasi, ini dapat menyebabkan kondisi balapan dan masalah ganjil lainnya. Saya baru - baru ini membuat blog tentang ini (tip ke-4 atau ke-5 di bawah halaman). Versi pendek: Tergantung pada aplikasi, menggunakan antrian pekerjaan seperti SQS atau solusi pihak ketiga seperti iron.io .


SQS tidak menjamin kode hanya akan dijalankan satu kali. Saya suka situs iron.io, saya akan memeriksanya.
Nathan H

Juga di posting blog Anda, Anda merekomendasikan menggunakan InnoDB di RDS. Saya menggunakan tabel di RDS untuk menyimpan tugas saya dan menggunakan fitur InnoDB "SELECT ... FOR UPDATE" untuk memastikan hanya satu server yang menjalankan tugas tersebut. Bagaimana aplikasi Anda menghubungi SQS tanpa tugas cron atau interaksi pengguna?
James Alday

1
@JamesAlday Pertanyaan SO ini cukup lama. Sejak saya menulis komentar di atas, AWS memperkenalkan cara elegan untuk menangani tugas cron di Elastic Beanstalk dengan memilih salah satu server yang berjalan sebagai master. Karena itu, sepertinya Anda menyalahgunakan cron + MySQL sebagai antrean pekerjaan. Saya perlu mengetahui banyak tentang aplikasi Anda sebelum saya dapat menawarkan rekomendasi konkret.
jamieb

Saya memiliki skrip yang berjalan melalui cron yang memeriksa tabel untuk pekerjaan yang akan dijalankan. Menggunakan transaksi mencegah beberapa server menjalankan pekerjaan yang sama. Saya telah memeriksa SQS tetapi Anda memerlukan server master yang menjalankan semua skrip alih-alih mendistribusikannya dan Anda masih perlu menulis logika untuk memastikan Anda tidak menjalankan skrip yang sama beberapa kali. Tapi saya masih bingung tentang bagaimana Anda menjalankan tugas tanpa interaksi pengguna atau cron - apa yang memicu aplikasi Anda untuk menjalankan tugas dalam antrian?
James Alday

5

2017: Jika Anda menggunakan Laravel5 +

Anda hanya perlu 2 menit untuk mengkonfigurasinya:

  • buat Worker Tier
  • instal laravel-aws-worker

    composer require dusterio/laravel-aws-worker

  • tambahkan cron.yaml ke folder root:

Tambahkan cron.yaml ke folder root aplikasi Anda (ini bisa menjadi bagian dari repo Anda atau Anda bisa menambahkan file ini tepat sebelum menerapkan ke EB - yang penting adalah file ini ada pada saat penerapan):

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

Itu dia!

Semua tugas Anda App\Console\Kernelsekarang akan dieksekusi

Instruksi dan penjelasan mendetail: https://github.com/dusterio/laravel-aws-worker

Cara menulis tugas di dalam Laravel: https://laravel.com/docs/5.4/scheduling


3

Solusi yang lebih mudah dibaca menggunakan filesalih-alih container_commands:

file:
  "/etc/cron.d/my_cron":
    mode: "000644"
    pemilik: root
    grup: root
    konten: |
      # timpa alamat email default
      MAILTO = "contoh@gmail.com"
      # jalankan perintah Symfony setiap lima menit (sebagai ec2-user)
      * / 10 * * * * ec2-user / usr / bin / php / var / app / current / app / console melakukan: sesuatu
    pengkodean: biasa
perintah:
  # hapus file cadangan yang dibuat oleh Elastic Beanstalk
  clear_cron_backup:
    perintah: rm -f /etc/cron.d/watson.bak

Perhatikan bahwa format ini berbeda dari format crontab biasa yang menetapkan pengguna untuk menjalankan perintah sebagai.


Satu masalah di sini adalah instans Elastic Beanstalk EC2 tidak memiliki layanan SMTP yang diatur secara default sehingga opsi MAILTO di sini mungkin tidak berfungsi.
Justin Finkelstein

3

Kontribusi 1 sen saya untuk 2018

Berikut adalah cara yang tepat untuk melakukannya (menggunakan django/pythondan django_crontabaplikasi):

di dalam .ebextensionsfolder buat file seperti ini 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

Ini harus menjadi container_commandsgantinyacommands



2

Contoh terbaru dari Amazon adalah yang termudah dan paling efisien (tugas berkala):

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

tempat Anda membuat tingkat pekerja terpisah untuk menjalankan salah satu tugas cron Anda. Buat file cron.yaml dan letakkan di folder root Anda. Satu masalah yang saya hadapi (setelah cron tampaknya tidak dijalankan) adalah menemukan bahwa CodePipeline saya tidak memiliki kewenangan untuk melakukan modifikasi dynamodb. Berdasarkan itu setelah menambahkan akses FullDynamoDB di bawah IAM -> peran -> saluran Anda dan menerapkan ulang (batang kacang elastis), itu bekerja dengan sempurna.



1

Jadi kami telah berjuang dengan ini selama beberapa waktu dan setelah beberapa diskusi dengan perwakilan AWS, saya akhirnya menemukan apa yang menurut saya adalah solusi terbaik.

Menggunakan tingkat pekerja dengan cron.yaml jelas merupakan perbaikan termudah. Namun, dokumentasi tidak menjelaskan dengan jelas bahwa ini akan menempatkan pekerjaan di akhir antrian SQS yang Anda gunakan untuk benar-benar menjalankan pekerjaan Anda. Jika tugas cron Anda peka waktu (sebanyak mungkin), ini tidak dapat diterima, karena akan bergantung pada ukuran antrian. Salah satu opsinya adalah menggunakan lingkungan yang benar-benar terpisah hanya untuk menjalankan tugas cron, tapi menurut saya itu berlebihan.

Beberapa opsi lain, seperti memeriksa untuk melihat apakah Anda adalah contoh pertama dalam daftar, juga tidak ideal. Bagaimana jika instance pertama saat ini sedang dalam proses penonaktifan?

Perlindungan instans juga dapat menimbulkan masalah - bagaimana jika instans itu terkunci / dibekukan?

Yang penting untuk dipahami adalah bagaimana AWS sendiri mengelola fungsionalitas cron.yaml. Ada daemon SQS yang menggunakan tabel Dynamo untuk menangani "pemilihan pemimpin". Ini sering menulis ke tabel ini, dan jika pemimpin saat ini belum menulis dalam waktu singkat, contoh berikutnya akan mengambil alih sebagai pemimpin. Beginilah cara daemon memutuskan instance mana yang akan mengaktifkan pekerjaan ke antrean SQS.

Kita dapat menggunakan kembali fungsionalitas yang ada daripada mencoba menulis ulang fungsi kita sendiri. Anda dapat melihat solusi lengkapnya di sini: https://gist.github.com/dorner/4517fe2b8c79ccb3971084ec28267f27

Itu ada di Ruby, tetapi Anda dapat dengan mudah menyesuaikannya dengan bahasa lain yang memiliki AWS SDK. Pada dasarnya, ini memeriksa pemimpin saat ini, kemudian memeriksa negara bagian untuk memastikannya dalam keadaan baik. Ini akan berulang sampai ada pemimpin saat ini dalam keadaan baik, dan jika contoh saat ini adalah pemimpin, jalankan pekerjaan.


0

Untuk mengontrol apakah Auto Scaling dapat menghentikan instance tertentu saat melakukan penskalaan, gunakan perlindungan instance. Anda dapat mengaktifkan pengaturan perlindungan instans pada grup Auto Scaling atau instans Auto Scaling individual. Saat Auto Scaling meluncurkan sebuah instans, instans tersebut mewarisi setelan perlindungan instans dari grup Penskalaan Otomatis. Anda dapat mengubah pengaturan perlindungan instans untuk grup Auto Scaling atau instans Auto Scaling kapan saja.

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


0

Saya punya solusi lain untuk ini jika file php perlu dijalankan melalui cron dan jika Anda telah menetapkan instance NAT apa pun maka Anda dapat meletakkan cronjob pada instance NAT dan menjalankan file php melalui wget.


0

berikut adalah perbaikan jika Anda ingin melakukan ini di PHP. Anda hanya perlu cronjob.config di folder .ebextensions Anda untuk membuatnya berfungsi seperti ini.

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 mendapatkan variabel lingkungan untuk file. Anda dapat men-debug output di tmp / sendemail.log seperti di atas.

Semoga ini membantu seseorang karena pasti membantu kami!


0

Berdasarkan prinsip-prinsip jawaban dari user1599237 , di mana Anda membiarkan cron job berjalan di semua contoh tetapi kemudian di awal pekerjaan menentukan apakah mereka harus diizinkan untuk berjalan, saya telah membuat solusi lain.

Alih-alih melihat instans yang berjalan (dan harus menyimpan kunci dan rahasia AWS Anda), saya menggunakan database MySQL yang sudah saya sambungkan dari semua instans.

Tidak ada kerugiannya, hanya positifnya:

  • tidak ada contoh atau biaya tambahan
  • solusi yang kokoh - tidak ada kemungkinan eksekusi ganda
  • scalable - secara otomatis bekerja saat instance Anda ditingkatkan dan diturunkan
  • failover - secara otomatis bekerja jika sebuah instance mengalami kegagalan

Alternatifnya, Anda juga dapat menggunakan sistem file yang umum dibagikan (seperti AWS EFS melalui protokol NFS) daripada database.

Solusi berikut dibuat dalam kerangka PHP Yii tetapi Anda dapat dengan mudah menyesuaikannya untuk kerangka kerja dan bahasa lain. Juga penangan pengecualian Yii::$app->systemadalah modul saya sendiri. Gantilah dengan apa pun yang Anda gunakan.

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

Ini adalah skema database yang saya gunakan:

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`)
)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.