AWS Elastic Container Service est un service d’orchestration de conteneurs 100% managé par Amazon. Il permet d’exécuter et de scaler facilement des charges de travail. Cet article s'adresse particulièrement aux infrastructures qui utilisent ECS pour héberger des applications conteneurisées et RDS stocker leurs données.
Exemple d’infrastructure compatible
Dans de nombreuses entreprises, les environnements cloud hors production ne sont utilisés que pendant les heures de bureau pour le développement, les tests et l'assurance qualité. En dehors de ces périodes, ces environnements restent actifs, consommant des ressources inutilement. Sur une journée, cela représente entre 10 et 14 heures où les clusters ECS et les bases RDS sont inactifs — soit plus de la moitié du temps.
En incluant les week-ends, pendant lesquels ces environnements sont rarement utilisés, les ressources provisionnées sont potentiellement inutilisées pendant 60% du temps. Cette situation offre une excellente opportunité d'optimisation FinOps : en éteignant automatiquement ces services hors des heures de travail, il est possible de réaliser des économies considérables sans affecter la productivité des équipes.
Au-delà de l'aspect financier, cette approche a également un impact environnemental positif. En réduisant le temps d'utilisation des ressources cloud, on diminue la consommation d'énergie des data centers. Cette démarche s'inscrit dans une logique de Green IT, contribuant à réduire l'empreinte carbone des infrastructures numériques. Ainsi, l'optimisation FinOps devient non seulement un levier économique, mais aussi un geste écologique responsable.
Par exemple, des clients de Theodo Cloud ont réussi à réduire significativement leurs coûts en appliquant cette stratégie. Les économies réalisées varient de 10 % à 17 % sur la facture finale comprenant tous les environnements, selon les cas. Cette approche, simple à mettre en œuvre, offre un retour sur investissement rapide sans la complexité d'autres optimisations techniques.
Pour optimiser les coûts liés à ECS, nous allons utiliser une Autoscaling ScheduledAction pour ajuster automatiquement le nombre de tâches en fonction des heures de travail. Les autoscaling scheduled actions sont des tâches automatisées permettant de mettre à jour les paramètres de son cluster ECS à un moment précis, par exemple pour anticiper un pic de charge.
Le paramétrage proposé dans la suite est sous forme de code Terraform, mais il est possible de le faire via la CLI AWS. En revanche, la gestion de l’autoscaling via des actions planifiées n’est pas disponible dans la console web AWS. Notons enfin que l’autoscaling doit être activé sur vos services. Voici comment procéder :
Il faut commencer par créer un module terraform afin de déployer les ressources nécessaires. Il est possible de les intégrer au module qui gère vos services ECS, ou bien d’en créer un nouveau. Afin de s’adapter au plus de cas possible, nous allons choisir la seconde solution ici.
Il faut donc créer un nouveau module, nous pouvons utiliser la structure suivante :
ecs-shutdown/
├── main.tf
├── variables.tf
├── outputs.tf
└── README.md
Cette structure de base comprend les fichiers essentiels pour un module Terraform :
Ensuite, il va falloir créer deux app autoscaling scheduled actions : une pour réduire le nombre de tâches en dehors des heures de travail, et une autre pour les augmenter au début de la journée :
resource "aws_appautoscaling_scheduled_action" "stop" {
for_each = var.services
name = "stop_${each.key}"
service_namespace = "ecs"
resource_id = each.value.resource_id
scalable_dimension = "ecs:service:DesiredCount"
schedule = each.value.stop_schedule != null ? each.value.stop_schedule : var.stop_schedule
scalable_target_action {
min_capacity = 0
max_capacity = 0
}
depends_on = [aws_appautoscaling_scheduled_action.start]
}
resource "aws_appautoscaling_scheduled_action" "start" {
name = "start_${each.key}"
service_namespace = "ecs"
resource_id = each.value.resource_id
scalable_dimension = "ecs:service:DesiredCount"
schedule = each.value.shutdown_schedule != null ? each.value.stop_schedule : var.stop_schedule
scalable_target_action {
max_capacity = each.value.original_max_capacity
min_capacity = each.value.original_min_capacity
}
}
Pour utiliser ce module, nous devons définir les variables nécessaires dans le fichier variables.tf. Voici un exemple de contenu pour ce fichier :
variable "services" {
type = map(object({
resource_id = string
original_max_capacity = number
original_min_capacity = number
stop_schedule = string
start_schedule = string
}))
description = "Map of ECS services to manage"
}
variable "stop_schedule" {
type = string
default = "cron(0 20 ? * MON-FRI *)"
description = "Default cron schedule for stopping services"
}
variable "start_schedule" {
type = string
default = "cron(0 8 ? * MON-FRI *)"
description = "Default cron schedule for starting services"
}
Ensuite, nous pouvons appeler notre module dans un fichier Terragrunt :
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "${get_repo_root()}/modules//ecs-shutdown"
}
inputs = {
services = {
"api-service" = {
resource_id = "service/my-cluster/api-service"
original_max_capacity = 5
original_min_capacity = 1
stop_schedule = "cron(0 19 ? * MON-FRI *)"
start_schedule = "cron(0 7 ? * MON-FRI *)"
},
"worker-service" = {
resource_id = "service/my-cluster/worker-service"
original_max_capacity = 3
original_min_capacity = 1
stop_schedule = "cron(0 2 ? * TUE-SAT)"
start_schedule = "cron(0 7 ? * MON-FRI)"
}
}
}
Cet exemple montre comment configurer deux services ECS avec des horaires d'arrêt et de démarrage personnalisés. Notons la subtilité au niveau des jours de démarrage lorsque la tâche doit terminer après minuit pour permettre des traitements nocturnes.
Côté bases de données, nous allons utiliser une MaintenanceWindow pour éteindre et rallumer automatiquement notre base RDS en fonction des heures de travail.
Les maintenance windows sont des tâches automatisées permettant de programmer et de lancer diverses tâches de maintenances : par exemple des mises à jour d’instances EC2, des évolutions de configuration de bases de données…
Nous pouvons exploiter l’automatisation liée à cette fonctionnalité pour éteindre et rallumer une RDS. Comme précédemment, le paramétrage proposé est sous forme de code Terraform, mais il est possible de le faire via la CLI AWS. Voici comment procéder :
Comme précédemment, nous allons créer un nouveau module afin de simplifier l’intégration dans toute codebase IaC.
rds-shutdown/
├── iam.tf
├── maintenance.tf
├── variables.tf
├── outputs.tf
└── README.md
Il faut dans un premier temps donner accès à un rôle IAM à notre maintenance window afin qu’elle puisse s’exécuter correctement et agir sur notre base. Ces accès sont liés au fonctionnement interne d’AWS, et donnent à la tâche les accès aux ressources dont elle a besoin pour s’exécuter, ainsi que les accès pour arrêter et démarrer les bases de données RDS.
data "aws_iam_policy_document" "start_stop_rds" {
statement {
actions = [
"rds:StartDBCluster",
"rds:StopDBCluster",
"rds:ListTagsForResource",
"rds:DescribeDBInstances",
"rds:StopDBInstance",
"rds:DescribeDBClusters",
"rds:StartDBInstance"
]
resources = [ var.rds_arn ]
}
statement {
actions = [
"states:DescribeExecution",
"states:StartExecution"
]
resources = [
"arn:aws:states:*:*:execution:*:*",
"arn:aws:states:*:*:stateMachine:*"
]
}
statement {
actions = [
"lambda:InvokeFunction"
]
resources = ["arn:aws:lambda:*:*:function:*"]
}
statement {
actions = [
"iam:PassRole"
]
resources = [
"*"
]
condition {
test = "StringEquals"
variable = "iam:PassedToService"
values = [
"ssm.amazonaws.com"
]
}
}
statement {
actions = [
"resource-groups:ListGroupResources",
"resource-groups:ListGroups",
"resource-groups:GetGroupQuery",
"resource-groups:GetTags",
"resource-groups:GetGroup",
"resource-groups:SearchResources",
"ssm:SendCommand",
"ssm:CancelCommand",
"ssm:ListCommands",
"ssm:ListCommandInvocations",
"ssm:GetCommandInvocation",
"ssm:GetAutomationExecution",
"ssm:StartAutomationExecution",
"ssm:ListTagsForResource",
"ssm:GetParameters",
"tag:GetResources"
]
resources = ["*"]
}
}
resource "aws_iam_policy" "maintenance" {
name = "StartStopRDS"
path = "/"
description = "The policy to start and stop any rds"
policy = data.aws_iam_policy_document.start_stop_rds.json
}
resource "aws_iam_role" "maintenance" {
name = "rds_ssm_maintenance_window"
managed_policy_arns = [aws_iam_policy.maintenance.arn]
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ssm.amazonaws.com"
}
},
]
})
}
Dans un premier temps, on doit définir un resource group qui cible notre base de données :
resource "aws_resourcegroups_group" "this" {
name = "rds-ssm-resource-group"
resource_query {
query = jsonencode({
ResourceTypeFilters = [
"AWS::RDS::DBInstance"
]
TagFilters = [
{
Key = "Name"
Values = [
var.identifier
]
}
]
})
}
}
Ensuite, nous pouvons créer la maintenance window avec le schedule souhaité :
resource "aws_ssm_maintenance_window" "start" {
name = "start-rds"
schedule_timezone = "Europe/Paris"
schedule = var.start_schedule
duration = 1
cutoff = 0
}
Nous allons maintenant définir la cible de cette maintenance (notre base RDS), ainsi que l’action à effectuer : AWS-StartRdsInstance. Cette action est définie par AWS
resource "aws_ssm_maintenance_window_target" "start" {
window_id = aws_ssm_maintenance_window.start.id
name = "start-rds"
resource_type = "RESOURCE_GROUP"
targets {
key = "resource-groups:Name"
values = [aws_resourcegroups_group.this.name]
}
}
resource "aws_ssm_maintenance_window_task" "start" {
task_arn = "AWS-StartRdsInstance" # AWS defined
name = "start-instance"
task_type = "AUTOMATION"
window_id = aws_ssm_maintenance_window.start.id
service_role_arn = aws_iam_role.maintenance.arn
max_concurrency = 1
max_errors = 5
targets {
key = "WindowTargetIds"
values = [aws_ssm_maintenance_window_target.start.id]
}
task_invocation_parameters {
automation_parameters {
document_version = "$LATEST"
parameter {
name = "InstanceId"
values = [""]
}
parameter {
name = "AutomationAssumeRole"
values = [aws_iam_role.maintenance.arn]
}
}
}
}
Il suffit ensuite de répliquer ces ressources pour la tâche d’arrêt des bases de données :
# #############
# STOP TASK #
# #############
resource "aws_ssm_maintenance_window" "stop" {
count = var.start_stop_enabled ? 1 : 0
name = "stop-rds"
schedule_timezone = "Europe/Paris"
schedule = var.stop_schedule
duration = 1
cutoff = 0
}
resource "aws_ssm_maintenance_window_target" "stop" {
count = var.start_stop_enabled ? 1 : 0
window_id = aws_ssm_maintenance_window.stop[0].id
name = "stop-rds"
resource_type = "RESOURCE_GROUP"
targets {
key = "resource-groups:Name"
values = [aws_resourcegroups_group.this[0].name]
}
}
resource "aws_ssm_maintenance_window_task" "stop" {
count = var.start_stop_enabled ? 1 : 0
task_arn = "AWS-StopRdsInstance"
name = "stop-instance"
task_type = "AUTOMATION"
window_id = aws_ssm_maintenance_window.stop[0].id
service_role_arn = aws_iam_role.maintenance[0].arn
max_concurrency = 1
max_errors = 5
targets {
key = "WindowTargetIds"
values = [aws_ssm_maintenance_window_target.stop[0].id]
}
task_invocation_parameters {
automation_parameters {
document_version = "$LATEST"
parameter {
name = "InstanceId"
values = [""]
}
parameter {
name = "AutomationAssumeRole"
values = [aws_iam_role.maintenance[0].arn]
}
}
}
}
Pour utiliser notre module terraform, nous pouvons l'appeler dans un fichier Terragrunt comme suit :
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "${get_repo_root()}/modules//rds-shutdown"
}
inputs = {
identifier = "my-rds-instance"
rds_arn = "arn:aws:rds:eu-west-1:123456789012:db:my-rds-instance"
start_schedule = "cron(0 7 ? * MON-FRI *)"
stop_schedule = "cron(0 19 ? * MON-FRI *)"
start_stop_enabled = true
}
Ce code configure notre module pour démarrer l'instance RDS à 7h00 et l'arrêter à 19h00 du lundi au vendredi.
En conclusion, cet article vous a présenté des outils natifs d'AWS pour optimiser vos coûts d'infrastructure et réduire votre empreinte carbone. Ces solutions, plus faciles à maintenir et transparentes que des fonctions Lambda, offrent une gestion automatisée des ressources ECS et RDS.
L'équipe d'infogérance de Theodo Cloud a déjà implémenté ces pratiques FinOps et Green IT chez plusieurs clients, démontrant un retour sur investissement rapide et significatif. Ainsi, l’adoption de ces méthodes, permet non seulement de réduire l’impact économique de votre infrastructure, mais aussi de contribuer à une utilisation plus responsable des ressources cloud.