استقرار ASP.NET Core روی Plesk ویندوز (IIS) قدم‌به‌قدم

پاسخ کوتاه: برنامه را publish کنید (اگر میزبان Hosting Bundle دات‌نت ۱۰ دارد framework-dependent، وگرنه self-contained برای win-x64)، به سایت application pool جداگانه با «No Managed Code» بدهید، یک web.config نگه دارید که ASP.NET Core Module را in-process اجرا کند، secretها را در متغیرهای محیطی داخل web.config خود سرور بگذارید و با این ترتیب مستقر کنید: اول app_offline.htm، بعد فایل‌ها، بعد migration، بعد حذف app_offline.htm. بیشتر خطاهای بعدی 500.30 هستند (برنامه هنگام شروع خراب شد؛ لاگ stdout را بخوانید) یا 500.31 (runtime نیست).

یک مثال واقعی: «روی سیستم من کار می‌کند، روی هاست ۵۰۰»

برنامه لوکال اجرا می‌شود، فایل‌ها آپلود شده‌اند و سایت HTTP Error 500.30 – ASP.NET Core app failed to start نشان می‌دهد. هیچ چیز در مرورگر نمی‌گوید چرا. علت‌های معمول کسل‌کننده‌اند: connection string روی سرور نیست، دیتابیس در دسترس برنامه نیست، پوشه‌ای که برنامه باید در آن بنویسد دسترسی ندارد، یا migrationی اجرا نشده. وقتی خطا را ببینید هر کدام پنج دقیقه کار دارد؛ وقتی نبینید خیلی بیشتر.

این راهنما ترتیب کارهایی است که این خطاها را دیدنی و قابل اجتناب می‌کند.

۱. publish

# Framework-dependent: small, needs the .NET 10 Hosting Bundle on the server
dotnet publish src/Site.Web -c Release -o artifacts/publish

# Self-contained fallback (HTTP 500.31): ships the runtime, about 100 MB
dotnet publish src/Site.Web -c Release -r win-x64 --self-contained -o artifacts/publish-sc

SDK مقدار درست processPath را در web.config منتشرشده می‌نویسد: برای framework-dependent همان dotnet همراه با .\Site.Web.dll و برای self-contained فایل .\Site.Web.exe.

۲. web.config

یک web.config در پروژه نگه دارید تا تنظیمات ماژول نسخه‌دار باشند:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet"
                  stdoutLogEnabled="false"
                  stdoutLogFile=".\logs\stdout"
                  hostingModel="inprocess" />
    </system.webServer>
  </location>
</configuration>

hostingModel="inprocess" برنامه را داخل خود worker process در IIS اجرا می‌کند؛ سریع‌تر از proxy کردن به یک Kestrel جداگانه.

۳. تنظیمات Plesk

  • application pool اختصاصی با .NET CLR version: No Managed Code (ASP.NET Core از CLR کلاسیک استفاده نمی‌کند)، pipeline از نوع Integrated و Enable 32-bit applications: False (برای build از نوع win-x64 لازم است).
  • در هر pool فقط یک برنامه ASP.NET Core. دو برنامه in-process در یک pool (مثلاً دامنه اصلی و زیردامنه beta.) خطای HTTP 500.35 می‌دهند.
  • SSL: گواهی Let's Encrypt بگیرید و ریدایرکت دائمی ۳۰۱ از HTTP به HTTPS را فعال کنید.

۴. secretها: متغیرهای محیطی در web.config سرور

هاست اشتراکی به‌ندرت جایی برای نگهداری secret دارد. ASP.NET Core Module می‌تواند متغیر محیطی تزریق کند و ASP.NET Core جداکننده __ را همان : می‌خواند:

<aspNetCore processPath="dotnet" arguments=".\Site.Web.dll" hostingModel="inprocess">
  <environmentVariables>
    <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" />
    <environmentVariable name="ConnectionStrings__Default"
                         value="Server=...;Database=...;User Id=...;Password=...;Encrypt=True;TrustServerCertificate=True" />
  </environmentVariables>
</aspNetCore>

این را فقط روی سرور و در File Manager پنل Plesk ویرایش کنید و web.config را از همگام‌سازی استقرار مستثنا کنید تا هیچ استقراری بازنویسی‌اش نکند. TrustServerCertificate=True اغلب لازم است چون SQL Server میزبان گواهی self-signed دارد و Microsoft.Data.SqlClient به‌صورت پیش‌فرض رمزگذاری می‌کند.

۵. استقرار بدون قفل فایل: app_offline.htm

