عنوان:

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


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۴/۲۹ ۰۸:۱۰
آدرس: www.dntips.ir
هدف پروژه:
در این پروژه، شما یک وب API ساده با ASP.NET Core خواهید ساخت. هدف اصلی، تمرین تمام مهارت‌های کلیدی است که در این دوره آموخته‌اید:
  • نوشتن کد کراس-پلتفرم با رعایت تفاوت‌های فایل سیستم.
  • مدیریت پیکربندی از طریق فایل json و متغیرهای محیطی.
  • کانتینرسازی اپلیکیشن با استفاده از داکر و یک Dockerfile بهینه.
  • استقرار نهایی اپلیکیشن روی یک سرور لینوکس به عنوان یک سرویس systemd پایدار.

مرحله ۱: ساخت پروژه پایه وب API

ابتدا، یک پروژه وب API ساده با استفاده از دات‌نت CLI ایجاد می‌کنیم.
یک پوشه برای پروژه خود بسازید و با ترمینال وارد آن شوید. دستور زیر را برای ساخت یک پروژه Minimal API اجرا کنید:
dotnet new webapi -n MyTodoApi
cd MyTodoApi
فایل Program.cs را باز کرده و کدهای موجود را با کد زیر جایگزین کنید تا یک API ساده برای مدیریت لیست کارها (Todo List) داشته باشیم. در این مرحله، داده‌ها را به صورت ثابت در حافظه نگه می‌داریم.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// یک لیست ثابت برای داده‌های نمونه
var todos = new List<Todo>
{
    new(1, "Learn .NET on Linux", false),
    new(2, "Build a Docker container", true)
};

// Endpoint برای گرفتن تمام کارها
app.MapGet("/todos", () => todos);

// Endpoint برای گرفتن یک کار با ID
app.MapGet("/todos/{id}", (int id) =>
{
    var todo = todos.FirstOrDefault(t => t.Id == id);
    return todo is not null ? Results.Ok(todo) : Results.NotFound();
});

app.Run();

// یک کلاس ساده برای مدل داده
public record Todo(int Id, string Title, bool IsCompleted);
برنامه را به صورت محلی اجرا کنید تا مطمئن شوید همه چیز درست کار می‌کند:
dotnet run
سپس در مرورگر به آدرس http://localhost:5000/todos (یا پورت مشابه) بروید و باید لیست کارها را در فرمت JSON ببینید.

مرحله ۲: پیاده‌سازی نیازمندی‌های کراس-پلتفرم

حالا قابلیت‌هایی را اضافه می‌کنیم که در این دوره یاد گرفتیم.
۱. خواندن پیکربندی:
یک پیام خوشامدگویی را از appsettings.json می‌خوانیم.
در فایل appsettings.json، کلید زیر را اضافه کنید:
"WelcomeMessage": "Hello from appsettings.json!"
یک Endpoint جدید در Program.cs اضافه کنید تا این پیام را نمایش دهد:
// این خط را بعد از app.MapGet(...) اضافه کنید
app.MapGet("/", (IConfiguration config) => config["WelcomeMessage"]);

۲. لاگ‌نویسی امن:
یک لاگ متنی در هنگام شروع برنامه در یک مسیر امن و مستقل از پلتفرم ایجاد می‌کنیم.
کد زیر را در Program.cs، درست قبل از خط app.Run() اضافه کنید:
// لاگ‌نویسی در یک مسیر امن
try
{
    // مسیر پوشه ApplicationData را در هر سیستم‌عاملی به درستی پیدا می‌کند
    var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
    // یک پوشه برای اپلیکیشن ما می‌سازد
    var logDirectory = Path.Combine(appDataPath, "MyTodoApiLogs");
    Directory.CreateDirectory(logDirectory);

    var logFilePath = Path.Combine(logDirectory, "startup.log");
    var logMessage = $"Application started at {DateTime.UtcNow} on {Environment.OSVersion}.{Environment.NewLine}";

    // از Environment.NewLine برای سازگاری با ویندوز و لینوکس استفاده می‌کند
    File.AppendAllText(logFilePath, logMessage);
    Console.WriteLine($"Startup log written to: {logFilePath}");
}
catch (Exception ex)
{
    Console.WriteLine($"Failed to write startup log: {ex.Message}");
}

