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ừ:

Quản lý file docker-compose.yml cho nhiều môi trường

From: https://www.docsity.com/en/news/it/funny-aspects-related-developers/

Trong quá trình làm việc Docker của mình thì docker-compose là một phần không thể thiếu được. Compose giúp định nghĩa và chạy một application với nhiều container (multi-container application). Chỉ với 1 file yaml thiết định và docker-compose up mình có thể dễ dàng tạo và chạy toàn bộ các services phục vụ cho việc chạy ứng dụng như Mysql, Fluentd, Redis, Nginx,..

Compose là một công cụ tuyệt với không chỉ dùng cho development, testing, staging environments, mà còn ứng dụng trong CI workflows.

Cũng giống như các công nghệ khác, luôn có rất nhiều điều để tìm hiểu về Docker, thường có 2 cách tiếp cận broad strokes (học rộng) and deep dives (học sâu).

Nhưng cách tiếp cận cá nhân của mình là tìm hiểu 1 lượng kiến thức vừa đủ để tự tin bắt đầu sử dụng một công nghệ, và để môi trường thực tiễn dạy cho mình những gì cần học tiếp theo. Phần lớn của việc sử dụng Docker trong môi trường thực tế.

Hôm nay mình sẽ chia sẻ cách quản lý file docker-compose.yml cho nhiều môi trường khác nhau như thế nào.

Mình dùng docker-compose file cho môi trường làm việc ở local, và môi trường staging, production. Bài toán thường gặp ở đây là mỗi môi trường khác nhau yêu cầu thiết định khác nhau. Ví dụ:

  • Port dùng ở local thường không được dùng ở môi trường production vì lý do security,…
  • Phải chạy nhiều docker-compose trên cùng một server
  • Cần thiết định logging driver cho từng môi trường khác nhau. Ví dụ trên production dùng AWS Cloudwatch logs, …
  • Ở môi trường production thì volume path khác
  • Dùng các cloud storage như RDS trên môi trường production, …

Rất may mắn là Docker-compose hỗ trợ cho các nhu cầu này bằng định nghĩa file docker-compose.yml

Compose supports two methods of sharing common configuration:

– Extending an entire Compose file by using multiple Compose files

– Extending individual services with the extends field (for Compose file versions up to 2.1)

https://docs.docker.com/compose/extends/

Có 2 cách tiếp cận
1) Tạo 1 file gốc docker-compose.yml base và nhiều file docker-compose cho các môi trường khác nhau để override các thiết định của file gốc

2) Định nghĩa file của từng service và tạo nhiều file docker-compose cho các môi trường khác nhau, các file này dùng cấu trúc extends để tùy chỉnh thiết định của từng service cho các môi trường.

Cá nhân mình thích cách 1) hơn vì nó tổng hợp các thiết định của môi trường vào từng file nên quản lý dễ dàng hơn. Chắc có lẽ mình đã quá quen với việc sử dụng .env để tùy biến các môi trường web rồi.

Việc sử dụng nhiều docker-compose file cho phép người dùng có thể override thiết định cho các môi trường staging, production.

Khi cần override thiết định cho môi trường staging thì mình sẽ thêm 1 file docker-compose.staging.yml, và viết các thiết định cần override vào đấy

Mình sẽ chuẩn bị 2 file

docker-compose.yml

# docker-compose.yml
web:
  image: example/my_web_app:latest
  depends_on:
    - db
    - cache

db:
  image: postgres:latest

cache:
  image: redis:latest

docker-compose.staging.yml với các thiết định volume, port, biến môi trường

web:
  build: .
  volumes:
    - '.:/code'
  ports:
    - 8883:80
  environment:
    DEBUG: 'true'

db:
  command: '-d'
  ports:
    - 5432:5432

cache:
  ports:
    - 6379:6379

Sau đấy chỉ cần chạy lệnh

docker-compose \
    -f docker-compose.yml \
    -f docker-compose.staging.yml \
    up -d

Khi chạy thì compose sẽ tự động override các thiết định trong docker-compose.yml bằng các thiết định trong docker-compose.staging.yml

Thật tuyệt vời đúng không? Bây giờ, các file tùy chỉnh cho môi trường sẽ được quản lý riêng biệt, có thể được lưu trữ trong một repo git khác hoặc được quản lý bởi một nhóm khác. Như thế vừa đảm bảo các thông sensitive, các file docker-compose.staging.yml, docker-compose.production.yml sẽ được team DevOps quản lý riêng biệt với team phát triển.

Happy hacking! Happy Docker life.

Additional, Other Kubernetes related topics is here. https://tuantranf.me/tag/kubernetes/