تا وقتی برنامه در حال اجراست، IIS فایل‌های DLL را باز نگه می‌دارد. ماژول دنبال فایلی به نام app_offline.htm می‌گردد: وقتی ظاهر شود، برنامه خاموش می‌شود و محتوای همان فایل نمایش داده می‌شود.

# 1. Take the app offline (upload a small "updating" page)
# 2. Upload everything from artifacts/publish EXCEPT web.config
# 3. Run pending migrations (next section)
# 4. Delete app_offline.htm; the next request starts the new version

Web Deploy یا FTP هر دو کار می‌کنند. اسکریپتش کنید و از سیستمی اجرا کنید که به میزبان دسترسی دارد.

۶. migration به‌عنوان گام جدا

در محیط تولید Database.Migrate() را هنگام شروع برنامه صدا نزنید؛ وگرنه یک migration ناموفق سایت را از کار می‌اندازد. migration را صریح اجرا کنید:

# A: migration bundle from your PC (needs remote SQL access to the host)
dotnet ef migrations bundle --project src/Site.Infrastructure --startup-project src/Site.Web -r win-x64 --self-contained -o efbundle.exe --force
./efbundle.exe --connection "<production connection string>"

# B: idempotent SQL script, run in the host's database web tool
dotnet ef migrations script --idempotent --project src/Site.Infrastructure --startup-project src/Site.Web -o migrate.sql

۷. دسترسی‌ها و لاگ

به کاربر application pool فقط روی پوشه‌هایی که برنامه در آن‌ها می‌نویسد دسترسی Modify بدهید: لاگ و آپلود (آپلودها را بیرون از wwwroot نگه دارید). هیچ چیز دیگری نباید قابل نوشتن باشد.

جدول رفع اشکال

خطا یعنی رفع
500.30 برنامه هنگام شروع خطا داد stdoutLogEnabled="true" را بگذارید، مطمئن شوید logs\ هست و قابل نوشتن است، خطا را بازتولید کنید، logs\stdout_*.log را بخوانید و بعد دوباره خاموشش کنید. معمولاً connection string نیست، دیتابیس در دسترس نیست، migrationی مانده یا دسترسی نوشتن نیست.
500.31 runtime پیدا نشد Hosting Bundle متناظر را نصب کنید، یا self-contained مستقر کنید.
500.32 ناهمخوانی ۳۲/۶۴ بیتی گزینه 32-bit applications را در pool خاموش کنید.
500.35 دو برنامه in-process در یک pool به هر سایت pool جدا بدهید.
500.19 پیکربندی نامعتبر یا ماژول نصب نیست XML را بررسی کنید؛ نصب Hosting Bundle را از میزبان بپرسید.

مزایا و معایب هاست اشتراکی ویندوز

مزایا

  • ارزان، با SQL Server، ایمیل، DNS و SSL در یک پنل.
  • میزبانی in-process در IIS برای یک سایت معمولی عملکرد خوبی دارد.
  • مدل آشنا: فایل‌ها، یک application pool، یک دیتابیس.

معایب

  • کنترل محدود: نمی‌توانید runtime نصب کنید یا تنظیمات سرور را خودتان تغییر دهید.
  • جایی برای secret نیست؛ web.config سرور جای پیکربندی می‌شود.
  • استقرار کپی فایل است؛ اتمیک بودن (app_offline.htm، اول migration) را باید خودتان بسازید.

روی خطای 500.30 گیر کرده‌اید یا مهاجرت به Plesk/IIS را برنامه‌ریزی می‌کنید؟ ببینید چطور می‌توانم کمک کنم.

پرسش‌های متداول

علت خطای HTTP 500.31 روی Plesk چیست؟

سرور runtime نسخه‌ای از ASP.NET Core را که برنامه‌تان هدف گرفته ندارد. از میزبان بخواهید Hosting Bundle متناظر را نصب کند، یا برنامه را self-contained برای win-x64 منتشر کنید تا runtime همراهش برود.

روی هاست اشتراکی بدون key vault، secretها کجا بروند؟

در متغیرهای محیطی‌ای که ASP.NET Core Module از web.config خود سرور (المان environmentVariables) تزریق می‌کند. آن فایل را فقط روی سرور نگه دارید و از همگام‌سازی استقرار مستثنا کنید تا secretها هیچ‌وقت وارد مخزن نشوند.

برنامه هنگام شروع migration دیتابیس را اجرا کند؟

در محیط تولید نه. آن را یک گام جدا در استقرار کنید: یک migration bundle از EF Core روی سرور، یا یک اسکریپت SQL idempotent در ابزار دیتابیس میزبان. آن‌وقت migration ناموفق استقرار را متوقف می‌کند، نه سایت را.