Le Blog Amazon Web Services

Utilisation de Amazon RDS Proxy avec AWS Lambda

La plateforme Serverless AWS vous permet de construire des applications qui se mettent à l’échelle automatiquement en réponse à la demande. Durant les périodes de forte activité, Amazon API Gateway et AWS Lambda se mettent à l’échelle automatiquement en réponse au trafic entrant. Souvent les développeurs ont besoin d’accéder à une base de données relationnelle depuis des fonctions Lambda. Il peut être alors compliqué de s’assurer que les invocations Lambda n’entrainent pas de surcharge en terme de connexions sur la base de données relationnelle. Le nombre maximum de connexions concurrentes pour une base de données relationnelle dépend de la taille de l’instance. Chaque connexion consomme de la mémoire ainsi que des ressources de calcul sur le serveur qui héberge l’instance de base de données. Les fonctions Lambda peuvent se mettre à l’échelle et atteindre des dizaines de milliers de connexions concurrentes. Ceci implique alors que la base de données consomme plus de ressources pour maintenir des connexions au détriment de l’exécution des requêtes. Se référer à l’article : “Comment concevoir vos applications Serverless pour le passage à l’échelle” pour plus de détails sur la mise à l’échelle.

Architectures Serverless avec Amazon RDS

Ce type d’architecture induit une importante charge sur la base de données relationnelle car AWS Lambda est en mesure d’atteindre facilement la dizaine de milliers de requêtes concurrentes. Dans la plupart des cas, les bases de données relationnelles ne sont pas dimensionnées pour accepter autant de connexions concurrentes.

Proxy de base de données pour Amazon RDS

Il est possible d’utiliser Amazon RDS Proxy en tant que composant intermédiaire entre l’application et une base de données Amazon RDS ou Amazon Aurora. Amazon RDS Proxy établit et gère un ensemble de connexions réutilisables à votre base de données, ce qui implique que l’application créé moins de connexions vers la base de données.

Dans ce contexte, vos fonctions Lambda interagissent avec RDS Proxy et non pas directement avec l’instance de base de données. RDS Proxy s’occupe de la gestion des connexions créées par les invocations concurrentes de AWS Lambda. Cela permet notamment aux fonctions Lambda de réutiliser des connexions existantes au lieu d’en créer systématiquement de nouvelles pour chacune des invocations.

Amazon RDS Proxy se met à l’échelle automatiquement, ce qui permet à votre base de données de diminuer son besoin en mémoire vive et en capacité de calcul pour la gestion des connexions. Il met également en oeuvre un mécanisme de préchauffe des connexions afin d’améliorer les performances. Grâce à Amazon RDS Proxy, il n’y a plus besoin d’utiliser des scripts ou du code pour gérer la terminaison des connexions inutilisées. Le code de vos fonctions est plus clair, et simple à maintenir.

Pour commencer

Amazon RDS Proxy, est un service entièrement géré et hautement disponible qui offre une solution de proxy de base de données. Il est disponible sur la plateforme AWS (la liste des régions supportées peut être consultée ici) et prend en charge Aurora (MySQL et PostgreSQL)RDS MySQL et RDS PostgreSQL.

Prérequis

Commençons avec une base de données existante. Nous allons stocker les informations de connexion à la base de données dans AWS Secret Manager, et créer une stratégie AWS (Identity and Access Management) IAM qui autorise Amazon RDS Proxy à lire ces informations.

