عنوان:

‫لینوکس برای توسعه‌دهندگان دات‌نت: از ویندوز تا کراس-پلتفرم؛ بخش سوم: دیپلوی و اجرای برنامه‌های دات‌نت روی لینوکس


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۴/۲۷ ۰۸:۰۵
آدرس: www.dntips.ir
تا اینجا یاد گرفتیم چگونه کدهای سازگار با لینوکس بنویسیم. اکنون زمان آن است که ببینیم چگونه اپلیکیشن خود را پکیج کرده و آن را به صورت پایدار و قابل اطمینان روی یک سرور لینوکس اجرا کنیم. در این بخش، سه روش اصلی و مدرن برای استقرار برنامه‌های دات‌نت را بررسی می‌کنیم.

۱. انواع دیپلوی (Deployment Models)

وقتی از دستور dotnet publish استفاده می‌کنید، باید تصمیم بگیرید که خروجی شما چگونه باشد. هر مدل مزایا و معایب خاص خود را دارد.

الف) وابسته به فریمورک (Framework-Dependent)

در این مدل، شما فقط کدهای کامپایل شده‌ی برنامه و کتابخانه‌های آن (DLLs) را منتشر می‌کنید.
  • مزیت: حجم خروجی بسیار کم است، چون فریمورک دات‌نت در آن وجود ندارد.
  • عیب: سرور لینوکس حتماً باید همان نسخه از .NET Runtime (یا نسخه‌ی بالاتر) را از قبل نصب شده داشته باشد.
دستور انتشار:
dotnet publish -c Release
نحوه‌ی اجرا روی سرور:
dotnet YourApp.dll

ب) مستقل و خودکفا (Self-Contained)

در این مدل، تمام اجزای .NET Runtime مورد نیاز به همراه اپلیکیشن شما در یک پوشه پکیج می‌شوند.
  • مزیت: برنامه شما روی هر سرور لینوکسی (با معماری یکسان) اجرا می‌شود، بدون هیچ پیش‌نیازی.
  • عیب: حجم خروجی به مراتب بیشتر است (معمولاً بالای ۶۰ مگابایت).
دستور انتشار (برای لینوکس ۶۴ بیتی):
dotnet publish -c Release --self-contained -r linux-x64
نحوه‌ی اجرا روی سرور: یک فایل اجرایی با نام پروژه‌ی شما ساخته می‌شود. ابتدا به آن مجوز اجرا بدهید:
chmod +x ./YourApp
./YourApp

ج) بهینه‌سازی‌های اضافی

  • ReadyToRun (R2R): این گزینه کدهای شما را از زبان میانی (IL) به کد ماشین (Native Code) کامپایل می‌کند. این کار سرعت اجرای اولیه‌ی برنامه (startup time) را به شدت افزایش می‌دهد، اما حجم خروجی را کمی بیشتر می‌کند. این گزینه را می‌توان با هر دو مدل بالا ترکیب کرد.
# مثال با مدل Self-Contained
dotnet publish -c Release --self-contained -r linux-x64 -p:PublishReadyToRun=true
  • فایل اجرایی واحد (Single File Executable): این گزینه تمام فایل‌های خروجی (شامل Runtime در حالت Self-Contained) را در یک فایل اجرایی واحد جمع می‌کند که جابجایی و اجرای آن را بسیار ساده می‌کند.
# مثال با هر دو بهینه‌سازی
dotnet publish -c Release --self-contained -r linux-x64 -p:PublishReadyToRun=true -p:PublishSingleFile=true

۲. اجرا به عنوان یک سرویس (Running as a Service) ⚙️

اجرای برنامه در خط فرمان (./YourApp) فقط برای تست مناسب است. اگر ترمینال را ببندید، برنامه متوقف می‌شود. در محیط پروداکشن، اپلیکیشن شما باید به عنوان یک سرویس (یا Daemon) در پس‌زمینه اجرا شود تا همیشه فعال بماند و در صورت ری‌استارت شدن سرور، به طور خودکار اجرا شود.
در ویندوز این کار با "Windows Services" انجام می‌شود. معادل مدرن و استاندارد آن در اکثر توزیع‌های لینوکس (مانند Ubuntu, CentOS, Debian) ابزاری به نام systemd است.

آموزش گام‌به‌گام ساخت یک سرویس systemd:

۱. ساخت فایل سرویس:
یک فایل با پسوند .service در مسیر /etc/systemd/system/ ایجاد کنید. نام فایل معمولاً نام اپلیکیشن شماست.
sudo nano /etc/systemd/system/kestrel-myapp.service

۲. نوشتن محتوای فایل سرویس:
محتوای زیر را در فایل کپی کرده و مطابق با اطلاعات پروژه خودتان ویرایش کنید.
[Unit]
Description=My Cool ASP.NET Core App running on Linux

[Service]
# مسیر پوشه‌ای که فایل‌های منتشر شده‌ی برنامه در آن قرار دارد
WorkingDirectory=/var/www/myapp

