Integración de AWS Step Functions con ECS

Introducción

En este artículo explicaremos cómo se realiza la integración de AWS Step Functions con ECS y como añadiendo otros servicios (AWS Lambda functions) puedes conseguir una solución completamente serverless para orquestar datos y servicios que se ejecuten en contenedores.

¿Qué es AWS Step Functions?

AWS Step Functions es un servicio de orquestación serverless que te permite definir una serie de pasos basados en eventos creando un flujo de trabajo. Puedes gestionar funciones AWS Lambda y otros servicios AWS para crear una aplicación distribuida como si fuera una máquina de estados.

En esta ocasión vamos a hablar de la integración de Lambdas con Amazon Elastic Container Service (Amazon ECS) en su modalidad serverless, Fargate, para definir un flujo en el que dado los datos de entrada decidimos qué tarea vamos a ejecutar, qué comando le vamos a pasar al contenedor, esperamos a que finalice y guardamos los logs de la ejecución en una carpeta de S3.

Cómo empezar: creación del step functions

Lo primero que tenemos que hacer es crear nuestro step functions, lo haremos a través de la consola para que sea más visual. Hablando de visualizar, desde hace poco AWS ha añadido la opción de diseñar un workflow de manera completamente visual (arrastrar y soltar) llamada Workflow Studio pero esto da para otro post.

Seleccionamos la opción de “Write your workflow in code” y la primera decisión que tenemos que tomar es el tipo de máquina de estados que queremos Standard o Express:

Al ver las características de cada uno de los tipos, en esta ocasión, seleccionamos el tipo Standard ya que nuestros contenedores pueden estar ejecutándose durante más de 5 minutos, por lo que el tipo Express queda descartado.

Para la definición de nuestro workflow tenemos que usar el lenguaje de estados de amazon y para probar y entender mejor el flujo de datos y cómo se pasan entre los diferentes steps se puede usar el simulador de flujo de datos que amazon proporciona:

Nuestro ejemplo quedaría de la siguiente forma:

¿Qué es ECS?

Amazon Elastic Container Service (Amazon ECS) es un servicio de control de contenedores totalmente administrado. Amazon ECS aprovecha la tecnología sin servidor de AWS Fargate para ofrecer operaciones autónomas de contenedores, lo que reduce el tiempo de configuración, seguridad y aplicación de parches. Se integra fácilmente con el resto de servicios de la plataforma AWS para construir soluciones seguras y fáciles de usar.

ECS: cluster y task definitions

El clúster y la definición de la tarea de ECS tiene que estar ya creado antes de la integración.

El clúster se crea a nivel de región y se necesita para agrupar instancias de contenedores en las cuales ejecutar las tareas.

Las definiciones de tareas especifican la información del contenedor de la aplicación. Puede tener uno o más contenedores (por ejemplo, se puede añadir el X-Ray Daemon para trazabilidad y se puede seleccionar si se quiere ejecutar en modo Fargate (infraestructura gestionada por AWS) o en modo EC2, también incluye la modalidad de poder usarlo con infraestructura on-premise.

Integración con ECS

Con nuestro ECS ya creado, nos centramos en la integración con ECS que tendría este aspecto dentro de la definición de nuestra máquina de estados:

 "image_1": {
      "Next": "task_finished_choice_step",
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "ResultPath": "$.error_result",
          "Next": "handle_error_step"
        }
      ],
      "Type": "Task",
      "Comment": "It runs a ECS task with scenarios mode image_1  image",
      "InputPath": "$.lambda_result.next_stage",
      "ResultPath": "$.image_1_result",
      "Resource": "arn:aws:states:::ecs:runTask.sync",
      "Parameters": {
        "Cluster": "arn:aws:ecs:{region}:{account_id}:cluster/ifgeek-ecs-cluster",
        "TaskDefinition": "ifgeek-image_1-task-name",
        "NetworkConfiguration": {
          "AwsvpcConfiguration": {
            "Subnets": [
              "{subnet_1}",
              "{subnet_2}",
              "{subnet_3}"
            ],
            "SecurityGroups": [
              "{sg-1}"
            ]
          }
        },
        "Overrides": {
          "ContainerOverrides": [
            {
              "Name": "ifgeek-image_1-container",
              "Command.$": "$.command",
              "Environment": [{
                  "Name": "image_2_USER",
                  "Value": "$.user"
                }]
            }
          ]
        },
        "LaunchType": "FARGATE",
        "PlatformVersion": "LATEST"
      }
    }

