# Como Agendar um START/STOP Automático no Fargate

## Introdução

Você pode ter diversos motivos e casos de uso diferentes para querer iniciar e parar de forma agendada e automática o AWS Fargate, no meu caso específico eu precisava de um Fargate para um ambiente de QA que não tinha a necessidade de estar funcionando o tempo todo, então precisava de alguma solução que me permitisse iniciar e parar o Fargate em horários agendados e de forma automática.

Isso me levou a analisar algumas opções, considerar alguns desafios e então este artigo irá descrever um pouco sobre tudo isso e demonstrar como consegui atingir esse objetivo.

---

## Algumas Opções

### Evite usar Cron dentro de um Container

Parece ser uma escolha fácil e óbvia, assim como faria em uma instância Linux através de uma conexão SSH a configuração de um `crontab -e`, a primeira opção que nos vem na mente é:

> "E se eu acessar meu container e criar um `cron` nele também, da mesma forma que eu faria em uma instância Linux?"

Alguns motivos para você responder **não** a esta pergunta são:

* Não é o padrão, seu container deve estar preocupado somente com o processo principal da sua aplicação, ele não deve se preocupar com processos adicionais como o `cron` por exemplo
    
* Não é "limpo", nosso objetivo é iniciar e parar o Fargate, então a cada vez que isso acontecer, seguindo o princípio da infraestrutura imutável, um novo container será criado e não o mesmo, então se você não automatizar a criação do `cron`, não haverá nenhum sentido no que estamos tentando fazer aqui, além disso essa automatização pode ser um obstáculo desnecessário
    
* Não é colaborativo e intuitivo, se você estiver em um projeto pequeno, ainda sim deverá lembrar dessa configuração, mas e se você estiver em um projeto maior e com uma equipe maior, certamente outros devs não lembrarão ou perceberão de forma fácil e intuitiva que existe um `cron` dentro do container
    

---

### Evite usar ECS Scheduled Tasks

Essa seria a segunda opção óbvia a ser usada, pois podemos encontrar essa opção no console AWS:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712150046782/187bd270-f993-4cd3-9958-ecc8dcec03ba.png align="center")

Porém, se possível evite usar essa opção ou pelo menos considere isso antes em seu caso de uso:

* Ao criar um `ECS Schedule Tasks`, no background a AWS estará criando de uma forma simplificada uma regra de evento no `Cloudwatch` que inicia uma `task` do Fargate com base no cronograma definido:
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712150520157/5c82cf5d-1ca4-4618-98e0-0af574183ea2.png align="center")

* O motivo para eu sugerir você considerar evitar isso, é porque nessa abordagem faltam algumas coisas importantes como:
    
    * Definir um tempo limite para o agendamento
        
    * Definir uma repetição se houver alguma falha no agendamento
        
    * Apenas inicia, mas não atualiza o número de tasks para cima ou para baixo
        
    * Também não interrompe as tasks, dessa forma não atualiza o tamanho desejado para 0
        
    * Em resumo, essa forma está operando no modo "disparar e esquecer", ou seja o Cloudwatch envia a requisição para iniciar e acaba nisso, sem garantias, sem controles e sem resiliência
        

Porém entendo que para alguns casos isso pode ser o suficiente e funcionar, só quis compartilhar esses pontos que achei relevante.

---

### Prefira usar o EventBridge

Essa seria a terceira opção que me veio a mente, a que acabei utilizando e a que descreverei o passo a passo de como consegui fazer isso, mas antes algumas notas importantes também:

* Ao contrário da abordagem anterior, aqui há mais resiliência, pois o `EventBridge` não irá apenas iniciar uma task, mas ele também terá a capacidade de atualizar o número de tasks para cima, para baixo ou interromper atualizando o tamanho desejado para 0
    
* O `EventBridge` possui opções mais avançadas e flexíveis que você poderá escolher se houver uma falha, como por exemplo se irá monitorar isso, se tentará novamente, se deseja adicionar em uma fila de processamento ou outra ação que você deseja tomar
    
* O `EventBridge` é mais avançado, permite chamar o nível da API ECS Fargate passando uma carga personalizada com `JSON`, que ajuda a personalizar a chamada API conforme necessário
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712153290506/30aabe2f-fe40-4ed7-bc98-cfc18bc1f85c.png align="center")

---

## Tutorial

<div data-node-type="callout">
<div data-node-type="callout-emoji">ℹ</div>
<div data-node-type="callout-text">Esse tutorial está considerando o START/STOP para um <strong>serviço</strong> do Fargate, e não para tasks individuais (sem serviço) e também não a nível de cluster para todos os serviços dentro dele, mas sim para um único serviço do Fargate dentro de um Cluster.</div>
</div>

