RDS Serverless : Une base de données scalable

Vous aussi vous vous demandez comment avoir une base de données qui puisse soutenir n'importe quelle charge tout en optimisant les coûts ?

On va voir quelles solutions sont disponibles chez Amazon Web Service (AWS), et plus particulièrement au travers de leur service de base de données en Platform as a Service (PaaS) : Relational Database Service (RDS). Ce service propose depuis longtemps des bases de données provisionnées de taille donnée qui s'adaptent très bien aux charges fixes et continues. Mais que doit-on faire lorsque la charge est répartie très inégalement, avec des pics importants alternant avec des périodes plus calmes ? Il faut pouvoir gérer la scalabilité de notre base de données le plus rapidement et efficacement possible.

Qu’est ce que AWS RDS ?

Il s’agit du service managé de base de données relationnelles d’AWS. Dans son offre la plus basique, on retrouve des moteurs de base de données classiques comme PostgreSQL, MySQL ou MariaDB qui tourneront de manière transparente sur des EC2.

AWS offre également Amazon Aurora, un SGBD compatible PostgreSQL ou MySQL. Aurora permet de lancer des instances sans se préoccuper du stockage : il provisionne dynamiquement selon l’utilisation de la BDD. C’est très pratique, on n’a plus besoin de redimensionner les disques Elastic Block Store (EBS).

Enfin, AWS propose un service de clustering de base de données, basé sur Aurora. Il y a un volume central, répliqué sur plusieurs zones de disponibilité, voire plusieurs régions avec Amazon Aurora global databases, et des instances décorrélées que l’on peut ajouter, supprimer, stopper sans impact sur les données. On peut également répliquer le cluster au niveau global, et avoir un cluster multi-AZ et multi-région. Ce cluster peut être constitué d’instances provisionnées, ou d’instances serverless. Une instance unique gère les écritures, et les autres sont des réplicas en lecture, qui peuvent prendre le rôle d’écriture en cas de failover. On parlera dans cet article uniquement des clusters de base de données Aurora.

Une charge imprévisible

Si la charge est prévisible, on peut prévoir de provisionner des instances plus conséquentes le temps de gérer la charge. Seulement, une instance met quasiment 15 minutes à être opérationnelle, ce qui est un peu long pour répondre à un pic de charge brusque, et il peut être compliqué de dimensionner l’instance face à la demande. Une telle opération est parfaite pour traiter des charges d’ampleur connue et maîtrisée, comme le traitement de batchs à heure fixe.

Le cluster à instances provisionnées est parfait pour des charges fixes où la charge évolue de façon connue et contrainte.

Voici un exemple de code terraform simplifié pour créer un cluster provisionné :

resource "aws_rds_cluster" "provisioned" {
cluster_identifier  = "rds-cluster-provisioned"
availability_zones  = [ for zone in ["a","b","c"]: "${var.region}${zone}" ]


 engine_mode = "provisioned"

database_name   = "ippon"
master_username = "ippon"
master_password = random_password.aurora_master_password.result

vpc_security_group_ids = [aws_security_group.aurora.id]
}

resource "aws_rds_cluster_instance" "provisioned" {
count = 2

identifier         = "${aws_rds_cluster.provisioned.id}-${count.index}"
cluster_identifier = aws_rds_cluster.provisioned.id

instance_class = "db.t4g.medium"

db_subnet_group_name = aws_db_subnet_group.aurora.name
}

Figure 1: Terraform - cluster Aurora provisionné

Pour exploiter au maximum le serverless, il faut faire face à une charge imprévisible, qui peut potentiellement décupler en quelques minutes. En effet, les clusters Aurora permettent de monter des instances de différentes tailles.

Aurora serverless v1 : pour le développement

En 2018, AWS a publié ce service qui permet de mettre à l’échelle le calcul du cluster Aurora automatiquement. Il n’y a plus d’instances mais des unités de calcul (ACU) qui valent 2 Go de mémoire et le CPU correspondant (non précisé par AWS, mais empiriquement valant environ 0.25 vCPU). Ces ACU se mettent à l’échelle automatiquement, en se basant sur le CPU, la mémoire et le nombre de connexions, et leur nombre double (ou se dédouble) à chaque mise à l’échelle (voir Figure 2, annotation 3). Un scale up peut prendre de quelques secondes à plusieurs minutes. Un scale down peut prendre jusqu’à 15 minutes.

Figure 2 : cluster Aurora Serverless v1 - ACU et CPU

