Lambda est la solution AWS pour les fonctions sans serveur.
OpenTelemetry est un open-source destiné à créer des traces et les envoyer à un backend et gagner en visibilité.
Le développeur soucieux de l’observabilité qui a des lambdas sans serveur dans sa pile s’attaquera sûrement au besoin de connecter OpenTelemetry avec lambda.
Si vous êtes un tel développeur, ce guide est pour vous.
Aujourd’hui, je vais vous montrer exactement comment déployer un lambda activé pour le traçage avec OpenTelemetry.
Cet article fait partie du Apparence Série Hello World, où nous abordons pour vous des sujets liés aux services distribués. Notre équipe recherche sur le Web des problèmes courants, puis nous les résolvons nous-mêmes et vous proposons des guides pratiques complets. Aspecto est une plate-forme de traçage distribué basée sur OpenTelemetry pour les développeurs et les équipes d’applications distribuées.
La mise en place
Créez un nouveau répertoire pour votre projet et ajoutez le package.json suivant (ou ce package à votre projet existant) :
{
"name": "lambda-otel-post",
"version": "1.0.0",
"description": "",
"main": "handler.js",
"dependencies": {
"@opentelemetry/api": "1.0.2",
"@opentelemetry/instrumentation": "0.25.0",
"@opentelemetry/auto-instrumentations-node": "0.25.0",
"@opentelemetry/instrumentation-aws-lambda": "0.25.0",
"@opentelemetry/instrumentation-http": "0.25.0",
"@opentelemetry/sdk-trace-base": "0.25.0",
"@opentelemetry/sdk-trace-node": "0.25.0",
"axios": "^0.24.0"
},
"devDependencies": {},
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"author": "",
"license": "ISC"
}
Exécutez les installations :
npm install
Ajouter le handler.js
Ce code est un simple point d’entrée lambda qui contient un appel à une API externe et renvoie un message.
Plus tard, nous voudrons nous assurer qu’un span a été créé pour cet appel HTTP, ainsi que pour l’invocation lambda réelle.
'use strict';
const axios = require("axios");
module.exports.hello = async (event) => {
const todoItem = await axios('https://jsonplaceholder.typicode.com/todos/1');
return {
statusCode: 200,
body: JSON.stringify(
{
message: 'Some Message Here',
input: event,
},
null,
2
),
};
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
Ajouter le fichier wrapper Lambda qui permet le traçage avec OpenTelemetry
Ajoutons le fichier lambda-wrapper.js suivant :
const { SimpleSpanProcessor, ConsoleSpanExporter } = require("@opentelemetry/sdk-trace-base");
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const { AwsLambdaInstrumentation } = require('@opentelemetry/instrumentation-aws-lambda');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node");
const provider = new NodeTracerProvider();
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()))
provider.register();
registerInstrumentations({
instrumentations: [
getNodeAutoInstrumentations(),
new AwsLambdaInstrumentation({
disableAwsContextPropagation: true
})
],
});
Remarquez que j’utilise ConsoleSpanExporter
, qui écrit toutes les données de télémétrie dans la console.
En production, vous voudriez probablement que cela soit envoyé à un autre outil comme Jaeger ou un fournisseur d’observabilité.
Pour cet article de blog, cependant, cet exportateur fera l’affaire.
Une note sur disableAwsContextPropagation
Une autre chose que vous vous demandez probablement est pourquoi j’ai ajouté disableAwsContextPropagation:true.
La raison en est que l’instrumentation lambda essaie d’utiliser les en-têtes de contexte X-Ray par défaut (même lorsque nous n’utilisons pas X-Ray), ce qui nous oblige à avoir un contexte non échantillonné et un NonRecordingSpan
.
Pour résoudre ce problème, nous utilisons le disableAwsContextPropagation
drapeau.
Vous trouverez plus d’informations à ce sujet ici et dans la documentation sur l’instrumentation.
Déployer le Lambda
Il existe différentes manières de déployer lambda sur S3 et ce n’est pas le cadre du didacticiel.
J’ai choisi d’utiliser un framework sans serveur, mais vous pouvez également utiliser l’AWS CLI / d’autres formulaires pour le faire.
Si vous utilisez serverless, il s’agit du fichier serverless.yml.
N’oubliez pas de définir la région et le nom de fonction corrects.
service: lambda-otel-post
# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
frameworkVersion: '2'
provider:
name: aws
runtime: nodejs12.x
lambdaHashingVersion: 20201221
environment:
NODE_OPTIONS: --require lambda-wrapper
region: eu-west-2
functions:
tom-otel-lambda-post:
handler: handler.hello
Ajouter une variable d’environnement
Pour que le code de traçage s’exécute, nous devons nous assurer que Node l’exige avant qu’un autre fichier ne soit requis.
C’est pourquoi nous devons ajouter cette valeur pour la variable d’environnement NODE_OPTIONS : « –require lambda-wrapper ».
Si vous utilisez serverless avec le fichier ci-dessus, cela est fait pour vous automatiquement.
Sinon, rendez-vous dans la section de configuration du lambda déployé et définissez-le :
La raison de cette nécessité est que le fichier wrapper doit être inclus avant tout autre fichier pour que les instrumentations OpenTelemetry fonctionnent correctement.
Appel de la fonction Lambda
Maintenant, lorsque vous exécutez votre lambda (j’ai utilisé l’utilitaire de test de la console AWS intégrée), vous devez vous attendre à voir 2 spans créés : un pour l’appel lambda et l’autre pour l’appel HTTP sortant.
L’interface utilisateur de la console AWS pour appeler la fonction lambda
En effet, c’est ce que nous obtenons :
Ceci est l’étendue HTTP sortante
{
traceId: '4f373b61315c23fa47605a72b94ab59e',
parentId: '7ce4ab2283755eda',
name: 'HTTPS GET',
id: '54c07955525dad7f',
kind: 2,
timestamp: 1635332193754154,
duration: 82864,
attributes: {
'http.url': 'https://jsonplaceholder.typicode.com/todos/1',
'http.method': 'GET',
'http.target': '/todos/1',
'net.peer.name': 'jsonplaceholder.typicode.com',
'net.peer.ip': '104.21.4.48',
'net.peer.port': 443,
'http.host': 'jsonplaceholder.typicode.com:443',
'http.response_content_length_uncompressed': 83,
'http.status_code': 200,
'http.status_text': 'OK',
'http.flavor': '1.1',
'net.transport': 'ip_tcp'
},
status: { code: 1 },
events: []
}
Et la durée d’invocation lambda :
{
traceId: '4f373b61315c23fa47605a72b94ab59e',
parentId: undefined,
name: 'lambda-otel-post-dev-tom-otel-lambda-post',
id: '7ce4ab2283755eda',
kind: 1,
timestamp: 1635332193747990,
duration: 93019,
attributes: {
'faas.execution': 'ed075caa-4d54-44f8-96b4-b96085acbf9a',
'faas.id': 'arn:aws:lambda:eu-west-2:MY-AWS-ID:function:lambda-otel-post-dev-tom-otel-lambda-post',
'cloud.account.id': 'MY-AWS-ID'
},
status: { code: 0 },
events: []
}
Ce serait tout pour les gens d’aujourd’hui, vous pouvez maintenant exporter ces plages où vous le souhaitez.
PS Si vous n’avez pas encore de moyen simple de visualiser ces traces, n’hésitez pas à consulter Apparence (c’est gratuit). Voici à quoi ressemblerait une seule trace :