app.Run(); // این خط باید در انتها باشد

مرحله ۳: کانتینرسازی با داکر

حالا برنامه را با داکر بسته‌بندی می‌کنیم.
در ریشه پروژه (MyTodoApi)، یک فایل به نام Dockerfile (بدون پسوند) با محتوای زیر ایجاد کنید:
# مرحله 1: بیلد کردن با SDK
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /source

COPY *.csproj .
RUN dotnet restore

COPY . .
RUN dotnet publish -c Release -o /app/out

# مرحله 2: ساخت ایمیج نهایی با Runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/out .

# تعریف متغیر محیطی پیش‌فرض (می‌تواند هنگام اجرا تغییر کند)
ENV ASPNETCORE_URLS=http://+:80
ENV WelcomeMessage="Hello from Docker default environment!"

EXPOSE 80
ENTRYPOINT ["dotnet", "MyTodoApi.dll"]
ایمیج داکر را بسازید:
docker build -t my-todo-api .
کانتینر را اجرا کنید و با استفاده از متغیر محیطی، پیام خوشامدگویی را override کنید:
docker run -d -p 8080:80 -e WelcomeMessage="Hello from a LIVE Docker Container!" --name todo-api-instance my-todo-api
تست:
  • به آدرس http://localhost:8080/todos بروید تا لیست کارها را ببینید.
  • به آدرس http://localhost:8080 بروید. باید پیام "Hello from a LIVE Docker Container!" را مشاهده کنید.

مرحله ۴: استقرار به عنوان سرویس systemd (بدون داکر)

در این مرحله، فرض می‌کنیم می‌خواهیم برنامه را مستقیماً (و نه در داکر) روی سرور اجرا کنیم. این روش جایگزین خوبی برای سناریوهایی است که استفاده از داکر ممکن نیست.
پابلیش برنامه: برنامه را برای لینوکس به صورت Self-Contained پابلیش کنید.
dotnet publish -c Release -r linux-x64 --self-contained
انتقال فایل‌ها به سرور: محتویات پوشه‌ی bin/Release/net8.0/linux-x64/publish را به یک پوشه روی سرور لینوکس خود منتقل کنید (مثلاً با scp). برای مثال، در مسیر /var/www/my-todo-api.
ساخت فایل سرویس: روی سرور لینوکس، فایل سرویس systemd را ایجاد کنید.
  • sudo nano /etc/systemd/system/my-todo-api.service
  • محتوای زیر را در آن قرار دهید:
[Unit]
Description=MyTodoApi - ASP.NET Core Web API

[Service]
WorkingDirectory=/var/www/my-todo-api
ExecStart=/var/www/my-todo-api/MyTodoApi
Restart=always
RestartSec=10
SyslogIdentifier=my-todo-api
User=www-data
Group=www-data
# اینجا می‌توانید متغیرهای محیطی را برای override کردن تنظیمات قرار دهید
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://localhost:5001
Environment=WelcomeMessage="Hello from a systemd service!"

[Install]
WantedBy=multi-user.target
مدیریت سرویس: دستورات زیر را روی سرور اجرا کنید:
sudo systemctl daemon-reload
sudo systemctl enable my-todo-api.service
sudo systemctl start my-todo-api.service
sudo systemctl status my-todo-api.service
تست: اکنون برنامه شما روی پورت 5001 سرور در حال اجراست. می‌توانید با دستور curl http://localhost:5001 آن را تست کنید.

تبریک! شما با موفقیت یک پروژه کامل را از ابتدا ساختید، آن را برای محیط‌های مختلف پیکربندی کردید و با دو روش استاندارد صنعتی (داکر و systemd) روی لینوکس مستقر کردید. این پروژه تمام مهارت‌های کلیدی این دوره را پوشش می‌دهد.