Pubblicato il 28/10/2023 da alnao nella categoria AWS

Uno dei servizi serverless più famosi di AWS è Step Functions, questo servizio permette la definizione di flussi di lavoro con macchine a stati, pensato per lavorare con la maggior parte degli altri servizi AWS e permette di costruire applicazioni distribuite con processi automatizzate. Spesso le singole definizione vengono chiamate State machine (in italiano macchine a stati), spesso abbreviato con la sigla sm. Ad oggi è studiato per poter lavorare con oltre 200 servizi AWS e oltre 1000 API, con la possibilità di creare strutture logiche condizionali e cicli di esecuzioni. Il servizio è studiato per non avere limiti di tempo (il timeout di default per una step function è un mese), prevede 4.000 transazioni di stato gratuite al mese e il costo di 0,025 $ per ogni mille transizioni di stato successive.

Spesso una macchina a stati viene rappresentata nella console web in maniera grafica con un grafico:

I casi d’uso più semplici sono l’invocazione di funzioni Lambda e integrazione con gli altri servizi serverless come Dynamo, SNS e SQS; per esempio si può pensare di voler definire una macchina a stati per salvare un ordine di un e-business, i passi da definire sono il salvataggio dell’ordine in una tabella (Dynamo), l’inserimento dell’ordine in una coda per il magazzino (SQS) e l’invio di una notifica al cliente (SNS).

Tuttavia anche questo servizio ha alcune caratteristiche che lo limitano:

  • ogni SM è sequenzale e, a meno di struttute molto complesse, non si può tornare indietro
  • ogni SM ha un unico inizio e, per terminare correttamente, deve avere un solo punto di fine
  • le esecuzioni hanno dei limiti di esecuzione descritti nella pagina ufficiale
  • ogni esecuzione ha un nome e una arn che identifica l’esecuzione
  • ogni esecuzione può avere in input ma deve essere in formato json
  • una esecuzione non può essere messa in pausa, può solo essere terminata

Tecnicamente i passi da eseguire in una macchina a stati sono rappresentati in un documenti json che prevede un unizio (startAt), una serie di passi che terminano con un End finale.

{
  "StartAt":"CopyInputFile",
  "States":{
    "CopyInputFile":{
      "Type":"Task",
      "Resource":"arn:aws:states:::aws-sdk:s3:copyObject",
      "Parameters":{
         "Bucket":"bucket1",
         "CopySource.$":'States.Format("bucket1/path/{}', $.filename)",
         "Key.$":"States.Format('bucket2/path2/{}-{}', $.filename, $$.Execution.StartTime)"
      },
      "ResultPath":null,
      "Next":"DeleteInputFile"
    },
    "DeleteInputFile":{
      "Type":"Task",
      "Resource":"arn:aws:states:::aws-sdk:s3:deleteObject",
      "Parameters":{
         "Bucket":"bucket1",
         "Key.$":"States.Format(\"path/{}\", $.filename)"
      },
      "ResultPath":null,
      "Next":"End"
    },
    "End":{
     "Type":"Pass",
     "End":true
    }
  }
}

AWS ha studiato un ambiente grafico nella console grafica dove è possibile creare una macchina a stati e definire tutte le configurazioni degli stati:


La documentazione ufficiale e il blog sono ricchi di esempi di questo tipo ovviamente è possibile gestire questo servizio con Cli e in maniera programmatica con SDK e CloudFormation.


La CLI un un numeroso elenco di comandi per la gestione delle state machine, i comandi più usati sono:

  • lista delle macchine a state definite
aws stepfunctions list-state-machines
aws stepfunctions list-state-machines --output table --query 'stateMachines[*].[name,stateMachineArn]'
  • dettaglio di una macchina a stati
aws stepfunctions describe-state-machine --state-machine-arn arn:aws:states:eu-west-1:740456629644:stateMachine:sfBonificiWaitingDaCedacri --output text
  • elenco di esecuzioni
aws stepfunctions list-executions --state-machine-arn arn:aws:states:eu-west-1:740456629644:stateMachine:sfBonificiWaitingDaCedacri --output table --query 'executions[*].[name,status,startDate,stopDate,executionArn]'
  • elenco di esecuzioni
aws stepfunctions list-executions --state-machine-arn arn:aws:states:eu-west-1:740456629644:stateMachine:sfBonificiWaitingDaCedacri --output table --query 'executions[*].[name,status,startDate,executionArn]' --status-filter FAILED
  • dettaglio di una esecuzione
aws stepfunctions describe-execution --execution-arn arn:aws:states:eu-west-1:740456629644:execution:sfBonificiWaitingDaCedacri:testDaCli
--output text
  • avvio di una macchina a stati
aws stepfunctions start-execution --state-machine-arn arn:aws:states:eu-west-1:740456629644:stateMachine:sfBonificiWaitingDaCedacri
  • avvio di una macchina a stati con un parametro
aws stepfunctions start-execution --state-machine-arn arn:aws:states:eu-west-1:740456629644:stateMachine:sfBonificiWaitingDaCedacri --name testDaCli --input "{ \"filename\": \"prova.csv\" , \"SkipDeleteSourceFile\" : false }"

Con SDK è possibile gestire le macchine a stati definire nel servizio Step Function le funzioni base:

def state_machine_list(profile_name):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('stepfunctions')
  response = client.list_state_machines(maxResults=100)
  if 'stateMachines' in response:
    return response['stateMachines']
  return[]
def state_machine_detail(profile_name,smArn):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('stepfunctions')
  response = client.describe_state_machine( stateMachineArn=smArn)
  return response
def state_machine_execution(profile_name,smArn):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('stepfunctions')
  response = client.list_executions(
    stateMachineArn=smArn,
    #statusFilter='RUNNING'|'SUCCEEDED'|'FAILED'|'TIMED_OUT'|'ABORTED',
    maxResults=100,
  )
  if 'executions' in response:
    return response['executions']
  return []
def state_machine_execution_detail(profile_name,esecutionArn):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('stepfunctions')
  response = client.describe_execution(executionArn=esecutionArn)
  return response
def state_machine_start(profile_name,stateMachineArn,name,input):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('stepfunctions')
  response = client.start_execution( stateMachineArn=stateMachineArn, name=name, input=input )
  return response

Con CloudFormation è possibile definire macchine a stati e lavorare con altri servizi in maniera “automatica”, la miglior strada è creare un file con la definizione della macchina e richiamare il file da un template, per esempio la definizione della risorsa:

StateMachine:
  Type: AWS::Serverless::StateMachine
  Properties:
    Name: !Ref SFName
    Type: STANDARD
    DefinitionUri: statemachine.yaml
    Role: !GetAtt StateMachineRole.Arn
    DefinitionSubstitutions:
      SourcePath: !Ref SourcePath
      SourceBucket: !Ref SourceBucket
      DestBucket: !Ref DestBucket
      DestFilePath: !Ref DestFilePath
    Logging:
      Destinations:
      - CloudWatchLogsLogGroup:
        LogGroupArn: !GetAtt StateMachineLogGroup.Arn
        Level: ALL
        IncludeExecutionData: True

Nell’esempio si può intuire che c’è da definire una regola IAM per impostare i permessi della macchina (che di default non può fare nulla se non specificato chiaramente in una una regola) e il gruppo di log su CloudWatch. La definizione della macchina può essere descritta in json o un yaml, un semplice esempio di step function che sposta un file è:

StartAt: CopyInputFile
States:
CopyInputFile:
  Type: Task
  Resource: arn:aws:states:::aws-sdk:s3:copyObject
  Parameters:
    Bucket: ${DestBucket}
    CopySource.$: States.Format('${SourceBucket}/${SourcePath}/{}', $.filename)
    Key.$:States.Format('${DestFilePath}/{}-{}',$.filename,$$.Execution.StartTime)
  ResultPath: null
  Next: DeleteInputFile
DeleteInputFile:
  Type: Task
  Resource: arn:aws:states:::aws-sdk:s3:deleteObject
  Parameters:
    Bucket: ${SourceBucket}
    Key.$: States.Format('${SourcePath}/{}', $.filename)
  ResultPath: null
  Next: End
End:
  Type: Pass
  End: true

Come è ovvio questo servizio non lavora mai da solo ma è studiato per essere integrato con altri servizi, nell’esempio completo si può vedere come EventBridge avvia una funzione lambda che poi esegue la macchina a stati che copia il file e poi lo elabora con un altra funzione lambda:

L’esempio completo funzionante può essere trovato al solito repository:

https://github.com/alnao/AWSCloudFormationExamples/tree/master/Esempio08stepFunction
MENU