Bash for Oracle DBAs قسمت دهم کار با cron و زمان‌بندی اسکریپت‌ها

یکی از اصلی‌ترین کاربردهای اسکریپت‌نویسی در دنیای Oracle DBA، خودکارسازی کارهای تکراری است.

کارهایی مثل:

  • اجرای روزانه RMAN backup
  • بررسی هفتگی فضای دیسک
  • مانیتورینگ شبانه وضعیت Instanceها
  • پاک کردن فایل‌های log قدیمی

این کارها را با ابزار cron در لینوکس انجام می‌دهیم.


cron و crontab چیست؟

  • cron: یک دیمون (daemon) یا سرویس پس‌زمینه در لینوکس است که مسئول اجرای زمان‌بندی شده دستورات است.
  • crontab: فایل تنظیماتی است که لیستی از دستورات زمان‌بندی شده را برای cron تعریف می‌کند. هر کاربر می‌تواند crontab مخصوص به خود را داشته باشد.

دستورات پایه crontab

  • crontab -e : ویرایش crontab کاربر فعلی (این دستور فایل را در ویرایشگر پیش‌فرض باز می‌کند).
  • crontab -l : نمایش محتویات crontab کاربر فعلی.
  • crontab -r : حذف کامل crontab کاربر فعلی (با احتیاط استفاده شود!).

ساختار یک خط در crontab

هر خط در crontab شامل ۵ فیلد زمانی و سپس دستور است:

* * * * * command_to_execute

این ۵ فیلد عبارتند از:

  1. Minute (دقیقه): 0-59
  2. Hour (ساعت): 0-23
  3. Day of Month (روز ماه): 1-31
  4. Month (ماه): 1-12
  5. Day of Week (روز هفته): 0-7 (0 و 7 هر دو یکشنبه هستند)

مثال‌های کاربردی

  1. اجرای اسکریپت backup روزانه ساعت 2 بامداد:
    0 2 * * * /path/to/your/backup_script.sh
 
      2. اجرای اسکریپت مانیتورینگ هر 15 دقیقه:
 
    */15 * * * * /path/to/your/monitor_script.sh
 

(*/15 یعنی مضرب‌های 15 دقیقه)

  1. اجرای اسکریپت پاکسازی log ساعت 3:30 بامداد روز اول هر ماه:
    30 3 1 * * /path/to/your/cleanup_script.sh

چالش مهم: محیط (Environment) در cron

اینجاست که خیلی از اسکریپت‌ها، مخصوصاً اسکریپت‌های اوراکل، مشکل پیدا می‌کنند.

وقتی اسکریپتی را مستقیم در ترمینال اجرا می‌کنید، محیط آن ترمینال (متغیرهای PATH، ORACLE_HOME، LD_LIBRARY_PATH و…) در دسترس اسکریپت قرار می‌گیرد.

اما cron این محیط را ندارد!

cron یک محیط مینیمال اجرا می‌کند.


راه‌حل‌ها برای مشکل محیط:

  1. تعریف کامل متغیرها در ابتدای اسکریپت Bash:

این بهترین و امن‌ترین روش است. تمام متغیرهای لازم را داخل اسکریپت Bash تعریف کنید.

    #!/bin/bash
    set -euo pipefail

    export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
    export PATH=$ORACLE_HOME/bin:$PATH
    export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH

    # Rest of your script
    sqlplus / as sysdba << EOF
    -- your SQL commands
    EXIT;
    EOF

  1. تعریف متغیرها در crontab:

می‌توانید متغیرها را مستقیماً در crontab تعریف کنید. این متغیرها فقط برای دستورات همان crontab معتبر خواهند بود.

  1. اجرای اسکریپت با source:

اگر یک فایل env.sh دارید که متغیرها را تنظیم می‌کند، می‌توانید اسکریپت اصلی را با source اجرا کنید.

    0 2 * * * . /path/to/your/env.sh; /path/to/your/backup_script.sh

