AWS Elastic Beanstalk, chạy một cronjob


89

Tôi muốn biết liệu có cách nào để thiết lập cronjob / task để thực thi mỗi phút không. Hiện tại bất kỳ phiên bản nào của tôi sẽ có thể chạy tác vụ này.

Đây là những gì tôi đã cố gắng thực hiện trong các tệp cấu hình mà không thành công:

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

Tôi thực sự không chắc liệu đây có phải là cách chính xác để làm điều đó hay không

Bất kỳ ý tưởng?


1
Lệnh có đúng không? Ý tôi là ... nó có thể là: command: echo "* / 1 * * * * root php /etc/httpd/myscript.php"> /etc/cron.d/something Dù bằng cách nào, tôi khuyên bạn nên sử dụng cờ leader_only, nếu không tất cả các máy sẽ cháy lên công việc định kỳ này cùng một lúc
aldrinleal

Đúng! chắc chắn bằng cách sử dụng cờ leader_only, tôi sẽ thử thay đổi lệnh.
Onema

Câu trả lời:


96

Đây là cách tôi thêm một công việc cron vào Elastic Beanstalk:

Tạo một thư mục ở gốc ứng dụng của bạn có tên .ebextensions nếu nó chưa tồn tại. Sau đó, tạo một tệp cấu hình bên trong thư mục .ebextensions. Tôi sẽ sử dụng example.config cho mục đích minh họa. Sau đó thêm nó vào 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

Đây là tệp cấu hình YAML cho Elastic Beanstalk. Đảm bảo khi bạn sao chép phần này vào trình soạn thảo văn bản, trình soạn thảo văn bản của bạn sử dụng dấu cách thay vì tab. Nếu không, bạn sẽ gặp lỗi YAML khi đẩy nó lên EB.

Vì vậy, những gì điều này làm là tạo một lệnh có tên 01_some_cron_job. Các lệnh được chạy theo thứ tự bảng chữ cái nên số 01 đảm bảo rằng nó chạy như lệnh đầu tiên.

Sau đó, lệnh này sẽ lấy nội dung của một tệp có tên some_cron_job.txt và thêm nó vào tệp có tên some_cron_job trong /etc/cron.d.

Sau đó, lệnh thay đổi các quyền trên tệp /etc/cron.d/some_cron_job.

Khóa leader_only đảm bảo lệnh chỉ được chạy trên cá thể ec2 được coi là người dẫn đầu. Thay vì chạy trên mọi phiên bản ec2 mà bạn có thể đang chạy.

Sau đó, tạo một tệp có tên some_cron_job.txt bên trong thư mục .ebextensions. Bạn sẽ đặt công việc cron của bạn trong tệp này.

Ví dụ:

# 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

Vì vậy, công việc cron này sẽ chạy mỗi phút mỗi giờ mỗi ngày với tư cách là người dùng root và loại bỏ đầu ra thành / dev / null. / usr / bin / php là đường dẫn đến php. Sau đó, thay thế some-php-script-here bằng đường dẫn đến tệp php của bạn. Điều này rõ ràng là giả sử công việc cron của bạn cần chạy một tệp PHP.

Ngoài ra, hãy đảm bảo rằng tệp some_cron_job.txt có dòng mới ở cuối tệp giống như nhận xét cho biết. Nếu không thì cron sẽ không chạy.

Cập nhật: Đã xảy ra sự cố với giải pháp này khi Elastic Beanstalk mở rộng quy mô các phiên bản của bạn. Ví dụ, giả sử bạn có một phiên bản với cron job đang chạy. Bạn nhận được sự gia tăng về lưu lượng truy cập vì vậy Elastic Beanstalk chia tỷ lệ cho bạn tối đa hai trường hợp. Leader_only sẽ đảm bảo bạn chỉ có một công việc cron chạy giữa hai trường hợp. Lưu lượng truy cập của bạn giảm và Elastic Beanstalk quy mô bạn xuống một trường hợp. Nhưng thay vì chấm dứt phiên bản thứ hai, Elastic Beanstalk chấm dứt phiên bản đầu tiên mà nó là thủ lĩnh. Bây giờ bạn không có bất kỳ công việc cron nào đang chạy vì chúng chỉ chạy trên phiên bản đầu tiên đã bị chấm dứt. Xem các bình luận bên dưới.

Cập nhật 2: Chỉ cần làm rõ điều này từ các nhận xét bên dưới: AWS hiện có bảo vệ chống lại việc chấm dứt phiên bản tự động. Chỉ cần kích hoạt nó trên phiên bản lãnh đạo của bạn và bạn đã sẵn sàng. - Nicolás Arévalo 28 tháng 10 '16 lúc 9:23