Il y a quelques points d’attention concernant le scale up : Aurora va chercher un “point de mise à l’échelle” (scaling point), où il n’y a pas de transactions, requêtes ou verrous en cours. Cela implique d’implémenter son code de sorte à ne pas avoir de longues requêtes. De plus, cela peut être très embêtant dans la mesure où lorsque l’on veut scale up, c’est qu’il y a a priori pleins de requêtes à soumettre. Aurora cherche son point de mise à l’échelle pendant 5 minutes puis abandonne son opération. Et là, outragé, vous vous dites : “Quoi ? Comment ? Mais c’est débile !”. Pile quand on a besoin de ressources, l'outil se bloque sous la charge et ne peut pas se mettre à l’échelle. On peut lui demander de forcer la mise à l’échelle au bout des 5 minutes. Aurora coupera alors toutes les connexions en cours (voir Figure 2, annotation 1). Cela implique de devoir implémenter du rejeu dans notre application cliente pour gérer les requêtes avortées. Cette opération peut être source d’incompréhension, de latence et de frustration, il vaut mieux le savoir.

Néanmoins, un des points forts d’Aurora Serverless v1 est que s’il n’y a aucune connexion ouverte, le serveur peut tomber à 0 ACU (voir Figure 2, annotation 2). Le cold start dans ce cas est d’environ 20 secondes ce qui rend cette feature impropre pour une DB de production mais peut trouver son utilité pour des bases de données de test.

A noter qu’avec Aurora Serverless v1 vient un service de Data API, qui permet de requêter la base de données. C’est un vrai bon point, car on peut s’authentifier avec IAM, plutôt qu’avec une authentification basique. Cela permet également de ne pas gérer de pool de connections. C’est particulièrement utile pour une intégration avec un applicatif hébergé sur AWS Lambda.

Côté implémentation, un cluster de base de données Aurora Serverless v1 s’instancie de la sorte :

resource "aws_rds_cluster" "serverless_v1" {
cluster_identifier = "rds-cluster-serverless-v1"
engine             = "aurora-postgresql"
engine_mode        = "serverless"

scaling_configuration {
  auto_pause               = true
  max_capacity             = 16
  min_capacity             = 2
  timeout_action           = "RollbackCapacityChange"
}

availability_zones  = [ for zone in ["a","b","c"]: "${var.region}${zone}" ]

database_name   = "ippon"
master_username = "ippon"
master_password = random_password.aurora_master_password.result

vpc_security_group_ids = [aws_security_group.aurora.id]

db_subnet_group_name            = aws_db_subnet_group.aurora.name
}

Figure 3: Terraform - cluster Aurora Serverless v1

Un bloc scaling_configuration vient donner les informations de taille maximum et minimum désirées, la possibilité de s’éteindre s’il n’y a aucune connexion et le comportement en cas de timeout lors du scale up.

Le cluster est traité comme une instance unique, et non une collection d’instances rattachées à un cluster. On voit les différences dans la console RDS :

Figure 4 : console RDS

Aurora Serverless v2 : production ready

En avril 2022, AWS a publié Aurora Serverless v2. On est en droit de se demander ce qu’il a de plus que son petit frère en v1. Amazon corrige ici les défauts de la version précédente : le scale up se fait de manière transparente, et en quelques millisecondes. Fini le scaling point. On retrouve la notion d’ACU, mais un cluster Aurora Serverless v2 se met à l’échelle par incréments de 0.5 ACU. Ceci permet d’adapter la taille au plus proche de la demande. En revanche, Aurora Serverless v2 ne peut pas venir tout seul à 0 ACU. Il garde un reliquat de minimum 0.5 ACU. Cela vient ternir l’appellation “Serverless”, on paye même sans que le service ne soit utilisé. En comparaison d’Aurora Serverless v1, cela implique aussi de ne pas avoir de cold start.

Figure 4 : cluster Aurora Serverless v2 - ACU et CPU

Ceci montre qu’Aurora Serverless v2 est un service bien plus adapté à la production, plus stable, et plus performant. Son instanciation se rapproche beaucoup plus de celle d’un cluster provisionné :

resource "aws_rds_cluster" "serverless_v2" {
cluster_identifier  = "rds-cluster-with-serverless-v2"
availability_zones  = [ for zone in ["a","b","c"]: "${var.region}${zone}" ]

 engine_mode = "provisioned"


database_name   = "ippon"
master_username = "ippon"
master_password = random_password.aurora_master_password.result

vpc_security_group_ids = [aws_security_group.aurora.id]


serverlessv2_scaling_configuration {
  max_capacity = 16
  min_capacity = 0.5
}
}

resource "aws_rds_cluster_instance" "serverless_v2" {
count = 2

identifier         = "${aws_rds_cluster.serverless_v2.id}-${count.index}"
cluster_identifier = aws_rds_cluster.serverless_v2.id

instance_class = "db.serverless"

db_subnet_group_name = aws_db_subnet_group.aurora.name
}

Figure 5 : Terraform - cluster Aurora Serverless v2

La différence avec un cluster provisionné est l’ajout d’un bloc serverlessv2_scaling_configuration pour configurer les capacités minimum et maximum des instances serverless attachées au cluster, et la classe de l’instance est de type “db.serverless”. Autrement, la configuration est identique. On peut d’ailleurs mélanger les deux mondes et attacher à un même cluster une instance serverless avec une instance provisionnée.

