AWS X-RayをJavaバッチ処理に適用したい

# AWS X-Rayとは

- APM(Application Performance Monitor)
- https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/aws-xray.html

----

# X-Rayの特徴

- managed service
- 従量料金（永久無料枠あり）
- agent（デーモン） はトレース情報の送信のみ
- トレース情報を作るのはApplicationに埋め込むSDK
  - ソース修正・ビルドが必要 

----

# X-Rayの使いどころ

- 商用APMのライセンス体系の隙間
  - マイクロサービス(有償agentフィーが…)
  - Serverless(agent入れようがない)
  - **多数のバッチ処理**
  - 開発環境(開発初期こそAPMが欲しい → 無料枠で）

----

# Javaバッチに適用

- SampleはほとんどWeb Application
- HTTP Requestをトレースするのは SDK入れてimport文を差し替えるだけ

この本筋の紹介ばかりが目立って、ググってもバッチ適用例に当たらないが…

> 非同期のシンプルなイベント呼び出し、3 層のウェブアプリケーション、数千のサービスで構成される複雑なマイクロサービスアプリケーションなど

「非同期のシンプルなイベント呼び出し」できると書いてある → やってみよ

----

# 見事な空振り

SDK入れるだけで、AWS API CALLはトレースされるって？
なにかAWSコンソールで見えるようになるかも？

1. 既存Javaバッチ処理に xray SDK 依存関係追加(Maven pom.xml)
2. ビルド・開発デプロイ
3. job実行

なにも起きなかった

----

# X-Ray デーモン

- UDP ポート 2000 のトラフィックをリッスン
- AWS X-Ray API に中継する

```
# curl .../xray-daemon/aws-xray-daemon-2.x.rpm -o xray.rpm
# yum install -y xray.rpm 
```
AWS X-Rayコンソールはピクリとも動かない

----

# デーモンログ

```
# ps ax | grep xray
13910 ?        Ssl    0:01 xray -f /var/log/xray/xray.log
# cat /var/log/xray/xray.log
2019-07-09T17:13:11+09:00 [Info] Initializing AWS X-Ray daemon 2.1.3
2019-07-09T17:13:11+09:00 [Info] Using buffer memory limit of 78 MB
2019-07-09T17:13:11+09:00 [Info] 1248 segment buffers allocated
2019-07-09T17:13:11+09:00 [Info] Using region: ap-northeast-1
```

デーモンは正常そうだ。port 2000に何も来てないっぽい。

----

# X-RayのHello World

https://github.com/aws-samples/eb-java-scorekeep/blob/xray/bin/xray_start.sh

```
# sh xray_start.sh
# tail /var/log/xray/xray.log
019-07-10T11:01:11+09:00 [Error] Sending segment batch failed with: AccessDeniedException: 
User: arn:aws:sts::...:assumed-role/...-role/i-... 
is not authorized to perform: xray:PutTraceSegments
```

AWS Ops のあるある　「Sigh. また権限追加か…」
追加後、X-Rayコンソールの Traces に1個出た！

----

# TraceIdとは

SDKの中で生成したり、SQSにもうTraceIDが入っているところから始まるsampleばかりで、どこで誰がどう作るのか？？？
xray_start.sh 読む

```sh
START_TIME=$(date +%s)
HEX_TIME=$(printf '%x\n' $START_TIME)
GUID=$(dd if=/dev/random bs=12 count=1 2>/dev/null | od -An -tx1 | tr -d ' \t\n')
TRACE_ID="1-$HEX_TIME-$GUID"
```

"1-" + Epoch Time の Hex表現 + "-" + GUID
どこの誰でも生成できるID体系：ユニーク性と概ね時系列が要件っぽい

----

# Javaバッチに追記

```java
import com.amazonaws.xray.AWSXRayRecorder;
import com.amazonaws.xray.AWSXRayRecorderBuilder;
import com.amazonaws.xray.entities.TraceID;
import com.amazonaws.xray.plugins.EC2Plugin;
...
    private static AWSXRayRecorder xRayRecorder = AWSXRayRecorderBuilder.standard().withPlugin(new EC2Plugin()).build();
    private static final TraceID traceId = new TraceID();
...
        xRayRecorder.beginSegment(traceId.toString());
...
        xRayRecorder.endSegment();
...
```

参考sample: https://github.com/aws-samples/aws-xray-apache-kafka-sample

----

# Java追記まとめ

1. import追加して
1. AWSXRayRecorderを build して
1. TraceID 生成して
1. 処理を AWSXRayRecorder.beginSegment(), endSegment() でくくる

----

# 出たぞ

```
[root@gin-mng-za-1 ~]# tail /var/log/xray/xray.log
...
2019-07-11T09:26:29+09:00 [Info] Successfully sent batch of 1 segments (0.086 seconds)
```

```json
{
    "Duration": 42.792,
    "Id": "1-5d26820a-5249372acaaa8b1b875a72e5",
    "Segments": [
        {
            "Document": {
                "id": "6626990748dee123",
                "name": "1-5d26820a-83c2abc49e5ca6595e9f9dbd",
                "start_time": 1562804746.446,
                "end_time": 1562804789.238,
                "aws": {
                    "ec2": {
                        "availability_zone": "ap-northeast-1a",
                        "instance_id": "i-..."
                    },
                    "xray": {
                        "sdk_version": "2.2.0",
                        "sdk": "X-Ray for Java"
                    }
                },
                "service": {
                    "runtime": "Java HotSpot(TM) 64-Bit Server VM",
                    "runtime_version": "1.8.0_..."
                },
                "trace_id": "1-5d26820a-5249372acaaa8b1b875a72e5",
                "origin": "AWS::EC2::Instance"
            },
            "Id": "6626990748dee123"
        }
    ]
}
```

----

# JDBCInterceptor

xray SDKを依存ライブラリに入れてあれば jdbc.properties に 1行追記だけ

```
dataSource.jdbcInterceptors=com.amazonaws.xray.sql.postgres.TracingInterceptor
```

subsegments がいっしょに PUTされた

```json
"subsegments": [
                    {
                        "id": "23894958bbe2a6a0",
                        "name": "s....@db.....rds",
                        "start_time": 1562824789.49,
                        "end_time": 1562824789.532,
                        "sql": {
                            "url": "jdbc:postgresql://db....rds:.../s...",
                            "database_type": "PostgreSQL",
                            "database_version": "9.....",
                            "driver_version": "...",
                            "user": "...",
                            "preparation": "statement"
                        },
                        "namespace": "remote"
                    }
                ]
```

----

# HTTP Service 呼出

Apache HttpClient を com.amazonaws.xray.proxies の互換classにソース上で差し替える

`import` 文置き換えだけでロジックそのままで済むんじゃないかな

```java
import com.amazonaws.xray.proxies.apache.http.HttpClientBuilder;
...
CloseableHttpClient httpclient = HttpClientBuilder.create().build();  //変更なし
```

これだけで、HttpClient経由のAPIが subsegments として追加される（はず）

----

# ソース変更まとめ

1. pom.xml に xray SDK追加して
1. main の import追加して
1. AWSXRayRecorderを build して
1. TraceID 生成して
1. 処理を AWSXRayRecorder.beginSegment(), endSegment() でくくる
1. jdbc.properties に interceptor 設定追加する
1. HttpClientの import文を xray SDK の class に差し替える

----
 
# インフラ変更まとめ

1. 対象instance に xray デーモンをインストール
2. 対象instance profile に xray:PutTraceSegments などを許可
  - 参考：AWS管理ポリシー AWSXRayDaemonWriteAccess 

----

# コスト試算

- 5分, 10分, 15分, 30分周期の job各1本
- その他1日1回のjob数本
- 合計 585 jobs/day = 18K/mo
- X-Rayの永久無料枠は 100K/mo  **ずっと無料**

※クエリ側料金もあるけどアドホック検索してるうちは無料枠内でしょう

----

# 残件

- SQL本文がまだ出せてない
- HTTP送信のsubsegment出力 未検証
  - 調査対象バッチの Apache HttpClient 3.x でSDK replaceできなかった
  - [httpclient3.x系から4.5まで上げたら大変だった…](https://qiita.com/yukiko-bass/items/db0e556cf0847a12b0c3) 
- 役に立つ分析が可能なのか　未実証