Pour créer le secret :

  1. Connectez vous sur AWS Secrets Manager et cliquez sur Stocker un nouveau secret.
  2. Sélectionnez Informations d’identification pour la base de données RDS comme type de secret.
  3. Saisissez le Nom d’utilisateur ainsi que le Mot de passe.
  4. Sélectionnez l’instance de base de données auquel ce secret se rapporte. Cliquez sur Suivant.
  5. Stocker un secret

    Stocker un secret

  6. Saisissez le Nom du secret, cliquez sur Suivant.
  7. Sauvegarder un secret

    Sauvegarder un secret

  8. Gardez les options par défaut et cliquez sur Stocker pour enregistrer le secret. Prenez note de l’ARN (Amazon Resource Number) associé au secret. Cette information est disponible au niveau des détails du secret.
  9. Détails du secret

    Détails du secret

  10. Créez maintenant un rôle IAM qui octroie à Amazon RDS Proxy l’accès en lecture seule au secret. Amazon RDS Proxy utilisera ce secret pour gérer les connexions à la base de données. Il convient de se rendre sur la console AWS IAM et créer un nouveau rôle, y adjoindre une stratégie qui octroie les permissions secretsmanager nécessaires sur le secret précédemment créé :
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "VisualEditor0",
          "Effect": "Allow",
          "Action": [
            "secretsmanager:GetResourcePolicy",
            "secretsmanager:GetSecretValue",
            "secretsmanager:DescribeSecret",
            "secretsmanager:ListSecretVersionIds"
          ],
          "Resource": [
            "arn:aws:secretsmanager:us-east-2:[your-account-number]:secret:gmao-rds-secret-YZ2MMN"
          ]
        },
        {
          "Sid": "VisualEditor1",
          "Effect": "Allow",
          "Action": [
            "secretsmanager:GetRandomPassword",
            "secretsmanager:ListSecrets"
          ],
          "Resource": "*"
        }
      ]
    }
  11. Ajoutez y la relation d’approbation suivante afin d’autoriser RDS à assumer le rôle. Prenez note de l’ARN du rôle IAM, car il sera nécessaire pour la suite.
    {
     "Version": "2012-10-17",
     "Statement": [
      {
       "Sid": "",
       "Effect": "Allow",
       "Principal": {
        "Service": "rds.amazonaws.com"
       },
       "Action": "sts:AssumeRole"
      }
     ]
    }
    

Créer et attacher un proxy à une fonction Lambda

Nous allons maintenant nous rendre sur la console AWS Lambda afin d’ajouter un proxy de base de données à une fonction Lambda.

  1. Rendez vous sur la console AWS Lambda et ouvrez la fonction Lambda sur laquelle vous souhaitez activer la connexion à la base de données à travers Amazon RDS Proxy. Cette fonction Lambda aura besoin d’accéder au même VPC et sous réseaux que la base de données Amazon RDS
  2. Cliquez sur l’onglet Configuration de la fonction Lambda et choisissez Proxies de base de données.
  3. Cliquez sur Ajouter un proxy de base de données et renseignez l’Identifiant du Proxy, l’Instance de base de données RDS, Le Secret stocké dans AWS Secrets Manager ainsi que le Rôle IAM précédemment créé.
  4. Configurer un proxy de base de données

    Configurer un proxy de base de données

  5. Patientez quelques minutes afin que le proxy RDS soit provisionné et que le statut passe à Disponible
  6. Proxy disponible

    Proxy disponible

  7. Sélectionnez le proxy pour accéder aux détails. Prenez note du point de terminaison du proxy (Proxy endpoint). Nous en aurons besoin plus tard dans le code de notre fonction Lambda.
  8. Configuration du proxy

    Configuration du proxy

Maintenant, votre fonction Lambda dispose des permissions nécessaires pour utiliser le proxy de base de données.

Utilisation du Proxy de base de données

Au lieu de se connecter directement à une base de données Amazon RDS, il convient de se connecter au point de terminaison du proxy de base de données. Nous disposons de deux options pour gérer la sécurité. Utiliser l’authentification IAM, ou bien utiliser le secret (nom d’utilisateur, mot de passe) stocké dans AWS Secrets Manager. Nous recommandons l’authentification IAM car cela vous affranchit de devoir embarquer ou lire des secrets dans le code de votre fonction Lambda. Le code suivant utilise l’authentification IAM pour accéder au proxy RDS. Vous pouvez utilisez n’importe quel language de programmation supporté par AWS Lambda, l’exemple ci-dessous utilise Node.js :

let AWS = require('aws-sdk');
var mysql2 = require('mysql2'); //https://www.npmjs.com/package/mysql2
let fs  = require('fs');

let connection;

exports.handler = async(event) => {
	const promise = new Promise(function(resolve, reject) {
        
		console.log("Starting query ...\n");
	  	console.log("Running iam auth ...\n");
      
      	//
    	var signer = new AWS.RDS.Signer({
	        region: '[insert your region here]', // example: us-east-2
	        hostname: '[insert your RDS Proxy endpoint here]',
	        port: 3306,
	        username: '[Your RDS User name]'
  		});
        
	    let token = signer.getAuthToken({
	      username: '[Your RDS User name]'
	    });
    
    	console.log ("IAM Token obtained\n");
    
        let connectionConfig = {
          host: process.env['endpoint'], // Store your endpoint as an env var
          user: '[Your RDS User name]',
          database: process.env['my_db'], // Store your DB schema name as an env var
          ssl: { rejectUnauthorized: false},
          password: token,
          authSwitchHandler: function ({pluginName, pluginData}, cb) {
              console.log("Setting new auth handler.");
          }
        };
   
		// Adding the mysql_clear_password handler
        connectionConfig.authSwitchHandler = (data, cb) => {
            if (data.pluginName === 'mysql_clear_password') {
              // See https://dev.mysql.com/doc/internals/en/clear-text-authentication.html
              console.log("pluginName: "+data.pluginName);
              let password = token + '\0';
              let buffer = Buffer.from(password);
              cb(null, password);
            }
        };
        connection = mysql2.createConnection(connectionConfig);
		
		connection.connect(function(err) {
			if (err) {
				console.log('error connecting: ' + err.stack);
				return;
			}
			
			console.log('connected as id ' + connection.threadId + "\n");
		 });

		connection.query("SELECT * FROM contacts", function (error, results, fields) {
			if (error){ 
		  		//throw error;
		  		reject ("ERROR " + error);
			}
		  	
			if(results.length > 0){
				let result = results[0].email + ' ' + results[0].firstname + ' ' + results[0].lastname;
				console.log(result);
				
				let response = {
			        "statusCode": 200,
			        "statusDescription": "200 OK",
			        "isBase64Encoded": false,
			        "headers":{
			        	"Content-Type": "text/html"
			        },
			        body: result,
			    };
				
				connection.end(function(error, results) {
					  if(error){
					    //return "error";
					    reject ("ERROR");
					  }
					  // The connection is terminated now 
					  console.log("Connection ended\n");
					  
					  resolve(response);
				});
			}
		});
	});
	return promise;
};

Vous aurez besoin de packager le module client MySQL NodeJS avec votre fonction. Dans notre exemple, nous utilisons le module MySQL2, qui peut être trouvé ici. Il conviendra simplement d’installer le module en utilisant la commande npm install –save mysql2 et d’inclure les dépendances dans votre package AWS Lambda. Dans l’exemple ci-dessus, nous utilisons les variables d’environnement pour stocker les informations de connexion. il s’agit là des meilleures pratiques pour la gestion de configuration des base de données. Cela vous permet de changer ces informations de connexion sans pour autant modifier votre code. La variable d’environnement endpoint correspond au point de terminaison Amazon RDS Proxy précédemment créé. La variable db quant à elle représente le nom de la base de données. Veuillez vous assurer que le rôle d’execution de votre fonction Lambda dispose bien de la permission rds-db:connect tel que décrit ici. La console AWS Lambda le fait automatiquement pour vous. C’est ce qui vous permettra de récupérer des informations d’identification temporaires à partir d’IAM afin que vous puissiez vous authentifier sur la base de données, au lieu d’utiliser les informations d’authentification natives (nom d’utilisateur, mot de passe).

Confirmer que votre Proxy Amazon RDS utilise bien l’authentification IAM

    1. Ouvrez la console Amazon RDS Proxies
    2. Sélectionnez votre Proxy dans la liste et cliquez sur Actions, puis Modifier
    3. Dans la section Connectivité, assurez vous que l’authentification IAM est définie comme Obligatoire
Confirmation authentification IAM

Confirmation authentification IAM

Ces étapes ne sont pas nécessaires, si vous utilisez des informations de connexion classiques.

Conclusion

RDS Proxy vous aide à gérer un large nombre de connexions depuis AWS Lambda vers les bases de données Amazon RDS et Amazon Aurora, cela en provisionnant et en maintenant un ensemble de connexions réutilisables vers votre base de données. Vos fonctions Lambda sont en mesure de passer à l’échelle en réponse à vos besoins et d’utiliser Amazon RDS Proxy pour répondre aux multiples requêtes concurrentes de votre application. Cela réduit les besoins en terme de capacité de calcul et mémoire vive et vous évite de gérer des connexions dans votre application. Les coûts associés à l’utilisation de Amazon RDS Proxy peuvent être consultés sur ce lien.

Article initialement publié par George Mao et traduit en Français par Nadir Djadi, Architecte de solutions AWS.