C’est vraiment intéressant car cela permet de réfléchir à pleins de scénarios : mettre deux instances serverless pour gérer l’écriture et la lecture avec des instances scalable, ou un couple serverless/provisionné pour absorber les requêtes d’écriture de manière flexible, tout en pouvant ajouter des instances provisionnées pour la lecture. A l’inverse, on peut absorber les requêtes en lecture facilement avec une ou plusieurs instances serverless tout en gérant une charge d’écriture fixe et/ou faible avec une instance provisionnée.

A noter qu’avec Aurora Serverless v2 on a pas accès à la fonctionnalité de Data API, ce qui rend plus difficile l’intégration à un environnement entièrement serverless.

Que choisir ?

Il faut bien sûr choisir la solution qui sera la plus adaptée à votre situation. J’aimerais maintenant aborder le sujet du coût. L’un des buts du serverless est d’optimiser sa facture tout en garantissant la performance et la résilience de son application.


Prix de base ($)

Prix ($) / Go de RAM / heure

Provisonné / db.r5.large 

(2 vCPU, 16 Go)

0.335

0.021

Serverless v1

0.07 / ACU

0.035

Serverless v2

0.14 / ACU

0.07

Figure 6: Comparatif des prix, relevé fin novembre 2022, pour la région eu-west-1 (Irlande)


Si l’option provisionnée reste globalement la moins chère, il vaut mieux mettre deux instances sur un cluster dans plusieurs AZ pour permettre un failover, ou profiter des réplicas en lecture. Le prix d’Aurora serverless v1 est plus cher à l’instance, mais il ne peut y avoir qu’une instance dans une AZ (c’est très dommage). Ramené au fait qu’on souhaite de la résilience, un cluster serverless v1 est moins cher qu’un cluster de deux instances provisionnées. Enfin, Aurora Serverless v2 est plus cher que les autres solutions. En respectant également le fait de mettre deux instances, les coûts sont prohibitifs : plus de 3 fois l’équivalent pour du provisionné, et 4 fois pour une instance serverless v1. Seulement, il faut mettre en perspective cette comparaison : les instances provisionnées sous utilisées ont toujours un prix fixe quand une instance serverless sous utilisée va prendre la taille nécessaire et ne nous fera payer que l’essentiel.

En somme, considérant la charge maximale soutenue par une instance provisionnée, si la charge moyenne est inférieure à 33% de cette valeur, il est moins coûteux d’opter pour du serverless v2. C’est intéressant quand il y a vraiment des pics de charge qui sont plus conséquents que ce que gère la base de données la majorité du temps (exemple sur la figure 4, annotation 1). Voici en quoi le serverless est adapté à des charges imprévisibles et “spiky”.

Les instances Aurora Serverless v1 trouveront toutes leur utilité pour des bases de données de test, de développement, voire de préproduction. Elles auront affaire à de petits besoins en calcul, pourront scaler assez bien face à quelques centaines de requêtes par secondes (tant que ce n’est pas des milliers), et surtout pourront revenir à 0 ACU toutes seules si vous implémentez un système d’extinction automatique des environnements de test. Par exemple, si vous implémentez un système de déploiement des environnements de développement par branche, avec un autostop programmé, et qu’au cours de la journée, aucun développeur ne pousse de code (ils sont malades, ont une rétro, un atelier…), il n’y aura pas d’application déployée en développement. La base de données serverless v1 de développement ne vous coûtera rien en calcul dans la journée.

En revanche, Aurora Serverless v1 est inadapté pour des tests de charge ou pour la production. Tout d’abord à cause du comportement qui ne permet pas de scale up de façon transparente 100% du temps, mais également à cause du fait qu’il n’y a qu’une “instance” de calcul, et en cas de failover (problème dans l’AZ ou autre), Aurora démarre une nouvelle instance dans une autre AZ. Le stockage reste répliqué sur plusieurs AZ donc rien à craindre de ce côté.

Il faut aussi savoir qu’Aurora Serverless v1 a tout un lot de limitations spécifiques qui ne le rend pas 100% compatible avec les fonctionnalités RDS dont vous pourriez avoir besoin, je vous laisse consulter la documentation pour connaître vos éventuels points de blocage à utiliser ce service.

Ce qu’il faut retenir

Les bases de données relationnelles serverless sont une belle proposition d’Amazon. Il ne faut pas pour autant se jeter tête baissée dans cette solution qui peut être très onéreuse si mal utilisée. Je pense qu’il est préférable de commencer avec des bases provisionnées. Puis, au fur et à mesure que le projet avance et qu’on a une idée de la charge qui est appliquée sur nos instances, on peut réfléchir à mettre en place du serverless v2 en production. En ce qui concerne le serverless v1, je pense qu’il est très utile pour des projets en POC, ou pour des bases de développement pour sa fonction de pause automatique qui permet de limiter les coûts quand la charge peut être nulle, mais guère plus.

La réponse d’aujourd’hui ne répondra peut-être pas au besoin de demain. C’est pourquoi il est important de jouer avec toutes les options qu’offre Amazon pour optimiser la configuration qui est adaptée à votre projet.