docker Kubernetes中的.NET 5 BackgroundService不会退出

zbq4xfa0  于 2024-01-06  发布在  Docker
关注(0)|答案(2)|浏览(234)

我能够在Ubuntu 18.04上的Azure Kubernetes集群中使用BackgroundService成功运行.NET 5控制台应用程序。事实上,BackgroundService是真正运行的全部:只是从队列中抓取消息,执行一些操作,然后在Kubernetes告诉它停止时终止,或者偶尔出现异常。
最后一种情况给我带来了问题。当BackgroundService遇到不可恢复的异常时,我希望容器停止(完成,或者任何会导致Kubernetes重新启动或销毁/重新创建容器的状态)。
不幸的是,任何时候遇到异常,BackgroundService * 似乎 * 会命中StopAsync()函数(从我在日志和控制台输出中看到的),但容器保持在运行状态,永远不会重新启动。我的Main()如下所示:

  1. public static async Task Main(string[] args)
  2. {
  3. // Build service host and execute.
  4. var host = CreateHostBuilder(args)
  5. .UseConsoleLifetime()
  6. .Build();
  7. // Attach application event handlers.
  8. AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
  9. AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
  10. try
  11. {
  12. Console.WriteLine("Beginning WebSec.Scanner.");
  13. await host.StartAsync();
  14. await host.WaitForShutdownAsync();
  15. Console.WriteLine("WebSec.Scanner has completed.");
  16. }
  17. finally
  18. {
  19. Console.WriteLine("Cleaning up...");
  20. // Ensure host is properly disposed.
  21. if (host is IAsyncDisposable ad)
  22. {
  23. await ad.DisposeAsync();
  24. }
  25. else if (host is IDisposable d)
  26. {
  27. d.Dispose();
  28. }
  29. }
  30. }

字符串
如果相关的话,ProcessExitUnhandledException的事件处理程序存在,以刷新AppInsights遥测通道(可能是阻塞它?):

  1. private static void OnProcessExit(object sender, EventArgs e)
  2. {
  3. // Ensure AppInsights logs are submitted upstream.
  4. Console.WriteLine("Flushing logs to AppInsights");
  5. TelemetryChannel.Flush();
  6. }
  7. private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
  8. {
  9. var thrownException = (Exception)e.ExceptionObject;
  10. Console.WriteLine("Unhandled exception thrown: {0}", thrownException.Message);
  11. // Ensure AppInsights logs are submitted upstream.
  12. Console.WriteLine("Flushing logs to AppInsights");
  13. TelemetryChannel.Flush();
  14. }


我只在BackgroundService中覆盖ExecuteAsync()

  1. protected async override Task ExecuteAsync(CancellationToken stoppingToken)
  2. {
  3. this.logger.LogInformation(
  4. "Service started.");
  5. try
  6. {
  7. // Loop until the service is terminated.
  8. while (!stoppingToken.IsCancellationRequested)
  9. {
  10. // Do some work...
  11. }
  12. }
  13. catch (Exception ex)
  14. {
  15. this.logger.LogWarning(
  16. ex,
  17. "Terminating due to exception.");
  18. }
  19. this.logger.LogInformation(
  20. "Service ending.",
  21. }


我的Dockerfile很简单,有这一行来运行服务:

  1. ENTRYPOINT ["dotnet", "MyService.dll"]


我是不是漏掉了什么明显的东西?我觉得为了让它正常运行,我忘记了把它作为一个Linux容器运行的一些东西。
谢谢你,谢谢

xkftehaa

xkftehaa1#

下面是如何使用IHostApplicationLifetime.StopApplication()的完整示例。

  1. void Main()
  2. {
  3. var host = Host.CreateDefaultBuilder()
  4. .ConfigureServices((context, services) =>
  5. {
  6. services.AddHostedService<MyService>();
  7. })
  8. .Build();
  9. Console.WriteLine("Starting service");
  10. host.Run();
  11. Console.WriteLine("Ended service");
  12. }
  13. // You can define other methods, fields, classes and namespaces here
  14. public class MyService : BackgroundService
  15. {
  16. private readonly IHostApplicationLifetime _lifetime;
  17. private readonly Random _rnd = new Random();
  18. public MyService(IHostApplicationLifetime lifetime)
  19. {
  20. _lifetime = lifetime;
  21. }
  22. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  23. {
  24. try
  25. {
  26. while (true)
  27. {
  28. stoppingToken.ThrowIfCancellationRequested();
  29. var nextNumber = _rnd.Next(10);
  30. if (nextNumber < 8)
  31. {
  32. Console.WriteLine($"We have number {nextNumber}");
  33. }
  34. else
  35. {
  36. throw new Exception("Number too high");
  37. }
  38. await Task.Delay(1000);
  39. }
  40. }
  41. // If the application is shutting down, ignore it
  42. catch (OperationCanceledException e) when (e.CancellationToken == stoppingToken)
  43. {
  44. Console.WriteLine("Application is shutting itself down");
  45. }
  46. // Otherwise, we have a real exception, so must ask the application
  47. // to shut itself down.
  48. catch (Exception e)
  49. {
  50. Console.WriteLine("Oh dear. We have an exception. Let's end the process.");
  51. // Signal to the OS that this was an error condition by
  52. // setting the exit code.
  53. Environment.ExitCode = 1;
  54. _lifetime.StopApplication();
  55. }
  56. }
  57. }

字符串
此程序的典型输出如下所示:

  1. Starting service
  2. We have number 0
  3. info: Microsoft.Hosting.Lifetime[0]
  4. Application started. Press Ctrl+C to shut down.
  5. info: Microsoft.Hosting.Lifetime[0]
  6. Hosting environment: Production
  7. info: Microsoft.Hosting.Lifetime[0]
  8. Content root path: C:\Users\rowla\AppData\Local\Temp\LINQPad6\_spgznchd\shadow-1
  9. We have number 2
  10. Oh dear. We have an exception. Let's end the process.
  11. info: Microsoft.Hosting.Lifetime[0]
  12. Application is shutting down...
  13. Ended service

展开查看全部
30byixjq

30byixjq2#

我偶然看到了这篇文章,我不认为RB的回答能解决我的问题,所以我想扩展一下他的回答,以防对其他人有帮助。我在kubernetes中使用.net7和.net8,有两个问题,1)我的后台服务不会退出,2)如果抛出错误,进程会退出,代码0表示kubernetes成功。
在大量的研究和测试之后,你会想要注入IHostApplicationLifetime并停止你的应用程序,因为后台服务将运行直到被取消。捕捉异常并设置Environment.ExitCode = 1以让kubernetes知道有一个失败。

  1. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  2. {
  3. try
  4. {
  5. // your long-running work
  6. }
  7. catch (OperationCanceledException)
  8. {
  9. // log or ignore
  10. }
  11. catch (Exception exception)
  12. {
  13. // BackgroundService always exits with code 0 which does not let kubernetes know
  14. // if there was an error. Explicitly setting exit code to 1 to relay a failure.
  15. Environment.ExitCode = 1;
  16. }
  17. finally
  18. {
  19. _lifetime.StopApplication();
  20. }
  21. }

字符串

展开查看全部

相关问题