12
Tôi đã sử dụng đề xuất của bạn một thời gian và gần đây đã gặp phải sự cố trong đó bằng cách nào đó người lãnh đạo đã chuyển đổi, dẫn đến nhiều trường hợp chạy cron. Để giải quyết vấn đề đó, tôi đã thay đổi 01_some_cron_jobđến 02_some_cron_jobvà nói thêm 01_remove_cron_jobsvới những điều sau đây: command: "rm /etc/cron.d/cron_jobs || exit 0". Bằng cách đó, sau mỗi lần triển khai chỉ có người lãnh đạo mới có cron_jobstệp. Nếu các nhà lãnh đạo thay đổi, bạn chỉ có thể triển khai lại và những kẻ lừa đảo sẽ được cố định để chạy lại một lần nữa.
Willem Renzema,

4
Tôi sẽ đề nghị không dựa vào leader_onlytài sản. Nó chỉ được sử dụng trong quá trình triển khai và nếu bạn mở rộng xuống hay "lãnh đạo" của mình dụ không bạn chắc chắn sẽ có vấn đề tham khảo
arnaslu

2
Đừng làm điều này. Nó quá không đáng tin cậy. Cách duy nhất để tôi làm việc này là chạy một phiên bản vi mô và chạy các công việc cron từ đó bằng cách sử dụng CURL. Điều này đảm bảo rằng chỉ một phiên bản chạy nó và người dẫn đầu đã cài đặt crons sẽ không bị chấm dứt.
Ben Sinclair

1
Tôi cố gắng để khắc phục điều này với một kịch bản ruby nhỏ, bạn có thể tìm thấy nó ở đây: github.com/SocialbitGmbH/AWSBeanstalkLeaderManager
Thomas Kekeisen

8
AWS hiện có bảo vệ chống lại việc chấm dứt phiên bản tự động. Chỉ cần kích hoạt nó trên phiên bản lãnh đạo của bạn và bạn đã sẵn sàng.
Nicolás Arévalo

58

Đây là cách chính thức để thực hiện ngay bây giờ (2015+). Vui lòng thử cách này trước, đây là phương pháp dễ nhất hiện có và cũng đáng tin cậy nhất.

Theo các tài liệu hiện tại, một người có thể chạy các tác vụ định kỳ trên cái gọi là cấp công nhân của họ .

Trích dẫn tài liệu:

AWS Elastic Beanstalk hỗ trợ các tác vụ định kỳ cho các cấp môi trường công nhân trong môi trường chạy cấu hình được xác định trước với ngăn xếp giải pháp có chứa "v1.2.0" trong tên vùng chứa. Bạn phải tạo một môi trường mới.

Cũng thú vị là phần về cron.yaml :

Để gọi các tác vụ định kỳ, gói nguồn ứng dụng của bạn phải bao gồm tệp cron.yaml ở cấp cơ sở. Tệp phải chứa thông tin về các nhiệm vụ định kỳ mà bạn muốn lên lịch. Chỉ định thông tin này bằng cú pháp crontab chuẩn.

Cập nhật: Chúng tôi đã có thể nhận được tác phẩm này. Dưới đây là một số mẹo quan trọng từ kinh nghiệm của chúng tôi (nền tảng Node.js):

  • Khi sử dụng tệp cron.yaml , hãy đảm bảo bạn có awsebcli mới nhất , vì các phiên bản cũ hơn sẽ không hoạt động bình thường.
  • Điều quan trọng nữa là tạo ra môi trường mới (ít nhất là trong trường hợp của chúng ta), chứ không chỉ sao chép môi trường cũ.
  • Nếu bạn muốn đảm bảo CRON được hỗ trợ trên phiên bản EC2 Worker Tier của mình, hãy nhập vào nó ( eb ssh) và chạy cat /var/log/aws-sqsd/default.log. Nó sẽ báo cáo là aws-sqsd 2.0 (2015-02-18). Nếu bạn không có phiên bản 2.0, đã xảy ra lỗi khi tạo môi trường và bạn cần tạo một môi trường mới như đã nêu ở trên.

2
Về cron.yaml, có một bài viết trên blog tuyệt vời: Chạy cron trên Amazon Web Services (AWS) Elastic Beanstalk - Medium
jwako

5
Cảm ơn vì câu hỏi này - câu hỏi dành cho tân binh - tôi cần cron kiểm tra cơ sở dữ liệu của ứng dụng web của mình hai lần một giờ để biết các sự kiện lịch sắp tới và gửi email nhắc nhở khi có. Cách thiết lập tốt nhất ở đây là gì, tôi có nên để URL cron.yaml trỏ đến một tuyến đường trên ứng dụng Web của mình không? Hay tôi nên cấp quyền truy cập ứng dụng env cho worker của mình vào cơ sở dữ liệu? Vì vậy, ít ra khỏi đó về điều này!
christian

5
@christian Theo cách chúng tôi thực hiện, chúng tôi có cùng một ứng dụng chạy trong hai môi trường khác nhau (do đó không cần cấu hình đặc biệt) - máy chủ web và máy chủ web chung. Môi trường công nhân có một số tuyến đặc biệt được kích hoạt bằng cách đặt biến ENV mà ứng dụng của chúng tôi tìm kiếm. Bằng cách này, bạn có thể đặt các tuyến đường đặc biệt chỉ dành cho nhân viên trong cron.yaml của mình trong khi vẫn có cơ sở mã được chia sẻ sang trọng với ứng dụng thông thường. Ứng dụng công nhân của bạn có thể dễ dàng truy cập vào các nguồn tài nguyên như máy chủ web một: cơ sở dữ liệu, mô hình, vv
xaralis

1
@JaquelinePassos v1.2.0 là phiên bản ngăn xếp giải pháp. Nó sẽ cho phép bạn chọn phiên bản của ngăn xếp giải pháp bạn muốn tạo khi tạo môi trường mới. Bất cứ điều gì mới hơn v1.2.0 đều nên làm. Về URL, đó phải là URL mà ứng dụng của bạn lắng nghe, không phải là đường dẫn tệp. Không thể chạy các lệnh quản lý Django, nó chỉ thực hiện các yêu cầu HTTP.
xaralis

4
Một điều mà tôi không rõ là có cách nào để tránh phải phân bổ thêm một máy EC2 chỉ để chạy các công việc cron thông qua cron.yaml hay không. Lý tưởng nhất là nó sẽ chạy trên cùng một máy với máy đang phục vụ các yêu cầu HTTP (tức là tầng web).
Wenzel Jakob

31

Về phản hồi của jamieb, và như alrdinleal đã đề cập, bạn có thể sử dụng thuộc tính 'leader_only' để đảm bảo rằng chỉ một cá thể EC2 chạy công việc cron.

Trích dẫn lấy từ http://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html :

bạn có thể sử dụng leader_only. Một trường hợp được chọn để dẫn đầu trong nhóm Tự động mở rộng quy mô. Nếu giá trị leader_only được đặt thành true, lệnh chỉ chạy trên cá thể được đánh dấu là leader.

Tôi đang cố gắng đạt được điều tương tự trên eb của mình, vì vậy sẽ cập nhật bài đăng của tôi nếu tôi giải quyết được nó.

CẬP NHẬT:

Được rồi, bây giờ tôi có cronjobs đang hoạt động bằng cách sử dụng cấu hình eb sau:

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

Về cơ bản, tôi tạo một tệp tạm thời với cronjobs và sau đó đặt crontab để đọc từ tệp tạm thời, sau đó xóa tệp tạm thời sau đó. Hi vọng điêu nay co ich.


3
Làm thế nào bạn đảm bảo rằng phiên bản chạy crontab này không bị kết thúc bởi tính năng tự động mở rộng? Theo mặc định, nó kết thúc phiên bản cũ nhất.
Sebastien

1
Đó là một vấn đề mà tôi chưa thể giải quyết. Nó khiến tôi thấy thiếu sót trong chức năng của amazon rằng lệnh leader_only không được áp dụng cho người lãnh đạo mới khi người lãnh đạo hiện tại bị EB chấm dứt. Nếu bạn nghĩ ra một cái gì đó, xin vui lòng chia sẻ!
beterthanlife

7
Vì vậy, tôi (cuối cùng) đã khám phá ra cách ngăn người lãnh đạo bị chấm dứt bằng cách tự động mở rộng quy mô - các chính sách chấm dứt tự động mở rộng tùy chỉnh. Xem docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/…
beterthanlife

1
@Nate Có thể bạn đã hiểu ra điều này ngay bây giờ, nhưng dựa trên việc tôi đọc thứ tự mà các lệnh này chạy, "lệnh" chạy trước "container_commands", vì vậy bạn sẽ tạo tệp, sau đó xóa nó, sau đó thử chạy crontab .
clearf

1
@Sebastien để giữ phương thức cũ nhất, đây là những gì tôi làm: 1 - thay đổi bảo vệ kết thúc của phương thức thành ENBABLE. 2 - Đi tới Nhóm quy mô tự động và tìm ID môi trường EBS của bạn, nhấp vào CHỈNH SỬA và thay đổi Chính sách chấm dứt thành "Mới nhất"
Ronaldo Bahia