# دستوری که برای اجرای برنامه استفاده می‌شود
# اگر از مدل Self-Contained استفاده کردید: ExecStart=/var/www/myapp/YourApp
# اگر از مدل Framework-Dependent استفاده کردید: ExecStart=/usr/bin/dotnet /var/www/myapp/YourApp.dll
ExecStart=/var/www/myapp/YourApp

# سرویس همیشه باید ری‌استارت شود، مگر اینکه به صورت دستی متوقف شود
Restart=always
# زمان انتظار قبل از ری‌استارت در صورت بروز خطا (به ثانیه)
RestartSec=10
# کاربر و گروهی که برنامه با دسترسی آن‌ها اجرا می‌شود (برای امنیت بیشتر)
User=www-data
Group=www-data
# متغیرهای محیطی مورد نیاز برنامه (مثلاً برای حالت پروداکشن)
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false

[Install]
WantedBy=multi-user.target
۳. مدیریت سرویس با systemctl:
  • بارگذاری مجدد systemd برای شناسایی سرویس جدید:
  • sudo systemctl daemon-reload
  • فعال‌سازی سرویس (برای اجرای خودکار پس از بوت):
  • sudo systemctl enable kestrel-myapp.service
  • اجرای فوری سرویس:
  • sudo systemctl start kestrel-myapp.service
  • بررسی وضعیت سرویس:
  • sudo systemctl status kestrel-myapp.service
  • مشاهده لاگ‌های زنده برنامه:
  • sudo journalctl -fu kestrel-myapp.service

۳. کانتینرسازی با داکر (Containerization with Docker) 🐳

داکر روشی است برای بسته‌بندی اپلیکیشن و تمام نیازمندی‌های آن (مانند .NET Runtime) در یک واحد قابل حمل به نام کانتینر. این روش تضمین می‌کند که برنامه شما در محیط توسعه، تست و پروداکشن دقیقاً به یک شکل عمل کند.

چرا داکر برای توسعه‌دهندگان دات‌نت عالی است؟

  • یکپارچگی: دیگر نگران نصب بودن یا نبودن نسخه‌ی صحیح دات‌نت روی سرور نیستید.
  • ایزوله‌سازی: هر کانتینر محیط ایزوله‌ی خود را دارد و با سایر برنامه‌ها تداخلی ایجاد نمی‌کند.
  • تکرارپذیری: با یک فایل به نام Dockerfile، می‌توانید ایمیج برنامه را بارها و بارها به صورت خودکار بسازید.

نوشتن یک Dockerfile بهینه

در ریشه‌ی پروژه، فایلی به نام Dockerfile (بدون پسوند) بسازید و محتوای زیر را در آن قرار دهید. این فایل از تکنیک Multi-stage builds استفاده می‌کند که حجم ایمیج نهایی را به شدت کاهش می‌دهد.
# ----- مرحله ۱: بیلد کردن برنامه (استفاده از ایمیج کامل SDK) -----
# از ایمیج رسمی .NET SDK به عنوان پایه‌ی بیلد استفاده می‌کنیم
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
WORKDIR /app

# فایل‌های پروژه را کپی می‌کنیم
COPY *.csproj ./
# پکیج‌های نوگت را ریستور می‌کنیم
RUN dotnet restore

# تمام سورس کد را کپی می‌کنیم
COPY . ./
# برنامه را در حالت Release پابلیش می‌کنیم
RUN dotnet publish -c Release -o out


# ----- مرحله ۲: ساخت ایمیج نهایی (استفاده از ایمیج سبک Runtime) -----
# از ایمیج سبک ASP.NET Runtime استفاده می‌کنیم که برای اجرای برنامه کافی است
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app

# فایل‌های پابلیش شده از مرحله‌ی بیلد را به این مرحله کپی می‌کنیم
COPY --from=build-env /app/out .

# مشخص می‌کنیم که برنامه روی کدام پورت داخل کانتینر گوش می‌دهد
EXPOSE 8080

# نقطه‌ی ورود یا دستوری که با اجرای کانتینر باید اجرا شود
ENTRYPOINT ["dotnet", "YourApp.dll"]

ساخت و اجرای کانتینر

ساخت ایمیج داکر: در ترمینالی که در کنار Dockerfile باز شده، دستور زیر را اجرا کنید:
  • docker build -t my-webapp .
  • (-t my-webapp یک تگ یا نام برای ایمیج شما مشخص می‌کند)
اجرای کانتینر:
  • docker run -d -p 8080:8080 my-webapp
  • -d: اجرای کانتینر در حالت detached (در پس‌زمینه).
  • -p 8080:8080: پورت 8080 سیستم میزبان (Host) را به پورت 8080 کانتینر متصل (map) می‌کند. حالا با مراجعه به http://localhost:8080 می‌توانید اپلیکیشن را ببینید.