از کار دستی تا Automation

چرا Automation؟

فرض کنید هر بار که می‌خواهید یک نسخه‌ی جدید از برنامه را deploy کنید، باید این کارها را انجام دهید:

  1. سرویس قدیمی را متوقف کنید
  2. کد جدید را از Git بگیرید
  3. dependencyها را نصب کنید
  4. تست‌ها را اجرا کنید
  5. build بگیرید
  6. پیکربندی را چک کنید
  7. سرویس جدید را بالا بیاورید
  8. health check انجام دهید

اگر این کارها را دستی انجام دهید، هر بار احتمال خطا وجود دارد: ممکن است یک مرحله را فراموش کنید، ترتیب را اشتباه بزنید یا در یکی از دستورها تایپ غلط داشته باشید. حتی اگر همه‌چیز را یادداشت کنید، باز هم زمان‌بر است و با افزایش تعداد سرورها، غیرممکن می‌شود.

راه‌حل این است که این فرایند را خودکار کنید: یک‌بار به‌درستی تعریف می‌کنید، بعد هربار که نیاز دارید، آن را اجرا می‌کنید. نتیجه همیشه یکسان است، سریع‌تر است و خطای انسانی را حذف می‌کند.

Script ساده vs. Automation قابل‌اتکا

نوشتن یک اسکریپت ساده کار سختی نیست — چند دستور را پشت‌سرهم می‌چینید و اجرا می‌کنید. اما یک automation قابل‌اتکا ویژگی‌های بیشتری دارد:

Exit Status

یک automation خوب وقتی با مشکل مواجه می‌شود، با کد خروج غیرصفر متوقف می‌شود و دلیل را گزارش می‌دهد. در Bash، این کار با set -e و بررسی $? انجام می‌شود:

1#!/usr/bin/env bash
2set -euo pipefail
3
4if ! systemctl is-active --quiet nginx; then
5  echo "ERROR: nginx is not running" >&2
6  exit 1
7fi
8
9echo "nginx is active"

اگر nginx خاموش باشد، اسکریپت با کد 1 خارج می‌شود و پیام خطا را به stderr می‌فرستد. این رفتار به ابزارهای بالاتر (مثل CI/CD) اجازه می‌دهد بفهمند اسکریپت موفق بوده یا نه.

Arguments و Configuration

اسکریپتی که مسیرها یا مقادیر را hardcode کرده، فقط روی یک سیستم کار می‌کند. یک automation خوب از متغیرهای محیطی و آرگومان‌ها استفاده می‌کند:

 1#!/usr/bin/env bash
 2set -euo pipefail
 3
 4DEPLOY_ENV="${1:-staging}"
 5APP_DIR="${APP_DIR:-/opt/myapp}"
 6
 7echo "Deploying to $DEPLOY_ENV environment"
 8echo "App directory: $APP_DIR"
 9
10cd "$APP_DIR" || exit 1
11git pull origin main
12systemctl restart "myapp-$DEPLOY_ENV"

حالا می‌توانید این اسکریپت (فرضی) را با ./deploy.sh production یا با تنظیم APP_DIR=/srv/app در محیط‌های مختلف اجرا کنید.

Error Handling

اگر یکی از مراحل شکست بخورد، چه اتفاقی باید بیفتد؟ آیا باید ادامه دهید یا متوقف شوید؟ یک automation خوب این را مشخص می‌کند:

 1#!/usr/bin/env bash
 2set -euo pipefail
 3
 4{
 5    # تلاش برای گرفتن قفل
 6    if ! flock -n 200; then
 7        echo "ERROR: Another deployment is running" >&2
 8        exit 1
 9    fi
10
11    # اگر تست‌ها fail شدند، deploy نکن
12    if ! make test; then
13        echo "ERROR: Tests failed" >&2
14        exit 1
15    fi
16
17    echo "Tests passed, deploying..."
18    make deploy
19} 200>/var/lock/deploy.lock

این اسکریپت از flock برای جلوگیری از اجرای هم‌زمان استفاده می‌کند و قبل از deploy، تست‌ها را چک می‌کند.

Logging و Output

اسکریپتی که فقط کارها را بی‌صدا انجام می‌دهد، وقتی مشکلی پیش می‌آید، عیب‌یابی آن سخت است. یک automation خوب گزارش می‌دهد چه کاری انجام می‌دهد:

 1#!/usr/bin/env bash
 2set -euo pipefail
 3
 4log() {
 5  echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" >&2
 6}
 7
 8log "Starting deployment"
 9log "Stopping service"
10systemctl stop myapp || { log "Failed to stop service"; exit 1; }
11
12log "Pulling latest code"
13git pull origin main || { log "Failed to pull code"; exit 1; }
14
15log "Restarting service"
16systemctl start myapp || { log "Failed to start service"; exit 1; }
17
18log "Deployment complete"

هر خط از log با timestamp همراه است و به stderr می‌رود تا با خروجی عادی اسکریپت مخلوط نشود.

تفاوت Script و Automation در عمل

یک script معمولاً کاری را یک‌بار انجام می‌دهد و فرض می‌کند همه‌چیز آماده است. یک automation باید:

  • چندبار اجرا شود و هربار نتیجهٔ یکسان بدهد (repeatability)
  • وضعیت فعلی سیستم را چک کند و فقط در صورت نیاز تغییر دهد (idempotency)
  • خطاهای قابل پیش‌بینی را مدیریت کند
  • در محیط‌های مختلف (dev, staging, production) کار کند
  • خروجی و لاگ قابل‌فهم داشته باشد

در بخش‌های بعدی، این مفاهیم را با مثال‌های عملی بیشتر می‌بینیم و یاد می‌گیریم چطور workflow چندمرحله‌ای بسازیم که این اصول را رعایت کند.

منابع یادگیری

مستندات و مقالات

ویدیو