برنامه نویسی

راه اندازی بدون درز در Kubernetes با ASP.NET Web API

Summarize this content to 400 words in Persian Lang
در دنیای امروز، بسته به نیازها، می توان خدمات را به طور مکرر در طول روز مستقر کرد و پیشرفت های جدید ویژه خدمات را می توان مورد استفاده قرار داد. گاهی اوقات این خدمات ممکن است خدماتی باشند که قطع آنی آن مشکلی ایجاد نخواهد کرد و گاهی ممکن است در صورت بروز قطعی حتی برای یک درخواست، طرف مشتری تحت تأثیر منفی قرار گیرد. در این مقاله، من در مورد آنچه که می توانیم برای از بین بردن این وقفه انجام دهیم، هم در Kubernetes و هم در سرویس توسعه یافته با ASP.NET صحبت خواهم کرد.

فهرست

استراتژی‌های پایان پاد، ایجاد و به‌روزرسانی در سمت Kubernetes
در سمت شبکه Host بررسی ساختار

IHostedLifecycleService، IHostedService ve IHostApplicationLifetime بررسی رابط ها
بررسی روند خاموش شدن هاست

Kind ایجاد یک خوشه Kubernetes با
مثال .Net پروژه، Dockerfile ve Deployment ایجاد مانیفست
استقرار سرویس در خوشه Kubernetes و انجام آزمایش
تاخیر بین ورودی Kubernetes و Kubernetes Control Plane

به روز رسانی استراتژی در Kubernetes

در سمت Kubernetes، در مانیفست شیء استقرار .spec.strategy.type استراتژی به روز رسانی در زیر بیان شده است. این استراتژی Recreate یا، که اگر داده نشود، رفتار پیش فرض است RollingUpdateکارگردان

در محدوده این مقاله، فرض بر این است که برنامه به عنوان یک شی Deployment منتشر شده است. در اشیاء StatefulSets و DaemonSets .spec.updateStrategy.type این استراتژی ها بر اساس مشخصات تعیین می شوند. این استراتژی ها هستند OnDelete ve RollingUpdateکارگردان

بازآفرینی کنید

spec:
replicas: 10
strategy:
type: Recreate

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Recreate در استراتژی به روز رسانی؛ ابتدا تمام غلاف ها خاتمه یافته و سپس پادهای نسخه جدید احیا می شوند. با توجه به مانیفست بالا، Kubernetes تمام 10 پاد را می کشد و سپس پادهای جدید را ایجاد می کند. در این استراتژی امکان پذیر است زمان از کار افتادنموجب (انقطاع) خواهد شد. دلیل این امر این است که قبل از Kubernetes، همه pods; قبل از تشکیل و جایگزینی غلاف های جدید خاتمه می یابد.

Rolling Update

spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: %25
maxSurge: %25

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

RollingUpdate در استراتژی به روز رسانی، پادها به تدریج با تصویر نسخه جدید جایگزین می شوند. ابتدا غلاف با شماره تصویر جدید ایجاد می شود و سپس غلاف با شماره تصویر قدیمی کشته می شود. این عملیات تا زمانی که برای همه غلاف ها تکمیل شود ادامه می یابد.

maxSurge ve maxUnavailable مولفه های RollingUpdate در استراتژی بیان شده است.

maxUnavailable: تعداد پادهایی را نشان می دهد که ممکن است در مرحله به روز رسانی در دسترس نباشند. مقدار را می توان به صورت درصد یا مستقیماً به عنوان تعداد غلاف ها ارائه کرد. یک فیلد اختیاری است و مقدار پیش فرض آن است %25کارگردان

maxSurge: به تعداد غلاف هایی اشاره دارد که ممکن است از تعداد کپی های مشخص شده در مانیفست استقرار بیشتر باشد. maxUnavailableدرصد یا مقدار مستقیم غلاف را می توان مانند در داده شود. یک فیلد اختیاری است و مقدار پیش فرض آن است %25کارگردان

در مواردی که مقادیر درصدی برای فیلدهای ذکر شده در بالا داده می شود. اگر تعداد غلاف ها عدد صحیح نباشد maxUnavailable این مقدار برای maxSurge برای این مقدار گرد شده است. به عنوان مثال، زمانی که 1 replica مشخص شده است maxSurge ارزش گرد شده 1'e maxUnavailable مقدار به پایین گرد می شود 0مطابقت دارد. در این حالت ابتدا یک پاد ایجاد می شود و پس از وارد شدن به حالت اجرا، پاد فعلی خاتمه می یابد.

با استراتژی Rolling Update، Kubernetes تضمین می‌کند که برنامه با حداقل وقفه اجرا می‌شود. در زیر مراحل و نمونه استقرار مانیفست برای استقرار با یک ماکت در طول عملیات Rolling Update آمده است.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        – name: nginx
          image: nginx:1.14.2
          ports:
            – containerPort: 80
          resources:
            limits:
              cpu: “0.3”
              memory: “75Mi”
            requests:
              cpu: “0.1”
              memory: “50Mi”
  strategy:
    type: RollingUpdate

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

kubectl set image deployment/nginx-deployment nginx=nginx:1.14.1 هنگامی که نسخه تصویر با اجرای دستور به روز می شود، مراحل زیر انجام می شود.

maxSurge ارزش 1 بنابراین، اول از همه، یک غلاف با شماره تصویر جدید شروع به ایستادن می کند.
وضعیت غلاف'ü Runningپس از جابجایی به، Pod موجود توسط Kubernetes قابل دسترسی است. SIGTERM سیگنال ارسال می شود. مسیریابی درخواست‌های جدیدی که به پاد می‌رسند قطع می‌شود و اتصالات باز برای تکمیل درخواست‌های موجود ایجاد می‌شوند. spec.terminationGracePeriodSeconds در دوره مورد انتظار است.
پاد خاتمه یافت spec.terminationGracePeriodSeconds اگر خیلی طولانی باشد، Kubernetes این کار را خواهد کرد SIGKILL سیگنال ارسال می شود و غلاف کشته می شود.

این دوره در سمت Kubernetes پیش بینی می شود، اما در سمت برنامه نیز مشخص شده است. POSIX در نتیجه سیگنال، باید برای خاموش شدن برازنده اقدام کرد.

برای استقرار بالا، هنگام به‌روزرسانی نسخه تصویر، وضعیت‌های غلاف در زیر نشان داده می‌شوند.

kubectl get pods -n default -w
NAME READY STATUS RESTARTS AGE
nginx-deployment-59994fb97c-5j4fv 1/1 Running 0 8m8s
nginx-deployment-59994fb97c-g789c 1/1 Running 0 8m9s
nginx-deployment-59994fb97c-nddlf 1/1 Running 0 8m9s
nginx-deployment-5fffc966ff-8crmb 0/1 Pending 0 1s
nginx-deployment-5fffc966ff-8crmb 0/1 Pending 0 1s
nginx-deployment-5fffc966ff-8crmb 0/1 ContainerCreating 0 1s
nginx-deployment-5fffc966ff-8crmb 1/1 Running 0 1s
nginx-deployment-59994fb97c-5j4fv 1/1 Terminating 0 8m16s
nginx-deployment-5fffc966ff-52knq 0/1 Pending 0 0s
nginx-deployment-5fffc966ff-52knq 0/1 Pending 0 0s
nginx-deployment-5fffc966ff-52knq 0/1 ContainerCreating 0 0s
nginx-deployment-59994fb97c-5j4fv 0/1 Terminating 0 8m16s
nginx-deployment-5fffc966ff-52knq 1/1 Running 0 1s
nginx-deployment-59994fb97c-g789c 1/1 Terminating 0 8m18s
nginx-deployment-5fffc966ff-jwmtt 0/1 Pending 0 0s
nginx-deployment-5fffc966ff-jwmtt 0/1 Pending 0 0s
nginx-deployment-5fffc966ff-jwmtt 0/1 ContainerCreating 0 0s
nginx-deployment-59994fb97c-5j4fv 0/1 Terminating 0 8m17s
nginx-deployment-59994fb97c-5j4fv 0/1 Terminating 0 8m17s
nginx-deployment-59994fb97c-5j4fv 0/1 Terminating 0 8m17s
nginx-deployment-59994fb97c-g789c 0/1 Terminating 0 8m18s
nginx-deployment-5fffc966ff-jwmtt 1/1 Running 0 1s
nginx-deployment-59994fb97c-g789c 0/1 Terminating 0 8m19s
nginx-deployment-59994fb97c-g789c 0/1 Terminating 0 8m19s
nginx-deployment-59994fb97c-g789c 0/1 Terminating 0 8m19s
nginx-deployment-59994fb97c-nddlf 1/1 Terminating 0 8m19s
nginx-deployment-59994fb97c-nddlf 0/1 Terminating 0 8m19s
nginx-deployment-59994fb97c-nddlf 0/1 Terminating 0 8m20s
nginx-deployment-59994fb97c-nddlf 0/1 Terminating 0 8m20s
nginx-deployment-59994fb97c-nddlf 0/1 Terminating 0 8m20s

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اظهار داشت spec.terminationGracePeriodSeconds مقدار به طور خاص برای غلاف تعریف شده است. مقدار پیش فرض دهه 30کارگردان

کانتینر سایدکار لارا SIGTERM قبل از ارسال سیگنال، کانتینر اصلی در همان غلاف خاتمه یافته و سپس کانتینرهای سایدکار به ترتیب معکوس که تعریف شده بودند، منتقل می شوند. SIGTERM سیگنال ارسال می شود. به این ترتیب، اطمینان حاصل می شود که محفظه سایدکار در غلاف در زمانی که دیگر نیازی به آن نیست، خاتمه می یابد.

برنامه Nginx در استقرار گنجانده شده است SIGTERM هنگامی که سیگنال را دریافت می کند، به سرعت فرآیند، از جمله اتصالات باز را خاتمه می دهد. از این رو خاموش شدن برازنده به منظور اینکه SIGTERM وقتی سیگنال می آید باید ردیابی شود و SIGQUIT باید سیگنال داده شود برای انجام یک خاموش کردن برازنده در سمت Nginx SIGQUIT منتظر سیگنال برای این کار می توان با bash عمل کرد.

مدل میزبان Net

فناوری جدید با چارچوب Net Core به دنیای ما می آید Host با رویکرد مدل Host هدف آن کپسوله کردن منابعی است که برنامه به آن نیاز دارد و عملکردهای چرخه حیات آن در داخل شی. با ساختار مشخص شده، همچنین هدف حذف بسیاری از کدهای دیگ بخار در قالب های پیش فرض است. با ایجاد هماهنگی‌های خاصی روی این شی، قابلیت‌های زیر به‌طور پیش‌فرض در دسترس هستند و بسته به نوع برنامه، در صورت نیاز قابل ویرایش هستند.

ساختار تزریق وابستگی (DI).
ورود به سیستم
پیکربندی
فرآیند خاموش شدن برنامه

IHostedService پیاده سازی

اظهار داشت Host رویکرد مدل نیز در قالب‌های برنامه متفاوت است، زیرا نسخه‌های فریمورک .Net تغییر کرده است. سه رویکرد مختلف در زیر با نسخه‌های Net آنها بیان شده است.

.NET Core 2.x با نسخه WebHost زیر کلاس CreateDefaultBuilder روش Host مدل ایجاد و پیکربندی شده است.

public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartupStartup>();
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در بالا dotnet در نسخه مشخص شده توسط کلیک کنید ASP.Net Core زمانی که پروژه از ابتدا ایجاد می شود Program.cs بلوک کد موجود در فایل منتقل شده است.

.NET Core 3.x با .NET 5 بین نسخه ها Host با ایجاد یک تغییر بزرگ در رویکرد مدل، پروژه های وب نیز می توانند به طور کلی مورد استفاده قرار گیرند. Host رویکرد برای ایجاد تغییر یافته است. با این تغییر با استفاده از همان کد پایه Host خدمات کارگر، خدمات gRPC و خدمات ویندوز را می توان با استفاده از این مدل توسعه داد. با این روش IWebHostBuilder رابط من یرین IHostBuilder روی رابط Host مدل ساخته شده است.

public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartupStartup>();
});
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در بالا .NET Core 3.x با .NET 5 بین نسخه ها dotnet ایجاد شده از ابتدا با cli ASP.NET از پروژه Program.cs بلوک کد موجود در فایل به اشتراک گذاشته شده است.

در هر دو رویکرد مشترک بالا، Startup این را نباید نادیده گرفت که کلاس به طور جدایی ناپذیر به برنامه وب وابسته است، اما IHostBuilder واقع در رابط Host ما بیان کرده ایم که رویکرد مدل را می توان در برنامه های مختلف غیر از برنامه های کاربردی وب توسعه داد. در این اپلیکیشن ها Startup ممکن است کلاس مورد نیاز نباشد. (مثلا Configure روشی که نامیده می شود برای پیکربندی میان افزار در برنامه استفاده می شود، اما برای سرویس های کارگر نیازی به چنین پیکربندی نیست.) به همین دلیل، توسعه دهندگان فریم ورک ConfigureWebHostDefaults آنها با روش گسترش بر این وضعیت غلبه کردند.

.NET 6 تنظیمات انجام شده در دو فایل مختلف با (Startup.cs ve Program.cs) در یک فایل و Host مدل IHostApplicationBuilder بالای کلاس قرار گرفته است dotnet تیم این رویکرد را در یادداشت های مهاجرت خود شرح می دهد. Minimal Hosting آن را به صورت و بیان کردند Minimal APIآنها به عنوان الگوی وب پیش فرض قرار گرفتند. مرجع

namespace Example.Processor.Api;

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        // Add services to the container.
        builder.Services.AddControllers();
        // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle

        builder.Services.AddEndpointsApiExplorer();
        builder.Services.AddSwaggerGen();

        var app = builder.Build();
        // Configure the HTTP request pipeline.
        if (app.Environment.IsDevelopment())

        {
            app.UseSwagger();
            app.UseSwaggerUI();
        }
        app.UseHttpsRedirection();
        app.UseAuthorization();
       
        app.MapControllers();
        app.Run();
    }
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در بالا .NET 6 با نسخه dotnet ایجاد شده از ابتدا در cli ASP.NET از پروژه Program.cs بلوک کد موجود در فایل به اشتراک گذاشته شده است. همانطور که می بینید، تمام تنظیمات در یک فایل قرار دارند و Startup کلاس وجود ندارد. علاوه بر این، با این رویکرد Host.CreateDefaultBuilder روش WebApplication.CreateBuilder با استفاده از روش و IHostBuilder در این مکان IHostApplicationBuilder در حال بازگرداندن است. .NET توسعه دهندگان .NET 7 همراه با Host.CreateApplicationBuilder این روش را معرفی کرد و از آن به عنوان رویکردی برای برنامه های کاربردی وب و غیر وب استفاده کرد. Host توصیه می شود که رویکرد مدل همانطور که در زیر ذکر شده است ادامه یابد. می توانید نظر دیوید فاولر را در مورد این موضوع پیدا کنید.

روش نمونه برای برنامه های کاربردی وب

var builder = WebApplication.CreateBuilder();

builder.Logging.AddConsole();

builder.Services.AddOptionsMyOptions>().BindConfiguration(“MyConfig”);

builder.Services.AddHostedServiceMyWorker>();

var app = builder.Build();

app.MapGet(“https://dev.to/”, () => “Hello World”);

app.Run();

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

روش نمونه برای برنامه های غیر وب

var builder = Host.CreateApplicationBuilder();

builder.Logging.AddConsole();

builder.Services.AddOptionsMyOptions>().BindConfiguration(“MyConfig”);

builder.Services.AddHostedServiceMyWorker>();

var host = builder.Build();

host.Run();

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

WebApplication کلاس 3 رابط مورد نیاز برای برنامه وب را پیاده سازی می کند.

IHost – مسئول راه اندازی و خاتمه هاست.

IApplicationBuilder – برای ایجاد خطوط لوله میان افزار استفاده می شود

IEndpointRouteBuilder – برای نقاط پایانی استفاده می شود

همچنین 3 سرویس زیر را ارائه می دهد: HostApplicationBuilder.Build() متد به محض فراخوانی به طور خودکار در ظرف DI ثبت می شود.

IHostApplicationLifetime – می توان آن را به هر کلاسی تزریق کرد و برای انجام عملیات خاموش کردن و پس از راه اندازی زیبا استفاده کرد.

IHostLifetime – زمان شروع یا پایان برنامه را کنترل می کند.

IHostEnvironment – نام برنامه، مسیر ریشه، نام محیط و غیره برای به دست آوردن اطلاعات استفاده می شود.

IHostApplicationLifetime، IHostLifecycleService، IHostedService بررسی رابط ها

در زیر مثالی برای درک رابط های مشخص شده و رویدادهای Lifetime پیاده سازی شده با این رابط ها آورده شده است. IHostedService اجرا منتقل شده است.

BackgroundService یکی abstract این یک کلاس است و برای ایجاد خدمات پس زمینه استفاده می شود. IHostedService در حالیکه BackgroundService این یک رابط است که توسط . که در Hostاین شامل روش هایی برای مدیریت است. Worker Service یک الگو برای ایجاد یک سرویس پس زمینه است. dotnet new worker با دستور ایجاد می شود.

فقط IHostedService امکان ایجاد سرویس‌هایی وجود دارد که بتوان آن‌ها را پیاده‌سازی کرد و پردازش پس‌زمینه را انجام داد، اما در اینجا، عملیات خاموش کردن برازنده باید به صورت دستی با گوش دادن به رویدادهای طول عمر برنامه در سرویس پیاده‌سازی شود. BackgroundServiceلغو می شود در ExecuteAsync به عنوان پارامتر به متد ارسال می شود CancellationToken با بررسی عملیات انجام شده، عملیات خاموش کردن برازنده را می توان با سهولت بیشتری انجام داد.

public class Worker : IHostedLifecycleService
{
private readonly ILoggerWorker> _logger;

public Worker(ILoggerWorker> logger, IHostApplicationLifetime applicationLifetime)
{
applicationLifetime.ApplicationStarted.Register(OnStarted);
applicationLifetime.ApplicationStopping.Register(OnStopping);
applicationLifetime.ApplicationStopped.Register(OnStopped);
_logger = logger;
}

public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation(“IHostedService StartAsync has been called”);
return Task.CompletedTask;
}

public Task StartedAsync(CancellationToken cancellationToken)
{
_logger.LogInformation(“IHostedLifecycleService StartedAsync has been called”);
return Task.CompletedTask;
}

public Task StartingAsync(CancellationToken cancellationToken)
{
_logger.LogInformation(“IHostedLifecycleService StartingAsync has been called”);
return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation(“IHostedService StopAsync has been called”);
return Task.CompletedTask;
}

public Task StoppedAsync(CancellationToken cancellationToken)
{
_logger.LogInformation(“IHostedLifecycleService StoppedAsync has been called”);
return Task.CompletedTask;
}

public Task StoppingAsync(CancellationToken cancellationToken)
{
_logger.LogInformation(“IHostedLifecycleService StoppingAsync has been called”);
return Task.CompletedTask;
}

private void OnStarted()
{
_logger.LogInformation(“IHostApplicationLifetime OnStarted has been called”);
}

private void OnStopped()
{
_logger.LogInformation(“IHostApplicationLifetime OnStopped has been called”);
}

private void OnStopping()
{
_logger.LogInformation(“IHostApplicationLifetime OnStopping has been called”);
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چون می خواهم پردازش پس زمینه انجام دهم IHostedServiceمن ابلاغ کردم که لازم است اجرا شود. اینجا IHostedLifecycleService دلیل استفاده از آن این است که این رابط در حال حاضر IHostedServiceاین به این دلیل است که از . این رابط .Net 8 با آن آورده شد. به این ترتیب می توانیم با سهولت بیشتری در چرخه حیات اپلیکیشن دخالت کرده و عملیات را انجام دهیم. این شامل 4 روش جدید است. اینها StartingAsync، StartedAsync، StoppingAsync ve StoppedAsync است. علاوه بر این IHostApplicationLifetimeاشاره کردم که در زمان ساخت هاست به صورت خودکار ثبت می شود. سه ویژگی زیر این کلاس در واقع سه ویژگی هستند. CancellationToken و با توجه به طول عمر میزبان فعال می شوند. همانطور که در بالا گفته شد برای این توکن ها ثبت نام می کنم.

در بالا ایجاد کردم Hosted Serviceمطابق شکل زیر در کانتینر DI ثبت نام می کنم.

using Example.Worker.Service;

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedServiceWorker>();

var host = builder.Build();
host.Run();

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برنامه را اجرا کنید و سپس CTRL+C وقتی این کار را تمام کردم، خروجی کنسول زیر را می بینم.

info: Example.Worker.Service.Worker[0] IHostedLifecycleService StartingAsync has been called
info: Example.Worker.Service.Worker[0] IHostedService StartAsync has been called
info: Example.Worker.Service.Worker[0] IHostedLifecycleService StartedAsync has been called
info: Example.Worker.Service.Worker[0] IHostApplicationLifetime OnStarted has been called
info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0] Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0] Content root path: C:\Codes\Example.Worker.Service
info: Example.Worker.Service.Worker[0] IHostApplicationLifetime OnStopping has been called
info: Microsoft.Hosting.Lifetime[0] Application is shutting down…
info: Example.Worker.Service.Worker[0] IHostedLifecycleService StoppingAsync has been called
info: Example.Worker.Service.Worker[0] IHostedService StopAsync has been called
info: Example.Worker.Service.Worker[0] IHostedLifecycleService StoppedAsync has been called
info: Example.Worker.Service.Worker[0] IHostApplicationLifetime OnStopped has been called

C:\Codes\Example.Worker.Service\bin\Debug\net8.0\Example.Worker.Service.exe (process 35456) exited with code 0.

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

وقتی خروجی را بررسی می کنیم، ترتیب به صورت زیر به نظر می رسد.

IHostedLifecycleService.StartingAsync دیده شد که او را صدا زدند. این روشی است که قبل از شروع برنامه فراخوانی می شود.

IHostedService.StartAsync نامیده شده است. این روشی است که زمانی فراخوانی می شود که هاست آماده راه اندازی سرویس باشد.

IHostedLifecycleService.StartedAsync فرا خوانده شده است. IHostedService.StartAsync بلافاصله پس از تکمیل فرآیند اولیه سازی فراخوانی می شود.

IHostApplicationLifetime.ApplicationStarted نشان می دهد که میزبان به طور کامل شروع شده است.

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

IHostApplicationLifetime.ApplicationStopping هنگامی که برنامه شروع به انجام عملیات خاموش کردن برازنده می کند، فعال می شود.

IHostedLifecycleService.StoppingAsync قبل از شروع به پایان برنامه فراخوانی می شود. IHostedService.StopAsync درست قبل از عمل انجام می شود.

IHostedService.StopAsync برنامه یک عملیات خاموش کردن برازنده را انجام می دهد.

IHostedLifecycleService.StoppedAsync زمانی فراخوانی می شود که برنامه عملیات خاموش کردن برازنده خود را کامل کند.

IHostApplicationLifetime.ApplicationStopped این نشان می دهد که عملیات خاموش کردن برازنده برنامه تکمیل شده است.

نکته مهم دیگر در اینجا این است که اپلیکیشن CTRL+C این به این دلیل است که با ترکیب خاتمه می یابد. به طور خودکار روی پیش فرض میزبان IHostLifetime رابط'ni ConsoleLifetime ثبت شده به عنوان . این امر در مورد سرویس وب و پس‌زمینه نیز صدق می‌کند. IHostLifetime اشاره کردم که اینترفیس زمان شروع و پایان برنامه را کنترل می کند. ConsoleLifetime با فشار دادن دکمه های ترکیبی که در بالا ذکر کردم می توانید به برنامه دسترسی پیدا کنید. SIGINT ما داریم سیگنال می دهیم. در Kubernetes، به دلایلی که در بخش اول توضیح دادم، غلاف باید با افزودن یک ظرف به ظرف خاتمه یابد. SIGTERM سیگنال را می فرستد. در نتیجه این سیگنال ها، عملیات خاموش کردن برازنده انجام می شود.

.Net 6 پیش از این، سیگنال‌های posix پشتیبانی نمی‌شدند، اما بر اساس نوع سیگنال مدیریت می‌شدند. .Net 6 پست- ConsoleLifetime، SIGINT، SIGQUIT، SIGTERM با گوش دادن به سیگنال ها، توانست عملیات خاموش کردن برازنده را انجام دهد.

فرآیند خاموش شدن میزبان

پیش فرض .Net 6 و سپس میزبان عمومی IHostLifetime رابط'ni ConsoleLifetime من بیان کردم که به عنوان اجرا شده است. برای خاتمه دادن به لطف میزبان نیز ConsoleLifetimeاین عملیات را می توان با ارسال سیگنال های زیر انجام داد:

SIGINT دور انداختن CTRL+C

SIGQUIT دور انداختن CTRL+BREAK (پنجره ها)

SIGTERM (در سمت Kubernetes، سیگنال برای پایان دادن به غلاف به ظرف ارسال می شود docker stop)

.Net 6 قبل از SIGTERM وقتی سیگنال آمد، عملیات خاموش کردن را نمی‌توان به خوبی انجام داد. با توجه به این وضعیت، کار کنید ConsoleLifetime در کنار، System.AppDomain.ProcessExit گوش دادن به رویداد ProcessExit موضوع متوقف می شود و میزبان منتظر می ماند تا متوقف شود.

فرآیندی که در حین عملیات خاموش شدن با ظرافت انجام می شود در بالا توضیح داده شده است. به ترتیب؛

از Kubernetes یا کاربر SIGTERM سیگنال در حال آمدن است در نتیجه این سیگنال IHostApplicationLifetime واقع در زیر StopApplication() روش راه اندازی می شود و ApplicationStopping رویداد پرتاب می شود. قبل از IHost.WaitForShutdownAsync متد برای این رویداد مشخص شده گوش می دهد و از آنجایی که رویداد راه اندازی می شود Main مسدود کردن عملیات

IHost.StopAsync() روش راه اندازی می شود و از درون این روش IHostedService.StopAsync() راه اندازی می شود و باعث می شود هر سرویس میزبانی شده متوقف شود و سپس رویدادهایی را نشان می دهد که متوقف شده است.
سرانجام IHost.WaitForShutdownAsync تکمیل می شود و بلوک های کدی که برنامه برای اجرا نیاز دارد اجرا می شود و عملیات خاموش کردن به خوبی انجام می شود.

پیکربندی در سمت میزبان انجام می شود ShutdownTimeout امکان تنظیم وجود دارد. این مقدار IHost.StopAsync() این مدت زمان تعیین شده برای روش است و مقدار پیش فرض آن 30 ثانیه است.

Kind بالا بردن یک خوشه Kubernetes با

به عنوان منبع باز توسعه یافته است Kind با این محصول، امکان راه اندازی سریع خوشه های Kubernetes به صورت محلی وجود دارد. خوشه Kubernetes که در آن برنامه توسعه یافته برای این مقاله منتشر خواهد شد Kind با محصول ایجاد شده است.

اولا Kind ما cli را به صورت محلی با دستور زیر نصب می کنیم.

curl.exe -Lo kind-windows-amd64.exe https://kind.sigs.k8s.io/dl/v0.23.0/kind-windows-amd64
Move-Item .\kind-windows-amd64.exe c:\some-dir-in-your-PATH\kind.exe

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای اینکه هر بار به دایرکتوری که cli در آن قرار دارد نروید و هیچ عملیاتی انجام ندهید، دایرکتوری که مشخص کرده اید را انتخاب کنید. Path فراموش نکنید که Environment Variable را به آن اضافه کنید.

سپس، فایل yaml مشخص شده در زیر برای راه اندازی یک کلاستر حاوی 3 گره کارگر ایجاد می شود.

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
– role: control-plane
kubeadmConfigPatches:

kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: “ingress-ready=true”
extraPortMappings:
– containerPort: 80
hostPort: 8081
protocol: TCP
– containerPort: 443
hostPort: 8443
protocol: TCP
– role: worker
– role: worker
– role: worker

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

وقتی فایل yaml را که در بالا ذکر کردم بررسی می کنیم. مشاهده می‌شود که برای هر نقش گره تعریفی وجود دارد و درخواست‌های ارسال شده به پورت‌های 8081 و 8443 به صورت محلی تعریف شده‌اند تا به کنترل‌کننده ورودی که روی خوشه نصب می‌کنیم ارسال شوند.

kind create cluster –config .\kind-cluster.yaml

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با دستوری که در بالا دادم خوشه را بیدار می کنم.

kind create cluster –config .\kind-config\kind-cluster.yaml
Creating cluster “kind” …
✓ Ensuring node image (kindest/node:v1.30.0) 🖼
✓ Preparing nodes 📦 📦 📦 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
✓ Joining worker nodes 🚜
Set kubectl context to “kind-kind”
You can now use your cluster with:

kubectl cluster-info –context kind-kind

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس با دستوری که در زیر ارائه کردم، کنترل کننده ورودی را نصب می کنم.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Kindدر این فایل مانیفست که به طور خاص برای .

namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

ایجاد Web API

.Net 8 وقتی یک پروژه web api با Program.cs فایل ظاهر می شود

public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{

}

app.UseAuthorization();

app.MapControllers();

app.Run();
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

خدماتی که مورد نیاز نیستند حذف شده اند

من نام Controller و Action را همانطور که در زیر ذکر شده است ویرایش می کنم. در بخش قبل ShutdownTimeoutعرض کردم که مقدار پیش فرض 30 ثانیه است. برای اینکه عملیات خطا دریافت کند، 35 ثانیه در اکشن منتظر می مانم و آن را طوری ترتیب می دهم که پاسخ داده شود.

[ApiController] [Route(“[controller]”)] public class PingPongController : ControllerBase
{
private readonly ILoggerPingPongController> _logger;

public PingPongController(ILoggerPingPongController> logger)
{
_logger = logger;
}

[HttpGet(Name = “Ping”)] public async TaskIActionResult> Ping()
{
await Task.Delay(TimeSpan.FromSeconds(35));
return Ok(“Pong”);
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Kind من در حال ایجاد مانیفست استقرار در زیر هستم تا استقرار در خوشه Kubernetes را که با آن ایجاد کرده‌ایم فعال کنم.

apiVersion: apps/v1
kind: Deployment
metadata:
name: pingapi-deployment
labels:
app: pingapi
spec:
replicas: 1
selector:
matchLabels:
app: pingapi
template:
metadata:
labels:
app: pingapi
spec:
containers:
– name: pingapi
image: pingapi:0.1
ports:
– containerPort: 8080
resources:
limits:
cpu: “0.5”
memory: “150Mi”
requests:
cpu: “0.1”
memory: “150Mi”

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

.Net 8 پورت پیش فرضی که برنامه در تصاویر منتشر شده با آن اجرا می شود 80″ 8080به تغییر یافته است. به همین دلیل، اطلاعات پورت در مانیفست گنجانده شده است. 8080 تنظیم شده است. سند مرتبط

Dockerfileمن به سادگی همانطور که در زیر گفته شد ایجاد می کنم.

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
COPY ./Publish .
ENTRYPOINT [“dotnet”, “Example.Ping.Api.dll”]

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

استقرار سرویس و انجام تست

اول از همه، درخواست من Publish زیر دایرکتوری منتشر میکنم

dotnet publish -o ./Publish

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس تصویر را می سازم.

docker build -t pingapi:0.1 .

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قبل از اعمال مانیفست استقرار در خوشه، تصویری را که برچسب گذاری کردم با دستور مشخص شده در زیر، به هر گره منتقل می کنم، در غیر این صورت، نیاز به پرتاب این تصویر به مخزن تصویر و بیرون کشیدن آن از خوشه وجود دارد. مهربان در اینجا راحتی را برای ما فراهم می کند.

kind load docker-image pingapi:0.1

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

kind load docker-image pingapi:0.1
Image: “pingapi:0.1” with ID “sha256:2e5cfec8e475ed2d2ccfd0ae9753a7f5feda0e01de0081718ab678203d25edcf” not yet present on node “kind-worker3”, loading…
Image: “pingapi:0.1” with ID “sha256:2e5cfec8e475ed2d2ccfd0ae9753a7f5feda0e01de0081718ab678203d25edcf” not yet present on node “kind-worker”, loading…
Image: “pingapi:0.1” with ID “sha256:2e5cfec8e475ed2d2ccfd0ae9753a7f5feda0e01de0081718ab678203d25edcf” not yet present on node “kind-control-plane”, loading…
Image: “pingapi:0.1” with ID “sha256:2e5cfec8e475ed2d2ccfd0ae9753a7f5feda0e01de0081718ab678203d25edcf” not yet present on node “kind-worker2”, loading…

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

من استقرار را با دستوری که در زیر ذکر کردم اعمال می کنم.

kubectl apply -f .\Kubernetes\deployment.yaml

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کانتینر 8080 پورت را با دستوری که در زیر ذکر کردم فوروارد می کنم.

kubectl port-forward deployment/pingapi-deployment -n default 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکنون می توانیم ابتدا آزمایش را شروع کنیم httpstat http://localhost:8080/pingpong/ping من یک درخواست با دستور و سپس برای حذف پاد ارسال می کنم kubectl delete pod/pingapi-deployment-5c78cbdfc-bfd9b فرمان را اجرا می کنم. همانطور که انتظار داشتیم، هر دو در سمت کوبرنتیس terminationGracePeriodSeconds از آنجایی که ما مدت زمان را در مانیفست استقرار مشخص نکرده‌ایم، مقدار پیش‌فرض 30 ثانیه است. .Net در کنار Host از شی ShutdownTimeout از آنجایی که مقدار پیش‌فرض 30 ثانیه را مشخص نکرده‌ایم، به نظر می‌رسد که همانطور که در خطا مشاهده می‌شود، برای درخواستی که انجام داده‌ایم، اتصال قطع شده است.

سناریوی مشابهی را می توان با استقرار یک نسخه تصویر جدید آزمایش کرد. هدف در اینجا نصب پاد است. SIGTERM از آنجایی که هدف ارسال سیگنال است، فرآیند آزمایش با عملیات حذف پاد انجام شد.

برای غلبه بر این وضعیت، ابتدا در سمت برنامه Hostمن پیکربندی می کنم، سپس در زیر مانیفست استقرار spec.terminationGracePeriodSeconds من مقدار نامگذاری شده را به روز می کنم.

Program.cs نسخه جدید فایل به شرح زیر ساخته شده است.

namespace Example.Ping.Api;

public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Host.ConfigureHostOptions(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(45));

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{

}

app.UseAuthorization();

app.MapControllers();

app.Run();
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

deployment.yaml نسخه جدید فایل به شرح زیر است؛

apiVersion: apps/v1
kind: Deployment
metadata:
name: pingapi-deployment
labels:
app: pingapi
spec:
replicas: 1
selector:
matchLabels:
app: pingapi
template:
metadata:
labels:
app: pingapi
spec:
containers:
– name: pingapi
image: pingapi:0.2
ports:
– containerPort: 8080
resources:
limits:
cpu: “0.5”
memory: “150Mi”
requests:
cpu: “0.1”
memory: “150Mi”
terminationGracePeriodSeconds: 50

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تصویری که به تازگی در مانیفست استقرار ساختیم pingapi:0.2 ما به عنوان به روز رسانی می کنیم.

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

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

علی‌رغم تنظیماتی که در بالا انجام دادیم، به‌ویژه برای سرویس‌های پرکاربرد RollingUpdate امکان دریافت خطا حتی برای چند درخواست در این مرحله وجود دارد. این به دلیل تاخیر بین ورودی و صفحه کنترل است.

دلیل این امر این است که هواپیمای ورودی و کنترل Kubernetes موجودیت های مختلفی در Kubernetes هستند و عملیات خود را به طور مستقل انجام می دهند. هنگامی که Kubernetes می‌خواهد یک پاد را خاتمه دهد، صفحه کنترل پادهایی را که قرار است خاتمه داده شوند را از سرویس حذف می‌کند و ورود از این موضوع مطلع می‌شود و ارسال درخواست‌ها به پادهایی که باید خاتمه داده شوند را متوقف می‌کند. بین این دو عملیات تاخیر وجود دارد زیرا ingress به صورت دوره ای این تغییرات ایجاد شده در سرویس های سمت خود را به روز می کند. در این موقعیت terminating این باعث می شود که درخواست ها به پادهای موجود در وضعیت با تعداد کمی درخواست منتقل شوند.

..Net برنامه در کنار IHost.StopAsync() پس از تماس، اجازه نمی دهد درخواست های جدید از طریق اتصالات از قبل باز شده انجام شود و اجازه نمی دهد که یک اتصال جدید همزمان باز شود. به همین دلیل تاخیر در این بین IHost.StopAsync() این بدان معنی است که ممکن است درخواست های جدید با شروع فرآیند ارائه شود. این منجر به خطا برای طرف درخواست کننده می شود.

به عنوان راه حلی برای این وضعیت، روشی که در زیر توضیح خواهم داد توسط تیم دات نت پیشنهاد شد. مرجع

قبلا IHostLifetimeگزارش شده است که زمان شروع یا توقف برنامه را کنترل می کند. به همین دلیل، اول از همه، یک جدید IHostLifetime ما در حال اجرا هستیم.

using System.Runtime.InteropServices;

public class DelayedShutdownHostLifetime : IHostLifetime, IDisposable
{
private IHostApplicationLifetime _applicationLifetime;
private TimeSpan _delay;
private IEnumerableIDisposable>? _disposables;

public DelayedShutdownHostLifetime(IHostApplicationLifetime applicationLifetime, TimeSpan delay) {
_applicationLifetime = applicationLifetime;
_delay = delay;
}

public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}

public Task WaitForStartAsync(CancellationToken cancellationToken)
{
_disposables = new IDisposable[] {
PosixSignalRegistration.Create(PosixSignal.SIGINT, HandleSignal),
PosixSignalRegistration.Create(PosixSignal.SIGQUIT, HandleSignal),
PosixSignalRegistration.Create(PosixSignal.SIGTERM, HandleSignal)
};
return Task.CompletedTask;
}

protected void HandleSignal(PosixSignalContext ctx)
{
ctx.Cancel = true;
Task.Delay(_delay).ContinueWith(t => _applicationLifetime.StopApplication());
}

public void Dispose()
{
foreach (var disposable in _disposables ?? Enumerable.EmptyIDisposable>())
{
disposable.Dispose();
}
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هنگامی که فرآیند انجام شده بررسی می شود، برنامه در هنگام ایستادن به وضوح قابل مشاهده است. POSIX عملیات ثبت برای سیگنال ها انجام شده است. به این ترتیب، وقتی یکی از این سیگنال ها به سمت برنامه می آید، IHostApplicationLifetime.StopApplication() قبل از شروع عملیات Task.Delay تاخیر با تعریف شده است. به لطف این تأخیر، به محض رسیدن سیگنال‌های مشخص‌شده، فرآیند خاموش کردن برازنده شروع نمی‌شود و درخواست‌های جدید به پاد را می‌توان در تأخیر مشخص‌شده برآورده کرد.

به عنوان مرحله نهایی Program.cs من فقط زیر ایجاد کردم IHostLifetime من اجرا را ثبت می کنم.

public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Host.ConfigureHostOptions(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(45));

// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSingletonIHostLifetime>(sp =>
new DelayedShutdownHostLifetime(sp.GetRequiredServiceIHostApplicationLifetime>(), TimeSpan.FromSeconds(5)));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{

}

app.UseAuthorization();

app.MapControllers();

app.Run();
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

HostApplicationBuilder.Build() در روش IHostApplicationLifetime ve IHostLifetime گزارش شد که به عنوان یک سرویس ثبت شده است. اینجا IHostLifetime با ثبت نام مجدد مشخص شده است DelayedShutdownHostLifetime با اجرای آن می توان از آن استفاده کرد.

با آخرین عملیات، یک فرآیند استقرار بدون وقفه در سمت Kubernetes حاصل شده است. ممنون که خواندید 🙂

منابع

در دنیای امروز، بسته به نیازها، می توان خدمات را به طور مکرر در طول روز مستقر کرد و پیشرفت های جدید ویژه خدمات را می توان مورد استفاده قرار داد. گاهی اوقات این خدمات ممکن است خدماتی باشند که قطع آنی آن مشکلی ایجاد نخواهد کرد و گاهی ممکن است در صورت بروز قطعی حتی برای یک درخواست، طرف مشتری تحت تأثیر منفی قرار گیرد. در این مقاله، من در مورد آنچه که می توانیم برای از بین بردن این وقفه انجام دهیم، هم در Kubernetes و هم در سرویس توسعه یافته با ASP.NET صحبت خواهم کرد.

فهرست

  • استراتژی‌های پایان پاد، ایجاد و به‌روزرسانی در سمت Kubernetes
  • در سمت شبکه Host بررسی ساختار
  • IHostedLifecycleService، IHostedService ve IHostApplicationLifetime بررسی رابط ها
  • بررسی روند خاموش شدن هاست
  • Kind ایجاد یک خوشه Kubernetes با
  • مثال .Net پروژه، Dockerfile ve Deployment ایجاد مانیفست
  • استقرار سرویس در خوشه Kubernetes و انجام آزمایش
  • تاخیر بین ورودی Kubernetes و Kubernetes Control Plane

به روز رسانی استراتژی در Kubernetes

در سمت Kubernetes، در مانیفست شیء استقرار .spec.strategy.type استراتژی به روز رسانی در زیر بیان شده است. این استراتژی Recreate یا، که اگر داده نشود، رفتار پیش فرض است RollingUpdateکارگردان

در محدوده این مقاله، فرض بر این است که برنامه به عنوان یک شی Deployment منتشر شده است. در اشیاء StatefulSets و DaemonSets .spec.updateStrategy.type این استراتژی ها بر اساس مشخصات تعیین می شوند. این استراتژی ها هستند OnDelete ve RollingUpdateکارگردان

بازآفرینی کنید

spec:
  replicas: 10
  strategy:
    type: Recreate
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Recreate در استراتژی به روز رسانی؛ ابتدا تمام غلاف ها خاتمه یافته و سپس پادهای نسخه جدید احیا می شوند. با توجه به مانیفست بالا، Kubernetes تمام 10 پاد را می کشد و سپس پادهای جدید را ایجاد می کند. در این استراتژی امکان پذیر است زمان از کار افتادنموجب (انقطاع) خواهد شد. دلیل این امر این است که قبل از Kubernetes، همه pods; قبل از تشکیل و جایگزینی غلاف های جدید خاتمه می یابد.

Rolling Update

spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
        maxUnavailable: %25
        maxSurge: %25
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

RollingUpdate در استراتژی به روز رسانی، پادها به تدریج با تصویر نسخه جدید جایگزین می شوند. ابتدا غلاف با شماره تصویر جدید ایجاد می شود و سپس غلاف با شماره تصویر قدیمی کشته می شود. این عملیات تا زمانی که برای همه غلاف ها تکمیل شود ادامه می یابد.

maxSurge ve maxUnavailable مولفه های RollingUpdate در استراتژی بیان شده است.

  • maxUnavailable: تعداد پادهایی را نشان می دهد که ممکن است در مرحله به روز رسانی در دسترس نباشند. مقدار را می توان به صورت درصد یا مستقیماً به عنوان تعداد غلاف ها ارائه کرد. یک فیلد اختیاری است و مقدار پیش فرض آن است %25کارگردان
  • maxSurge: به تعداد غلاف هایی اشاره دارد که ممکن است از تعداد کپی های مشخص شده در مانیفست استقرار بیشتر باشد. maxUnavailableدرصد یا مقدار مستقیم غلاف را می توان مانند در داده شود. یک فیلد اختیاری است و مقدار پیش فرض آن است %25کارگردان

در مواردی که مقادیر درصدی برای فیلدهای ذکر شده در بالا داده می شود. اگر تعداد غلاف ها عدد صحیح نباشد maxUnavailable این مقدار برای maxSurge برای این مقدار گرد شده است. به عنوان مثال، زمانی که 1 replica مشخص شده است maxSurge ارزش گرد شده 1'e maxUnavailable مقدار به پایین گرد می شود 0مطابقت دارد. در این حالت ابتدا یک پاد ایجاد می شود و پس از وارد شدن به حالت اجرا، پاد فعلی خاتمه می یابد.

با استراتژی Rolling Update، Kubernetes تضمین می‌کند که برنامه با حداقل وقفه اجرا می‌شود. در زیر مراحل و نمونه استقرار مانیفست برای استقرار با یک ماکت در طول عملیات Rolling Update آمده است.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.14.2
          ports:
            - containerPort: 80
          resources:
            limits:
              cpu: "0.3"
              memory: "75Mi"
            requests:
              cpu: "0.1"
              memory: "50Mi"
  strategy:
    type: RollingUpdate
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

kubectl set image deployment/nginx-deployment nginx=nginx:1.14.1 هنگامی که نسخه تصویر با اجرای دستور به روز می شود، مراحل زیر انجام می شود.

  1. maxSurge ارزش 1 بنابراین، اول از همه، یک غلاف با شماره تصویر جدید شروع به ایستادن می کند.
  2. وضعیت غلاف'ü Runningپس از جابجایی به، Pod موجود توسط Kubernetes قابل دسترسی است. SIGTERM سیگنال ارسال می شود. مسیریابی درخواست‌های جدیدی که به پاد می‌رسند قطع می‌شود و اتصالات باز برای تکمیل درخواست‌های موجود ایجاد می‌شوند. spec.terminationGracePeriodSeconds در دوره مورد انتظار است.
  3. پاد خاتمه یافت spec.terminationGracePeriodSeconds اگر خیلی طولانی باشد، Kubernetes این کار را خواهد کرد SIGKILL سیگنال ارسال می شود و غلاف کشته می شود.

این دوره در سمت Kubernetes پیش بینی می شود، اما در سمت برنامه نیز مشخص شده است. POSIX در نتیجه سیگنال، باید برای خاموش شدن برازنده اقدام کرد.

برای استقرار بالا، هنگام به‌روزرسانی نسخه تصویر، وضعیت‌های غلاف در زیر نشان داده می‌شوند.

kubectl get pods -n default -w
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-59994fb97c-5j4fv   1/1     Running   0          8m8s
nginx-deployment-59994fb97c-g789c   1/1     Running   0          8m9s
nginx-deployment-59994fb97c-nddlf   1/1     Running   0          8m9s
nginx-deployment-5fffc966ff-8crmb   0/1     Pending   0          1s
nginx-deployment-5fffc966ff-8crmb   0/1     Pending   0          1s
nginx-deployment-5fffc966ff-8crmb   0/1     ContainerCreating   0          1s
nginx-deployment-5fffc966ff-8crmb   1/1     Running             0          1s
nginx-deployment-59994fb97c-5j4fv   1/1     Terminating         0          8m16s
nginx-deployment-5fffc966ff-52knq   0/1     Pending             0          0s
nginx-deployment-5fffc966ff-52knq   0/1     Pending             0          0s
nginx-deployment-5fffc966ff-52knq   0/1     ContainerCreating   0          0s
nginx-deployment-59994fb97c-5j4fv   0/1     Terminating         0          8m16s
nginx-deployment-5fffc966ff-52knq   1/1     Running             0          1s
nginx-deployment-59994fb97c-g789c   1/1     Terminating         0          8m18s
nginx-deployment-5fffc966ff-jwmtt   0/1     Pending             0          0s
nginx-deployment-5fffc966ff-jwmtt   0/1     Pending             0          0s
nginx-deployment-5fffc966ff-jwmtt   0/1     ContainerCreating   0          0s
nginx-deployment-59994fb97c-5j4fv   0/1     Terminating         0          8m17s
nginx-deployment-59994fb97c-5j4fv   0/1     Terminating         0          8m17s
nginx-deployment-59994fb97c-5j4fv   0/1     Terminating         0          8m17s
nginx-deployment-59994fb97c-g789c   0/1     Terminating         0          8m18s
nginx-deployment-5fffc966ff-jwmtt   1/1     Running             0          1s
nginx-deployment-59994fb97c-g789c   0/1     Terminating         0          8m19s
nginx-deployment-59994fb97c-g789c   0/1     Terminating         0          8m19s
nginx-deployment-59994fb97c-g789c   0/1     Terminating         0          8m19s
nginx-deployment-59994fb97c-nddlf   1/1     Terminating         0          8m19s
nginx-deployment-59994fb97c-nddlf   0/1     Terminating         0          8m19s
nginx-deployment-59994fb97c-nddlf   0/1     Terminating         0          8m20s
nginx-deployment-59994fb97c-nddlf   0/1     Terminating         0          8m20s
nginx-deployment-59994fb97c-nddlf   0/1     Terminating         0          8m20s
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اظهار داشت spec.terminationGracePeriodSeconds مقدار به طور خاص برای غلاف تعریف شده است. مقدار پیش فرض دهه 30کارگردان

کانتینر سایدکار لارا SIGTERM قبل از ارسال سیگنال، کانتینر اصلی در همان غلاف خاتمه یافته و سپس کانتینرهای سایدکار به ترتیب معکوس که تعریف شده بودند، منتقل می شوند. SIGTERM سیگنال ارسال می شود. به این ترتیب، اطمینان حاصل می شود که محفظه سایدکار در غلاف در زمانی که دیگر نیازی به آن نیست، خاتمه می یابد.

برنامه Nginx در استقرار گنجانده شده است SIGTERM هنگامی که سیگنال را دریافت می کند، به سرعت فرآیند، از جمله اتصالات باز را خاتمه می دهد. از این رو خاموش شدن برازنده به منظور اینکه SIGTERM وقتی سیگنال می آید باید ردیابی شود و SIGQUIT باید سیگنال داده شود برای انجام یک خاموش کردن برازنده در سمت Nginx SIGQUIT منتظر سیگنال برای این کار می توان با bash عمل کرد.

مدل میزبان Net

فناوری جدید با چارچوب Net Core به دنیای ما می آید Host با رویکرد مدل Host هدف آن کپسوله کردن منابعی است که برنامه به آن نیاز دارد و عملکردهای چرخه حیات آن در داخل شی. با ساختار مشخص شده، همچنین هدف حذف بسیاری از کدهای دیگ بخار در قالب های پیش فرض است. با ایجاد هماهنگی‌های خاصی روی این شی، قابلیت‌های زیر به‌طور پیش‌فرض در دسترس هستند و بسته به نوع برنامه، در صورت نیاز قابل ویرایش هستند.

  • ساختار تزریق وابستگی (DI).
  • ورود به سیستم
  • پیکربندی
  • فرآیند خاموش شدن برنامه
  • IHostedService پیاده سازی

اظهار داشت Host رویکرد مدل نیز در قالب‌های برنامه متفاوت است، زیرا نسخه‌های فریمورک .Net تغییر کرده است. سه رویکرد مختلف در زیر با نسخه‌های Net آنها بیان شده است.

.NET Core 2.x با نسخه WebHost زیر کلاس CreateDefaultBuilder روش Host مدل ایجاد و پیکربندی شده است.

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartupStartup>();
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در بالا dotnet در نسخه مشخص شده توسط کلیک کنید ASP.Net Core زمانی که پروژه از ابتدا ایجاد می شود Program.cs بلوک کد موجود در فایل منتقل شده است.

.NET Core 3.x با .NET 5 بین نسخه ها Host با ایجاد یک تغییر بزرگ در رویکرد مدل، پروژه های وب نیز می توانند به طور کلی مورد استفاده قرار گیرند. Host رویکرد برای ایجاد تغییر یافته است. با این تغییر با استفاده از همان کد پایه Host خدمات کارگر، خدمات gRPC و خدمات ویندوز را می توان با استفاده از این مدل توسعه داد. با این روش IWebHostBuilder رابط من یرین IHostBuilder روی رابط Host مدل ساخته شده است.

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartupStartup>();
            }); 
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در بالا .NET Core 3.x با .NET 5 بین نسخه ها dotnet ایجاد شده از ابتدا با cli ASP.NET از پروژه Program.cs بلوک کد موجود در فایل به اشتراک گذاشته شده است.

در هر دو رویکرد مشترک بالا، Startup این را نباید نادیده گرفت که کلاس به طور جدایی ناپذیر به برنامه وب وابسته است، اما IHostBuilder واقع در رابط Host ما بیان کرده ایم که رویکرد مدل را می توان در برنامه های مختلف غیر از برنامه های کاربردی وب توسعه داد. در این اپلیکیشن ها Startup ممکن است کلاس مورد نیاز نباشد. (مثلا Configure روشی که نامیده می شود برای پیکربندی میان افزار در برنامه استفاده می شود، اما برای سرویس های کارگر نیازی به چنین پیکربندی نیست.) به همین دلیل، توسعه دهندگان فریم ورک ConfigureWebHostDefaults آنها با روش گسترش بر این وضعیت غلبه کردند.

.NET 6 تنظیمات انجام شده در دو فایل مختلف با (Startup.cs ve Program.cs) در یک فایل و Host مدل IHostApplicationBuilder بالای کلاس قرار گرفته است dotnet تیم این رویکرد را در یادداشت های مهاجرت خود شرح می دهد. Minimal Hosting آن را به صورت و بیان کردند Minimal APIآنها به عنوان الگوی وب پیش فرض قرار گرفتند. مرجع

namespace Example.Processor.Api;

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        // Add services to the container.
        builder.Services.AddControllers();
        // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle

        builder.Services.AddEndpointsApiExplorer();
        builder.Services.AddSwaggerGen();

        var app = builder.Build();
        // Configure the HTTP request pipeline.
        if (app.Environment.IsDevelopment())

        {
            app.UseSwagger();
            app.UseSwaggerUI();
        }
        app.UseHttpsRedirection();
        app.UseAuthorization();
        
        app.MapControllers();
        app.Run();
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در بالا .NET 6 با نسخه dotnet ایجاد شده از ابتدا در cli ASP.NET از پروژه Program.cs بلوک کد موجود در فایل به اشتراک گذاشته شده است. همانطور که می بینید، تمام تنظیمات در یک فایل قرار دارند و Startup کلاس وجود ندارد. علاوه بر این، با این رویکرد Host.CreateDefaultBuilder روش WebApplication.CreateBuilder با استفاده از روش و IHostBuilder در این مکان IHostApplicationBuilder در حال بازگرداندن است. .NET توسعه دهندگان .NET 7 همراه با Host.CreateApplicationBuilder این روش را معرفی کرد و از آن به عنوان رویکردی برای برنامه های کاربردی وب و غیر وب استفاده کرد. Host توصیه می شود که رویکرد مدل همانطور که در زیر ذکر شده است ادامه یابد. می توانید نظر دیوید فاولر را در مورد این موضوع پیدا کنید.

  • روش نمونه برای برنامه های کاربردی وب
var builder = WebApplication.CreateBuilder();

builder.Logging.AddConsole();

builder.Services.AddOptionsMyOptions>().BindConfiguration("MyConfig");

builder.Services.AddHostedServiceMyWorker>();

var app = builder.Build();

app.MapGet("https://dev.to/", () => "Hello World");

app.Run();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  • روش نمونه برای برنامه های غیر وب
var builder = Host.CreateApplicationBuilder();

builder.Logging.AddConsole();

builder.Services.AddOptionsMyOptions>().BindConfiguration("MyConfig");

builder.Services.AddHostedServiceMyWorker>();

var host = builder.Build();

host.Run();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

WebApplication کلاس 3 رابط مورد نیاز برای برنامه وب را پیاده سازی می کند.

  • IHost – مسئول راه اندازی و خاتمه هاست.
  • IApplicationBuilder – برای ایجاد خطوط لوله میان افزار استفاده می شود
  • IEndpointRouteBuilder – برای نقاط پایانی استفاده می شود

همچنین 3 سرویس زیر را ارائه می دهد: HostApplicationBuilder.Build() متد به محض فراخوانی به طور خودکار در ظرف DI ثبت می شود.

  • IHostApplicationLifetime – می توان آن را به هر کلاسی تزریق کرد و برای انجام عملیات خاموش کردن و پس از راه اندازی زیبا استفاده کرد.
  • IHostLifetime – زمان شروع یا پایان برنامه را کنترل می کند.
  • IHostEnvironment – نام برنامه، مسیر ریشه، نام محیط و غیره برای به دست آوردن اطلاعات استفاده می شود.

IHostApplicationLifetime، IHostLifecycleService، IHostedService بررسی رابط ها

در زیر مثالی برای درک رابط های مشخص شده و رویدادهای Lifetime پیاده سازی شده با این رابط ها آورده شده است. IHostedService اجرا منتقل شده است.

BackgroundService یکی abstract این یک کلاس است و برای ایجاد خدمات پس زمینه استفاده می شود. IHostedService در حالیکه BackgroundService این یک رابط است که توسط . که در Hostاین شامل روش هایی برای مدیریت است. Worker Service یک الگو برای ایجاد یک سرویس پس زمینه است. dotnet new worker با دستور ایجاد می شود.

فقط IHostedService امکان ایجاد سرویس‌هایی وجود دارد که بتوان آن‌ها را پیاده‌سازی کرد و پردازش پس‌زمینه را انجام داد، اما در اینجا، عملیات خاموش کردن برازنده باید به صورت دستی با گوش دادن به رویدادهای طول عمر برنامه در سرویس پیاده‌سازی شود. BackgroundServiceلغو می شود در ExecuteAsync به عنوان پارامتر به متد ارسال می شود CancellationToken با بررسی عملیات انجام شده، عملیات خاموش کردن برازنده را می توان با سهولت بیشتری انجام داد.

public class Worker : IHostedLifecycleService
{
    private readonly ILoggerWorker> _logger;

    public Worker(ILoggerWorker> logger, IHostApplicationLifetime applicationLifetime)
    {
        applicationLifetime.ApplicationStarted.Register(OnStarted);
        applicationLifetime.ApplicationStopping.Register(OnStopping);
        applicationLifetime.ApplicationStopped.Register(OnStopped);
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("IHostedService StartAsync has been called");
        return Task.CompletedTask;
    }

    public Task StartedAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("IHostedLifecycleService StartedAsync has been called");
        return Task.CompletedTask;
    }

    public Task StartingAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("IHostedLifecycleService StartingAsync has been called");
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("IHostedService StopAsync has been called");
        return Task.CompletedTask;
    }

    public Task StoppedAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("IHostedLifecycleService StoppedAsync has been called");
        return Task.CompletedTask;
    }

    public Task StoppingAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("IHostedLifecycleService StoppingAsync has been called");
        return Task.CompletedTask;
    }

    private void OnStarted()
    {
        _logger.LogInformation("IHostApplicationLifetime OnStarted has been called");
    }

    private void OnStopped()
    {
        _logger.LogInformation("IHostApplicationLifetime OnStopped has been called");
    }

    private void OnStopping()
    {
        _logger.LogInformation("IHostApplicationLifetime OnStopping has been called");
    }
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چون می خواهم پردازش پس زمینه انجام دهم IHostedServiceمن ابلاغ کردم که لازم است اجرا شود. اینجا IHostedLifecycleService دلیل استفاده از آن این است که این رابط در حال حاضر IHostedServiceاین به این دلیل است که از . این رابط .Net 8 با آن آورده شد. به این ترتیب می توانیم با سهولت بیشتری در چرخه حیات اپلیکیشن دخالت کرده و عملیات را انجام دهیم. این شامل 4 روش جدید است. اینها StartingAsync، StartedAsync، StoppingAsync ve StoppedAsync است. علاوه بر این IHostApplicationLifetimeاشاره کردم که در زمان ساخت هاست به صورت خودکار ثبت می شود. سه ویژگی زیر این کلاس در واقع سه ویژگی هستند. CancellationToken و با توجه به طول عمر میزبان فعال می شوند. همانطور که در بالا گفته شد برای این توکن ها ثبت نام می کنم.

در بالا ایجاد کردم Hosted Serviceمطابق شکل زیر در کانتینر DI ثبت نام می کنم.

using Example.Worker.Service;

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedServiceWorker>();


var host = builder.Build();
host.Run();

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برنامه را اجرا کنید و سپس CTRL+C وقتی این کار را تمام کردم، خروجی کنسول زیر را می بینم.

info: Example.Worker.Service.Worker[0]
      IHostedLifecycleService StartingAsync has been called
info: Example.Worker.Service.Worker[0]
      IHostedService StartAsync has been called
info: Example.Worker.Service.Worker[0]
      IHostedLifecycleService StartedAsync has been called
info: Example.Worker.Service.Worker[0]
      IHostApplicationLifetime OnStarted has been called
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:\Codes\Example.Worker.Service
info: Example.Worker.Service.Worker[0]
      IHostApplicationLifetime OnStopping has been called
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
info: Example.Worker.Service.Worker[0]
      IHostedLifecycleService StoppingAsync has been called
info: Example.Worker.Service.Worker[0]
      IHostedService StopAsync has been called
info: Example.Worker.Service.Worker[0]
      IHostedLifecycleService StoppedAsync has been called
info: Example.Worker.Service.Worker[0]
      IHostApplicationLifetime OnStopped has been called

C:\Codes\Example.Worker.Service\bin\Debug\net8.0\Example.Worker.Service.exe (process 35456) exited with code 0.
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

وقتی خروجی را بررسی می کنیم، ترتیب به صورت زیر به نظر می رسد.

  1. IHostedLifecycleService.StartingAsync دیده شد که او را صدا زدند. این روشی است که قبل از شروع برنامه فراخوانی می شود.
  2. IHostedService.StartAsync نامیده شده است. این روشی است که زمانی فراخوانی می شود که هاست آماده راه اندازی سرویس باشد.
  3. IHostedLifecycleService.StartedAsync فرا خوانده شده است. IHostedService.StartAsync بلافاصله پس از تکمیل فرآیند اولیه سازی فراخوانی می شود.
  4. IHostApplicationLifetime.ApplicationStarted نشان می دهد که میزبان به طور کامل شروع شده است.

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

  1. IHostApplicationLifetime.ApplicationStopping هنگامی که برنامه شروع به انجام عملیات خاموش کردن برازنده می کند، فعال می شود.
  2. IHostedLifecycleService.StoppingAsync قبل از شروع به پایان برنامه فراخوانی می شود. IHostedService.StopAsync درست قبل از عمل انجام می شود.
  3. IHostedService.StopAsync برنامه یک عملیات خاموش کردن برازنده را انجام می دهد.
  4. IHostedLifecycleService.StoppedAsync زمانی فراخوانی می شود که برنامه عملیات خاموش کردن برازنده خود را کامل کند.
  5. IHostApplicationLifetime.ApplicationStopped این نشان می دهد که عملیات خاموش کردن برازنده برنامه تکمیل شده است.

نکته مهم دیگر در اینجا این است که اپلیکیشن CTRL+C این به این دلیل است که با ترکیب خاتمه می یابد. به طور خودکار روی پیش فرض میزبان IHostLifetime رابط'ni ConsoleLifetime ثبت شده به عنوان . این امر در مورد سرویس وب و پس‌زمینه نیز صدق می‌کند. IHostLifetime اشاره کردم که اینترفیس زمان شروع و پایان برنامه را کنترل می کند. ConsoleLifetime با فشار دادن دکمه های ترکیبی که در بالا ذکر کردم می توانید به برنامه دسترسی پیدا کنید. SIGINT ما داریم سیگنال می دهیم. در Kubernetes، به دلایلی که در بخش اول توضیح دادم، غلاف باید با افزودن یک ظرف به ظرف خاتمه یابد. SIGTERM سیگنال را می فرستد. در نتیجه این سیگنال ها، عملیات خاموش کردن برازنده انجام می شود.

.Net 6 پیش از این، سیگنال‌های posix پشتیبانی نمی‌شدند، اما بر اساس نوع سیگنال مدیریت می‌شدند. .Net 6 پست- ConsoleLifetime، SIGINT، SIGQUIT، SIGTERM با گوش دادن به سیگنال ها، توانست عملیات خاموش کردن برازنده را انجام دهد.

فرآیند خاموش شدن میزبان

پیش فرض .Net 6 و سپس میزبان عمومی IHostLifetime رابط'ni ConsoleLifetime من بیان کردم که به عنوان اجرا شده است. برای خاتمه دادن به لطف میزبان نیز ConsoleLifetimeاین عملیات را می توان با ارسال سیگنال های زیر انجام داد:

  • SIGINT دور انداختن CTRL+C
  • SIGQUIT دور انداختن CTRL+BREAK (پنجره ها)
  • SIGTERM (در سمت Kubernetes، سیگنال برای پایان دادن به غلاف به ظرف ارسال می شود docker stop)

.Net 6 قبل از SIGTERM وقتی سیگنال آمد، عملیات خاموش کردن را نمی‌توان به خوبی انجام داد. با توجه به این وضعیت، کار کنید ConsoleLifetime در کنار، System.AppDomain.ProcessExit گوش دادن به رویداد ProcessExit موضوع متوقف می شود و میزبان منتظر می ماند تا متوقف شود.

فرآیند خاموش شدن میزبان

فرآیندی که در حین عملیات خاموش شدن با ظرافت انجام می شود در بالا توضیح داده شده است. به ترتیب؛

  1. از Kubernetes یا کاربر SIGTERM سیگنال در حال آمدن است در نتیجه این سیگنال IHostApplicationLifetime واقع در زیر StopApplication() روش راه اندازی می شود و ApplicationStopping رویداد پرتاب می شود. قبل از IHost.WaitForShutdownAsync متد برای این رویداد مشخص شده گوش می دهد و از آنجایی که رویداد راه اندازی می شود Main مسدود کردن عملیات
  2. IHost.StopAsync() روش راه اندازی می شود و از درون این روش IHostedService.StopAsync() راه اندازی می شود و باعث می شود هر سرویس میزبانی شده متوقف شود و سپس رویدادهایی را نشان می دهد که متوقف شده است.
  3. سرانجام IHost.WaitForShutdownAsync تکمیل می شود و بلوک های کدی که برنامه برای اجرا نیاز دارد اجرا می شود و عملیات خاموش کردن به خوبی انجام می شود.

پیکربندی در سمت میزبان انجام می شود ShutdownTimeout امکان تنظیم وجود دارد. این مقدار IHost.StopAsync() این مدت زمان تعیین شده برای روش است و مقدار پیش فرض آن 30 ثانیه است.

Kind بالا بردن یک خوشه Kubernetes با

به عنوان منبع باز توسعه یافته است Kind با این محصول، امکان راه اندازی سریع خوشه های Kubernetes به صورت محلی وجود دارد. خوشه Kubernetes که در آن برنامه توسعه یافته برای این مقاله منتشر خواهد شد Kind با محصول ایجاد شده است.

اولا Kind ما cli را به صورت محلی با دستور زیر نصب می کنیم.

curl.exe -Lo kind-windows-amd64.exe https://kind.sigs.k8s.io/dl/v0.23.0/kind-windows-amd64
Move-Item .\kind-windows-amd64.exe c:\some-dir-in-your-PATH\kind.exe
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای اینکه هر بار به دایرکتوری که cli در آن قرار دارد نروید و هیچ عملیاتی انجام ندهید، دایرکتوری که مشخص کرده اید را انتخاب کنید. Path فراموش نکنید که Environment Variable را به آن اضافه کنید.

سپس، فایل yaml مشخص شده در زیر برای راه اندازی یک کلاستر حاوی 3 گره کارگر ایجاد می شود.

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  -
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"    
  extraPortMappings:
  - containerPort: 80
    hostPort: 8081
    protocol: TCP
  - containerPort: 443
    hostPort: 8443
    protocol: TCP
- role: worker
- role: worker
- role: worker
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

وقتی فایل yaml را که در بالا ذکر کردم بررسی می کنیم. مشاهده می‌شود که برای هر نقش گره تعریفی وجود دارد و درخواست‌های ارسال شده به پورت‌های 8081 و 8443 به صورت محلی تعریف شده‌اند تا به کنترل‌کننده ورودی که روی خوشه نصب می‌کنیم ارسال شوند.

kind create cluster --config .\kind-cluster.yaml
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با دستوری که در بالا دادم خوشه را بیدار می کنم.

kind create cluster --config .\kind-config\kind-cluster.yaml
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.30.0) 🖼
 ✓ Preparing nodes 📦 📦 📦 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-kind"
You can now use your cluster with:

kubectl cluster-info --context kind-kind

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس با دستوری که در زیر ارائه کردم، کنترل کننده ورودی را نصب می کنم.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Kindدر این فایل مانیفست که به طور خاص برای .

namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

ایجاد Web API

.Net 8 وقتی یک پروژه web api با Program.cs فایل ظاهر می شود

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        // Add services to the container.
        builder.Services.AddControllers();

        var app = builder.Build();

        // Configure the HTTP request pipeline.
        if (app.Environment.IsDevelopment())
        {

        }

        app.UseAuthorization();

        app.MapControllers();

        app.Run();
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

خدماتی که مورد نیاز نیستند حذف شده اند

من نام Controller و Action را همانطور که در زیر ذکر شده است ویرایش می کنم. در بخش قبل ShutdownTimeoutعرض کردم که مقدار پیش فرض 30 ثانیه است. برای اینکه عملیات خطا دریافت کند، 35 ثانیه در اکشن منتظر می مانم و آن را طوری ترتیب می دهم که پاسخ داده شود.

[ApiController]
[Route("[controller]")]
public class PingPongController : ControllerBase
{
    private readonly ILoggerPingPongController> _logger;

    public PingPongController(ILoggerPingPongController> logger)
    {
        _logger = logger;
    }

    [HttpGet(Name = "Ping")]
    public async TaskIActionResult> Ping()
    {
        await Task.Delay(TimeSpan.FromSeconds(35));
        return Ok("Pong");
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Kind من در حال ایجاد مانیفست استقرار در زیر هستم تا استقرار در خوشه Kubernetes را که با آن ایجاد کرده‌ایم فعال کنم.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pingapi-deployment
  labels:
    app: pingapi
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pingapi
  template:
    metadata:
      labels:
        app: pingapi
    spec:
      containers:
        - name: pingapi
          image: pingapi:0.1
          ports:
            - containerPort: 8080
          resources:
            limits:
              cpu: "0.5"
              memory: "150Mi"
            requests:
              cpu: "0.1"
              memory: "150Mi"

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

.Net 8 پورت پیش فرضی که برنامه در تصاویر منتشر شده با آن اجرا می شود 808080به تغییر یافته است. به همین دلیل، اطلاعات پورت در مانیفست گنجانده شده است. 8080 تنظیم شده است. سند مرتبط

Dockerfileمن به سادگی همانطور که در زیر گفته شد ایجاد می کنم.

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
COPY ./Publish .
ENTRYPOINT ["dotnet", "Example.Ping.Api.dll"]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

استقرار سرویس و انجام تست

اول از همه، درخواست من Publish زیر دایرکتوری منتشر میکنم

dotnet publish -o ./Publish
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس تصویر را می سازم.

docker build -t pingapi:0.1 .
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قبل از اعمال مانیفست استقرار در خوشه، تصویری را که برچسب گذاری کردم با دستور مشخص شده در زیر، به هر گره منتقل می کنم، در غیر این صورت، نیاز به پرتاب این تصویر به مخزن تصویر و بیرون کشیدن آن از خوشه وجود دارد. مهربان در اینجا راحتی را برای ما فراهم می کند.

kind load docker-image pingapi:0.1
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

kind load docker-image pingapi:0.1
Image: "pingapi:0.1" with ID "sha256:2e5cfec8e475ed2d2ccfd0ae9753a7f5feda0e01de0081718ab678203d25edcf" not yet present on node "kind-worker3", loading...
Image: "pingapi:0.1" with ID "sha256:2e5cfec8e475ed2d2ccfd0ae9753a7f5feda0e01de0081718ab678203d25edcf" not yet present on node "kind-worker", loading...
Image: "pingapi:0.1" with ID "sha256:2e5cfec8e475ed2d2ccfd0ae9753a7f5feda0e01de0081718ab678203d25edcf" not yet present on node "kind-control-plane", loading...
Image: "pingapi:0.1" with ID "sha256:2e5cfec8e475ed2d2ccfd0ae9753a7f5feda0e01de0081718ab678203d25edcf" not yet present on node "kind-worker2", loading...
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

من استقرار را با دستوری که در زیر ذکر کردم اعمال می کنم.

kubectl apply -f .\Kubernetes\deployment.yaml
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کانتینر 8080 پورت را با دستوری که در زیر ذکر کردم فوروارد می کنم.

kubectl port-forward deployment/pingapi-deployment -n default 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکنون می توانیم ابتدا آزمایش را شروع کنیم httpstat http://localhost:8080/pingpong/ping من یک درخواست با دستور و سپس برای حذف پاد ارسال می کنم kubectl delete pod/pingapi-deployment-5c78cbdfc-bfd9b فرمان را اجرا می کنم. همانطور که انتظار داشتیم، هر دو در سمت کوبرنتیس terminationGracePeriodSeconds از آنجایی که ما مدت زمان را در مانیفست استقرار مشخص نکرده‌ایم، مقدار پیش‌فرض 30 ثانیه است. .Net در کنار Host از شی ShutdownTimeout از آنجایی که مقدار پیش‌فرض 30 ثانیه را مشخص نکرده‌ایم، به نظر می‌رسد که همانطور که در خطا مشاهده می‌شود، برای درخواستی که انجام داده‌ایم، اتصال قطع شده است.

سناریوی مشابهی را می توان با استقرار یک نسخه تصویر جدید آزمایش کرد. هدف در اینجا نصب پاد است. SIGTERM از آنجایی که هدف ارسال سیگنال است، فرآیند آزمایش با عملیات حذف پاد انجام شد.

نمای ترمینال

برای غلبه بر این وضعیت، ابتدا در سمت برنامه Hostمن پیکربندی می کنم، سپس در زیر مانیفست استقرار spec.terminationGracePeriodSeconds من مقدار نامگذاری شده را به روز می کنم.

Program.cs نسخه جدید فایل به شرح زیر ساخته شده است.


namespace Example.Ping.Api;

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.Host.ConfigureHostOptions(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(45));

        // Add services to the container.

        builder.Services.AddControllers();
        // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
        builder.Services.AddEndpointsApiExplorer();
        var app = builder.Build();


        // Configure the HTTP request pipeline.
        if (app.Environment.IsDevelopment())
        {

        }

        app.UseAuthorization();

        app.MapControllers();

        app.Run();
    }
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

deployment.yaml نسخه جدید فایل به شرح زیر است؛

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pingapi-deployment
  labels:
    app: pingapi
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pingapi
  template:
    metadata:
      labels:
        app: pingapi
    spec:
      containers:
        - name: pingapi
          image: pingapi:0.2
          ports:
            - containerPort: 8080
          resources:
            limits:
              cpu: "0.5"
              memory: "150Mi"
            requests:
              cpu: "0.1"
              memory: "150Mi"
      terminationGracePeriodSeconds: 50
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تصویری که به تازگی در مانیفست استقرار ساختیم pingapi:0.2 ما به عنوان به روز رسانی می کنیم.

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

سیاهههای مربوط به ترمینال

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

علی‌رغم تنظیماتی که در بالا انجام دادیم، به‌ویژه برای سرویس‌های پرکاربرد RollingUpdate امکان دریافت خطا حتی برای چند درخواست در این مرحله وجود دارد. این به دلیل تاخیر بین ورودی و صفحه کنترل است.

دلیل این امر این است که هواپیمای ورودی و کنترل Kubernetes موجودیت های مختلفی در Kubernetes هستند و عملیات خود را به طور مستقل انجام می دهند. هنگامی که Kubernetes می‌خواهد یک پاد را خاتمه دهد، صفحه کنترل پادهایی را که قرار است خاتمه داده شوند را از سرویس حذف می‌کند و ورود از این موضوع مطلع می‌شود و ارسال درخواست‌ها به پادهایی که باید خاتمه داده شوند را متوقف می‌کند. بین این دو عملیات تاخیر وجود دارد زیرا ingress به صورت دوره ای این تغییرات ایجاد شده در سرویس های سمت خود را به روز می کند. در این موقعیت terminating این باعث می شود که درخواست ها به پادهای موجود در وضعیت با تعداد کمی درخواست منتقل شوند.

..Net برنامه در کنار IHost.StopAsync() پس از تماس، اجازه نمی دهد درخواست های جدید از طریق اتصالات از قبل باز شده انجام شود و اجازه نمی دهد که یک اتصال جدید همزمان باز شود. به همین دلیل تاخیر در این بین IHost.StopAsync() این بدان معنی است که ممکن است درخواست های جدید با شروع فرآیند ارائه شود. این منجر به خطا برای طرف درخواست کننده می شود.

به عنوان راه حلی برای این وضعیت، روشی که در زیر توضیح خواهم داد توسط تیم دات نت پیشنهاد شد. مرجع

قبلا IHostLifetimeگزارش شده است که زمان شروع یا توقف برنامه را کنترل می کند. به همین دلیل، اول از همه، یک جدید IHostLifetime ما در حال اجرا هستیم.

using System.Runtime.InteropServices;

public class DelayedShutdownHostLifetime : IHostLifetime, IDisposable
{
    private IHostApplicationLifetime _applicationLifetime;
    private TimeSpan _delay;
    private IEnumerableIDisposable>? _disposables;

    public DelayedShutdownHostLifetime(IHostApplicationLifetime applicationLifetime, TimeSpan delay) { 
        _applicationLifetime = applicationLifetime;
        _delay = delay;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    public Task WaitForStartAsync(CancellationToken cancellationToken)
    {
        _disposables = new IDisposable[]
        {
            PosixSignalRegistration.Create(PosixSignal.SIGINT, HandleSignal),
            PosixSignalRegistration.Create(PosixSignal.SIGQUIT, HandleSignal),
            PosixSignalRegistration.Create(PosixSignal.SIGTERM, HandleSignal)
        };
        return Task.CompletedTask;
    }

    protected void HandleSignal(PosixSignalContext ctx)
    {
        ctx.Cancel = true;
        Task.Delay(_delay).ContinueWith(t => _applicationLifetime.StopApplication());
    }

    public void Dispose()
    {
        foreach (var disposable in _disposables ?? Enumerable.EmptyIDisposable>()) 
        {
            disposable.Dispose(); 
        }
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هنگامی که فرآیند انجام شده بررسی می شود، برنامه در هنگام ایستادن به وضوح قابل مشاهده است. POSIX عملیات ثبت برای سیگنال ها انجام شده است. به این ترتیب، وقتی یکی از این سیگنال ها به سمت برنامه می آید، IHostApplicationLifetime.StopApplication() قبل از شروع عملیات Task.Delay تاخیر با تعریف شده است. به لطف این تأخیر، به محض رسیدن سیگنال‌های مشخص‌شده، فرآیند خاموش کردن برازنده شروع نمی‌شود و درخواست‌های جدید به پاد را می‌توان در تأخیر مشخص‌شده برآورده کرد.

به عنوان مرحله نهایی Program.cs من فقط زیر ایجاد کردم IHostLifetime من اجرا را ثبت می کنم.

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.Host.ConfigureHostOptions(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(45));

        // Add services to the container.
        builder.Services.AddControllers();
        // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
        builder.Services.AddEndpointsApiExplorer();
        builder.Services.AddSingletonIHostLifetime>(sp =>
            new DelayedShutdownHostLifetime(sp.GetRequiredServiceIHostApplicationLifetime>(), TimeSpan.FromSeconds(5)));

        var app = builder.Build();

        // Configure the HTTP request pipeline.
        if (app.Environment.IsDevelopment())
        {

        }

        app.UseAuthorization();

        app.MapControllers();

        app.Run();
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

HostApplicationBuilder.Build() در روش IHostApplicationLifetime ve IHostLifetime گزارش شد که به عنوان یک سرویس ثبت شده است. اینجا IHostLifetime با ثبت نام مجدد مشخص شده است DelayedShutdownHostLifetime با اجرای آن می توان از آن استفاده کرد.

با آخرین عملیات، یک فرآیند استقرار بدون وقفه در سمت Kubernetes حاصل شده است. ممنون که خواندید 🙂

منابع

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا