Amazon Web Services ブログ
AWS Distro for OpenTelemetry の新機能 – トレースのサポートが一般的に利用可能に
昨年の re:Invent の前、AWS でサポートされている OpenTelemetry プロジェクトの安全なディストリビューションである AWS Distro for OpenTelemetry のパブリックプレビューをご紹介しました。OpenTelemetry は、アプリケーションの動作とパフォーマンスをよりよく理解するために、テレメトリデータをインストルメント化、生成、収集、およびエクスポートするためのツール、API、および SDK を提供します。2021 年 9 月 22 日、アップストリームの OpenTelemetry は、そのコンポーネントのトレース安定性マイルストーンを発表しました。2021 年 9 月 23 日、トレースのサポートが AWS Distro for OpenTelemetry で一般的に利用可能になったことをお知らせします。
OpenTelemetry を使用すると、アプリケーションを一度だけインストルメント化し、トレースを複数のモニタリングソリューションに送信できます。
AWS Distro for OpenTelemetry を使用して、Amazon Elastic Compute Cloud (Amazon EC2)、Amazon Elastic Container Service (Amazon ECS)、Amazon Elastic Kubernetes Service (EKS)、および AWS Lambda、ならびにオンプレミスで実行されているアプリケーションをインストルメント化できます。AWS Fargate で実行され、ECS または EKS を介してオーケストレートされたコンテナもサポートされています。
AWS Distro for OpenTelemetry によって収集されたトレーシングデータを AWS X-Ray や、次のようなパートナーの送信先に送信できます。
- OpenTelemetry Protocol (OTLP) エクスポーターをネイティブにサポートする AppDynamics、Dynatrace、Grafana、Honeycomb、Lightstep、NewRelic、および SumoLogic
- 独自のエクスポーターを備えている Datadog、Logz.io、Splunk
自動インストルメンテーションエージェントを使用すると、コードを変更せずにトレースを収集できます。現在、自動インストルメンテーションは Java および Python アプリケーションで利用できます。Python の自動インストルメンテーションのサポートは、現在 AWS SDK のみを対象としています。OpenTelemetry SDK を使用して、他のプログラミング言語 (Go、Node.js、.NET など) を使用してアプリケーションをインストルメント化できます。
これが Java アプリケーションで実際にどのように機能するのか見てみましょう。
自動インストルメンテーションを使用した Java アプリケーションのトレースの視覚化
Amazon Simple Storage Service (Amazon S3) バケットと Amazon DynamoDB テーブルのリストを表示するシンプルな Java アプリケーションを作成します。
package com.example.myapp;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;
import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import java.util.List;
/**
* Hello world!
*
*/
public class App {
public static void listAllTables(DynamoDbClient ddb) {
System.out.println("DynamoDB Tables:");
boolean moreTables = true;
String lastName = null;
while (moreTables) {
try {
ListTablesResponse response = null;
if (lastName == null) {
ListTablesRequest request = ListTablesRequest.builder().build();
response = ddb.listTables(request);
} else {
ListTablesRequest request = ListTablesRequest.builder().exclusiveStartTableName(lastName).build();
response = ddb.listTables(request);
}
List<String> tableNames = response.tableNames();
if (tableNames.size() > 0) {
for (String curName : tableNames) {
System.out.format("* %s\n", curName);
}
} else {
System.out.println("No tables found!");
System.exit(0);
}
lastName = response.lastEvaluatedTableName();
if (lastName == null) {
moreTables = false;
}
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
System.out.println("Done!\n");
}
public static void listAllBuckets(S3Client s3) {
System.out.println("S3 Buckets:");
ListBucketsRequest listBucketsRequest = ListBucketsRequest.builder().build();
ListBucketsResponse listBucketsResponse = s3.listBuckets(listBucketsRequest);
listBucketsResponse.buckets().stream().forEach(x -> System.out.format("* %s\n", x.name()));
System.out.println("Done!\n");
}
public static void listAllBucketsAndTables(S3Client s3, DynamoDbClient ddb) {
listAllBuckets(s3);
listAllTables(ddb);
}
public static void main(String[] args) {
Region region = Region.EU_WEST_1;
S3Client s3 = S3Client.builder().region(region).build();
DynamoDbClient ddb = DynamoDbClient.builder().region(region).build();
listAllBucketsAndTables(s3, ddb);
s3.close();
ddb.close();
}
}
Apache Maven を使用してアプリケーションをパッケージ化します。S3 および DynamoDB とのインタラクションに使用する AWS SDK for Java 2.x などの依存関係を管理する Project Object Model (POM) ファイルを次に示します。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<groupId>com.example.myapp</groupId>
<artifactId>myapp</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>myapp</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.17.38</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>dynamodb</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.example.myapp.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>
Maven を使用して、すべての依存関係を含む Java Archive (JAR) 実行可能ファイルを作成します。
アプリケーションを実行してトレーシングデータを取得するには、次の 2 つのコンポーネントが必要です。
- AWS Distro for OpenTelemetry Auto-Instrumentation Agent for Java。任意の Java 8+ アプリケーションにアタッチできる Java エージェントであり、AWS SDK を含む多くの一般的なライブラリやフレームワークからテレメトリをキャプチャします。
- AWS Distro for OpenTelemetry Collector。テレメトリデータを受信、処理、およびモニタリング対象にエクスポートできる実行可能ファイルです。
1 つのターミナルで、Docker を使用して AWS Distro for OpenTelemetry Collector を実行します。
コレクターは、トレースを受信してモニタリングプラットフォームに転送する準備ができました。デフォルトでは、AWS Distro for OpenTelemetry Collector は AWS X-Ray にトレースを送信します。コレクターの設定を編集して、エクスポーターを変更したり、さらにエクスポーターを追加したりできます。例えば、ドキュメントに従って、OLTP プロトコルを使用してテレメトリデータを送信するように OLTP エクスポーターを設定できます。ドキュメントには、他のパートナーの送信先を設定する方法も記載されています。
AWS Distro for OpenTelemetry Auto-Instrumentation Java Agent の最新バージョンをダウンロードします。ここで、アプリケーションを実行し、エージェントを使用してテレメトリデータをキャプチャします。コードに特定のインストルメンテーションを追加する必要はありません。OTEL_RESOURCE_ATTRIBUTES
環境変数で、サービスの名前と名前空間を設定します。
想定どおり、S3 バケットのリストをグローバルに取得し、リージョン内の DynamoDB テーブルのリストを取得します。
より多くのトレーシングデータを生成するために、前のコマンドを数回実行します。アプリケーションを実行するたびに、テレメトリデータがエージェントによって収集され、コレクターに送信されます。コレクターはデータをバッファリングし、設定されているエクスポーターに送信します。デフォルトでは、トレースは X-Ray に送信されています。
ここで、AWS X-Ray コンソールでサービスマップを見て、アプリケーションと他のサービスとのインタラクションを確認します。
表示されています! コードに変更がなければ、アプリケーションによる S3 API と DynamoDB API に対する呼び出しが表示されます。エラーは発生していないため、すべての円が緑色になっています。円の中には、呼び出しの平均レイテンシーと 1 分あたりのトランザクション数が表示されています。
Java アプリケーションへのスパンの追加
自動的に収集される情報は、トレースを使用してより多くの情報を提供することで改善できます。例えば、アプリケーションのさまざまな部分で同じサービスとのインタラクションがある場合があり、それらのインタラクションをサービスマップで分離すると便利です。この方法によれば、エラーやレイテンシーが高い場合は、アプリケーションのどの部分が影響を受けるかを知ることができます。
その方法の 1 つは、スパンまたはセグメントを使用することです。スパンは、論理的に関連するアクティビティのグループを表します。例えば、listAllBucketsAndTables
メソッドは 2 つのオペレーションを実行しています。1 つは S3 を使用しており、もう 1 つは DynamoDB を使用しています。それらをスパンでまとめてグループ化したいと思います。OpenTelemetry を使用する最も迅速な方法は、@WithSpan
アノテーションをメソッドに追加することです。通常、メソッドの結果は引数に依存するため、@SpanAttribute
アノテーションを使用して、メソッド呼び出しのどの引数を自動的に属性としてスパンに追加するかを記述します。
@WithSpan
public static void listAllBucketsAndTables(@SpanAttribute("title") String title, S3Client s3, DynamoDbClient ddb) {
System.out.println(title);
listAllBuckets(s3);
listAllTables(ddb);
}
@WithSpan
アノテーションと @SpanAttribute
アノテーションを使用できるようにするには、それらをコードにインポートし、必要な OpenTelemetry 依存関係を POM に追加する必要があります。これらの変更はすべて OpenTelemetry の仕様に基づいており、使用している実際の実装や、テレメトリデータの視覚化または分析に使用するツールには依存していません。これらの変更は、アプリケーションをインストルメント化するために一度だけ行う必要があります。これは素晴らしいことですよね。
スパンの仕組みをよりよく理解するために、同じオペレーションを逆の順序で実行する別のメソッドを作成します。まず DynamoDB テーブルをリストし、次に S3 バケットをリストします。
@WithSpan
public static void listTablesFirstAndThenBuckets(@SpanAttribute("title") String title, S3Client s3, DynamoDbClient ddb) {
System.out.println(title);
listAllTables(ddb);
listAllBuckets(s3);
}
これで、アプリケーションは 2 つのメソッド (listAllBucketsAndTables
と listTablesFirstAndThenBuckets
) を交互に実行しています。簡単にするために、インストルメント化されたアプリケーションの完全なコードを次に示します。
package com.example.myapp;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;
import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import java.util.List;
import io.opentelemetry.extension.annotations.SpanAttribute;
import io.opentelemetry.extension.annotations.WithSpan;
/**
* Hello world!
*
*/
public class App {
public static void listAllTables(DynamoDbClient ddb) {
System.out.println("DynamoDB Tables:");
boolean moreTables = true;
String lastName = null;
while (moreTables) {
try {
ListTablesResponse response = null;
if (lastName == null) {
ListTablesRequest request = ListTablesRequest.builder().build();
response = ddb.listTables(request);
} else {
ListTablesRequest request = ListTablesRequest.builder().exclusiveStartTableName(lastName).build();
response = ddb.listTables(request);
}
List<String> tableNames = response.tableNames();
if (tableNames.size() > 0) {
for (String curName : tableNames) {
System.out.format("* %s\n", curName);
}
} else {
System.out.println("No tables found!");
System.exit(0);
}
lastName = response.lastEvaluatedTableName();
if (lastName == null) {
moreTables = false;
}
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
System.out.println("Done!\n");
}
public static void listAllBuckets(S3Client s3) {
System.out.println("S3 Buckets:");
ListBucketsRequest listBucketsRequest = ListBucketsRequest.builder().build();
ListBucketsResponse listBucketsResponse = s3.listBuckets(listBucketsRequest);
listBucketsResponse.buckets().stream().forEach(x -> System.out.format("* %s\n", x.name()));
System.out.println("Done!\n");
}
@WithSpan
public static void listAllBucketsAndTables(@SpanAttribute("title") String title, S3Client s3, DynamoDbClient ddb) {
System.out.println(title);
listAllBuckets(s3);
listAllTables(ddb);
}
@WithSpan
public static void listTablesFirstAndThenBuckets(@SpanAttribute("title") String title, S3Client s3, DynamoDbClient ddb) {
System.out.println(title);
listAllTables(ddb);
listAllBuckets(s3);
}
public static void main(String[] args) {
Region region = Region.EU_WEST_1;
S3Client s3 = S3Client.builder().region(region).build();
DynamoDbClient ddb = DynamoDbClient.builder().region(region).build();
listAllBucketsAndTables("My S3 buckets and DynamoDB tables", s3, ddb);
listTablesFirstAndThenBuckets("My DynamoDB tables first and then S3 bucket", s3, ddb);
s3.close();
ddb.close();
}
}
そして、追加の OpenTelemetry 依存関係を含む更新された POM を次に示します。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<groupId>com.example.myapp</groupId>
<artifactId>myapp</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>myapp</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.16.60</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>dynamodb</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-extension-annotations</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>1.5.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.example.myapp.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>
これらの変更でアプリケーションをコンパイルし、数回再実行します。
ここで、これらのアノテーションによって提供される追加情報を使用してコンピューティングされた X-Ray サービスマップを見てみましょう。
2 つのメソッドと、それらが呼び出す他のサービスが表示されました。エラーや高レイテンシーがある場合、2 つの方法がどのように影響を受けるかを簡単に理解できます。
X-Ray コンソールの [Traces] (トレース) セクションで、いくつかのトレースの raw データを確認します。title
引数には @SpanAttribute
アノテーションが付けられているため、各トレースは metadata
セクションでその引数の値を持ちます。
Lambda 関数からのトレースの収集
前のステップは、オンプレミス、EC2、およびコンテナで実行されているアプリケーションで機能します。トレースを収集し、Lambda 関数で自動インストルメンテーションを使用するには、AWS マネージド OpenTelemetry Lambda Layers を使用できます (いくつかの例がリポジトリに含まれています)。
関数に Lambda レイヤーを追加したら、環境変数 OPENTELEMETRY_COLLECTOR_CONFIG_FILE
を使用して、独自の設定をコレクターに渡すことができます。AWS Distro for OpenTelemetry with AWS Lambda の使用の詳細については、ドキュメントを参照してください。
可用性と料金
AWS Distro for OpenTelemetry を使用して、オンプレミスと AWS で実行されているアプリケーションからテレメトリデータを取得できます。AWS Distro for OpenTelemetry を使用するための追加コストはありません。設定によっては、OpenTelemetry データの送信先である AWS のサービス (AWS X-Ray、Amazon CloudWatch、および Amazon Managed Service for Prometheus (AMP)) の料金をお支払いいただく場合があります。
AWS Distro for OpenTelemetry を使用して、アプリケーションのインストルメンテーションを簡素化し、可観測性を向上させましょう。
– Danilo
原文はこちらです。