یا

    # backup_script.sh
    source /path/to/your/env.sh
    # ... rest of script

مدیریت خروجی cron

cron خروجی دستورات (stdout و stderr) را به ایمیل کاربر مدیر سیستم (root یا کاربری که crontab را اجرا کرده) ارسال می‌کند.

برای مدیریت این خروجی:

  • ارسال به فایل log:

    0 2 * * * /path/to/your/backup_script.sh >> /var/log/backup.log 2>&1

این دستور هم stdout و هم stderr را به فایل backup.log هدایت می‌کند.

  • نادیده گرفتن خروجی:

    0 2 * * * /path/to/your/backup_script.sh > /dev/null 2>&1

مثال واقعی و عملی: اتوماسیون RMAN Backup با منطق Incremental/Full

در این بخش، یک سناریوی حرفه‌ای را پیاده‌سازی می‌کنیم. فرض کنید می‌خواهیم:

  1. شنبه تا پنج‌شنبه: بکاپ Incremental Level 1 گرفته شود.
  2. جمعه: بکاپ Full (Incremental Level 0) گرفته شود.
  3. قبل از هر بکاپ، یک PFILE برای احتیاط ساخته شود.
  4. تمام لاگ‌ها، وضعیت سلامت فایل‌ها و پاکسازی فایل‌های قدیمی (Obsolete/Expired) مدیریت شود.

گام ۱: ایجاد اسکریپت اصلی Bash (rman_backup_manager.sh)

این اسکریپت ورودی inc یا full را می‌گیرد و محیط اوراکل را تنظیم می‌کند.

#!/bin/bash
# rman_backup_manager.sh
set -euo pipefail

# --- تنظیم متغیرهای محیطی (بسیار مهم برای Cron) ---
export ORACLE_HOSTNAME=vahiddb01.rhrac.rh
export ORACLE_UNQNAME=vahiddb
export ORACLE_BASE=/u01/app/oracle
export ORACLE_HOME=$ORACLE_BASE/product/21.3.0/db_1
export ORACLE_SID=vahiddb1
export PATH=$ORACLE_HOME/bin:/usr/sbin:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib

# --- مسیرها و تنظیمات ---
BKP_DIR="/BKP-RMAN-18/vahiddbbackup"
SCRIPT_DIR="/BKP-RMAN-18/scripts"
SQL_DIR="/home/oracle/scripts/rman"
DATE_TAG=$(date +%Y-%m-%d--%H-%M-%S)

# بررسی ورودی اسکریپت
MODE="${1:-}" # گرفتن مقدار inc یا full

if [[ "$MODE" != "inc" && "$MODE" != "full" ]]; then
    echo "خطا: لطفا نوع بکاپ را مشخص کنید: {inc|full}"
    exit 1
fi

mkdir -p "$BKP_DIR"

# ۱. تهیه PFILE از روی SPFILE (طبق متد مقاله)
echo "Creating PFILE backup..."
sqlplus -s / as sysdba @"$SQL_DIR/createPfileFromSpfile.sql" > /dev/null

# ۲. انتخاب فایل RMAN بر اساس ورودی
if [[ "$MODE" == "full" ]]; then
    RMAN_CMD_FILE="$SCRIPT_DIR/rman_full_script.rman"
    LOG_FILE="${BKP_DIR}/vahiddb_FULL_${DATE_TAG}.log"
else
    RMAN_CMD_FILE="$SCRIPT_DIR/rman_inc_script.rman"
    LOG_FILE="${BKP_DIR}/vahiddb_INC_${DATE_TAG}.log"
fi

# ۳. اجرای RMAN
echo "Starting RMAN $MODE backup..."
rman target / cmdfile="$RMAN_CMD_FILE" log="$LOG_FILE"

# بررسی وضعیت نهایی
if [ $? -eq 0 ]; then
    echo "بکاپ با موفقیت انجام شد. Log: $LOG_FILE"