12

Như đã đề cập ở trên, lỗ hổng cơ bản khi thiết lập bất kỳ cấu hình crontab nào là nó chỉ xảy ra khi triển khai. Khi cụm được tự động mở rộng quy mô và sau đó giảm xuống, nó cũng được ưu tiên là máy chủ đầu tiên bị tắt. Ngoài ra, sẽ không có thất bại nào, đối với tôi là rất quan trọng.

Tôi đã thực hiện một số nghiên cứu, sau đó nói chuyện với chuyên gia tài khoản AWS của chúng tôi để đưa ra các ý tưởng và xác thực giải pháp mà tôi đưa ra. Bạn có thể thực hiện điều này với OpsWorks , mặc dù nó giống như sử dụng một ngôi nhà để giết một con ruồi. Cũng có thể sử dụng Data Pipeline với Task Runner , nhưng điều này có khả năng hạn chế trong các tập lệnh mà nó có thể thực thi và tôi cần để có thể chạy các tập lệnh PHP, với quyền truy cập vào toàn bộ cơ sở mã. Bạn cũng có thể dành một phiên bản EC2 bên ngoài cụm ElasticBeanstalk, nhưng sau đó bạn sẽ không gặp lỗi lần nữa.

Vì vậy, đây là những gì tôi đã nghĩ ra, điều này rõ ràng là độc đáo (như đại diện AWS đã nhận xét) và có thể bị coi là một vụ hack, nhưng nó hoạt động và chắc chắn với sự cố. Tôi đã chọn một giải pháp mã hóa bằng cách sử dụng SDK, mà tôi sẽ hiển thị bằng PHP, mặc dù bạn có thể thực hiện phương pháp tương tự bằng bất kỳ ngôn ngữ nào bạn thích.

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

Vì vậy, hãy đi qua điều này và cách nó hoạt động ... Bạn gọi các tập lệnh từ crontab như bạn thường làm trên mọi phiên bản EC2. Mỗi tập lệnh bao gồm điều này ở phần đầu (hoặc bao gồm một tệp duy nhất cho mỗi tập lệnh, khi tôi sử dụng nó), thiết lập một đối tượng ElasticBeanstalk và truy xuất danh sách tất cả các trường hợp. Nó chỉ sử dụng máy chủ đầu tiên trong danh sách và kiểm tra xem nó có khớp với chính nó hay không, nếu nó tiếp tục, nếu không nó sẽ chết và đóng cửa. Tôi đã kiểm tra và danh sách trả về dường như nhất quán, về mặt kỹ thuật, nó chỉ cần nhất quán trong một phút hoặc lâu hơn, vì mỗi phiên bản thực thi cron đã lên lịch. Nếu nó thay đổi, nó sẽ không thành vấn đề, vì một lần nữa nó chỉ phù hợp với cửa sổ nhỏ đó.

Điều này không trang nhã bằng bất kỳ phương tiện nào, nhưng phù hợp với nhu cầu cụ thể của chúng tôi - không phải để tăng chi phí với một dịch vụ bổ sung hoặc phải có phiên bản EC2 chuyên dụng, và sẽ dự phòng trong trường hợp có bất kỳ lỗi nào. Các tập lệnh cron của chúng tôi chạy các tập lệnh bảo trì được đặt vào SQS và mỗi máy chủ trong cụm sẽ giúp thực thi. Ít nhất điều này có thể cung cấp cho bạn một tùy chọn thay thế nếu nó phù hợp với nhu cầu của bạn.

-Davey


Tôi thấy rằng php_uname ('n') trả về tên DNS riêng (ví dụ: ip-172.24.55.66), không phải là ID phiên bản mà bạn đang tìm kiếm. Thay vì sử dụng php_uname (), tôi đã kết thúc bằng cách sử dụng cái này: $instanceId = file_get_contents("http://instance-data/latest/meta-data/instance-id"); Sau đó chỉ cần sử dụng $ instanceId var đó để thực hiện so sánh.
Valorum

1
Có đảm bảo rằng mảng Phiên bản trình bày cùng một thứ tự trên mỗi lệnh gọi Mô tả không? Tôi khuyên bạn nên trích xuất trường ['Id'] của mỗi mục nhập vào một mảng và sắp xếp chúng trong PHP, trước khi bạn kiểm tra xem mục nhập được sắp xếp đầu tiên có phải là instanceId hiện tại của bạn hay không.
Gabriel