1- Acesse o console **AWS → IAM → Policies → crie uma política** conforme abaixo:

`Name:`

* Defina um nome de sua preferência
    
* Por exemplo:
    
    * `ecs-fargate-scheduler-qa-policy`
        

`Description:`

* Defina uma descrição de sua preferência
    
* Por exemplo:
    
    * `Policy that grants EventBridge permission to automatically start and stop Fargate tasks in the QA environment`
        

```json
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "VisualEditor0",
			"Effect": "Allow",
			"Action": [
				"ecs:ListServicesByNamespace",
				"ecs:GetTaskProtection",
				"ecs:ListAttributes",
				"ecs:DescribeTaskSets",
				"ecs:DescribeTaskDefinition",
				"ecs:DescribeClusters",
				"ecs:ListServices",
				"ecs:ListAccountSettings",
				"ecs:DescribeCapacityProviders",
				"ecs:ListTagsForResource",
				"ecs:ListTasks",
				"ecs:ListTaskDefinitionFamilies",
				"ecs:DescribeServices",
				"ecs:ListContainerInstances",
				"ecs:DescribeContainerInstances",
				"ecs:DescribeTasks",
				"ecs:ListTaskDefinitions",
				"ecs:ListClusters",
				"ecs:UpdateService"
			],
			"Resource": "*"
		}
	]
}
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712154063107/c0a154a1-8e59-4ffc-b1ee-93b00c218c42.png align="center")

2- Agora vá para **IAM → Role → crie uma role** conforme abaixo:

* `Name:`
    
    * Defina um nome de sua preferência
        
    * Por exemplo:
        
        * `ecs-fargate-scheduler-qa-role`
            
* `Description:`
    
    * Defina uma descrição de sua preferência
        
    * Por exemplo:
        
        * `Role with necessary EventBridge permissions to automatically start and stop Fargate tasks in the QA environment`
            

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712154620079/8888d667-53a1-4dab-b059-0c4dd14c049a.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712154685762/784e6a82-bbb0-4d96-be27-9b6eb46d862c.png align="center")

<div data-node-type="callout">
<div data-node-type="callout-emoji">ℹ</div>
<div data-node-type="callout-text">Remova a política anexada automaticamente</div>
</div>

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712154821801/0895780f-fea0-4f60-b161-3c11e485a9b3.png align="center")

<div data-node-type="callout">
<div data-node-type="callout-emoji">ℹ</div>
<div data-node-type="callout-text">Adicione a política que criamos nesta role</div>
</div>

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712154967263/aac37703-8d37-485b-b460-52fe080c6b2f.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712155022060/a754b106-e77a-43c7-b2de-bf213de3126e.png align="center")

<div data-node-type="callout">
<div data-node-type="callout-emoji">ℹ</div>
<div data-node-type="callout-text">Edite a aba <code>Trust Relashionship</code> para conceder permissão ao <code>EventBridge</code>, conforme abaixo</div>
</div>

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "scheduler.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712155108350/4e1084fd-68df-4896-b4b0-89fec19087de.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712155183891/2c3c8c3a-f538-436a-a2b8-fd1c0b2fea80.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712772037873/73470231-d999-4ff4-a142-7fb30ad7c461.png align="center")

---

3- Agora vá para o console **AWS → EventBridge → Rules → Create rule**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712155395639/5baeb5f7-5f09-4500-831a-9842e7e49d90.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712155489308/dfe8e6f7-d100-4208-b484-096ce80bf39d.png align="center")

4- Defina um nome e descrição de sua preferência, por exemplo:

* **Name:**`fargate-start-tasks-qa-monday-to-friday-morning`
    
* **Description:**`Rule that automatically starts QA environment ECS Fargate`
    
* Altere para a opção `Schedule`
    
* Clique em `Continue in EventBridge Scheduler`
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712155787463/11339b8c-a9b2-4c7a-a894-279043ed7a32.png align="center")

5- Altere para `Recurring schedule` → defina uma expressão `cron` de sua preferência

<div data-node-type="callout">
<div data-node-type="callout-emoji">ℹ</div>
<div data-node-type="callout-text">Por exemplo, eu gostaria de iniciar as tasks no Fargate de segunda a sexta as 9 horas (horário de Brasília)</div>
</div>

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712156399173/4e15e434-0d73-442d-b596-a858797e2090.png align="center")

Por exemplo, para executar de segunda a sexta às `09 horas` (UTC -3), isso seria:

```bash
00 9 ? * MON-FRI *
```

* **00**: é o minuto em que o cron será executado. Neste caso, o que significa que a tarefa será executada quando o relógio marcar exatamente 0 minutos.
    
* **9**: é a hora em que o cron será executado. Neste caso, o que significa que a tarefa será executada às 9 horas. (UTC -3)
    
* **?:** isso indica que não estamos definindo um dia específico do mês. A tarefa será executada independentemente do dia do mês.
    
* **\***: isso indica que a tarefa será executada em todos os meses. Não estamos definindo um mês específico.
    
* **MON-FRI**: especifica os dias da semana em que a tarefa será executada. Neste caso, a tarefa será executada de segunda a sexta-feira.
    
* **\***: indica que a tarefa será executada em todos os anos. Não estamos definindo um ano específico.
    

Uma vez compreendido o formato do `cron`, lembre-se de ajustar seu `Time Zone` e preencher de acordo com suas necessidades.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712157457290/69fe2d29-acee-41bb-bb6f-c5bbd119c624.png align="center")

6- Defina a opção `Flexible Time Window`, essa opção é útil quando você quiser que o `EventBridge` funcione dentro do tempo dessa janela que você definiu, pense nisso em casos que as tasks ou targets ainda não estão prontos, desta forma você define um intervalo de tempo para que isso seja feito.

Por exemplo, se eu definisse o `cron as 9 horas` e um `Flexible Time Window de 15 minutos`, na prática significa que o `EventBridge Scheduler` garantirá que ele seja executado dentro deste período, ou seja entre `9:00 às 9:15`, mas isso não significa que ele será executado as `9:15`, mas sim que será acionado dentro deste intervalo a qualquer momento a partir do horário agendado (no exemplo as `9:00 + flexible time window`).

Isso é muito bom para distribuir as execuções nesta janela de tempo, reduzindo os impactos de várias solicitações ao mesmo tempo em um ambiente grande.

Como neste meu caso isso não é um requisito necessário, defino como `Off`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712157724902/83d52048-0374-4e1f-9365-382035d0b6d2.png align="center")

7- Você também pode definir uma data de início e término, como eu espero que a expressão `cron` funcione e não planejo inserir uma data para que esse `cron` comece a funcionar ou terminar, **não preencho** e clico em `Next`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712161872657/54132aa9-b8ce-41c5-85e7-5805fa61988f.png align="center")

8- Agora altere para `All APIs` → pesquise por `ecs` → selecione a opção `ECS`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712162072765/01b311ac-2b68-4bc2-8966-3120e6fef809.png align="center")

9- Agora pesquise por `update` → selecione a opção `UpdateService`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712162193596/2dd25538-1c92-445a-9fb8-a144e664ee94.png align="center")

10- Como nessa regra estamos querendo iniciar o Fargate, defino o campo `DesiredCount` como 1 para então ter uma task em execução, você pode ajustar isso para suas necessidades e preferência:

<div data-node-type="callout">
<div data-node-type="callout-emoji">⚠</div>
<div data-node-type="callout-text">Lembre-se de alterar <code>&lt;Service-ARN&gt;</code> e <code>&lt;Cluster-ARN&gt;</code> para seus valores reais</div>
</div>

```json
{
     "DesiredCount": 1,
     "Service": "<Service-ARN>",
     "Cluster": "<Cluster-ARN>"
    
}
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712162373517/829c41d1-927c-4e43-b9b7-3f9eba42f4ae.png align="center")

