AWS Elastic Beanstalk เรียกใช้ cronjob


89

ฉันต้องการทราบว่ามีวิธีตั้งค่า cronjob / task เพื่อดำเนินการทุกนาทีหรือไม่ ขณะนี้อินสแตนซ์ใด ๆ ของฉันควรจะรันงานนี้ได้

นี่คือสิ่งที่ฉันพยายามทำในไฟล์ config โดยไม่ประสบความสำเร็จ:

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

ฉันไม่แน่ใจจริงๆว่านี่เป็นวิธีที่ถูกต้องหรือไม่

ความคิดใด ๆ ?


1
คำสั่งถูกหรือไม่? ฉันหมายถึง ... มันอาจจะเป็น: command: echo "* / 1 * * * * root php /etc/httpd/myscript.php"> /etc/cron.d/something ไม่ว่าจะด้วยวิธีใดฉันขอแนะนำให้คุณใช้ leader_only ตั้งค่าสถานะมิฉะนั้นเครื่องจักรทั้งหมดจะเริ่มงาน cron นี้พร้อมกัน
aldrinleal

ใช่ ใช้แฟล็ก leader_only อย่างแน่นอนฉันจะลองเปลี่ยนคำสั่ง
Onema

คำตอบ:


98

นี่คือวิธีที่ฉันเพิ่มงาน cron ใน Elastic Beanstalk:

สร้างโฟลเดอร์ที่รากของแอปพลิเคชันของคุณที่เรียกว่า. ebextensions หากยังไม่มีอยู่ จากนั้นสร้างไฟล์กำหนดค่าภายในโฟลเดอร์. ebextensions ฉันจะใช้ example.config เพื่อเป็นภาพประกอบ จากนั้นเพิ่มสิ่งนี้ใน example.config

container_commands:
  01_some_cron_job:
    command: "cat .ebextensions/some_cron_job.txt > /etc/cron.d/some_cron_job && chmod 644 /etc/cron.d/some_cron_job"
    leader_only: true

นี่คือไฟล์คอนฟิกูเรชัน YAML สำหรับ Elastic Beanstalk ตรวจสอบให้แน่ใจว่าเมื่อคุณคัดลอกสิ่งนี้ลงในโปรแกรมแก้ไขข้อความของคุณว่าโปรแกรมแก้ไขข้อความของคุณใช้ช่องว่างแทนแท็บ มิฉะนั้นคุณจะได้รับข้อผิดพลาด YAML เมื่อคุณส่งไปที่ EB

สิ่งนี้คือสร้างคำสั่งชื่อ 01_some_cron_job คำสั่งจะทำงานตามลำดับตัวอักษรดังนั้น 01 จะทำให้แน่ใจว่ารันเป็นคำสั่งแรก

จากนั้นคำสั่งจะรับเนื้อหาของไฟล์ที่เรียกว่า some_cron_job.txt และเพิ่มลงในไฟล์ที่เรียกว่า some_cron_job ใน /etc/cron.d

จากนั้นคำสั่งจะเปลี่ยนสิทธิ์ในไฟล์ /etc/cron.d/some_cron_job

คีย์ leader_only ทำให้แน่ใจว่าคำสั่งรันบนอินสแตนซ์ ec2 ที่ถือว่าเป็นผู้นำเท่านั้น แทนที่จะเรียกใช้ในทุกอินสแตนซ์ ec2 ที่คุณอาจเรียกใช้

จากนั้นสร้างไฟล์ชื่อ some_cron_job.txt ภายในโฟลเดอร์. ebextensions คุณจะวางงาน cron ของคุณในไฟล์นี้

ตัวอย่างเช่น:

# The newline at the end of this file is extremely important.  Cron won't run without it.
* * * * * root /usr/bin/php some-php-script-here > /dev/null

ดังนั้นงาน cron นี้จะทำงานทุกนาทีของทุก ๆ ชั่วโมงของทุกวันในฐานะผู้ใช้รูทและทิ้งผลลัพธ์ไปที่ / dev / null / usr / bin / php คือพา ธ ไปยัง php จากนั้นแทนที่ some-php-script-here ด้วยพา ธ ไปยังไฟล์ php ของคุณ เห็นได้ชัดว่านี่เป็นการสมมติว่างาน cron ของคุณต้องเรียกใช้ไฟล์ PHP

นอกจากนี้ตรวจสอบให้แน่ใจว่าไฟล์ some_cron_job.txt มีขึ้นบรรทัดใหม่ที่ท้ายไฟล์เช่นเดียวกับความคิดเห็นที่ระบุ มิฉะนั้น cron จะไม่ทำงาน

อัปเดต: มีปัญหากับโซลูชันนี้เมื่อ Elastic Beanstalk ขยายขนาดอินสแตนซ์ของคุณ ตัวอย่างเช่นสมมติว่าคุณมีหนึ่งอินสแตนซ์ที่มีงาน cron ทำงานอยู่ คุณได้รับปริมาณการใช้งานที่เพิ่มขึ้นดังนั้น Elastic Beanstalk จึงขยายคุณได้ถึงสองอินสแตนซ์ leader_only จะทำให้แน่ใจว่าคุณมีงาน cron เพียงงานเดียวที่ทำงานระหว่างสองอินสแตนซ์ ปริมาณการใช้งานของคุณลดลงและ Elastic Beanstalk จะลดขนาดคุณเป็นหนึ่งอินสแตนซ์ แต่แทนที่จะยุติอินสแตนซ์ที่สอง Elastic Beanstalk จะยุติอินสแตนซ์แรกที่เป็นผู้นำ ตอนนี้คุณไม่มีงาน cron ทำงานเนื่องจากงานเหล่านี้ทำงานในอินสแตนซ์แรกที่ถูกยกเลิกเท่านั้น ดูความคิดเห็นด้านล่าง

อัปเดต 2: เพียงแค่ทำให้สิ่งนี้ชัดเจนจากความคิดเห็นด้านล่าง: ขณะนี้ AWS ได้ป้องกันการยกเลิกอินสแตนซ์อัตโนมัติแล้ว เพียงเปิดใช้งานบนอินสแตนซ์ผู้นำของคุณคุณก็พร้อมที่จะไป - NicolásArévalo 28 ต.ค. 59 เวลา 09:23 น