Dựa trên câu trả lời này, tôi đã đưa ra giải pháp này: stackoverflow.com/questions/14077095/… - nó rất giống nhưng KHÔNG có cơ hội thực thi gấp đôi.
TheStoryCoder

11

Tôi đã nói chuyện với đại lý hỗ trợ AWS và đây là cách chúng tôi làm việc này cho tôi. Giải pháp năm 2015:

Tạo một tệp trong thư mục .ebextensions của bạn với your_file_name.config. Trong đầu vào tệp cấu hình:

các tập tin:
  "/etc/cron.d/cron_example":
    chế độ: "000644"
    chủ sở hữu: root
    nhóm: root
    nội dung: |
      * * * * * root /usr/local/bin/cron_example.sh

  "/usr/local/bin/cron_example.sh":
    chế độ: "000755"
    chủ sở hữu: root
    nhóm: root
    nội dung: |
      #! / bin / bash

      /usr/local/bin/test_cron.sh || lối ra
      echo "Cron đang chạy tại" `date` >> /tmp/cron_example.log
      # Bây giờ thực hiện các tác vụ chỉ nên chạy trên 1 phiên bản ...

  "/usr/local/bin/test_cron.sh":
    chế độ: "000755"
    chủ sở hữu: root
    nhóm: root
    nội dung: |
      #! / bin / bash

      METADATA = / opt / aws / bin / ec2-metadata
      INSTANCE_ID = `$ METADATA -i | awk '{print $ 2}' '
      KHU VỰC = `$ METADATA -z | awk '{print substr ($ 2, 0, length ($ 2) -1)}' '

      # Tìm tên Nhóm Chia tỷ lệ Tự động của chúng tôi.
      ASG = `aws ec2 description-tags --filters" Name = resource-id, Values ​​= $ INSTANCE_ID "\
        - vùng $ REGION - văn bản đầu ra | awk '/ aws: autoscaling: groupName / {print $ 5}' '

      # Tìm bản sao đầu tiên trong Nhóm
      FIRST = `aws autoscaling description-auto-scaling-groups --auto-scale-group-names $ ASG \
        - vùng $ REGION - văn bản đầu ra | awk '/ InService $ / {print $ 4}' | sắp xếp | đầu -1 '

      # Kiểm tra xem chúng có giống nhau không.
      ["$ FIRST" = "$ INSTANCE_ID"]

lệnh:
  rm_old_cron:
    lệnh: "rm * .bak"
    cwd: "/etc/cron.d"
    bỏ quaErrors: true

Giải pháp này có 2 nhược điểm:

  1. Trong các lần triển khai tiếp theo, Beanstalk đổi tên tập lệnh cron hiện có thành .bak, nhưng cron vẫn sẽ chạy nó. Cron của bạn hiện thực thi hai lần trên cùng một máy.
  2. Nếu môi trường của bạn mở rộng quy mô, bạn sẽ có một số trường hợp, tất cả đều chạy tập lệnh cron của bạn. Điều này có nghĩa là ảnh chụp thư của bạn được lặp lại hoặc các kho lưu trữ cơ sở dữ liệu của bạn bị sao

Cách giải quyết:

  1. Đảm bảo mọi tập lệnh .ebextensions tạo cron cũng xóa các tệp .bak trong các lần triển khai tiếp theo.
  2. Có một tập lệnh trợ giúp thực hiện những việc sau: - Nhận ID Phiên bản hiện tại từ Siêu dữ liệu - Nhận tên Nhóm tự động mở rộng hiện tại từ Thẻ EC2 - Nhận danh sách Phiên bản EC2 trong Nhóm đó, được sắp xếp theo thứ tự bảng chữ cái. - Lấy ví dụ đầu tiên từ danh sách đó. - So sánh ID phiên bản từ bước 1 với ID phiên bản đầu tiên từ bước 4. Sau đó, các tập lệnh cron của bạn có thể sử dụng tập lệnh trợ giúp này để xác định xem chúng có nên thực thi hay không.

Cảnh báo:

  • Vai trò IAM được sử dụng cho các phiên bản Beanstalk cần quyền ec2: DescribeTags và tự động phân hạng: DescribeAutoScalingGroups quyền
  • Các phiên bản được chọn là những phiên bản được hiển thị dưới dạng InService bằng Auto Scaling. Điều này không nhất thiết có nghĩa là chúng đã được khởi động hoàn toàn và sẵn sàng chạy cron của bạn.

Bạn sẽ không phải đặt Vai trò IAM nếu bạn đang sử dụng vai trò cây đậu mặc định.


7

