اصول Automation در DevOps

چهار اصل کلیدی

وقتی automation را برای production می‌نویسید، چهار مفهوم به شما کمک می‌کنند تا کدی قابل‌اتکا بسازید:

1. Repeatability — تکرارپذیری

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

مثال بد:

1# فقط وابسته به زمان اجرا — امکان دارد در روزهای بعد overwrite کند 
2backup_file="backup-$(date +%H%M).tar.gz"
3tar -czf "$backup_file" /var/www

مثال خوب:

1# نام فایل از timestamp کامل ساخته می‌شود
2backup_file="backup-$(date +%Y%m%d-%H%M%S).tar.gz"
3tar -czf "/backups/$backup_file" /var/www || exit 1
4echo "Backup created: $backup_file"

2. Consistency — یکنواختی

یکنواختی یعنی در محیط‌های مختلف رفتار یکسان داشته باشید. اسکریپتی که روی لپ‌تاپ شما کار می‌کند اما روی سرور production شکست می‌خورد، مشکل consistency دارد.

راه‌حل:

  • از مسیرهای مطلق استفاده کنید
  • dependencyها را چک کنید
  • محیط را به‌صورت صریح تنظیم کنید
 1#!/usr/bin/env bash
 2set -euo pipefail
 3
 4# چک کردن ابزارهای مورد نیاز
 5for cmd in git systemctl nginx; do
 6  if ! command -v "$cmd" &>/dev/null; then
 7    echo "ERROR: $cmd not found" >&2
 8    exit 1
 9  fi
10done
11
12# استفاده از مسیر مطلق
13APP_DIR="/opt/myapp"
14cd "$APP_DIR" || exit 1
15
16# محیط صریح
17export PATH="/usr/local/bin:/usr/bin:/bin"

3. Idempotency — تک‌نتیجه‌بودن

Idempotency یعنی اجرای چندباره یک عملیات، همان نتیجهٔ یک‌بار اجرا را می‌دهد. این مفهوم در automation بسیار مهم است چون اجازه می‌دهد اسکریپت را بدون نگرانی از عوارض جانبی دوباره اجرا کنید.

مثال non-idempotent:

1# هر بار یک خط اضافه می‌شود
2echo "nameserver 8.8.8.8" >> /etc/resolv.conf

مثال idempotent:

1# فقط اگر وجود نداشته باشد، اضافه می‌شود
2if ! grep -q "nameserver 8.8.8.8" /etc/resolv.conf; then
3  echo "nameserver 8.8.8.8" >> /etc/resolv.conf
4fi

یک مثال واقعی‌تر:

 1#!/usr/bin/env bash
 2set -euo pipefail
 3
 4# ایجاد کاربر — فقط اگر وجود نداشته باشد
 5if ! id myuser &>/dev/null; then
 6  useradd -r -s /usr/sbin/nologin myuser
 7  echo "User created"
 8else
 9  echo "User already exists"
10fi
11
12# ایجاد دایرکتوری — idempotent است
13mkdir -p /opt/myapp/data
14
15# نصب پکیج — package manager خودش idempotent است
16apt-get install -y nginx

Idempotency به شما اجازه می‌دهد اسکریپت را در CI/CD یا به‌عنوان بخشی از recovery اجرا کنید بدون این‌که نگران تغییرات ناخواسته باشید.

4. Failure Handling — مدیریت خطا

