前提条件
X-Ray初心者向け。なんとなくは知ってるけど、具体的に何がどこまでできて、どうやって実装するかを色々試してみながら確認した記事。
環境としては、Spring Boot+MavenでWebアプリを実装している。依存関係の解決方法以外はGradleも同じはず。
X-RayのDockerコンテナイメージを作る。
まずは、適当なEC2とかでコンテナイメージを作る。
今回は、AWSのサンプルをベースに作ってみよう。
$ git clone https://github.com/aws-samples/aws-xray-fargate
buildspec.yml に書いてあるコマンドを、実行していってみる。
$ aws ecr get-login --no-include-email --region ap-northeast-1
でログイン情報を取得後、実行。ap-northeast-1
は利用しているリージョンを指定する。
$ docker build -t xray:latest .
作ったコンテナイメージは正常性確認をしてみよう。以下を参考にする。
それぞれのdocker runのオプションの意味も記載されているので読んでみると良い。
$ docker run \
--attach STDOUT \
-v ~/.aws/:/root/.aws/:ro \
--net=host \
-e AWS_REGION=ap-northeast-1 \
--name xray \
-p 2000:2000/udp \
xray -o
あと、EC2のロールにAWSXRayDaemonWriteAccess
のIAMポリシをつけておく。
アプリケーションのX-Ray対応
受信リクエスト対応
以下の開発者ガイドを参考にしながら、アプリケーション側でX-Rayのログをデーモンに送るようにする。
受信リクエストの対応をするだけなら簡単なようだ。WebConfig.java
なクラスを以下の様に実装する。ApigwTest
は、サンプルで作ったWebアプリの名前。
package com.example;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import javax.servlet.Filter;
import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter;
@Configuration
public class WebConfig {
@Bean
public Filter TracingFilter() {
return new AWSXRayServletFilter("ApigwTest");
}
}
X-RayはAWSのSDKを使う必要があるため、pom.xmlに以下を記述して依存関係を解決する。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-xray-recorder-sdk-bom</artifactId>
<version>2.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-xray-recorder-sdk-core</artifactId>
</dependency>
</dependencies>
さて、このアプリを起動してアクセスすると、↓こんな感じでトレースができるようになる。
詳細情報の取得
さらに、受信リクエストを受けたノードの情報を取得できるように、↑で作ったWebConfig
クラスに以下のコードを入れてみる。
import com.amazonaws.xray.AWSXRay;
import com.amazonaws.xray.AWSXRayRecorderBuilder;
import com.amazonaws.xray.plugins.EC2Plugin;
import com.amazonaws.xray.plugins.ECSPlugin;
static {
AWSXRayRecorderBuilder builder = AWSXRayRecorderBuilder.standard().withPlugin(new EC2Plugin()).withPlugin(new ECSPlugin());
AWSXRay.setGlobalRecorder(builder.build());
}
これをEC2上で実行した場合は、以下のように、サービスマップにEC2インスタンスであることが表示されるようになる。
ダウンストリームのトレース対応
さらに、HTTPクライアントとして、ダウンストリームのWebサービスの情報も取得する場合は、コントローラの中に↓こんなのを入れてみる。
import com.amazonaws.xray.proxies.apache.http.HttpClientBuilder;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import java.io.IOException;
(中略)
public int HttpClient(String URL) throws IOException {
int statusCode = 500;
CloseableHttpClient httpclient = HttpClientBuilder.create().build();
HttpGet httpGet = new HttpGet(URL);
CloseableHttpResponse clientResponse = httpclient.execute(httpGet);
try {
statusCode = clientResponse.getStatusLine().getStatusCode();
} finally {
clientResponse.close();
}
return statusCode;
}
また、com.amazonaws.xray.proxies.apache.http.HttpClientBuilder
をインポートするために、pom.xmlにも以下の依存関係を追記する。
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-xray-recorder-sdk-apache-http</artifactId>
</dependency>
</dependencies>
すると、以下のようにダウンストリームの情報も取得できるようになる。
ちなみに、このダウンストリームのサービスはLambda関数で実装していたので、ふと気になってLambda関数側でX-Rayの設定を有効化すると、↓こんな感じで、重複した要素は取得しなくなった。賢い。
ECS on Fargateで動かす
まずは、ここまでで動作確認したX-RayのコンテナをECRにPUSHする。
事前にxrayのリポジトリを作っておくのを忘れないように。
$ docker tag xray:latest [AWSのアカウントID].dkr.ecr.[リージョン].amazonaws.com/xray:latest
$ docker push [AWSのアカウントID].dkr.ecr.[リージョン].amazonaws.com/xray:latest
また、ここでもECSのタスクロールとタスク実行ロールにAWSXRayDaemonWriteAccess
を付与しておく。
コンテナのタスク定義に、↑でPUSHしたxrayのイメージを入れる。
CloudFormationテンプレートで言えば、ContainerDefinitions
内で、既存のコンテナ定義に並べて以下の定義を書く。awsvpcを使わないECS on EC2の場合は他にも設定が必要だが、Fargateの場合は、awsvpcで動くので、簡単に加えられるようだ。
- Name: X-Ray-Daemon
Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/xray:latest
Cpu: 32
MemoryReservation: 256
PortMappings:
- ContainerPort: 2000
Protocol: udp
Lambda側のアクティブトレースを有効化すると、EC2と同様に取得することができた。
ただし、Lambda側のアクティブトレースを無効にすると、リクエストに対するトレースは取得できたが、ダウンストリームのトレースが取得できなかった。何かまだ足りていない設定があるのかもしれない。