Linux Cron thực tiễn – Tất tần tật những gì bạn cần biết

You have a cron – you have a job
Image from: https://www.reddit.com/r/ProgrammerHumor/comments/6ng8f7/cron_job/

Đợt vừa rồi hết cách ly xã hội dự án lại đi vào phase release, nên lu bu quá không viết được bài nào. Hôm nay mình sẽ note lại một số kỹ thuật chú ý khi dùng Cron job trong Linux như thiết định job theo giờ, cài đặt crontab trong Docker, thiêt định job theo đơn vị giây, …

Cron là gì

Cron job là những tác vụ, chương trình cần chạy hằng ngày, hằng giờ hoặc vài phút 1 lần, …

Trong Linux (Ubuntu) thì crontab là daemon để quản lý thiết định cron. Bài này của mình chủ yếu trình bày các thiết định trên Ubuntu nếu bạn cần thiết định trên CentOS, Redhat thì có thể trao đổi với mình hoặc chịu khó google một chút sẽ tìm thấy cách làm tương ứng ở hệ điều hành khác.

Các file thiết định cron

/etc/
├── cron.d
│   ├── anacron
├── cron.daily
├── cron.hourly
├── cron.monthly
├── crontab
├── cron.weekly

cron.d

Thông thường mọi người sẽ tạo quản lý file thiết định tại thư mục này.

cron.***

Các file thiết định được chia theo ngày, tháng, giờ. Bạn có thể tạo file thiết định trong các thư mục này cho dễ quản lý. Tuy nhiên theo kinh nghiệm của mình thì nên dùng cron.d thì các file thiết định tập trung một chỗ sẽ dễ quản lý hơn.

crontab

Đây là file thiết định chính của cron trên Linux, ngoài ra bạn có thể dùng command crontab để thiết đinh.

Tạo một cron của bạn

Hãy thử tạo một cron job bằng cách tạo 1 file trong thư mục /etc/cron.d. Mình khuyến khích nên copy từ file crontab và chỉnh sửa

$ cp /etc/crontab /etc/cron.d/test_cron

Xong. bạn đã có 1 file thiết định cron. Đơn giản quá phải không?

Tiếp theo mình sẽ cũng xem chi tiết cách thiết định thời gian, chương trình chạy, và khởi động file cron vừa tạo nhé.

Cách viết cron

Hãy check nội dung file /etc/cron.d/test_cron

# cat /etc/cron.d/test_cron
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user  command
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#

Hãy thử thay đổi nội dung file test_cron như sau.

# m h dom mon dow user  command
Minute Hour DayOfMonth Month DayOfWeek User Command

# every 1 minute
* *    * * *  root   touch /home/ubuntu/test.txt

# every minute in 1:00〜1:59 everyday
* 1    * * *  root   touch /home/ubuntu/test.txt

# 1:00 AM everyday
0 1    * * *  root   touch /home/ubuntu/test.txt

# at 00:00 from day 12 to 20 every month
0 0    12-20 * *  root   touch /home/ubuntu/test.txt

# at 00:00 from Monday to Friday every week
0 0    * * 1-5  root   touch /home/ubuntu/test.txt
#

Chú ý 1: cú pháp sử dụng Tab, Space trong thiết đinh. Vị trí của Tab,Space như sau nhé.

*[Space]*[Tab]*[Space]*[Space]*[Tab]root[Tab]Commands

Chú ý 2: Cuối file thiết định cron luôn thêm một dòng trống.

Cập nhật thay đổi Cron

Cập nhật thay đổi của cron bằng cách khởi động lại dịch vụ (daemon) cron.

$ sudo service cron restart

Thiết định file log cho cron

Dùng syslog

Thêm thiết định cho phép syslog thu thập cron log bằng cách bỏ comment out trong file thiết định syslog.

Nhớ khởi động lại service rsyslog sau khi lưu file thiết định. Log này sẽ giúp bạn debug cron nếu xảy ra lỗi trong file thiết định

$ vim /etc/rsyslog.d/50-default.conf

# Remove this comment out
# cron.*                          /var/log/cron.log

$ sudo service rsyslog restart

Thiết định log cho command trong Cron

Mình hay thiết định log cho các tác vụ cron để đảm bảo job chạy đúng cho phép xem lại log khi có lỗi hay vấn đề gì.

Ta có thể thay đổi file /etc/cron.d/test_cron như sau

