Codoloper

تصویر شاخص پراکسی چیست و چطور با Nginx یک سرور پروداکشن واقعی بسازیم؟

تصویر شاخص  پراکسی چیست و چطور با Nginx یک سرور پروداکشن واقعی بسازیم؟ | عکس

در این مقاله با مفهوم ریورس پراکسی آشنا می‌شوید، یاد می‌گیرید چرا هر سرور پروداکشنی به آن نیاز دارد، و قدم‌به‌قدم یک تنظیم واقعی با Nginx پیاده‌سازی می‌کنیم.

یک سوال ساده برای شروع

فرض کنید یک اپلیکیشن Node.js نوشته‌اید که روی پورت 3000 اجرا می‌شود. وقتی می‌خواهید آن را روی سرور منتشر کنید، چند سوال ذهنتان را درگیر می‌کند:

  • کاربر که example.com می‌زند، چطور به پورت 3000 می‌رسد؟

  • اگر چند سرویس مختلف داشتم، چطور مدیریتشان کنم؟

  • SSL چطور تنظیم می‌شود؟

  • اگر ترافیک زیاد شد، بار را چطور تقسیم کنم؟

جواب همه این سوال‌ها یک چیز است: ریورس پراکسی.

ریورس پراکسی دقیقاً چیست؟

قبل از اینکه وارد Nginx شویم، باید مفهوم پراکسی را درست بفهمیم.

فرق پراکسی معمولی با ریورس پراکسی

  • پراکسی معمولی (Forward Proxy): جلوی کلاینت می‌نشیند. یعنی درخواست‌های کاربر را می‌گیرد و به اینترنت می‌فرستد. سرور مقصد نمی‌داند درخواست از کجا آمده. در شبکه‌های سازمانی یا VPNها از این نوع استفاده می‌شود.

  • ریورس پراکسی (Reverse Proxy): جلوی سرور می‌نشیند. یعنی کلاینت فکر می‌کند دارد مستقیم با سرور اصلی صحبت می‌کند، اما در واقع با ریورس پراکسی در ارتباط است. ریورس پراکسی درخواست را می‌گیرد، پردازش می‌کند و به سرور مناسب می‌فرستد.

یک تصویر ذهنی ساده: ریورس پراکسی مثل نگهبان یا منشی جلوی یک ساختمان بزرگ است. هر کسی که می‌آید، او راهنمایی می‌کند به کدام اتاق برود.

چرا ریورس پراکسی اینقدر مهم است؟

در یک محیط پروداکشن واقعی، ریورس پراکسی وظایف زیادی به عهده می‌گیرد:

  • SSL Termination: مدیریت گواهینامه HTTPS در یک جا متمرکز می‌شود. سرویس‌های پشتی می‌توانند بدون SSL روی شبکه داخلی کار کنند.

  • Load Balancing: ترافیک بین چند نمونه از یک سرویس تقسیم می‌شود تا هیچ کدام اشباع نشوند.

  • Caching: پاسخ‌های تکراری ذخیره می‌شوند تا سرور اصلی بار کمتری داشته باشد.

  • Static File Serving: فایل‌های CSS، JS و تصاویر مستقیم از ریورس پراکسی سرو می‌شوند بدون اینکه سرور برنامه اصلی درگیر شود.

  • Security: آدرس و پورت سرورهای اصلی از کاربر پنهان می‌ماند. می‌توان rate limiting و فیلتر IP را در این لایه اعمال کرد.

  • Logging: تمام درخواست‌ها در یک نقطه لاگ می‌شوند.

چرا Nginx؟

Nginx (تلفظ: انجین-ایکس) یک وب‌سرور با کارایی بالاست که به عنوان ریورس پراکسی هم استفاده می‌شود. در سال ۲۰۰۴ توسط Igor Sysoev ساخته شد و امروز بیش از ۳۴ درصد از وب‌سرورهای دنیا از آن استفاده می‌کنند.