12
ฉันใช้คำแนะนำของคุณมาระยะหนึ่งแล้วและเมื่อเร็ว ๆ นี้พบปัญหาที่เปลี่ยนผู้นำส่งผลให้มีหลายอินสแตนซ์ที่เรียกใช้ cron เพื่อแก้ปัญหานั้นฉันเปลี่ยน01_some_cron_jobเป็น02_some_cron_jobและเพิ่ม01_remove_cron_jobsสิ่งต่อไปนี้: command: "rm /etc/cron.d/cron_jobs || exit 0". ด้วยวิธีนี้หลังจากการปรับใช้ทุกครั้งผู้นำเท่านั้นที่จะมีcron_jobsไฟล์ หากผู้นำเปลี่ยนไปคุณสามารถทำให้ใช้งานได้อีกครั้งและ crons จะได้รับการแก้ไขให้ทำงานอีกครั้ง
Willem Renzema

4
ฉันขอแนะนำให้ไม่พึ่งพาleader_onlyทรัพย์สิน ใช้เฉพาะในระหว่างการปรับใช้และหากคุณลดขนาดหรืออินสแตนซ์ "ผู้นำ" ของคุณล้มเหลวคุณจะต้องมีปัญหาในการอ้างอิง
arnaslu

2
อย่าทำอย่างนี้ มันไม่น่าเชื่อถือเกินไป วิธีเดียวที่ฉันทำให้สิ่งนี้ได้ผลคือการเรียกใช้ไมโครอินสแตนซ์และเรียกใช้งาน cron จากที่นั่นโดยใช้ CURL สิ่งนี้รับประกันได้ว่ามีเพียงอินสแตนซ์เดียวเท่านั้นที่รันและผู้นำที่ติดตั้ง crons จะไม่ถูกยกเลิก
Ben Sinclair

1
ฉันพยายามแก้ไขปัญหานี้ด้วยสคริปต์ทับทิมขนาดเล็กคุณสามารถค้นหาได้ที่นี่: github.com/SocialbitGmbH/AWSBeanstalkLeaderManager
Thomas Kekeisen

8
ขณะนี้ AWS มีการป้องกันการยุติอินสแตนซ์อัตโนมัติ เพียงเปิดใช้งานบนอินสแตนซ์ผู้นำของคุณคุณก็พร้อมที่จะไป
NicolásArévalo

58

นี่เป็นวิธีที่เป็นทางการในการดำเนินการตอนนี้ (2015+) โปรดลองทำก่อนซึ่งเป็นวิธีที่ง่ายที่สุดในปัจจุบันและน่าเชื่อถือที่สุดเช่นกัน

ตามเอกสารปัจจุบันหนึ่งคือสามารถที่จะเรียกใช้งานเป็นระยะ ๆในสิ่งที่เรียกว่าพวกเขาปฏิบัติงานชั้น

อ้างถึงเอกสาร:

AWS Elastic Beanstalk รองรับงานเป็นระยะสำหรับระดับสภาพแวดล้อมของผู้ปฏิบัติงานในสภาพแวดล้อมที่เรียกใช้การกำหนดค่าที่กำหนดไว้ล่วงหน้าด้วยชุดโซลูชันที่มี "v1.2.0" ในชื่อคอนเทนเนอร์ คุณต้องสร้างสภาพแวดล้อมใหม่

สิ่งที่น่าสนใจคือส่วนเกี่ยวกับcron.yaml :

ในการเรียกใช้งานเป็นระยะบันเดิลซอร์สแอปพลิเคชันของคุณต้องมีไฟล์ cron.yaml ที่ระดับรูท ไฟล์ต้องมีข้อมูลเกี่ยวกับงานประจำงวดที่คุณต้องการกำหนดเวลา ระบุข้อมูลนี้โดยใช้ไวยากรณ์ crontab มาตรฐาน

อัปเดต:เราสามารถทำงานนี้ได้ นี่คือ gotcha ที่สำคัญบางส่วนจากประสบการณ์ของเรา (แพลตฟอร์ม Node.js):

  • เมื่อใช้ไฟล์cron.yamlตรวจสอบให้แน่ใจว่าคุณมีawsebcliล่าสุดเนื่องจากเวอร์ชันเก่าจะทำงานไม่ถูกต้อง
  • นอกจากนี้ยังมีความสำคัญในการสร้างสภาพแวดล้อมใหม่ (อย่างน้อยก็ในกรณีของเรา) ไม่ใช่แค่โคลนสภาพแวดล้อมเก่า
  • หากคุณต้องการให้แน่ใจว่า CRON รับการสนับสนุนบนอินสแตนซ์ Tier EC2 คนงานของคุณเป็นมัน SSH ( eb ssh) cat /var/log/aws-sqsd/default.logและวิ่ง aws-sqsd 2.0 (2015-02-18)มันควรจะรายงานเป็น หากคุณไม่มีเวอร์ชัน 2.0 มีบางอย่างผิดพลาดเมื่อสร้างสภาพแวดล้อมของคุณและคุณต้องสร้างใหม่ตามที่ระบุไว้ข้างต้น

2
เกี่ยวกับ cron.yaml มีบล็อกโพสต์ที่ยอดเยี่ยม: การเรียกใช้งาน cron บน Amazon Web Services (AWS) Elastic Beanstalk - Medium
jwako

5
ขอบคุณสำหรับสิ่งนี้ - คำถามมือใหม่ - ฉันต้องการ cron เพื่อตรวจสอบฐานข้อมูลของเว็บแอปของฉันสองครั้งต่อชั่วโมงสำหรับกิจกรรมในปฏิทินที่กำลังจะมาถึงและส่งอีเมลแจ้งเตือนเมื่อเป็นเช่นนั้น อะไรคือการตั้งค่าที่ดีที่สุดที่นี่ฉันควรให้ URL ของ cron.yaml ชี้ไปที่เส้นทางบนเว็บแอปของฉันหรือไม่ หรือฉันควรให้แอป env แก่พนักงานของฉันเข้าถึงฐานข้อมูล สิ่งนี้มีน้อยมาก!
christian

