AWS Lambda : comment nous réduisons nos coûts d’hébergement

AWS Lambda : comment nous réduisons nos coûts d’hébergement

Pourquoi faire tourner (et payer pour) un serveur lorsqu’aucun utilisateur n’utilise l’application ? Si vous vous posez la question et espérez couper les ressources inutiles à l’image d’Elon Musk, vous êtes au bon endroit. Nous verrons cependant des solutions plus raisonnables et moins dévastatrices que celle entreprise par le CEO de Twitter.

Cet article est le premier d’une série sur les services appelés serverless. Cette série vise à présenter certaines technologies que nous utilisons à Kernix pour réduire les coûts et notre empreinte carbone. L’objectif de ce premier article est de présenter le déploiement et l’exécution de fonctions à la demande, c’est-à-dire d’utiliser des ressources matérielles uniquement lorsqu’on en a besoin.

Nous y présentons, dans un premier temps, le principe d’une solution particulière qui est l’objet de cet article : AWS Lambda. Ensuite, nous détaillerons un exemple concret : arrêter des serveurs lors de périodes creuses. Enfin nous présenterons les options pour automatiser le déploiement des fonctions à la demande.

Green IT et réduction des coûts

Pour qu’une application web fonctionne sans discontinuité, il faut qu’un serveur tourne en permanence. Ainsi, avec un déploiement traditionnel, le coût est fixe quelque soit la charge d’utilisation.
Une mutualisation des ressources matérielles peut être opérée pour que plusieurs applications qui consomment peu de ressources tournent sur les mêmes machines. Lors d’un pic de charge, les ressources matérielles peuvent devenir dédiées à une seule application. Cette logique peut être poussée à l’extrême : en divisant une application en différentes fonctions, chaque fonction peut être déployée indépendamment. Le cas échéant, une fonction appelée intensément peut-être dupliquée sur plusieurs machines pour absorber la charge. C’est sur ce principe que reposent les technologies dites « Function as a Service ».

Nous allons, dans ce présent article, nous intéresser à une utilisation limitée de ce type de service. En particulier, nous chercherons à utiliser très peu de ressources matérielles pour exécuter une fonction de temps en temps. Cette fonction peu énergivore va nous permettre de faire des économies drastiques puisqu’elle sera utilisée pour éteindre des serveurs la nuit. Certains d’entre eux, en particulier les serveurs de développement, n’ont en effet aucun besoin de tourner la nuit.

AWS Lambda : Function as a Service

Nous allons dans cette partie utiliser un service cloud d’Amazon Web Services : AWS Lambda. Ce service consiste à définir une fonction, en l’occurrence pour notre exemple, du code Python. Il faudra ensuite faire connaître cette fonction à AWS ; c’est l’étape de déploiement. Puis, il faudra donner une règle pour exécuter la fonction lorsqu’on le souhaite. Enfin, il faudra autoriser la fonction à faire les actions qu’elle doit effectuer : éteindre une certaine machine virtuelle.

Définition d’une fonction

Voilà le code Python qui permet d’arrêter une machine virtuelle d’Amazon :

import boto3

region = 'eu-west-1'
instances = ['i-xxxx'] # l’identifiant de la machine virtuelle

def lambda_handler(event, context):
    ec2 = boto3.client('ec2', region_name=region)
    ec2.stop_instances(InstanceIds=instances)
    print(f'stopped your instances: {instances}')

Nous utilisons pour cela le package boto3 qui permet d’interagir avec les services AWS en Python. Les variables region et instances définissent respectivement la région dans laquelle les machines virtuelles sont déployées, et les instances que l’on souhaite arrêter. Vient ensuite la fonction lambda_handler qui est la fonction qui sera appelée par la suite. Cette fonction prend deux arguments que nous n’utilisons pas ici mais qui sont nécessaires puisque faisant partie de l’API du service AWS Lambda. Cette fonction arrête les instances puis enregistre un log pour indiquer quelles sont celles qui ont été arrêtées.

Déploiement de la fonction

Maintenant que nous avons le code qu’il faudra exécuter, soumettons le à AWS. Rendez-vous sur https://console.aws.amazon.com/lambda puis cliquez sur le bouton « Create function ». Renseignons un nom et sélectionnons « Python 3.8 » comme environnement d’exécution comme sur la figure suivante :

AWS -1

Lorsqu’on clique sur « Create function », une fonction est déployée. Il suffit alors de renseigner le code qu’on a défini précédemment comme sur la figure suivante :

AWS -2

Appel de la fonction

Définissons à présent une règle qui exécute notre fonction tous les soirs de la semaine. Pour celà, le bouton « Add trigger » nous permet d’utiliser le service d’AWS qui s’occupe de ça : AWS EventBridge.

AWS-3