11- Deixo a opção `Schedule`**habilitada**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712162487788/6deb866d-c979-46e6-b08b-1501924f4dd5.png align="center")

12- Não defino nenhuma ação após o agendamento ser executado, pois neste caso de uso não planejo excluir o agendamento

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712162750996/1194d900-0a48-43de-bb55-48f932613017.png align="center")

13- Agora você pode definir uma `DLQ (Dead Letter Queue)`, isso seria uma fila `SQS` para armazenar os eventos que falharam, aqui ainda existem dois tipos:

* **Recuperáveis**
    
    * Se houver uma falha ou uma entrega mal sucedida no `target (que no nosso caso é a API UpdateService do ECS)`, por padrão o EventBridge tentará fazer ela novamente por 24 horas e até 185 vezes.
        
* **Não recuperáveis**
    
    * Se houver um erro não recuperável, como `AccessDenied` por exemplo, isso não será repetido pelo EventBridge, mas ele poderá adicionar se você quiser isso em uma fila DLQ para ser tratado ou processado posteriormente
        

<div data-node-type="callout">
<div data-node-type="callout-emoji">ℹ</div>
<div data-node-type="callout-text">Mesmo desabilitando a opção <code>Retry Policy</code>, ainda sim o EventBridge tentará repetir para eventos recuperáveis por 24 horas e até 185 vezes. Isso é um comportamento padrão, você configurando ou não.</div>
</div>

