0

I’m trying to achieve the following in Azure: allow users to programmatically spin up a docker container for a predetermined period of time after which the container is automatically stopped. The aim is to avoid the costs of idle containers that are only used sporadically for short periods. So far my idea has been to use Azure Container Instances and two Azure functions:

  • runContainer: takes the name of the image in the container registry and the time the container will be allowed to run + other parameters (ram, cores...). This azure function starts the container and then asynchronously calls the stopContainer function with the UpTime parameter value and the parameters required to stop the container.
  • stopContainer: sleeps for UpTime hours then stops the container.

The problem I’ve encountered is that when testing locally, the stopContainer function appears to never be called when invoked within Start-ThreadJob that should allow the call to be non-blocking. I’m using Powershell as the functions' language because I want to use the New-AzContainerGroup cmndlet to start the container as per this tutorial.

Minimal example for the runContainer run.ps1

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "This is the calling function."

# Interact with query parameters or the body of the request.
$UpTime = $Request.Query.UpTime

### Insert code to start the container here ###
# Asynchronously call function that will stop the container
Start-ThreadJob {Invoke-WebRequest -UseBasicParsing -Uri "http://localhost:7071/api/stopContainer?UpTime=$UpTime"}
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = [HttpStatusCode]::OK
    Body = "Called the waiting function with UpTime=$UpTime"
})

Minimal example for the stopContainer run.ps1

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "This is the waiting function"

# Interact with query parameters or the body of the request.
$UpTime = $Request.Query.UpTime

Start-Sleep -Seconds $UpTime
### Insert code to stop the container here ###
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = [HttpStatusCode]::OK
    Body = "Waited for $UpTime seconds, stopping container."
})

I get the following log when starting the functions locally on Windows.

Azure Functions Core Tools
Core Tools Version:       3.0.3160 Commit hash: 00aa7f43cc5c5f15241b5e6e5363256f19ceb990
Function Runtime Version: 3.0.14916.0


Functions:

        startContainer: [GET] http://localhost:7071/api/startContainer

        stopContainer: [GET] http://localhost:7071/api/stopContainer

For detailed output, run func with --verbose flag.
[2021-01-14T12:50:27.025Z] Worker process started and initialized.
[2021-01-14T12:50:31.698Z] Executing 'Functions.startContainer' (Reason='This function was programmatically called via the host APIs.', Id=ee48845e-274b-4532-80ae-85372e4474fc)
[2021-01-14T12:50:32.203Z] INFORMATION: This is the calling function.
[2021-01-14T12:50:32.362Z] OUTPUT:
[2021-01-14T12:50:32.375Z] OUTPUT: Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
[2021-01-14T12:50:32.379Z] OUTPUT: --     ----            -------------   -----         -----------     --------             -------
[2021-01-14T12:50:32.382Z] OUTPUT: 1      Job1            ThreadJob       Running       False           PowerShell           Invoke-WebRequest -UseBa.
[2021-01-14T12:50:32.388Z] OUTPUT:
[2021-01-14T12:50:32.498Z] Executed 'Functions.startContainer' (Succeeded, Id=ee48845e-274b-4532-80ae-85372e4474fc, Duration=810ms)

The stopContainer function is apparently never called. Any idea on how to proceed? Is an asynchronous call to a function stopping the container even a good solution here?

Rok
  • 97
  • 6

1 Answers1

1

Your issue is probably here: sleeps for UpTime hours

An Azure Function - at least in Consumption Plan - can only run for about 5minutes. And sleeping counts as "running" here. You should switch to something like a Durable Function (in preview currently for PowerShell). Create a Durable Timer that will do the trick for you.

As Anatoli added in the comments (thanks for that!) here is an example of Durable Timers in PS: https://github.com/Azure/azure-functions-powershell-worker/blob/dev/examples/durable/DurableApp/MonitorOrchestrator/run.ps1

silent
  • 14,494
  • 4
  • 46
  • 86
  • 1
    Durable Timers in PowerShell are not officially documented yet, but here is a working example: https://github.com/Azure/azure-functions-powershell-worker/blob/dev/examples/durable/DurableApp/MonitorOrchestrator/run.ps1 (see the `Start-DurableTimer` invocation). – Anatoli Beliaev Jan 14 '21 at 20:29
  • thats a lot @AnatoliBeliaev! I'll add that to my answer if thats alright – silent Jan 14 '21 at 21:56
  • Thank you, durable functions and a durable timer got me on the right path! – Rok Jan 27 '21 at 14:45
  • @AnatoliBeliaev is there any way of setting a custom orchestration status with PowerShell? (like for [other languages](https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-custom-orchestration-status?tabs=csharp)) I'd like to keep the object returned by `New-OrchestrationCheckStatusResponse` but add my own info to it. – Rok Jan 27 '21 at 14:46
  • @Rok Custom orchestration status is not implemented for PowerShell yet, but should be available within a month or so. – Anatoli Beliaev Jan 27 '21 at 21:51