La figure suivante indique la règle que l’on veut mettre en place : cron(0 18 ? * MON-FRI *)
Nous avons paramétré l’outil pour que la fonction s’exécute tous les jours de la semaine à 18h UTC.

Autorisations

Notre fonction est désormais appelée tous les soirs mais il nous reste une chose à régler pour qu’elle fonctionne : l’autoriser à éteindre les instances EC2. Pour cela, nous pouvons cliquer sur le nom du rôle d’exécution :

AWS-5

Ceci nous mène sur une page du service IAM d’AWS qui s’occupe des gestions de droits. En cliquant sur « Add permissions » nous pouvons définir une politique :

AWS- 6

La politique consiste à autoriser l’arrêt des instances EC2. Le JSON qui correspond à la politique à définir est donné dans l’image suivante :

AWS-7

Frameworks IaC

Déployer une fonction grâce à l’interface graphique devient laborieux lorsque l’on doit en définir plusieurs. Fort heureusement, il existe des frameworks qui permettent des gains de temps.

Les frameworks permettent de définir l’infrastructure, ou dans notre cas, quand les fonctions doivent être exécutées automatiquement. Ils permettent de faire cela par du code, ou par des fichiers de configuration. L’objectif principal est de faciliter la reproductibilité puisqu’il suffit de lancer une commande pour le redéploiement lorsque des modifications sont faites. Ils permettent une certaine lisibilité puisque tout est défini à un seul endroit, évitant ainsi de naviguer sur l’interface graphique du fournisseur de service.

Différentes philosophies de frameworks

Il existe des frameworks pour tous les goûts, suivant que l’utilisateur privilégie le déclaratif ou l’impératif, l’exhaustivité ou la concision, etc.

Par exemple, le framework CloudFormation d’AWS est déclaratif, dans le sens où on écrit un fichier JSON ou bien YAML qui décrit les configurations des différents composants et leurs interactions. C’est le cas aussi du framework serverless. Cependant, serverless est beaucoup plus concis puisque ce framework est dogmatique : il ajoute automatiquement des composants considérés comme des bonnes pratiques, comme l’enregistrement des logs.

Avec une approche différente, AWS propose une interface en ligne de commande, AWS CLI, ainsi qu’une bibliothèque Python : Boto3. Ces deux outils permettent d’exécuter successivement des instructions et offrent ainsi une grande flexibilité.

Exemple avec le framework serverless

Illustrons le déploiement avec le framework serverless. Nous devons pour cela déclarer ce qu’on veut déployer dans un fichier serverless.yml qui permet de définir notre fonction Lambda et son déclenchement automatique le soir, dont voici le contenu :

service: my-lambda-project

provider:
  name: aws
  region: eu-west-1
  iam:
    role:
      statements:
        - Effect: Allow
          Resource: "*"
          Action:
            - "ec2:Stop"

functions:
  mylambda:
    runtime: python3.8
    handler: mylambda.handler
    events:
      - schedule: cron(0 18 ? * MON-FRI *)

Il suffit ensuite d’écrire le code de la fonction dans le fichier mylambda.py puis de déployer avec la commande : serverless deploy

Le même exemple serait beaucoup plus long avec le framework CloudFormation. Il faudrait en effet ajouter un bucket S3 pour y déposer le code de la fonction, une gestion des logs avec CloudWatch, une gestion des droits d’écriture de logs avec AWS IAM.
Pour chaque composant, la syntaxe est aussi verbeuse, par exemple, les deux lignes suivantes avec le framework serverless :

events:
  - schedule: cron(0 18 ? * MON-FRI *)

se traduisent par le bloc suivant avec la syntaxe CloudFormation :

"MylambdaEventsRuleSchedule1": {
  "Type": "AWS::Events::Rule",
  "Properties": {
    "ScheduleExpression": "cron(0 18 ? * MON-FRI *)",
    "State": "ENABLED",
    "Targets": [
      {
        "Arn": {
          "Fn::GetAtt": [
            "MylambdaLambdaFunction",
            "Arn"
          ]
        },
        "Id": "mylambdaSchedule"
      }
    ]
  }
}

Autres utilisations

Nous avons vu l’utilisation du service AWS Lambda à travers un exemple simple : arrêter des serveurs à certains horaires. Ce type de service (les autres fournisseurs cloud proposent des équivalents à l’instar de Google Cloud Functions) ne se limitent pas à ce simple usage. Nous pouvons en effet définir une application complète avec ce type de solutions. Les avantages principaux sont les suivants :

  • Les ressources utilisées sont très faibles lorsque l’application est peu utilisée (peu d’appels de fonctions), le coût est dans ce cas très faible.
  • L’application peut absorber un pic de charge.

Nous verrons dans un prochain article comment l’utiliser pour ces cas d’applications plus complètes.

Illustration : Jade KAÏDI

AWS Lambda : comment nous réduisons nos coûts d’hébergement