5
@christian วิธีที่เราทำเรามีแอพเดียวกันที่ทำงานในสองสภาพแวดล้อมที่แตกต่างกัน (จึงไม่จำเป็นต้องมีการกำหนดค่าพิเศษ) - คนงานและเว็บเซิร์ฟเวอร์ทั่วไป สภาพแวดล้อมของผู้ปฏิบัติงานมีเส้นทางพิเศษบางอย่างที่เปิดใช้งานโดยการตั้งค่าตัวแปร ENV ที่แอปของเราค้นหา ด้วยวิธีนี้คุณสามารถกำหนดเส้นทางพิเศษเฉพาะสำหรับผู้ปฏิบัติงานใน cron.yaml ของคุณในขณะที่มีโค้ดเบสที่ใช้ร่วมกันอย่างหรูหรากับแอปปกติ แอปสำหรับผู้ปฏิบัติงานของคุณสามารถเข้าถึงทรัพยากรเดียวกันกับเว็บเซิร์ฟเวอร์ที่หนึ่ง: ฐานข้อมูลโมเดล ฯลฯ
xaralis

1
@JaquelinePassos v1.2.0 เป็นโซลูชันสแต็กเวอร์ชัน ควรให้คุณเลือกเวอร์ชันของสแต็กโซลูชันที่คุณต้องการสร้างเมื่อสร้างสภาพแวดล้อมใหม่ สิ่งที่ใหม่กว่า v1.2.0 ควรทำ เกี่ยวกับ URL ควรเป็น URL ที่แอปพลิเคชันของคุณรับฟังไม่ใช่เส้นทางของไฟล์ ไม่สามารถเรียกใช้คำสั่งการจัดการ Django ได้ แต่จะทำเฉพาะคำขอ HTTP เท่านั้น
xaralis

4
สิ่งหนึ่งที่ไม่ชัดเจนสำหรับฉันคือหากมีวิธีหลีกเลี่ยงการจัดสรรเครื่อง EC2 เพิ่มเติมเพื่อเรียกใช้งาน cron ผ่าน cron.yaml ตามหลักการแล้วมันจะทำงานบนเครื่องเดียวกับเครื่องที่ให้บริการคำขอ HTTP (เช่นระดับเว็บ)
Wenzel Jakob

32

เกี่ยวกับการตอบสนองของ jamieb และตามที่ alrdinleal กล่าวถึงคุณสามารถใช้คุณสมบัติ 'leader_only' เพื่อให้แน่ใจว่าอินสแตนซ์ EC2 เพียงตัวเดียวรันงาน cron

อ้างจากhttp://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html :

คุณสามารถใช้ leader_only อินสแตนซ์หนึ่งได้รับเลือกให้เป็นผู้นำในกลุ่ม Auto Scaling ถ้าค่า leader_only ถูกตั้งค่าเป็น true คำสั่งจะทำงานบนอินสแตนซ์ที่ถูกทำเครื่องหมายเป็นผู้นำเท่านั้น

ฉันพยายามที่จะบรรลุสิ่งที่คล้ายกันใน eb ของฉันดังนั้นจะอัปเดตโพสต์ของฉันหากฉันแก้ปัญหาได้

อัพเดท:

ตกลงตอนนี้ฉันมี cronjobs ที่ใช้งานได้โดยใช้การกำหนดค่า eb ต่อไปนี้:

files:
  "/tmp/cronjob" :
    mode: "000777"
    owner: ec2-user
    group: ec2-user
    content: |
      # clear expired baskets
      */10 * * * * /usr/bin/wget -o /dev/null http://blah.elasticbeanstalk.com/basket/purge > $HOME/basket_purge.log 2>&1
      # clean up files created by above cronjob
      30 23 * * * rm $HOME/purge*
    encoding: plain 
container_commands:
  purge_basket: 
    command: crontab /tmp/cronjob
    leader_only: true
commands:
  delete_cronjob_file: 
    command: rm /tmp/cronjob

โดยพื้นฐานแล้วฉันสร้างไฟล์ temp ด้วย cronjobs จากนั้นตั้งค่า crontab ให้อ่านจากไฟล์ temp จากนั้นจึงลบไฟล์ temp ในภายหลัง หวังว่านี่จะช่วยได้


3
คุณจะแน่ใจได้อย่างไรว่าอินสแตนซ์ที่เรียกใช้ crontab นี้จะไม่ถูกยกเลิกโดยการปรับขนาดอัตโนมัติ โดยค่าเริ่มต้นจะยกเลิกอินสแตนซ์ที่เก่าที่สุด
Sebastien

1
นั่นเป็นปัญหาที่ฉันยังไม่สามารถแก้ไขได้ มันทำให้ฉันมีข้อบกพร่องในฟังก์ชันการทำงานของ amazon ที่คำสั่ง leader_only ไม่ได้ใช้กับผู้นำคนใหม่เมื่อ EB ปัจจุบันสิ้นสุดลง หากคุณคิดอะไรขึ้นมาโปรดแชร์!
beterthanlife

7
ดังนั้นฉัน (ในที่สุด) ก็ค้นพบวิธีป้องกันไม่ให้ผู้นำถูกยกเลิกโดยการปรับขนาดอัตโนมัติ - นโยบายการยกเลิกการปรับขนาดอัตโนมัติที่กำหนดเอง ดู docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/…
beterthanlife

1
@Nate ตอนนี้คุณคงคิดออกแล้ว แต่จากการอ่านคำสั่งของฉันว่าคำสั่งเหล่านี้ทำงานอยู่ "คำสั่ง" ทำงานก่อน "container_commands" ดังนั้นคุณจะสร้างไฟล์จากนั้นลบออกจากนั้นลองเรียกใช้ crontab .
ชัดเจน

1
@ เซบาสเตียนเพื่อรักษาเจตนารมณ์ที่เก่าแก่ที่สุดนี่คือสิ่งที่ฉันทำ: 1 - เปลี่ยนการป้องกันการสิ้นสุดของเจตนาเป็นเปิดใช้งาน 2 - ไปที่ Auto Scale Group และค้นหา EBS Environment ID ของคุณคลิกแก้ไขและเปลี่ยนนโยบายการยุติเป็น "ใหม่ล่าสุดInstance"
Ronaldo Bahia

