بلاگ/دیپلوی اپلیکیشن Go روی سرور اختصاصی و VPS با systemd و Nginx
به‌روز شده

دیپلوی اپلیکیشن Go روی سرور اختصاصی و VPS با systemd و Nginx

1405/07/170 بازدید
دیپلوی اپلیکیشن Go روی سرور اختصاصی و VPS با systemd و Nginx

دیپلوی اپلیکیشن Go روی سرور اختصاصی و VPS با systemd و Nginx

اگر اپلیکیشن Go نوشته‌اید و می‌خواهید آن را روی یک سرور اختصاصی یا VPS اجرا کنید، لازم نیست سراغ Docker یا Kubernetes بروید. خروجی کامپایل Go یک فایل باینری مستقل است که بدون نیاز به runtime یا پکیج جانبی روی هر سرور لینوکسی اجرا می‌شود. تنها چیزی که کم دارد یک مدیر پردازش برای بالا نگه داشتن سرویس و یک reverse proxy برای مدیریت ترافیک HTTP/HTTPS است؛ همین‌جاست که systemd و Nginx وارد می‌شوند.

دیپلوی اپلیکیشن Go روی سرور اختصاصی و VPS با systemd و Nginx

چرا دیپلوی Go ساده‌تر از خیلی زبان‌های دیگر است؟

در زبان‌هایی مثل Node.js یا Python، روی سرور باید runtime مناسب، پکیج منیجر، و وابستگی‌ها را نصب کنید. در Go این مرحله حذف می‌شود. کامپایلر Go کد را به یک باینری استاتیک تبدیل می‌کند که تمام وابستگی‌ها داخلش است. کافی است این فایل را روی سرور کپی کنید و اجرا شود.

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

ساخت باینری استاتیک برای دیپلوی

برای این‌که باینری واقعاً مستقل باشد و به کتابخانه‌های داینامیک سیستم (مثل glibc) وابسته نباشد، باید CGO را غیرفعال کنید:

GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o myapp main.go
  • GOOS=linux GOARCH=amd64 کامپایل برای معماری سرور لینوکسی را مشخص می‌کند، حتی اگر این دستور را روی مک یا ویندوز اجرا کنید.
  • CGO_ENABLED=0 مانع لینک‌شدن به کتابخانه‌های C سیستم می‌شود و باینری را کاملاً استاتیک می‌کند.
  • -ldflags="-s -w" اطلاعات دیباگ و symbol table را حذف می‌کند و حجم فایل خروجی را کاهش می‌دهد.

اگر سرور شما معماری ARM دارد (مثلاً برخی سرورهای اختصاصی جدید)، کافی است GOARCH=arm64 را جایگزین کنید.

آماده‌سازی سرور قبل از دیپلوی

قبل از انتقال باینری، چند قدم پایه روی سرور لازم است:

  1. یک کاربر غیر روت برای اجرای اپلیکیشن بسازید، چون اجرای سرویس‌های شبکه با کاربر root توصیه نمی‌شود: bash sudo adduser --system --group appuser
  2. یک دایرکتوری مشخص برای اپلیکیشن بسازید: bash sudo mkdir -p /opt/myapp sudo chown appuser:appuser /opt/myapp
  3. فایل باینری را با scp یا rsync به سرور منتقل کنید: bash scp myapp user@server-ip:/opt/myapp/myapp

همین سه قدم کافی است، چه روی یک VPS اجرا کنید چه روی سرور اختصاصی.

اجرای اپلیکیشن با systemd

اجرای مستقیم باینری در یک ترمینال باز مشکل‌ساز است: با بسته‌شدن session، پردازش هم متوقف می‌شود. systemd این مشکل را حل می‌کند و اپلیکیشن را به‌صورت یک سرویس لینوکسی مدیریت می‌کند، با راه‌اندازی خودکار بعد از ریبوت سرور و ری‌استارت خودکار در صورت کرش.

فایل سرویس را در مسیر /etc/systemd/system/myapp.service بسازید:

[Unit]
Description=My Go Application
After=network.target

[Service]
Type=simple
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/myapp
Restart=on-failure
RestartSec=5
Environment=PORT=8080
EnvironmentFile=-/opt/myapp/.env

[Install]
WantedBy=multi-user.target

نکات کلیدی این فایل:

  • Restart=on-failure باعث می‌شود systemd در صورت خروج غیرمنتظره اپلیکیشن، آن را دوباره اجرا کند.
  • EnvironmentFile=-/opt/myapp/.env متغیرهای محیطی (مثل connection string دیتابیس) را از یک فایل جداگانه می‌خواند؛ علامت - در ابتدا یعنی اگر فایل وجود نداشت، systemd خطا ندهد.
  • User=appuser تضمین می‌کند اپلیکیشن با دسترسی محدود اجرا شود.

اگر چند اپلیکیشن روی یک سرور اجرا می‌کنید و می‌خواهید مصرف CPU و RAM هرکدام را محدود کنید، systemd از طریق cgroups این امکان را هم می‌دهد؛ جزئیات در محدود کردن مصرف CPU و RAM سرویس‌ها با systemd و cgroups.

حالا سرویس را فعال و اجرا کنید:

sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp
sudo systemctl status myapp

برای دیدن لاگ‌های برنامه:

sudo journalctl -u myapp -f

هر بار که فایل سرویس را تغییر دادید، دستور daemon-reload را دوباره اجرا کنید؛ فراموش‌کردن این مرحله یکی از رایج‌ترین دلایل این است که تغییرات اعمال نمی‌شوند.

تنظیم Nginx به‌عنوان ریورس پروکسی

اکثر اپلیکیشن‌های Go روی پورتی مثل 8080 گوش می‌دهند، نه پورت 80 یا 443. قرار دادن Nginx جلوی اپلیکیشن چند مزیت دارد: مدیریت HTTPS، امکان اجرای چند اپلیکیشن پشت یک IP با دامنه‌های متفاوت، و لایه‌ای اضافه برای rate limiting یا caching.

یک فایل جدید در /etc/nginx/sites-available/myapp بسازید:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
    }
}

سپس آن را فعال کنید:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

معماری دیپلوی Go: درخواست کلاینت از طریق Nginx به عنوان ریورس پروکسی HTTPS به اپلیکیشن Go که با systemd روی پورت داخلی اجرا می‌شود

فعال‌سازی HTTPS با Let's Encrypt

برای گرفتن گواهی SSL رایگان با Let's Encrypt و Certbot و تنظیم خودکار HTTPS در Nginx، از certbot استفاده کنید:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

certbot فایل تنظیمات Nginx را خودش به‌روزرسانی می‌کند و redirect از HTTP به HTTPS را هم اضافه می‌کند. تمدید گواهی هم به‌صورت خودکار از طریق یک cron job یا systemd timer انجام می‌شود که همراه بسته نصب می‌شود.

دیپلوی مستقیم با systemd در مقابل Docker یا Kubernetes

برای یک اپلیکیشن Go، قبل از انتخاب روش دیپلوی خوب است هزینه و پیچیدگی هر گزینه را بسنجید:

معیارsystemd + NginxDocker / Kubernetes
پیچیدگی راه‌اندازی اولیهکم، چند فایل تنظیماتبیشتر، نیاز به image build و orchestration
مصرف منابع سرورحداقلی، بدون overhead کانتینربالاتر به دلیل لایه‌های اضافه
مناسب براییک یا چند سرویس روی یک یا چند سرور مشخصتعداد زیاد سرویس با نیاز به مقیاس‌پذیری افقی خودکار
سرعت دیباگمستقیم با journalctl و دسترسی به فایل‌سیستم سرورنیاز به ابزار اضافه برای لاگ و دسترسی به کانتینر
هزینه یادگیری تیمکمبیشتر

اگر پروژه یک یا چند سرویس Go دارد که روی تعداد مشخصی سرور اجرا می‌شوند و نیازی به auto-scaling پیچیده نیست، ترکیب systemd و Nginx ساده‌تر، سبک‌تر و قابل نگهداری‌تر است. Docker و Kubernetes زمانی ارزش overhead خودشان را نشان می‌دهند که تعداد سرویس‌ها و نیاز به مقیاس‌پذیری خودکار زیاد شود.

نکات امنیتی و نگهداری

چند نکته برای این‌که این روش دیپلوی در محیط پروداکشن پایدار بماند:

  • فایروال را فقط روی پورت‌های لازم (80، 443، و پورت SSH) باز نگه دارید؛ پورت اپلیکیشن (مثلاً 8080) نباید مستقیماً از بیرون قابل دسترسی باشد.
  • برای به‌روزرسانی بدون قطعی، باینری جدید را با نام دیگری کنار باینری قبلی بگذارید، لینک symlink را به نسخه جدید تغییر دهید و فقط systemctl restart myapp را اجرا کنید؛ این کار downtime را به چند صدم ثانیه محدود می‌کند.
  • لاگ‌های journald را با journalctl --vacuum-time=30d محدود کنید تا فضای دیسک پر نشود.
  • اگر چند اپلیکیشن Go روی یک سرور دارید، برای هر کدام یک فایل سرویس و یک server block جدا در Nginx تعریف کنید تا مدیریت و دیباگ هرکدام مستقل باشد.

جمع‌بندی

دیپلوی Go روی سرور اختصاصی یا VPS نیازی به ابزارهای پیچیده ندارد. یک باینری استاتیک، یک فایل سرویس systemd، و یک تنظیم ساده Nginx برای reverse proxy و HTTPS کافی است تا اپلیکیشن به‌صورت پایدار و با ری‌استارت خودکار در پروداکشن اجرا شود. این روش برای اغلب پروژه‌های Go، به‌خصوص در مراحل اولیه یا با مقیاس متوسط، سریع‌تر از راه‌اندازی Docker یا Kubernetes به نتیجه می‌رسد.

اگر هنوز سروری برای اجرای این تنظیمات ندارید، می‌توانید با یک VPS آریانت در چند دقیقه شروع کنید. برای پروژه‌هایی که به منابع اختصاصی و پایدارتر نیاز دارند، سرور اختصاصی آریانت گزینه مناسبی برای اجرای همین تنظیمات در مقیاس بزرگ‌تر است.