سیستم‌های واقعی همیشه کامل نیستند: شبکه می‌تواند قطع شود، دیسک پر شود، سرویس پاسخ ندهد. یک automation خوب باید خطاهای قابل پیش‌بینی را مدیریت کند:

 1#!/usr/bin/env bash
 2set -euo pipefail
 3
 4MAX_RETRIES=3
 5RETRY_DELAY=5
 6
 7download_with_retry() {
 8  local url="$1"
 9  local output="$2"
10  local attempt=1
11
12  while [ $attempt -le $MAX_RETRIES ]; do
13    echo "Attempt $attempt of $MAX_RETRIES"
14    
15    if curl -fsSL -o "$output" "$url"; then
16      echo "Download successful"
17      return 0
18    fi
19    
20    echo "Download failed, retrying in ${RETRY_DELAY}s..."
21    sleep $RETRY_DELAY
22    attempt=$((attempt + 1))
23  done
24
25  echo "ERROR: Download failed after $MAX_RETRIES attempts" >&2
26  return 1
27}
28
29# استفاده
30download_with_retry "https://example.com/file.tar.gz" "/tmp/file.tar.gz"

یک مثال دیگر — rollback در صورت شکست:

 1#!/usr/bin/env bash
 2set -euo pipefail
 3
 4deploy_new_version() {
 5  local backup_dir="/backups/app-$(date +%s)"
 6  
 7  # گرفتن backup قبل از تغییر
 8  echo "Creating backup..."
 9  cp -a /opt/myapp "$backup_dir"
10  
11  # متوقف کردن سرویس
12  systemctl stop myapp
13  
14  # تلاش برای deploy نسخه جدید
15  if ! git pull origin main; then
16    echo "ERROR: Git pull failed, rolling back..." >&2
17    rm -rf /opt/myapp
18    mv "$backup_dir" /opt/myapp
19    systemctl start myapp
20    exit 1
21  fi
22  
23  # اگر service بالا نیامد، rollback
24  systemctl start myapp
25  sleep 2
26  
27  if ! systemctl is-active --quiet myapp; then
28    echo "ERROR: Service failed to start, rolling back..." >&2
29    rm -rf /opt/myapp
30    mv "$backup_dir" /opt/myapp
31    systemctl start myapp
32    exit 1
33  fi
34  
35  echo "Deployment successful"
36  rm -rf "$backup_dir"
37}
38
39deploy_new_version

تمرین: تشخیص مشکلات

این اسکریپت چه مشکلاتی دارد؟

1#!/bin/bash
2cd /var/www
3rm -rf *
4git clone https://github.com/user/repo.git .
5npm install
6npm run build
7service apache2 restart

مشکلات:

  1. No error handling: set -e ندارد — اگر git clone fail شود، بقیه دستورها اجرا می‌شوند
  2. Destructive without backup: قبل از rm -rf * backup نمی‌گیرد
  3. Not idempotent: git clone دفعهٔ دوم fail می‌شود چون دایرکتوری پر است
  4. No dependency check: چک نمی‌کند که git یا npm وجود دارند
  5. No logging: در صورت خطا، نمی‌دانید در کجا مشکل بوده

نسخهٔ بهتر:

 1#!/usr/bin/env bash
 2set -euo pipefail
 3
 4REPO_URL="https://github.com/user/repo.git"
 5DEPLOY_DIR="/var/www"
 6
 7# Check dependencies
 8for cmd in git npm; do
 9  command -v "$cmd" >/dev/null || { echo "ERROR: $cmd not found" >&2; exit 1; }
10done
11
12# Backup before deploy
13backup_dir="/backups/www-$(date +%s)"
14mkdir -p "$(dirname "$backup_dir")"
15cp -a "$DEPLOY_DIR" "$backup_dir"
16
17cd "$DEPLOY_DIR" || exit 1
18
19# Idempotent git pull
20if [ -d .git ]; then
21  git pull origin main
22else
23  git clone "$REPO_URL" .
24fi
25
26npm install
27npm run build
28
29# Restart and verify
30systemctl restart apache2
31sleep 2
32
33if systemctl is-active --quiet apache2; then
34  echo "Deployment successful"
35  rm -rf "$backup_dir"
36else
37  echo "ERROR: Apache failed to start, rolling back" >&2
38  rm -rf "$DEPLOY_DIR"
39  mv "$backup_dir" "$DEPLOY_DIR"
40  systemctl start apache2
41  exit 1
42fi

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

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