I have a .Net Worker service that runs as a K8S Cronjob but when it runs to completion the service is not exiting as expected. The pod remains in running state and thus the K8S job never completes as in the logs below :
[40m[1m[33mwarn[39m[22m[49m: UploaderCron.Worker[0]
Worker STARTING
[40m[32minfo[39m[22m[49m: UploaderCron.Worker[0]
Worker running at: 11/04/2022 11:27:01 +00:00
Connected to SFTP Server
/taat/DEV-AAA-20221104T112701/DatabaseA20221104112606.gz
File uploaded successfully
/taat/DEV-BBB-20221104T112701/DatabaseB20221104112606.gz
File uploaded successfully
....
....
Backup file successfully purged
Backup file successfully purged
[40m[32minfo[39m[22m[49m: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
[40m[32minfo[39m[22m[49m: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
[40m[32minfo[39m[22m[49m: Microsoft.Hosting.Lifetime[0]
Content root path: /app
[40m[32minfo[39m[22m[49m: UploaderCron.Worker[0]
Worker running at: 11/04/2022 11:35:46 +00:00
Connected to SFTP Server
[40m[32minfo[39m[22m[49m: UploaderCron.Worker[0]
Worker running at: 11/04/2022 11:44:08 +00:00
Connected to SFTP Server
[40m[32minfo[39m[22m[49m: UploaderCron.Worker[0]
Worker running at: 11/04/2022 11:52:31 +00:00
Connected to SFTP Server
await Task.Delay(450000, stoppingToken);
In each of these cases the service is connecting again to the sftp server and creating the upload directory (no files are being uploaded this time, only the directory is being created).
_logger.LogWarning("Worker STOPPING: {time}", DateTimeOffset.Now);
This is my Worker code :
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Helper.LoadFiles();
await Task.Delay(450000, stoppingToken);
}
}
public override Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogWarning("Worker STARTING");
return base.StartAsync(cancellationToken);
}
public override Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogWarning("Worker STOPPING: {time}", DateTimeOffset.Now);
return base.StopAsync(cancellationToken);
}
}
Helper.cs
public static class Helper
{
public static async Task UploadFiles()
{
using (var fileStream = System.IO.File.OpenRead(localFilePath))
{
sftp.UploadFile(fileStream, remoteFilepath);
Console.WriteLine("File uploaded successfully");
}
//more upload logic
}
public static async Task LoadFiles()
{
await Helper.UploadFiles();
}
}
The K8S CronJob for the worker service (container uploader) :
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: dbbackup
spec:
schedule: "26 11 * * *" #Cron job everyday at 11:26am
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/name: mongodb
topologyKey: "kubernetes.io/hostname"
initContainers:
- name: backup
image: backup:latest
imagePullPolicy: "IfNotPresent"
volumeMounts:
- mountPath: /tmp
name: datadir
command: ['sh', '-c',"./dump.sh"]
containers:
- name: uploader
image: uploader:latest
imagePullPolicy: "IfNotPresent"
volumeMounts:
- mountPath: /tmp
name: datadir
restartPolicy: OnFailure
volumes:
- name: datadir
persistentVolumeClaim:
claimName: mongodb-taat
NB:For context I am following the reference implementation given here
What am I missing so that the container stops once the task is completed ?
Well, right here your background service is deliberately running until it is requested to stop, periodically doing its work:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Helper.LoadFiles();
await Task.Delay(450000, stoppingToken);
}
}
If you want the application to exit when it completes, then just do that:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Helper.LoadFiles();
}
finally
{
_hostApplicationLifetime.StopApplication();
}
}
private readonly IHostApplicationLifetime _hostApplicationLifetime;
public Worker(ILogger<Worker> logger, IHostApplicationLifetime hostApplicationLifetime)
{
_logger = logger;
_hostApplicationLifetime = hostApplicationLifetime;
}