دلیل محبوبیتش ساختار event-driven آن است. برخلاف Apache که برای هر درخواست یک thread جدید باز می‌کند، Nginx می‌تواند هزاران اتصال همزمان را با منابع کم مدیریت کند.

گزینه‌های دیگری مثل Apache، Caddy، Traefik و HAProxy هم وجود دارند که هرکدام کاربرد خودشان را دارند. اما Nginx به دلیل پرفورمنس بالا، مستندات غنی و جامعه بزرگ، رایج‌ترین انتخاب برای محیط‌های پروداکشن است.

راه‌اندازی عملی: از صفر تا پروداکشن

محیط ما

فرض می‌کنیم یک سرور Ubuntu 22.04 داریم و می‌خواهیم:

  • یک اپ Node.js روی پورت 3000 داریم

  • یک اپ Python/FastAPI روی پورت 8000 داریم

  • دامنه example.com را داریم

  • می‌خواهیم SSL داشته باشیم

  • می‌خواهیم استاتیک فایل‌ها بهینه سرو شوند

مرحله اول: نصب Nginx

sudo apt update
sudo apt install nginx -y

# بررسی وضعیت سرویس
sudo systemctl status nginx

# اگر اجرا نیست:
sudo systemctl start nginx
sudo systemctl enable nginx

بعد از نصب، با رفتن به آدرس IP سرور باید صفحه پیش‌فرض Nginx را ببینید.

مرحله دوم: آشنایی با ساختار فایل‌های Nginx

/etc/nginx/
├── nginx.conf              ← فایل اصلی تنظیمات
├── sites-available/        ← تنظیمات سایت‌ها (غیرفعال)
├── sites-enabled/          ← تنظیمات فعال (لینک به sites-available)
├── conf.d/                 ← فایل‌های تنظیمات اضافی
└── snippets/               ← قطعه‌های قابل استفاده مجدد

فلسفه کار این است که تنظیمات را در sites-available می‌نویسید و با یک symlink در sites-enabled فعالشان می‌کنید. این باعث می‌شود بتوانید سایت‌ها را راحت فعال و غیرفعال کنید.

مرحله سوم: اولین تنظیم ریورس پراکسی

یک فایل تنظیمات برای اپ Node.js بسازیم:

sudo nano /etc/nginx/sites-available/myapp

server {
    listen 80;
    server_name example.com www.example.com;

    # لاگ‌های اختصاصی این سایت
    access_log /var/log/nginx/myapp_access.log;
    error_log /var/log/nginx/myapp_error.log;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        
        # هدرهای ضروری برای ریورس پراکسی
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # برای WebSocket اگر استفاده می‌کنید
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_cache_bypass $http_upgrade;
    }
}

حالا آن را فعال می‌کنیم:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/

# بررسی صحت تنظیمات - این دستور را قبل از هر reload اجرا کنید
sudo nginx -t

# اعمال تغییرات
sudo systemctl reload nginx

چرا آن هدرها ضروری هستند؟

  • proxy_set_header Host $host: بدون این، سرور اصلی نمی‌داند درخواست برای کدام دامنه آمده.

  • X-Real-IP و X-Forwarded-For: چون ریورس پراکسی جلوی سرور است، IP کلاینت در سرور اصلی به درستی نمایش داده نشده است. این هدرها IP واقعی کاربر را انتقال می‌دهند.

  • X-Forwarded-Proto: به سرور اصلی می‌گوید کلاینت با HTTP آمده یا HTTPS. بدون این، اگر HTTPS داشته باشید و redirect تولید کنید، ممکن است loop ایجاد شود.

مرحله چهارم: مدیریت چند سرویس

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