12

ดังที่ได้กล่าวมาแล้วข้อบกพร่องพื้นฐานในการสร้างคอนฟิกูเรชัน crontab คือเกิดขึ้นที่การปรับใช้เท่านั้น เนื่องจากคลัสเตอร์ได้รับการปรับขนาดโดยอัตโนมัติจากนั้นจึงกลับลงมาจึงเป็นที่นิยมให้ปิดเซิร์ฟเวอร์แรกด้วย นอกจากนี้จะไม่มีความล้มเหลวซึ่งสำหรับฉันเป็นเรื่องสำคัญ

ฉันได้ทำการค้นคว้าข้อมูลบางอย่างจากนั้นได้พูดคุยกับผู้เชี่ยวชาญบัญชี AWS ของเราเพื่อตีกลับแนวคิดและแก้ไขปัญหาที่ฉันคิดขึ้นมา คุณสามารถทำสิ่งนี้ได้ด้วยOpsWorksแม้ว่ามันจะเหมือนกับการใช้บ้านเพื่อฆ่าแมลงวันก็ตาม นอกจากนี้ยังสามารถใช้Data Pipeline กับ Task Runnerได้ แต่สิ่งนี้มีความสามารถ จำกัด ในสคริปต์ที่สามารถเรียกใช้งานได้และฉันต้องสามารถเรียกใช้สคริปต์ PHP ได้โดยสามารถเข้าถึงฐานรหัสทั้งหมดได้ คุณยังสามารถกำหนดอินสแตนซ์ EC2 นอกคลัสเตอร์ ElasticBeanstalk ได้ แต่คุณจะไม่มีความล้มเหลวอีกเลย

นี่คือสิ่งที่ฉันคิดขึ้นมาซึ่งเห็นได้ชัดว่าไม่เป็นทางการ (ตามที่ตัวแทนของ AWS ให้ความเห็น) และอาจถือได้ว่าเป็นการแฮ็ก แต่ใช้งานได้และมั่นคงเมื่อล้มเหลว ฉันเลือกโซลูชันการเข้ารหัสโดยใช้ SDK ซึ่งฉันจะแสดงเป็น PHP แม้ว่าคุณจะใช้วิธีการเดียวกันในภาษาใดก็ได้ที่คุณต้องการ

// contains the values for variables used (key, secret, env)
require_once('cron_config.inc'); 

// Load the AWS PHP SDK to connection to ElasticBeanstalk
use Aws\ElasticBeanstalk\ElasticBeanstalkClient;

$client = ElasticBeanstalkClient::factory(array(
    'key' => AWS_KEY,
    'secret' => AWS_SECRET,
    'profile' => 'your_profile',
    'region'  => 'us-east-1'
));

$result = $client->describeEnvironmentResources(array(
    'EnvironmentName' => AWS_ENV
));

if (php_uname('n') != $result['EnvironmentResources']['Instances'][0]['Id']) {
    die("Not the primary EC2 instance\n");
}

ดังนั้นการเดินผ่านสิ่งนี้และวิธีการทำงาน ... คุณเรียกสคริปต์จาก crontab ตามปกติในทุกอินสแตนซ์ EC2 แต่ละสคริปต์รวมสิ่งนี้ไว้ที่จุดเริ่มต้น (หรือรวมไฟล์เดียวสำหรับแต่ละไฟล์ตามที่ฉันใช้) ซึ่งสร้างอ็อบเจ็กต์ ElasticBeanstalk และดึงรายการอินสแตนซ์ทั้งหมด ใช้เฉพาะเซิร์ฟเวอร์แรกในรายการและตรวจสอบว่าตรงกับตัวเองหรือไม่ซึ่งหากดำเนินการต่อมิฉะนั้นจะตายและปิดตัวลง ฉันตรวจสอบแล้วและรายการที่ส่งคืนดูเหมือนจะสอดคล้องกันซึ่งในทางเทคนิคแล้วจะต้องมีความสอดคล้องกันประมาณหนึ่งนาทีเท่านั้นเนื่องจากแต่ละอินสแตนซ์จะเรียกใช้ cron ที่กำหนด หากมีการเปลี่ยนแปลงก็ไม่สำคัญเพราะอีกครั้งจะเกี่ยวข้องกับหน้าต่างเล็ก ๆ นั้นเท่านั้น

สิ่งนี้ไม่ได้หรูหราด้วยวิธีการใด ๆ แต่เหมาะกับความต้องการเฉพาะของเราซึ่งไม่ได้เป็นการเพิ่มต้นทุนด้วยบริการเพิ่มเติมหรือต้องมีอินสแตนซ์ EC2 เฉพาะและจะล้มเหลวในกรณีที่เกิดความล้มเหลว สคริปต์ cron ของเราเรียกใช้สคริปต์การบำรุงรักษาซึ่งวางไว้ใน SQS และแต่ละเซิร์ฟเวอร์ในคลัสเตอร์จะช่วยดำเนินการ อย่างน้อยสิ่งนี้อาจทำให้คุณมีทางเลือกอื่นหากตรงกับความต้องการของคุณ

- ดาวี่


ฉันพบว่า php_uname ('n') ส่งคืนชื่อ DNS ส่วนตัว (เช่น ip-172.24.55.66) ซึ่งไม่ใช่รหัสอินสแตนซ์ที่คุณกำลังมองหา แทนที่จะใช้ php_uname () ฉันลงเอยด้วยการใช้สิ่งนี้: $instanceId = file_get_contents("http://instance-data/latest/meta-data/instance-id"); จากนั้นใช้ $ instanceId var เพื่อทำการเปรียบเทียบ
Valorum

1
มีการรับประกันหรือไม่ว่าอาร์เรย์อินสแตนซ์จะแสดงลำดับเดียวกันในการเรียก Describe แต่ละครั้ง ฉันขอแนะนำให้แยกฟิลด์ ['Id'] ของแต่ละรายการลงในอาร์เรย์และจัดเรียงเป็น PHP ก่อนที่คุณจะตรวจสอบว่ารายการที่เรียงลำดับแรกเป็น instanceId ปัจจุบันของคุณหรือไม่
Gabriel