else
    echo "بکاپ با خطا مواجه شد. Log را بررسی کنید."
    exit 1
fi

گام ۲: فایل‌های کمکی (SQL و RMAN)

فایل SQL برای PFILE (createPfileFromSpfile.sql):

create pfile='/BKP-RMAN-18/vahiddbbackup/vahiddb_pfile.ora' from spfile;
exit;

فایل RMAN برای Full (جمعه‌ها) (rman_full_script.rman):

در این فایل از incremental level 0 استفاده می‌کنیم که به عنوان پایه بکاپ‌های بعدی عمل می‌کند.

run {
  allocate channel c1 device type disk format '/BKP-RMAN-18/vahiddbbackup/full_%T_%U';
  allocate channel c2 device type disk format '/BKP-RMAN-18/vahiddbbackup/full_%T_%U';
  # ... (تعریف کانال‌های بیشتر در صورت نیاز)
  
  crosscheck archivelog all;
  delete noprompt obsolete;
  delete noprompt expired backup;
  delete noprompt expired archivelog all;

  backup incremental level 0 cumulative 
    filesperset 5 
    as COMPRESSED BACKUPSET 
    tag 'VAHIDDB_DAILY_FULL' 
    database;

  backup as backupset archivelog all format '/BKP-RMAN-18/vahiddbbackup/arch_%T_%U';
  backup current controlfile format '/BKP-RMAN-18/vahiddbbackup/ctrl_%T_%U';
  
  sql 'alter system switch all logfile';
  backup as backupset archivelog all not backed up format '/BKP-RMAN-18/vahiddbbackup/arch_%T_%U';
  
  delete force archivelog all backed up 3 times to device type disk;
  delete noprompt obsolete;
  release channel c1; release channel c2;
}

(نکته: برای فایل rman_inc_script.rman کافیست خط level 0 را به level 1 تغییر دهید).

گام ۳: تنظیم Crontab برای اجرای خودکار

حالا نوبت به زمان‌بندی می‌رسد. طبق خواسته ما:

  • شنبه تا پنج‌شنبه (روزهای 0,1,2,3,4,6 در لینوکس): اجرای نسخه inc
  • جمعه (روز 5): اجرای نسخه full

دستور crontab -e را بزنید و این دو خط را اضافه کنید:

# شنبه تا پنج‌شنبه ساعت ۲۱:۰۰ - بکاپ Incremental
00 21 * * 0-4,6 /BKP-RMAN-18/scripts/rman_backup_manager.sh inc >> /BKP-RMAN-18/vahiddbbackup/cron_inc.log 2>&1

# جمعه‌ها ساعت ۲۱:۰۰ - بکاپ Full (Level 0)
00 21 * * 5     /BKP-RMAN-18/scripts/rman_backup_manager.sh full >> /BKP-RMAN-18/vahiddbbackup/cron_full.log 2>&1

چرا این روش عالی است؟

  1. جداسازی منطق: اسکریپت Bash وظیفه مدیریت سیستم‌عامل و ورودی‌ها را دارد و فایل RMAN فقط دستورات دیتابیسی را اجرا می‌کند.
  2. پایداری در Cron: تمام متغیرهای محیطی به صورت صریح (Explicit) تعریف شده‌اند، پس اسکریپت در محیط محدود Cron از کار نمی‌افتد.
  3. امنیت داده: با گرفتن PFILE و سوئیچ لاگ بلافاصله بعد از بکاپ کنترل‌فایل، خیالتان از بابت داشتن آخرین تغییرات راحت است.
  4. مدیریت فضا: استفاده از delete obsolete و compressed backupset باعث می‌شود فضای دیسک بهینه مصرف شود.

تمرین نهایی:

یک بار اسکریپت را به صورت دستی با دستور ./rman_backup_manager.sh inc تست کنید و سپس در فایل crontab قرار دهید تا از اجرای صحیح آن در زمان‌بندی مطمئن شوید.