Nếu đang sử dụng Rails, bạn có thể sử dụng gembeanstalk bất cứ khi nào . Nó cho phép bạn chạy các công việc cron trên tất cả các phiên bản hoặc chỉ một phiên bản. Nó kiểm tra mỗi phút để đảm bảo rằng chỉ có một phiên bản "leader" và sẽ tự động thăng hạng một máy chủ lên "leader" nếu không có. Điều này là cần thiết vì Elastic Beanstalk chỉ có khái niệm người dẫn đầu trong quá trình triển khai và có thể đóng bất kỳ phiên bản nào bất kỳ lúc nào trong khi mở rộng quy mô.

CẬP NHẬT Tôi đã chuyển sang sử dụng AWS OpsWorks và không còn duy trì viên ngọc này nữa. Nếu bạn cần nhiều chức năng hơn so với những điều cơ bản về Elastic Beanstalk, tôi thực sự khuyên bạn nên chuyển sang OpsWorks.


Bạn có phiền cho chúng tôi biết cách bạn đã giải quyết nó bằng OpsWorks không? Bạn có đang chạy các lớp tùy chỉnh thực hiện các công việc cron không?
Tommie

Vâng, tôi có một lớp quản trị / cron chỉ chạy trên một máy chủ. Tôi đã thiết lập một cuốn sách dạy nấu ăn tùy chỉnh chứa tất cả các công việc cron của tôi. AWS có một hướng dẫn tại docs.aws.amazon.com/opsworks/latest/userguide/… .
dignoe

@dignoe nếu bạn chỉ định một máy chủ để chạy công việc cron bằng OpsWorks, điều tương tự khi sử dụng Elastic Beanstalk, tôi có thể sử dụng môi trường với một máy chủ để chạy công việc cron. Ngay cả với Load Balancer, các phiên bản tối đa và tối thiểu được đặt thành một, để bảo toàn ít nhất một phiên bản máy chủ.
Jose Nobile

6

Bạn thực sự không muốn chạy công việc cron trên Elastic Beanstalk. Vì bạn sẽ có nhiều phiên bản ứng dụng, điều này có thể gây ra tình trạng cuộc đua và các vấn đề kỳ quặc khác. Tôi thực sự gần đây đã viết blog về điều này (mẹo thứ 4 hoặc thứ 5 xuống trang). Phiên bản ngắn: Tùy thuộc vào ứng dụng, sử dụng một hàng đợi công việc như SQS hoặc một giải pháp của bên thứ ba như iron.io .


SQS không đảm bảo mã sẽ chỉ được chạy một lần. Tôi thích trang iron.io, tôi sẽ kiểm tra nó.
Nathan H

Cũng trong bài đăng trên blog của bạn, bạn khuyên bạn nên sử dụng InnoDB trên RDS. Tôi sử dụng một bảng trên RDS để lưu trữ các tác vụ của mình và sử dụng tính năng "CHỌN ... CHO CẬP NHẬT" của InnoDB để đảm bảo chỉ một máy chủ chạy các tác vụ đó. Làm cách nào để ứng dụng của bạn liên hệ với SQS mà không cần công việc cron hoặc tương tác với người dùng?
James Alday

1
@JamesAlday Câu hỏi SO này khá cũ. Vì tôi đã viết nhận xét ở trên, AWS đã giới thiệu một cách thanh lịch để xử lý các công việc cron trên Elastic Beanstalk bằng cách chọn một trong các máy chủ đang chạy làm máy chủ. Nói như vậy, có vẻ như bạn đang lạm dụng cron + MySQL làm hàng đợi công việc. Tuy nhiên, tôi sẽ cần biết nhiều về ứng dụng của bạn trước khi có thể đưa ra các đề xuất cụ thể.
jamieb

Tôi có một tập lệnh chạy qua cron để kiểm tra một bảng để chạy các công việc. Việc sử dụng các giao dịch ngăn nhiều máy chủ chạy cùng một công việc. Tôi đã xem xét SQS nhưng bạn cần một máy chủ chính chạy tất cả các tập lệnh thay vì phân phối nó và bạn vẫn cần viết logic để đảm bảo bạn không chạy cùng một tập lệnh nhiều lần. Nhưng tôi vẫn còn bối rối về cách bạn bắt các tác vụ chạy mà không có sự tương tác của người dùng hoặc cron - điều gì kích hoạt ứng dụng của bạn chạy các tác vụ trong hàng đợi?
James Alday

4

2017: Nếu bạn đang sử dụng Laravel5 +

Bạn chỉ cần 2 phút để cấu hình nó:

  • tạo một Cấp công nhân
  • cài đặt laravel-aws-worker

    composer require dusterio/laravel-aws-worker

  • thêm cron.yaml vào thư mục gốc:

Thêm cron.yaml vào thư mục gốc của ứng dụng của bạn (đây có thể là một phần của repo của bạn hoặc bạn có thể thêm tệp này ngay trước khi triển khai lên EB - điều quan trọng là tệp này hiện diện tại thời điểm triển khai):

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

Đó là nó!

Tất cả nhiệm vụ của bạn App\Console\Kernelbây giờ sẽ được thực thi

Hướng dẫn chi tiết và giải thích: https://github.com/dusterio/laravel-aws-worker

Cách viết các tác vụ bên trong Laravel: https://laravel.com/docs/5.4/scheduling


3

Một giải pháp dễ đọc hơn bằng cách sử dụng filesthay vì container_commands:

các tập tin:
  "/etc/cron.d/my_cron":
    chế độ: "000644"
    chủ sở hữu: root
    nhóm: root
    nội dung: |
      # ghi đè địa chỉ email mặc định
      MAILTO = "example@gmail.com"
      # chạy lệnh Symfony năm phút một lần (với tư cách là người dùng ec2)
      * / 10 * * * * ec2-user / usr / bin / php / var / app / current / app / console do: something
    mã hóa: đồng bằng
lệnh:
  # xóa tệp sao lưu do Elastic Beanstalk tạo
  clear_cron_backup:
    lệnh: rm -f /etc/cron.d/watson.bak

Lưu ý rằng định dạng khác với định dạng crontab thông thường ở chỗ nó chỉ định người dùng chạy lệnh dưới dạng.


Một vấn đề ở đây là các phiên bản Elastic Beanstalk EC2 không được thiết lập dịch vụ SMTP theo mặc định, vì vậy tùy chọn MAILTO ở đây có thể không hoạt động.
Justin Finkelstein

3

1 xu đóng góp của tôi cho năm 2018

Đây là cách phù hợp để làm điều đó (sử dụng django/pythondjango_crontabứng dụng):

bên trong .ebextensionsthư mục tạo một tệp như thế này 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

Nó cần phải được container_commandsthay vìcommands



2

Ví dụ mới nhất từ ​​Amazon là dễ nhất và hiệu quả nhất (nhiệm vụ định kỳ):

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

nơi bạn tạo một cấp công nhân riêng biệt để thực hiện bất kỳ công việc cron nào của bạn. Tạo tệp cron.yaml và đặt nó vào thư mục gốc của bạn. Một vấn đề tôi gặp phải (sau khi cron dường như không thực thi) là nhận thấy rằng CodePipeline của tôi không có quyền thực hiện sửa đổi động cơ. Dựa trên điều đó sau khi thêm quyền truy cập FullDynamoDB trong IAM -> vai trò -> yourpipeline và triển khai lại (cây đậu đàn hồi) nó hoạt động hoàn hảo.


1

Đây là lời giải thích đầy đủ về giải pháp:

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


không sử dụng leader_only để tạo một phiên bản duy nhất trong ASG. ASG không bao giờ đảm bảo rằng bạn sẽ có thể giữ lại phiên bản cụ thể đó nhưng nó chỉ đảm bảo số lượng phiên bản trong dịch vụ. Phiên bản lãnh đạo có thể chấm dứt do kiểm tra sức khỏe EB không thành công.
mst

1

Vì vậy, chúng tôi đã vật lộn với điều này trong một thời gian và sau một số cuộc thảo luận với đại diện AWS, cuối cùng tôi đã đưa ra giải pháp mà tôi nghĩ là tốt nhất.

Sử dụng cấp công nhân với cron.yaml chắc chắn là cách khắc phục dễ dàng nhất. Tuy nhiên, điều mà tài liệu không nói rõ là điều này sẽ đặt công việc ở cuối hàng đợi SQS mà bạn đang sử dụng để thực sự chạy công việc của mình. Nếu công việc cron của bạn nhạy cảm về thời gian (nhiều như vậy), thì điều này không thể chấp nhận được, vì nó sẽ phụ thuộc vào kích thước của hàng đợi. Một lựa chọn là sử dụng một môi trường hoàn toàn riêng biệt chỉ để chạy các công việc cron, nhưng tôi nghĩ điều đó là quá mức cần thiết.

Một số tùy chọn khác, như kiểm tra xem bạn có phải là người đầu tiên trong danh sách hay không, cũng không lý tưởng. Điều gì sẽ xảy ra nếu phiên bản đầu tiên hiện tại đang trong quá trình ngừng hoạt động?

Bảo vệ phiên bản cũng có thể đi kèm với các vấn đề - điều gì sẽ xảy ra nếu phiên bản đó bị khóa / đóng băng?