จากคำตอบนี้ฉันได้ทำโซลูชันนี้: stackoverflow.com/questions/14077095/… - มันคล้ายกันมาก แต่ไม่มีโอกาสที่จะดำเนินการซ้ำซ้อน
TheStoryCoder

11

ฉันได้พูดคุยกับตัวแทนฝ่ายสนับสนุนของ AWS และนี่คือวิธีที่เราทำให้สิ่งนี้ได้ผลสำหรับฉัน วิธีแก้ปัญหาปี 2558:

สร้างไฟล์ในไดเร็กทอรี. ebextensions ของคุณด้วย your_file_name.config ในอินพุตไฟล์กำหนดค่า:

ไฟล์:
  "/etc/cron.d/cron_example":
    โหมด: "000644"
    เจ้าของ: root
    กลุ่ม: ราก
    เนื้อหา: |
      * * * * * รูท /usr/local/bin/cron_example.sh

  "/usr/local/bin/cron_example.sh":
    โหมด: "000755"
    เจ้าของ: root
    กลุ่ม: ราก
    เนื้อหา: |
      #! / bin / ทุบตี

      /usr/local/bin/test_cron.sh || ทางออก
      echo "Cron ทำงานเมื่อ" วันที่ ">> /tmp/cron_example.log
      # ตอนนี้ทำงานที่ควรทำงานใน 1 อินสแตนซ์ ...

  "/usr/local/bin/test_cron.sh":
    โหมด: "000755"
    เจ้าของ: root
    กลุ่ม: ราก
    เนื้อหา: |
      #! / bin / ทุบตี

      METADATA = / opt / aws / bin / ec2-metadata
      INSTANCE_ID = `$ METADATA -i | awk "{พิมพ์ $ 2}" "
      ภูมิภาค = `$ METADATA -z | awk '{print substr ($ 2, 0, length ($ 2) -1)} "

      # ค้นหาชื่อกลุ่ม Auto Scaling ของเรา
      ASG = `aws ec2 อธิบายแท็ก - ตัวกรอง" Name = resource-id, Values ​​= $ INSTANCE_ID "\
        - ภูมิภาค $ REGION - ข้อความเอาต์พุต | awk '/ aws: การปรับขนาดอัตโนมัติ: groupName / {print $ 5}' '

      # ค้นหาอินสแตนซ์แรกในกลุ่ม
      FIRST = `aws การปรับขนาดอัตโนมัติอธิบาย-auto-scaling-groups --auto-scaling-group-names $ ASG \
        - ภูมิภาค $ REGION - ข้อความเอาต์พุต | awk '/ InService $ / {print $ 4}' | เรียงลำดับ | หัว -1`

      # ทดสอบว่าเหมือนกันหรือไม่
      ["$ FIRST" = "$ INSTANCE_ID"]

คำสั่ง:
  rm_old_cron:
    คำสั่ง: "rm * .bak"
    cwd: "/etc/cron.d"
    IgnoreErrors: จริง

โซลูชันนี้มีข้อเสีย 2 ประการ:

  1. ในการปรับใช้ครั้งต่อ ๆ ไป Beanstalk จะเปลี่ยนชื่อสคริปต์ cron ที่มีอยู่เป็น. bak แต่ cron จะยังคงรันอยู่ ตอนนี้ Cron ของคุณทำงานสองครั้งบนเครื่องเดียวกัน
  2. หากสภาพแวดล้อมของคุณขยายใหญ่ขึ้นคุณจะได้รับหลายอินสแตนซ์ทั้งหมดที่รันสคริปต์ cron ของคุณ ซึ่งหมายความว่าภาพอีเมลของคุณซ้ำหรือที่เก็บฐานข้อมูลของคุณซ้ำ

วิธีแก้ปัญหา:

  1. ตรวจสอบให้แน่ใจว่าสคริปต์. ebextensions ใด ๆ ที่สร้าง cron ยังลบไฟล์. bak ในการปรับใช้ในภายหลัง
  2. มีสคริปต์ตัวช่วยซึ่งทำสิ่งต่อไปนี้: - รับรหัสอินสแตนซ์ปัจจุบันจากข้อมูลเมตา - รับชื่อกลุ่มการปรับขนาดอัตโนมัติปัจจุบันจากแท็ก EC2 - รับรายการอินสแตนซ์ EC2 ในกลุ่มนั้นโดยเรียงตามตัวอักษร - นำตัวอย่างแรกจากรายการนั้น - เปรียบเทียบรหัสอินสแตนซ์จากขั้นตอนที่ 1 กับรหัสอินสแตนซ์แรกจากขั้นตอนที่ 4 สคริปต์ cron ของคุณสามารถใช้สคริปต์ตัวช่วยนี้เพื่อพิจารณาว่าควรดำเนินการหรือไม่

ข้อแม้:

  • บทบาท IAM ที่ใช้สำหรับอินสแตนซ์ Beanstalk ต้องการ ec2: DescribeTags และการปรับขนาดอัตโนมัติ: DescribeAutoScalingGroups สิทธิ์
  • อินสแตนซ์ที่เลือกจะแสดงเป็น InService โดย Auto Scaling นี่ไม่ได้หมายความว่าพวกมันจะถูกบูตอย่างเต็มที่และพร้อมที่จะเรียกใช้ cron ของคุณ

คุณไม่จำเป็นต้องตั้งค่าบทบาท IAM หากคุณใช้บทบาทต้นถั่วดีฟอลต์


7

หากคุณกำลังใช้ Rails, คุณสามารถใช้อัญมณีเมื่อใดก็ตามที่-elasticbeanstalk ช่วยให้คุณสามารถเรียกใช้งาน cron ในทุกอินสแตนซ์หรือเพียงงานเดียว โดยจะตรวจสอบทุกนาทีเพื่อให้แน่ใจว่ามีอินสแตนซ์ "ผู้นำ" เพียงรายการเดียวและจะเลื่อนระดับเซิร์ฟเวอร์หนึ่งเซิร์ฟเวอร์เป็น "ผู้นำ" โดยอัตโนมัติหากไม่มี สิ่งนี้จำเป็นเนื่องจาก Elastic Beanstalk มีเพียงแนวคิดของผู้นำในระหว่างการปรับใช้และอาจปิดอินสแตนซ์เมื่อใดก็ได้ในขณะที่ปรับขนาด

อัปเดต ฉันเปลี่ยนไปใช้ AWS OpsWorks และฉันไม่ได้ดูแลอัญมณีนี้อีกต่อไป หากคุณต้องการฟังก์ชันมากกว่าที่มีอยู่ในพื้นฐานของ Elastic Beanstalk ฉันขอแนะนำให้เปลี่ยนไปใช้ OpsWorks


คุณช่วยบอกเราได้ไหมว่าคุณแก้ไขมันโดยใช้ OpsWorks ได้อย่างไร คุณกำลังเรียกใช้เลเยอร์แบบกำหนดเองที่ cron-job หรือไม่?
Tommie

ใช่ฉันมีเลเยอร์ admin / cron ที่ทำงานบนเซิร์ฟเวอร์เดียวเท่านั้น ฉันตั้งค่าตำราอาหารแบบกำหนดเองที่เก็บงาน cron ทั้งหมดของฉัน AWS มีคู่มือที่docs.aws.amazon.com/opsworks/latest/userguide/...
dignoe

@dignoe ถ้าคุณกำหนดเซิร์ฟเวอร์หนึ่งเครื่องสำหรับรันงาน cron โดยใช้ OpsWorks เช่นเดียวกับการใช้ Elastic Beanstalk ฉันสามารถใช้สภาพแวดล้อมกับเซิร์ฟเวอร์หนึ่งเพื่อเรียกใช้งาน cron แม้จะมีการตั้งค่าอินสแตนซ์โหลดบาลานเซอร์สูงสุดและต่ำสุดเป็นหนึ่งอินสแตนซ์เพื่อประหยัดอินสแตนซ์เซิร์ฟเวอร์เสมอเป็นอย่างน้อย
Jose Nobile

6

คุณไม่ต้องการทำงาน cron บน Elastic Beanstalk จริงๆ เนื่องจากคุณจะมีแอปพลิเคชันหลายอินสแตนซ์จึงอาจทำให้เกิดเงื่อนไขการแข่งขันและปัญหาแปลก ๆ อื่น ๆ ฉันเพิ่งเขียนบล็อกเกี่ยวกับเรื่องนี้ (เคล็ดลับที่ 4 หรือ 5 ในหน้านี้) รุ่นสั้น: ขึ้นอยู่กับโปรแกรมที่ใช้งานเช่นคิว SQS หรือวิธีการแก้ปัญหาของบุคคลที่สามเช่นiron.io


SQS ไม่รับประกันว่าโค้ดจะทำงานเพียงครั้งเดียว ฉันชอบไซต์ iron.io ฉันจะลองดู
Nathan H

นอกจากนี้ในบล็อกโพสต์ของคุณแนะนำให้ใช้ InnoDB บน ​​RDS ฉันใช้ตารางบน RDS เพื่อจัดเก็บงานของฉันและใช้คุณลักษณะ InnoDB "SELECT ... FOR UPDATE" เพื่อให้แน่ใจว่ามีเซิร์ฟเวอร์เพียงเครื่องเดียวเท่านั้นที่รันงานเหล่านั้น แอปของคุณติดต่อ SQS โดยไม่มีงาน cron หรือการโต้ตอบกับผู้ใช้อย่างไร
James Alday

1
@JamesAlday คำถามนี้ค่อนข้างเก่า ตั้งแต่ฉันเขียนความคิดเห็นข้างต้น AWS จึงนำเสนอวิธีที่ยอดเยี่ยมในการจัดการงาน cron บน Elastic Beanstalk โดยการเลือกหนึ่งในเซิร์ฟเวอร์ที่ทำงานอยู่เป็นหลัก ต้องบอกว่าดูเหมือนว่าคุณใช้ cron + MySQL ในทางที่ผิดเป็นคิวงาน ฉันจำเป็นต้องรู้มากเกี่ยวกับแอปของคุณก่อนที่จะสามารถเสนอคำแนะนำที่เป็นรูปธรรมได้
jamieb

ฉันมีสคริปต์ที่ทำงานผ่าน cron ซึ่งตรวจสอบตารางสำหรับงานที่จะรัน การใช้ธุรกรรมจะป้องกันไม่ให้เซิร์ฟเวอร์หลายเครื่องรันงานเดียวกัน ฉันได้ตรวจสอบ SQS แล้ว แต่คุณต้องการเซิร์ฟเวอร์หลักที่รันสคริปต์ทั้งหมดแทนที่จะแจกจ่ายและคุณยังต้องเขียนตรรกะเพื่อให้แน่ใจว่าคุณจะไม่เรียกใช้สคริปต์เดียวกันหลายครั้ง แต่ฉันยังสับสนเกี่ยวกับวิธีที่คุณทำให้งานทำงานโดยไม่มีการโต้ตอบกับผู้ใช้หรือ cron อะไรทำให้แอปของคุณทำงานในคิว
James Alday

5

2017: หากคุณใช้ Laravel5 +

คุณต้องใช้เวลา 2 นาทีในการกำหนดค่า:

  • สร้าง Worker Tier
  • ติดตั้ง laravel-aws-worker

    composer require dusterio/laravel-aws-worker

  • เพิ่ม cron.yaml ในโฟลเดอร์รูท:

เพิ่ม cron.yaml ลงในโฟลเดอร์รูทของแอปพลิเคชันของคุณ (อาจเป็นส่วนหนึ่งของ repo ของคุณหรือคุณสามารถเพิ่มไฟล์นี้ได้ก่อนที่จะปรับใช้กับ EB - สิ่งสำคัญคือไฟล์นี้จะปรากฏในเวลาที่ปรับใช้):

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

แค่นั้นแหละ!

งานทั้งหมดของคุณใน App\Console\Kernelตอนนี้จะถูกดำเนินการ

คำแนะนำและคำอธิบายโดยละเอียด: https://github.com/dusterio/laravel-aws-worker

วิธีเขียนงานภายใน Laravel: https://laravel.com/docs/5.4/scheduling


3

โซลูชันที่อ่านได้มากขึ้นโดยใช้filesแทนcontainer_commands :

ไฟล์:
  "/etc/cron.d/my_cron":
    โหมด: "000644"
    เจ้าของ: root
    กลุ่ม: ราก
    เนื้อหา: |
      # แทนที่ที่อยู่อีเมลเริ่มต้น
      MAILTO = "example@gmail.com"
      # เรียกใช้คำสั่ง Symfony ทุก ๆ ห้านาที (ในฐานะผู้ใช้ ec2)
      * / 10 * * * * ec2-user / usr / bin / php / var / app / current / app / console do: บางอย่าง
    การเข้ารหัส: ธรรมดา
คำสั่ง:
  # ลบไฟล์สำรองที่สร้างโดย Elastic Beanstalk
  clear_cron_backup:
    คำสั่ง: rm -f /etc/cron.d/watson.bak

โปรดสังเกตว่ารูปแบบจะแตกต่างจากรูปแบบ crontab ทั่วไปตรงที่ระบุให้ผู้ใช้รันคำสั่งเป็น


ปัญหาหนึ่งที่นี่คืออินสแตนซ์ Elastic Beanstalk EC2 ไม่ได้ตั้งค่าบริการ SMTP ตามค่าเริ่มต้นดังนั้นตัวเลือก MAILTO ที่นี่อาจไม่ทำงาน
Justin Finkelstein

3

ผลงาน 1 เปอร์เซ็นต์ของฉันสำหรับปี 2018

นี่คือวิธีที่ถูกต้อง (โดยใช้django/pythonและdjango_crontabแอพ):

ภายใน.ebextensionsโฟลเดอร์สร้างไฟล์98_cron.configดังนี้:

files:
  "/tmp/98_create_cron.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/bin/sh
      cd /
      sudo /opt/python/run/venv/bin/python /opt/python/current/app/manage.py crontab remove > /home/ec2-user/remove11.txt
      sudo /opt/python/run/venv/bin/python /opt/python/current/app/manage.py crontab add > /home/ec2-user/add11.txt 

container_commands:
    98crontab:
        command: "mv /tmp/98_create_cron.sh /opt/elasticbeanstalk/hooks/appdeploy/post && chmod 774 /opt/elasticbeanstalk/hooks/appdeploy/post/98_create_cron.sh"
        leader_only: true

จะต้องเป็นcontainer_commandsแทนcommands


2

มีคนสงสัยเกี่ยวกับปัญหาการปรับขนาดอัตโนมัติของ leader_only เมื่อมีผู้นำคนใหม่เกิดขึ้น ฉันคิดไม่ออกว่าจะตอบกลับความคิดเห็นของพวกเขาอย่างไร แต่ดูลิงก์นี้: http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling- สิ่งแวดล้อม /


2

ดังนั้นเราจึงพยายามดิ้นรนกับเรื่องนี้มาระยะหนึ่งแล้วและหลังจากการพูดคุยกับตัวแทน AWS ในที่สุดฉันก็ได้พบกับสิ่งที่ฉันคิดว่าเป็นทางออกที่ดีที่สุด

การใช้ระดับคนงานกับ cron.yaml เป็นการแก้ไขที่ง่ายที่สุด อย่างไรก็ตามสิ่งที่เอกสารประกอบไม่ชัดเจนก็คือสิ่งนี้จะทำให้งานสิ้นสุดลงของคิว SQS ที่คุณใช้เพื่อเรียกใช้งานของคุณจริง หากงาน cron ของคุณมีความละเอียดอ่อนด้านเวลา (มากที่สุด) สิ่งนี้ไม่สามารถยอมรับได้เนื่องจากจะขึ้นอยู่กับขนาดของคิว ทางเลือกหนึ่งคือใช้สภาพแวดล้อมที่แยกจากกันโดยสิ้นเชิงเพียงเพื่อเรียกใช้งาน cron แต่ฉันคิดว่ามันเกินความจำเป็น

ตัวเลือกอื่น ๆ บางอย่างเช่นการตรวจสอบว่าคุณเป็นอินสแตนซ์แรกในรายการหรือไม่ก็ไม่เหมาะเช่นกัน จะเกิดอะไรขึ้นถ้าอินสแตนซ์แรกในปัจจุบันอยู่ระหว่างการปิดระบบ?

การป้องกันอินสแตนซ์อาจมาพร้อมกับปัญหา - จะเกิดอะไรขึ้นหากอินสแตนซ์นั้นถูกล็อก / หยุดทำงาน

สิ่งสำคัญที่ต้องทำความเข้าใจคือ AWS จัดการฟังก์ชัน cron.yaml อย่างไร มี SQS daemon ซึ่งใช้ตาราง Dynamo เพื่อจัดการ "การเลือกตั้งผู้นำ" มันเขียนลงในตารางนี้บ่อยครั้งและหากผู้นำคนปัจจุบันไม่ได้เขียนในช่วงสั้น ๆ อินสแตนซ์ถัดไปจะเข้ามาเป็นผู้นำ นี่คือวิธีที่ daemon ตัดสินใจว่าอินสแตนซ์ใดที่จะเริ่มงานในคิว SQS

เราสามารถนำฟังก์ชันการทำงานที่มีอยู่กลับมาใช้ใหม่ได้แทนที่จะพยายามเขียนขึ้นใหม่ คุณสามารถดูโซลูชันทั้งหมดได้ที่นี่: https://gist.github.com/dorner/4517fe2b8c79ccb3971084ec28267f27

นั่นอยู่ใน Ruby แต่คุณสามารถปรับให้เป็นภาษาอื่นที่มี AWS SDK ได้อย่างง่ายดาย โดยพื้นฐานแล้วจะตรวจสอบผู้นำปัจจุบันจากนั้นตรวจสอบสถานะเพื่อให้แน่ใจว่าอยู่ในสถานะที่ดี มันจะวนซ้ำจนกว่าจะมีผู้นำปัจจุบันอยู่ในสถานะที่ดีและถ้าอินสแตนซ์ปัจจุบันเป็นผู้นำให้ดำเนินการงาน


2

ตัวอย่างล่าสุดจาก Amazon เป็นงานที่ง่ายและมีประสิทธิภาพที่สุด (งานตามระยะเวลา):

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

ที่คุณสร้างระดับผู้ปฏิบัติงานแยกกันเพื่อดำเนินการงาน cron ของคุณ สร้างไฟล์ cron.yaml และวางไว้ในโฟลเดอร์รูทของคุณ ปัญหาหนึ่งที่ฉันมี (หลังจาก cron ดูเหมือนจะไม่ดำเนินการ) คือพบว่า CodePipeline ของฉันไม่มีสิทธิ์ในการแก้ไข dynamodb จากนั้นหลังจากเพิ่มการเข้าถึง FullDynamoDB ภายใต้ IAM -> บทบาท -> ท่อส่งของคุณและการปรับใช้ใหม่ (ต้นถั่วยืดหยุ่น) มันทำงานได้อย่างสมบูรณ์


1

นี่คือคำอธิบายทั้งหมดของโซลูชัน:

http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling-environment/


อย่าใช้ leader_only เพื่อสร้างอินสแตนซ์เฉพาะภายใน ASG ASG ไม่รับประกันว่าคุณจะสามารถรักษาอินสแตนซ์เฉพาะนั้นไว้ได้ แต่จะรับประกันจำนวนอินสแตนซ์ที่ให้บริการเท่านั้น อินสแตนซ์ผู้นำอาจยุติเนื่องจากการตรวจสอบประสิทธิภาพการทำงานของ EB ล้มเหลว
mst

0

หากต้องการควบคุมว่า Auto Scaling สามารถยุติอินสแตนซ์เฉพาะเมื่อทำการขยายให้ใช้การป้องกันอินสแตนซ์ คุณสามารถเปิดใช้งานการตั้งค่าการป้องกันอินสแตนซ์ในกลุ่ม Auto Scaling หรืออินสแตนซ์ Auto Scaling แต่ละรายการ เมื่อ Auto Scaling เปิดตัวอินสแตนซ์อินสแตนซ์จะสืบทอดการตั้งค่าการป้องกันอินสแตนซ์ของกลุ่ม Auto Scaling คุณสามารถเปลี่ยนการตั้งค่าการป้องกันอินสแตนซ์สำหรับกลุ่ม Auto Scaling หรืออินสแตนซ์ Auto Scaling ได้ตลอดเวลา

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


0

ฉันมีวิธีแก้ปัญหาอื่นหากไฟล์ php ต้องทำงานผ่าน cron และหากคุณตั้งค่าอินสแตนซ์ NAT ใด ๆ คุณสามารถใส่ cronjob ในอินสแตนซ์ NAT และเรียกใช้ไฟล์ php ผ่าน wget


0

นี่คือการแก้ไขในกรณีที่คุณต้องการทำใน PHP คุณต้องใช้ cronjob.config ในโฟลเดอร์. ebextensions ของคุณเพื่อให้มันทำงานเช่นนี้

files:
  "/etc/cron.d/my_cron":
    mode: "000644"
    owner: root
    group: root
    content: |
        empty stuff
    encoding: plain
commands:
  01_clear_cron_backup:
    command: "rm -f /etc/cron.d/*.bak"
  02_remove_content:
    command: "sudo sed -i 's/empty stuff//g' /etc/cron.d/my_cron"
container_commands:
  adding_cron:
    command: "echo '* * * * * ec2-user . /opt/elasticbeanstalk/support/envvars && /usr/bin/php /var/app/current/index.php cron sendemail > /tmp/sendemail.log 2>&1' > /etc/cron.d/my_cron"
    leader_only: true

envvars รับตัวแปรสภาพแวดล้อมสำหรับไฟล์ คุณสามารถดีบักเอาต์พุตบน tmp / sendemail.log ตามด้านบน

หวังว่านี่จะช่วยใครสักคนเพราะมันช่วยเราได้อย่างแน่นอน!


0

ตามหลักการของคำตอบจากuser1599237ซึ่งคุณปล่อยให้งาน cron ทำงานบนอินสแตนซ์ทั้งหมด แต่ในช่วงเริ่มต้นของงานจะพิจารณาว่าควรได้รับอนุญาตให้ทำงานหรือไม่ฉันได้ทำวิธีแก้ปัญหาอื่นแล้ว

แทนที่จะดูอินสแตนซ์ที่กำลังทำงานอยู่ (และต้องจัดเก็บคีย์และความลับ AWS ของคุณ) ฉันใช้ฐานข้อมูล MySQL ที่ฉันเชื่อมต่ออยู่แล้วจากอินสแตนซ์ทั้งหมด

ไม่มีข้อเสียมี แต่ผลบวก:

  • ไม่มีอินสแตนซ์หรือค่าใช้จ่ายเพิ่มเติม
  • สารละลายแข็งของหิน - ไม่มีโอกาสในการดำเนินการสองครั้ง
  • ปรับขนาดได้ - ทำงานโดยอัตโนมัติเมื่ออินสแตนซ์ของคุณถูกปรับขนาดขึ้นและลง
  • เฟลโอเวอร์ - ทำงานโดยอัตโนมัติในกรณีที่อินสแตนซ์เกิดความล้มเหลว

หรือคุณยังสามารถใช้ระบบไฟล์ที่ใช้ร่วมกันทั่วไป (เช่นAWS EFSผ่านโปรโตคอล NFS) แทนฐานข้อมูล

โซลูชันต่อไปนี้สร้างขึ้นภายในเฟรมเวิร์ก PHP Yiiแต่คุณสามารถปรับให้เข้ากับเฟรมเวิร์กและภาษาอื่นได้อย่างง่ายดาย นอกจากนี้ตัวจัดการข้อยกเว้นYii::$app->systemยังเป็นโมดูลของฉันเอง แทนที่ด้วยสิ่งที่คุณกำลังใช้

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

นี่คือสคีมาฐานข้อมูลที่ฉันใช้:

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`)
)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.