server {
    listen 80;
    server_name example.com;

    # مسیر اصلی به Node.js می‌رود
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # مسیر /api به FastAPI می‌رود
    location /api/ {
        proxy_pass http://localhost:8000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # فایل‌های استاتیک مستقیم از Nginx سرو می‌شوند
    location /static/ {
        root /var/www/myapp;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

یک نکته مهم: توجه به slash آخر در proxy_pass http://localhost:8000/ مهم است. وقتی slash دارد، مسیر /api/users به /users تبدیل می‌شود. اگر slash نباشد، به /api/users ارسال می‌شود.

مرحله پنجم: SSL با Certbot

# نصب certbot
sudo apt install certbot python3-certbot-nginx -y

# دریافت و نصب خودکار گواهینامه
sudo certbot --nginx -d example.com -d www.example.com

Certbot به صورت خودکار تنظیمات Nginx را برای HTTPS ویرایش می‌کند. بعد از اجرا، فایل تنظیمات شما چیزی شبیه به این خواهد بود:

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass http://localhost:3000;
        # ... بقیه هدرها
    }
}

# ریدایرکت HTTP به HTTPS
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

تجدید خودکار گواهینامه هم از قبل تنظیم شده. می‌توانید با این دستور بررسی کنید:

sudo certbot renew --dry-run

تنظیمات پروداکشن که اغلب نادیده گرفته می‌شوند

بهینه‌سازی فایل اصلی nginx.conf

# /etc/nginx/nginx.conf

user www-data;

# معمولاً برابر با تعداد هسته‌های CPU
worker_processes auto;

events {
    # حداکثر اتصال همزمان هر worker
    worker_connections 1024;
    use epoll;
    multi_accept on;
}

http {
    # پنهان کردن نسخه Nginx در هدرها
    server_tokens off;
    
    # بهینه‌سازی ارسال فایل
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    
    # فشرده‌سازی پاسخ‌ها
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
    gzip_min_length 1000;
    gzip_vary on;
    
    # timeoutهای مناسب
    keepalive_timeout 65;
    client_max_body_size 10M;
    
    # هدرهای امنیتی
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Rate Limiting

جلوگیری از درخواست‌های بیش از حد یک کلاینت:

http {
    # تعریف zone برای rate limiting
    # zone=api: اسم zone
    # 10m: ۱۰ مگابایت حافظه برای ذخیره IPها
    # rate=10r/s: حداکثر ۱۰ درخواست در ثانیه
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
    
    server {
        location /api/ {
            limit_req zone=api burst=20 nodelay;
            proxy_pass http://localhost:3000;
        }
        
        location /login {
            limit_req zone=login burst=3;
            proxy_pass http://localhost:3000;
        }
    }
}

burst=20 یعنی اگر ناگهان ۲۰ درخواست آمد قبول می‌شود، اما بعدش باید به نرخ عادی برگردد.

Load Balancing واقعی

http {
    # تعریف upstream با چند سرور
    upstream nodejs_backend {
        # روش پیش‌فرض: round robin
        server localhost:3000;
        server localhost:3001;
        server localhost:3002;
        
        # keepalive برای بهبود پرفورمنس
        keepalive 32;
    }
    
    # یا با وزن‌دهی (اگر یک سرور قوی‌تر است)
    upstream weighted_backend {
        server server1.internal weight=3;
        server server2.internal weight=1;
    }
    
    # ip_hash: هر کاربر همیشه به یک سرور می‌رود (مفید برای session)
    upstream sticky_backend {
        ip_hash;
        server server1.internal;
        server server2.internal;
    }
    
    server {
        location / {
            proxy_pass http://nodejs_backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }
    }
}

Caching برای API

http {
    # تعریف cache zone
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m 
                     max_size=1g inactive=60m use_temp_path=off;
    
    server {
        location /api/public/ {
            proxy_cache api_cache;
            proxy_cache_valid 200 10m;
            proxy_cache_valid 404 1m;
            
            # هدری که نشان می‌دهد پاسخ از cache آمده یا نه
            add_header X-Cache-Status $upstream_cache_status;
            
            proxy_pass http://localhost:3000;
        }
    }
}

یک سناریوی کامل پروداکشن

بگذارید همه چیز را در یک تنظیم واقعی کنار هم بگذاریم. فرض می‌کنیم یک استارتاپ داریم با:

  • فرانت‌اند React (فایل استاتیک)

  • بک‌اند Node.js

  • سرویس جداگانه برای آپلود فایل

# /etc/nginx/sites-available/startup.com

# Upstream تعریف‌ها
upstream api_backend {
    server localhost:3000;
    server localhost:3001 backup;
    keepalive 16;
}

upstream upload_service {
    server localhost:4000;
}

# Redirect HTTP به HTTPS
server {
    listen 80;
    server_name startup.com www.startup.com;
    return 301 https://$host$request_uri;
}

# سرور اصلی HTTPS
server {
    listen 443 ssl http2;
    server_name startup.com www.startup.com;

    # SSL
    ssl_certificate /etc/letsencrypt/live/startup.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/startup.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;

    # لاگ‌ها
    access_log /var/log/nginx/startup_access.log;
    error_log /var/log/nginx/startup_error.log warn;

    # فایل‌های استاتیک React
    root /var/www/startup/build;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
        expires 1h;
        add_header Cache-Control "public";
    }

    # فایل‌های استاتیک با cache طولانی
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # API
    location /api/ {
        limit_req zone=api burst=30 nodelay;

        proxy_pass http://api_backend/;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Timeout مناسب برای API
        proxy_connect_timeout 5s;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }

    # آپلود فایل (timeout بیشتر)
    location /upload/ {
        client_max_body_size 50M;
        proxy_read_timeout 300s;

        proxy_pass http://upload_service/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # WebSocket
    location /ws/ {
        proxy_pass http://api_backend/ws/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400s;
    }
}

دیباگ و عیب‌یابی

دستورات مفید

# بررسی صحت تنظیمات
sudo nginx -t

# reload بدون قطع اتصالات فعال
sudo systemctl reload nginx

# بررسی لاگ‌های خطا به صورت real-time
sudo tail -f /var/log/nginx/error.log

# بررسی اتصالات فعال
sudo nginx -s status

# نمایش تنظیمات کامل بعد از include
sudo nginx -T

# بررسی اینکه کدام فایل تنظیمات اعمال شده
sudo nginx -T | grep server_name

خطاهای رایج

  • 502 Bad Gateway: سرور اصلی در دسترس نیست یا crash کرده. ابتدا بررسی کنید آیا سرویس روی پورت مورد نظر اجرا است:

    curl http://localhost:3000/health
    netstat -tlnp | grep 3000
    
  • 504 Gateway Timeout: درخواست خیلی طول کشیده. proxy_read_timeout را افزایش دهید یا مشکل سرور اصلی را پیدا کنید.

  • 413 Request Entity Too Large: client_max_body_size را افزایش دهید.

  • WebSocket کار نمی‌کند: هدرهای Upgrade و Connection را بررسی کنید.

Nginx در برابر رقبا

Nginx در برابر Apache

Apache مدل process-based دارد و برای هر اتصال یک thread باز می‌کند. این در ترافیک بالا منابع زیادی مصرف می‌کند. Nginx با مدل event-driven چندین هزار اتصال را با یک worker process مدیریت می‌کند. اما Apache مزیت‌هایی هم دارد: .htaccess که اجازه می‌دهد تنظیمات در سطح دایرکتوری تعریف شوند، و پشتیبانی بهتر از برخی فناوری‌های قدیمی مثل mod_php.

Nginx در برابر Caddy

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

Nginx در برابر Traefik

Traefik برای محیط‌های container-native (Kubernetes، Docker Swarm) طراحی شده و کشف خودکار سرویس‌ها را پشتیبانی می‌کند. اگر با Kubernetes کار می‌کنید، Traefik یا ingress-nginx انتخاب‌های بهتری هستند.

مزایا و معایب این رویکرد

مزایا

ریورس پراکسی لایه‌ای از انتزاع بین کاربر و زیرساخت ایجاد می‌کند. می‌توانید سرورهای پشتی را بدون اطلاع کاربر تغییر دهید. SSL در یک جا مدیریت می‌شود. پرفورمنس بهتری برای فایل‌های استاتیک دارید. امنیت بیشتری دارید چون سرورهای اصلی مستقیم در معرض اینترنت نیستند.

معایب

یک لایه پیچیدگی اضافه می‌شود. دیباگ کردن مشکلات شبکه کمی سخت‌تر می‌شود چون باید مطمئن شوید مشکل از Nginx است یا سرویس پشتی. اگر Nginx crash کند، همه چیز از کار می‌افتد (که با high availability می‌توان حل کرد).

جمع‌بندی

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

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

نظر شخصی: اگر تازه شروع می‌کنید، با یک تنظیم ساده شروع کنید و کم‌کم گسترش دهید. سعی نکنید از ابتدا همه چیز را کامل کنید. اما حتماً از همان ابتدا SSL داشته باشید و هدرهای پراکسی را درست تنظیم کنید، این دو مورد را نمی‌توانید بعداً راحت اضافه کنید بدون اینکه کارها خراب شود.

سوالات متداول

آیا می‌توانم بدون Nginx هم اپلیکیشن را deploy کنم؟ بله، می‌توانید اپلیکیشن را مستقیم روی پورت 80 یا 443 اجرا کنید. اما در عمل این کار مشکلاتی دارد: SSL مدیریت کردن سخت‌تر است، نمی‌توانید چند سرویس روی یک IP داشته باشید، و پرفورمنس سرو فایل‌های استاتیک کمتر است.

تفاوت proxy_pass http://localhost:3000 و proxy_pass http://localhost:3000/ چیست؟ وقتی slash آخر هست، مسیر از URL حذف می‌شود. مثلاً location /api/ با proxy_pass http://localhost:3000/ یعنی /api/users به /users تبدیل می‌شود. بدون slash، مسیر کامل منتقل می‌شود.

چطور بفهمم Nginx درست کانفیگ شده؟ با دستور sudo nginx -t صحت syntax را بررسی کنید. بعد با ابزاری مثل curl -v هدرهای پاسخ را بررسی کنید و ببینید هدر Server چه می‌گوید و هدرهایی که تنظیم کرده‌اید وجود دارند یا نه.

آیا Nginx برای WebSocket مناسب است؟ بله، اما باید هدرهای Upgrade و Connection را تنظیم کنید و proxy_read_timeout را بالا ببرید چون اتصالات WebSocket طولانی‌مدت هستند.

چطور یک سرور Nginx را بدون downtime ریستارت کنم؟ از sudo systemctl reload nginx یا sudo nginx -s reload استفاده کنید. این دستورها کانفیگ جدید را بارگذاری می‌کنند بدون اینکه اتصالات فعال قطع شوند.

آیا Nginx می‌تواند جایگزین یک load balancer سخت‌افزاری شود؟ برای اکثر پروژه‌ها بله. Nginx در محیط‌هایی با صدها هزار درخواست در ثانیه هم به خوبی کار می‌کند. تنها در مقیاس‌های بسیار بزرگ (میلیون‌ها درخواست در ثانیه) ممکن است به راه‌حل‌های تخصصی‌تر نیاز باشد.

نوشته ها در
تبلیغات
کامنت جدید

برای ثبت کامنت وارد شوید

برای اینکه بتوانید زیر این پست کامنت بگذارید، باید وارد حساب کاربری خود شوید.

برای ادامه، وارد حساب خود شوید

بعد از ورود، دوباره به همین پست برمی‌گردید و می‌توانید کامنتتان را ثبت کنید.

ورود به حساب
کامنت‌ها

نظرات کاربران

دیدگاه‌هایی که برای این نوشته ثبت شده‌اند.

هنوز کامنتی برای این پست ثبت نشده است.