Para este tutorial, defino isso como `Disable`, mas se isso for importante para você, este é o momento de configurar de acordo com suas preferências:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712163699095/40c03df6-e176-4b4b-b9d1-cf9c02bb3e6d.png align="center")

14- Não defino nenhuma criptografia

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712163737855/b07d1ae9-1c12-4185-a7ae-a804f0411565.png align="center")

15- Seleciono a `Role` que criamos no início do tutorial

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712163804355/8376dcfe-5461-4e9e-bbb0-de5b9212419a.png align="center")

16- Clico em `Create`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712163915275/b270e815-da93-45fb-b347-fedb1f20dba2.png align="center")

---

Pronto! 😄

Porém criamos até agora apenas para iniciar o Fargate, ainda falta uma parte importante que seria parar o Fargate, para isso basta repetir os mesmos passos, porém mudando os nomes, descrições e principalmente o código `JSON` para o `DesireCount` igual a 0, isso significa que não desejamos nenhuma task em execução:

```json
{
     "DesiredCount": 0,
     "Service": "<Service-ARN>",
     "Cluster": "<Cluster-ARN>"
    
}
```

Para não tornar o tutorial muito grande e ser repetitivo, não vou descrever todo o passo a passo novamente, mas para sua comodidade, aqui estariam a sequência de imagens para parar o Fargate também:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712164202206/16f2b802-99e4-4311-9352-83cc03372e45.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712164337268/d789db10-dde5-418a-ad34-9ab1d0da8eda.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712164518730/39346c86-ec36-4bf0-8a2d-a29cc5e62904.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712164588037/e429b9ed-5d6c-478c-965b-0a3f26059843.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712164625792/945d995b-d6b8-48e0-8508-fabaa7a013d9.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712164693919/48aae342-3e94-43a8-b82b-3cfd8f1a4159.png align="center")

---

## Limitações

* O Event Bridge é capaz de fazer um único request por vez, isso significa que você não pode adicionar vários Fargates no mesmo request, e isso pode gerar duplicação de trabalho, pois deverá repetir este passo a passo para cada Fargate que desejar.
    
* O Fargate e o Event Bridge precisam estar na mesma região.
    

---

## Conclusão

Pronto! 😄

Agora sim temos todo o nosso objetivo completo.

Vimos neste artigo algumas opções para iniciar e parar o Fargate, analisamos e consideramos alguns desafios e limitações entre as opções, elegemos o uso do EventBridge como melhor opção para este caso e fizemos um tutorial passo a passo para iniciar e parar o Fargate que faz:

1. O `EventBridge Scheduler` configurado com uma expressão `cron` que **inicia** o Fargate enviando uma chamada `API` para o `UpdateService` do `ECS`
    
2. Definindo o `DesiredCount` como `1` task por exemplo.
    
3. E também repetimos isso para **parar** o Fargate, mas agora definindo o `DesiredCount` para `0` para que não haja tasks em execução.
    

---

Por fim, depois que o `cron` for executado em seu horário definido, poderemos ver no `cluster` do fargate nenhuma task originada pelo nosso agendamento automático:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712181202837/d86e9787-705f-4b12-91d3-bd59607829a4.png align="center")

E no `service` também podemos ver o `DesiredCount` como `0` como definimos:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712181185051/28c5efc1-ec33-483f-a4fe-4459791a82f8.png align="center")

E depois novamente a task iniciada também pelo nosso agendamento automático em seu horário definido:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712762758540/0e9a6069-464a-44a5-9f1d-fac9de3cc06a.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712762887100/0ce16d17-4fa4-4248-a8b7-fb273dfc8af1.png align="center")

<div data-node-type="callout">
<div data-node-type="callout-emoji">ℹ</div>
<div data-node-type="callout-text">Lembre-se de excluir todos os recursos ao final para não obter cobranças indesejadas e manter sua conta AWS limpa e organizada.</div>
</div>

<div data-node-type="callout">
<div data-node-type="callout-emoji">✅</div>
<div data-node-type="callout-text">Espero que essas informações tenham sido úteis para você!</div>
</div>