Los campos más importantes en esta integración son los siguientes:

  1. Definir el estado de tipo “Task”, esto representa una única unidad de trabajo.

  2. InputPath son los datos de entrada que se transmiten desde la lambda (plan_next_step), ahí se decide qué tarea de ECS se tiene que ejecutar, cuál será su comando de ejecución y se configuran las variables de entorno que sean necesarias. Nota que en el valor del campo InputPath ("InputPath": "$.lambda_result.next_stage" ) hemos usado JsonPath para transferir los valores a la entrada de la tarea ECS:

{
  "name": "image_1",
  "input": {
    "input_file": "input/uploads/example.jpg",
    "lambda_result": {
      "total_stages_count": 1,
      "next_stage": {
        "image": "image_1",
        "command": ["echo", "hello", "world"],
        "user": "ifgeek"
      },
      "processed_stages_count": 1,
      "config": []
    }
  },
  "inputDetails": {
    "truncated": false
  }
}
  1. "Resource": "arn:aws:states:::ecs:runTask.sync": Indica que la integración es con ECS y que se ejecuta un runTask cuando llegue a este paso la ejecución y espera a que termine. Hay otro tipo de recurso: "arn:aws:states:::ecs:runTask.waitForTaskToken" que ejecuta la tarea ECS y entonces espera a que el token de la tarea sea devuelto.

  2. En Parameters tenemos que definir la configuración de nuestro clúster de ECS y el TaskDefinition que queremos ejecutar además de la configuración de red que siempre es conveniente en entornos productivos configurar varias subnets para tener multi AZ.

    Pero el campo más interesante en términos de configuración es el campo “overrides” que nos permite sobreescribir la configuración y más concretamente el campo “ContainerOverride” que sobreescribe el comando con que se definió el contenedor en el TaskDefinition. También se puede usar para modificar los valores de variables de entorno, lo que ofrece una forma de cambiar la configuración rápida y con muchas posibilidades.

  3. "LaunchType" puede ser de tipo "FARGATE" o de tipo EC2 para nuestra solución no necesitamos tener un contener ejecutándose de forma continuada por lo que optamos por la solución serverless con Fargate.

Con esto ya lo tendríamos todo para poder realizar una ejecución y probar nuestro flujo, en nuestro caso desarrollamos un api gateway para poder invocar a nuestra máquina de estados a través de un api, pero igualmente se puede comenzar una ejecución desde la propia consola de Step Functions.

Durante la ejecución se puede controlar en qué paso de la máquina de estados esta, la entrada y salida de los pasos anteriores y si termino de forma satisfactoria o con fallo.

Ejemplos de ejecución terminada en fallo y en éxito:

Además de visualmente, se muestra una tabla con todos los estados, tiempo transcurrido y en todas las integraciones se muestran los links a los servicios para que sea más fácil la trazabilidad y se delega a steps functions revisar si el contenedor que se está ejecutando ha acabado correctamente o no (comprueba si ha tenido un exit code diferente de 0) lo que hace más fácil gestionar y controlar los errores.

Consola de ECS:

Detalle del comando que estamos ejecutando:

Ejecución acabada con exit code 1:

Conclusión

Como hemos visto, la integración de servicios con AWS Step Functions y más concretamente con ECS Fargate es rápida y fácil de empezar a usar.  A partir de este diseño básico se puede enriquecer con más servicios como por ejemplo el envío de notificaciones con Amazon SNS. También se puede explotar el api de Step Functions y ECS y desarrollar endpoints para realizar el seguimiento de los pasos de la máquina de estados, ya es adaptarlo a cada caso de uso, ¡la imaginación es el límite!

 

Guia introduccion MuleSoft AnyPoint