Điều quan trọng cần hiểu là AWS tự quản lý chức năng cron.yaml như thế nào. Có một daemon SQS sử dụng bảng Dynamo để xử lý "bầu cử lãnh đạo". Nó ghi vào bảng này thường xuyên, và nếu người lãnh đạo hiện tại không viết trong một thời gian ngắn, trường hợp tiếp theo sẽ đảm nhận vai trò người lãnh đạo. Đây là cách daemon quyết định phiên bản nào sẽ kích hoạt công việc vào hàng đợi SQS.

Chúng tôi có thể sử dụng lại chức năng hiện có hơn là cố gắng viết lại chức năng của riêng mình. Bạn có thể xem toàn bộ giải pháp tại đây: https://gist.github.com/dorner/4517fe2b8c79ccb3971084ec28267f27

Đó là trong Ruby, nhưng bạn có thể dễ dàng điều chỉnh nó với bất kỳ ngôn ngữ nào khác có AWS SDK. Về cơ bản, nó kiểm tra nhà lãnh đạo hiện tại, sau đó kiểm tra trạng thái để đảm bảo rằng nó ở trạng thái tốt. Nó sẽ lặp lại cho đến khi có một người lãnh đạo hiện tại ở trạng thái tốt và nếu trường hợp hiện tại là người lãnh đạo, hãy thực thi công việc.


0

Để kiểm soát xem Tự động mở rộng quy mô có thể kết thúc một phiên bản cụ thể khi mở rộng quy mô hay không, hãy sử dụng tính năng bảo vệ phiên bản. Bạn có thể bật cài đặt bảo vệ phiên bản trên một nhóm Tự động mở rộng quy mô hoặc một cá thể Chia tỷ lệ tự động riêng lẻ. Khi Tự động mở rộng quy mô khởi chạy một cá thể, cá thể đó sẽ kế thừa cài đặt bảo vệ cá thể của nhóm Tự động mở rộng quy mô. Bạn có thể thay đổi cài đặt bảo vệ phiên bản cho một nhóm Tự động mở rộng quy mô hoặc một phiên bản Tỷ lệ tự động bất kỳ lúc nào.

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


0

Tôi đã có một giải pháp khác cho vấn đề này nếu một tệp php cần được chạy qua cron và nếu bạn đã đặt bất kỳ phiên bản NAT nào thì bạn có thể đặt cronjob trên phiên bản NAT và chạy tệp php thông qua wget.


0

đây là một bản sửa lỗi trong trường hợp bạn muốn làm điều này trong PHP. Bạn chỉ cần cronjob.config trong thư mục .ebextensions của mình để làm cho nó hoạt động như thế này.

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 nhận các biến môi trường cho các tệp. Bạn có thể gỡ lỗi đầu ra trên tmp / sendemail.log như trên.

Hy vọng điều này sẽ giúp ai đó vì nó chắc chắn đã giúp chúng tôi!


0

Dựa trên các nguyên tắc của câu trả lời từ người dùng1599237 , nơi bạn cho phép các công việc cron chạy trên tất cả các trường hợp nhưng thay vào đó khi bắt đầu công việc xác định xem chúng có được phép chạy hay không, tôi đã đưa ra một giải pháp khác.

Thay vì xem xét các phiên bản đang chạy (và phải lưu trữ khóa và bí mật AWS của bạn), tôi đang sử dụng cơ sở dữ liệu MySQL mà tôi đã kết nối từ tất cả các phiên bản.

Nó không có nhược điểm, chỉ có mặt tích cực:

  • không có thêm trường hợp hoặc chi phí
  • dung dịch rắn đá - không có cơ hội thực hiện kép
  • có thể mở rộng - tự động hoạt động khi các phiên bản của bạn được mở rộng và thu nhỏ
  • chuyển đổi dự phòng - tự động hoạt động trong trường hợp một phiên bản bị lỗi

Ngoài ra, bạn cũng có thể sử dụng hệ thống tệp được chia sẻ chung (như AWS EFS thông qua giao thức NFS) thay vì cơ sở dữ liệu.

Giải pháp sau được tạo trong khuôn khổ PHP Yii nhưng bạn có thể dễ dàng điều chỉnh nó cho một khuôn khổ và ngôn ngữ khác. Ngoài ra, trình xử lý ngoại lệ Yii::$app->systemlà một mô-đun của riêng tôi. Thay thế nó bằng bất cứ thứ gì bạn đang sử dụng.

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

Đây là lược đồ cơ sở dữ liệu tôi đang sử dụng:

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`)
)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.