* *    * * *  root   touch /home/ubuntu/test.txt >> /var/log/test-cron.log 2>> /var/log/test-cron-error.log

File /var/log/test-cron.log sẽ chứa các output thường của cron job. File /var/log/test-cron-error.log sẽ chứa các lỗi khi thực hiện cron job

Thiết định cron job theo đơn vị giây

Thông thường, cron job chỉ cho phép thiết định theo đơn vị phút. Trường hợp bạn cần thiết định 10 giây chạy một lần thì phải làm thế nào?

Chúng ta cần một trick khi thiết định.

Hãy thử thay đổi file test_cron để chạy 10 giây 1 lần nhé.

# for i in `seq [second_from] [num_of_seconds] [second_to]`;do (sleep ${i}; [your command] ) & done;
# every 10 seconds
* *    * * *  root   for i in `seq 0 10 59`;do (sleep ${i}; touch /home/ubuntu/test.txt ) & done;

Xong. Quá đơn giản phải không? Ta sẽ thiết định một vòng for chạy từ giây second_from đến giây thứ second_to theo khoảng cách num_of_seconds. Trong ví dụ trên, mình đã thiết định vòng for chạy theo seq từ giây 0 đến giây 59 theo khoảng cách 10 giây 1. Bạn có thể sửa lại cho phép chạy 5s 1 lần hay từ giây thứ 10 đến giây thứ 30, …

Bạn còn có thể dùng cấu trúc while … do để thiết định. Ví dụ:

while : ; do sleep 10 ; some_command || break ; done

Thiết định cron duy nhất 1 process 1 lần

Một số trường hợp bắt buộc chỉ được chạy job 1 process 1 lần tránh bị duplicate

Đối với Ubuntu bạn có thể dùng run-one hoặc flock để đảm bảo chỉ 1 process duy nhất. Ví dụ

* *    * * *  root   run-once touch /home/ubuntu/test.txt >> /var/log/test-cron.log 2>> /var/log/test-cron-error.log
* *    * * *  root   run-once flock -n /tmp/test-cron.lock  /home/ubuntu/test.txt >> /var/log/test-cron.log 2>> /var/log/test-cron-error.log

Bạn có thể tìm hiểu thêm thêm về run-one hoặc flock nếu cần.

Đọc biến môi trường từ file thiết định bên ngoài

Bạn cũng có thể thiết định cron job cho phép đọc các thiết định từ file bên ngoài. Mình thường dùng các file .env để quản lý môi trường

Ví dụ: Mình sẽ lưu khoảng cách thực hiện 10 giây ở ví dụ trên vào file /home/ubuntu/test-cron.env và load nó khi chạy cron job.

TIME=10

Sửa lại file thiết định test_cron

# Thiết định Shell type là bash
SHELL=/bin/bash
# Load biến môi trường TIME từ file .env trước khi chạy cronjob
* *    * * *  root   for i in `source /home/ubuntu/test-cron.env && seq 0 ${TIME} 59`;do (sleep ${i}; touch /home/ubuntu/test.txt ) & done;

Thiết định cron job cho trong Dockerfile

Bạn có thể tham khảo Stackoverflow topic sau

https://stackoverflow.com/questions/37458287/how-to-run-a-cron-job-inside-a-docker-container

Ví dụ trường hợp test-cron thì có thể thiết định như sau.
Dockerfile

FROM ubuntu:latest

RUN apt-get update && apt-get -y install cron

# Copy test-cron file to the cron.d directory
COPY test-cron /etc/cron.d/test-cron

# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/test-cron

# Apply cron job
RUN crontab /etc/cron.d/test-cron

# Create the log file to be able to run tail
RUN touch /var/log/cron.log

# Run the command on container startup
CMD ["cron", "-f"]

File test-cron sẽ được sửa như sau để đảm bảo redirect log sang standard output của Docker.

* *    * * *  root   run-once touch /home/ubuntu/test.txt > /proc/1/fd/1 2>/proc/1/fd/2

Conclusion

Mình đã chia sẻ tất tần tật những kinh nghiệm thiết định cron job mình cóp nhặt được. Nếu bạn làm chủ được các kỹ thuật trên thì có thể mạnh dạn áp dụng vào sản phẩm hiện có, hoặc tự tin chém gió khi phỏng vấn.

Happy hacking.

Bài viết có tham khảo từ: