راه اندازی بدون درز در 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
veIHostApplicationLifetime
بررسی رابط ها - بررسی روند خاموش شدن هاست
-
Kind
ایجاد یک خوشه Kubernetes با - مثال
.Net
پروژه،Dockerfile
veDeployment
ایجاد مانیفست - استقرار سرویس در خوشه Kubernetes و انجام آزمایش
- تاخیر بین ورودی Kubernetes و Kubernetes Control Plane
به روز رسانی استراتژی در Kubernetes
در سمت Kubernetes، در مانیفست شیء استقرار .spec.strategy.type
استراتژی به روز رسانی در زیر بیان شده است. این استراتژی Recreate
یا، که اگر داده نشود، رفتار پیش فرض است RollingUpdate
کارگردان
در محدوده این مقاله، فرض بر این است که برنامه به عنوان یک شی Deployment منتشر شده است. در اشیاء StatefulSets و DaemonSets
.spec.updateStrategy.type
این استراتژی ها بر اساس مشخصات تعیین می شوند. این استراتژی ها هستندOnDelete
veRollingUpdate
کارگردان
بازآفرینی کنید
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'emaxUnavailable
مقدار به پایین گرد می شود 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
باید سیگنال داده شود برای انجام یک خاموش کردن برازنده در سمت NginxSIGQUIT
منتظر سیگنال برای این کار می توان با 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
veIHostLifetime
گزارش شد که به عنوان یک سرویس ثبت شده است. اینجاIHostLifetime
با ثبت نام مجدد مشخص شده استDelayedShutdownHostLifetime
با اجرای آن می توان از آن استفاده کرد.
با آخرین عملیات، یک فرآیند استقرار بدون وقفه در سمت Kubernetes حاصل شده است. ممنون که خواندید 🙂
منابع