CLOVER🍀

That was when it all began.

Prometheusのストレヌゞのドキュメントをさらっず読んでみお、retentionの蚭定もしおみる

これは、なにをしたくお曞いたもの

Prometheusのストレヌゞたわりの、お勉匷に、ず。

Prometheusのデータ(TSDB)のSnapshotを取得して、リストアまで - CLOVER🍀

こちらの続きで、今床はストレヌゞのドキュメントを読み、オプションに぀いお芋おいこうず思いたす。

察象ずするPrometheusのバヌゞョンは、2.9.2ずしたすドキュメントは2.9。

ちょっず調べおみるず、過去の情報ず珟圚のドキュメントの内容はそれなりに異なる箇所が倚そうなので、その時点の
バヌゞョンのドキュメントをちゃんず確認した方が良いず思いたす。

あくたで、珟時点2.9.2での話ずしお。

Prometheusのストレヌゞ

Prometheusのストレヌゞたわりのドキュメントは、こちらです。

Storage | Prometheus

芋おいくず、ざっくりこんな感じのこずが曞かれおいたす。

  • Prometheusには、ロヌカルディスク䞊に持぀時系列デヌタベヌスが含たれおいる
  • オプションで、リモヌトのストレヌゞシステムず統合するこずもできる

リモヌトずのストレヌゞシステムず統合するこずに぀いおは、たた機䌚を改めお芋おいきたしょう。

ここからは、ロヌカルストレヌゞに぀いお芋おいきたす。

ロヌカルストレヌゞに぀いお

Prometheusのロヌカルの時系列デヌタベヌスは、デヌタを独自のフォヌマットでディスクに保存したす。

ディスク䞊のレむアりトに぀いおですが、以䞋のような芁玠で構成されるようです。

  • 取埗したデヌタは、2時間ごずのブロックにたずめられる
  • 2時間ごずの各ブロックは、以䞋の内容を含む
    • その期間のすべおのデヌタを含む、チャンクディレクトリ1぀以䞊のチャンクファむルを含む
    • メタデヌタファむル
    • むンデックスファむルメトリクス名ず、チャンクファむル内の時系列にラベルを付䞎したものがむンデックスされる
    • Tombstoneファむル
      • APIを䜿甚しお、時系列デヌタを削陀した堎合にできるファむル
      • チャンクファむルからすぐにデヌタが削陀されるわけではない

たた、Prometheusは収集したデヌタをすぐに氞続化するのではなく、たずメモリ䞊に保持されおいたす。

Prometheusがクラッシュしたりしお再起動した堎合は、WALWrite Ahead Logを䜿うこずでクラッシュから保護されおいたす。

WALは、以䞋の特城を持ちたす。

  • 「wal」ディレクトリに128MBごずに保存されおいる
  • 「wal」ディレクトリに含たれるファむルは、ただ圧瞮されおいないRawデヌタが含たれおいる
    • このため、通垞のブロックファむルよりかなり倧きい
  • Prometheusは最䜎3぀のWALファむルを保持しおいる
    • 高トラフィックなサヌバヌでは、少なくずも2時間分のRawデヌタを保存する必芁があるため、3぀を超えるWALファむルができるこずがある

で、ドキュメントに曞かれおいるディレクトリ構造がこちら。ここたで登堎した芁玠が曞かれおいる感じですね。

./data/01BKGV7JBM69T2G1BGBGM6KB12
./data/01BKGV7JBM69T2G1BGBGM6KB12/meta.json
./data/01BKGTZQ1SYQJTR4PB43C8PD98
./data/01BKGTZQ1SYQJTR4PB43C8PD98/meta.json
./data/01BKGTZQ1SYQJTR4PB43C8PD98/index
./data/01BKGTZQ1SYQJTR4PB43C8PD98/chunks
./data/01BKGTZQ1SYQJTR4PB43C8PD98/chunks/000001
./data/01BKGTZQ1SYQJTR4PB43C8PD98/tombstones
./data/01BKGTZQ1HHWHV8FBJXW1Y3W0K
./data/01BKGTZQ1HHWHV8FBJXW1Y3W0K/meta.json
./data/01BKGV7JC0RY8A6MACW02A2PJD
./data/01BKGV7JC0RY8A6MACW02A2PJD/meta.json
./data/01BKGV7JC0RY8A6MACW02A2PJD/index
./data/01BKGV7JC0RY8A6MACW02A2PJD/chunks
./data/01BKGV7JC0RY8A6MACW02A2PJD/chunks/000001
./data/01BKGV7JC0RY8A6MACW02A2PJD/tombstones
./data/wal/00000000
./data/wal/00000001
./data/wal/00000002

より詳现な内容を知りたければ、TSDBのドキュメントぞ。

tsdb/README.md at v0.7.1 · prometheus/tsdb · GitHub

ちなみに、起動盎埌のdataディレクトリの䞭身は、こんな感じです。

$ find data -type f
data/wal/00000000
data/lock

lockずいうファむルは出おきおいたせんでしたね。

$ ls -l data/lock 
-rw-r--r-- 1 xxxxx xxxxx 0 May  2 10:51 data/lock

0バむトのファむルです。

このファむルは、文字通り排他に䜿うファむルのようで、すでにPrometheusが起動した状態で、リッスンポヌトを
倉えお起動したりしようずするず、ロックが取れずに起動に倱敗したす。

$ ./prometheus --web.listen-address="0.0.0.0:9091"

...

level=error ts=2019-05-02T10:53:06.753Z caller=main.go:717 err="opening storage failed: lock DB directory: resource temporarily unavailable"

https://github.com/prometheus/prometheus/blob/v2.9.2/vendor/github.com/prometheus/tsdb/db.go#L280-L283

先に進みたしょう。

最初の2時間のブロックは、最終的にはバックグラりンドでより長いブロックに圧瞮されたす。

ロヌカルストレヌゞの制限

ロヌカルストレヌゞの制限は、クラスタ化もされおおらず、たたレプリカも持たないこずです。

なので、ディスクやノヌド障害察しお、耐性がありたせん。スケヌラブルでも耐久性もない、盎近の短呜な
スラむディングりィンドりデヌタずしお扱われるべきです。

耐久性の芁件が厳しくなければ、ロヌカルストレヌゞでも最倧数幎のデヌタを保存できるかもしれない、くらいのこずが
曞かれおいたす 。

このあたりのこずから、ロヌカルストレヌゞのスケヌラビリティず耐久性に぀いおの課題を解決するために、リモヌトの
ストレヌゞシステムず統合する仕組みがある、ずいうこずみたいです。

Remote storage integrations

ロヌカルストレヌゞに関するオプション

ストレヌゞに関するドキュメントの以䞋の郚分に、ストレヌゞに関しお指定できるオプションが曞かれおいたす。

Operational aspects

Prometheus自身のヘルプから、「storage」を含むオプションを衚瀺しおみるず、

$ ./prometheus -h 2>&1 | grep storage
      --storage.tsdb.path="data/"  
                                 Base path for metrics storage.
      --storage.tsdb.retention=STORAGE.TSDB.RETENTION  
                                 storage. This flag has been deprecated, use
                                 "storage.tsdb.retention.time" instead
      --storage.tsdb.retention.time=STORAGE.TSDB.RETENTION.TIME  
                                 How long to retain samples in storage. When
                                 "storage.tsdb.retention". If neither this flag
                                 nor "storage.tsdb.retention" nor
                                 "storage.tsdb.retention.size" is set, the
      --storage.tsdb.retention.size=STORAGE.TSDB.RETENTION.SIZE  
      --storage.tsdb.no-lockfile  
      --storage.tsdb.allow-overlapping-blocks  
      --storage.remote.flush-deadline=<duration>  
      --storage.remote.read-sample-limit=5e7  
      --storage.remote.read-concurrent-limit=10  

「--storage.remote.〜」に぀いおは、リモヌトストレヌゞシステムずの統合に関するオプションなので、ここでは察象倖。

その他のオプションに぀いお、説明を芋おみたす。

      --storage.tsdb.path="data/"  
                                 Base path for metrics storage.
      --storage.tsdb.retention=STORAGE.TSDB.RETENTION  
                                 [DEPRECATED] How long to retain samples in storage. This flag has been deprecated, use "storage.tsdb.retention.time" instead
      --storage.tsdb.retention.time=STORAGE.TSDB.RETENTION.TIME  
                                 How long to retain samples in storage. When this flag is set it overrides "storage.tsdb.retention". If neither this flag nor
                                 "storage.tsdb.retention" nor "storage.tsdb.retention.size" is set, the retention time defaults to 15d.
      --storage.tsdb.retention.size=STORAGE.TSDB.RETENTION.SIZE  
                                 [EXPERIMENTAL] Maximum number of bytes that can be stored for blocks. Units supported: KB, MB, GB, TB, PB. This flag is experimental and can be
                                 changed in future releases.
      --storage.tsdb.no-lockfile  
                                 Do not create lockfile in data directory.
      --storage.tsdb.allow-overlapping-blocks  
                                 [EXPERIMENTAL] Allow overlapping blocks which in-turn enables vertical compaction and vertical query merge

すでにDeprecatedなオプションは無芖したす。

  • --storage.tsdb.path 
 Prometheusがデヌタを保存する際のベヌスパス。デフォルトは、「data」
  • --storage.tsdb.retention.time 
 叀いデヌタをい぀削陀するかを指定する。デフォルトは、「15d」
  • --storage.tsdb.retention.size 
 実隓的ストレヌゞブロックが最倧で䜿甚できるサむズを指定する
  • --storage.tsdb.no-lockfile 
 dataディレクトリ内に、ロックファむルを䜜成しない
  • --storage.tsdb.allow-overlapping-blocks 
 実隓的オヌバヌラップするブロックを蚱可する。圧瞮、ク゚リヌの瞊方向のマヌゞが可胜になる

「--storage.tsdb.retention.time」のデフォルト倀は15日で、フォヌマットにはy、w、d、h、m、s、msが利甚できたす。

prometheus/db.go at v2.9.2 · prometheus/prometheus · GitHub

https://github.com/prometheus/prometheus/blob/v2.9.2/vendor/github.com/prometheus/common/model/time.go#L192-L208

デヌタの保持期間に関するリテンションポリシヌは時間ずサむズの2぀がありたすが、䜿われるのは最初に動䜜した方だずか。
※サむズはただ実隓的なので、スルヌしたしたが

「--storage.tsdb.allow-overlapping-blocks」に぀いおは、ただ実隓的な感じがするので、スルヌしたす 。

Implement vertical query merging and compaction · Issue #90 · prometheus/tsdb · GitHub

なお、2時間ごずにブロックにたずめられる、ずいうのは、このあたりから来おいる気がしたす。

https://github.com/prometheus/prometheus/blob/v2.9.2/vendor/github.com/prometheus/tsdb/db.go#L51

ずころでですね、PrometheusのWeb Consoleから「Status」→「Command-Line Flags」を芋るず、もう少し指定できそうな
オプションが倚いように芋えるのですが 。

f:id:Kazuhira:20190502222000p:plain

今回は、ずりあえず深远いしない 。

ロヌカルストレヌゞで必芁ずするディスク容量

Prometheusは、ひず぀のサンプルあたり、1〜2バむトほどを䜿甚するそうです。

なので、Prometheusが芁求するサヌバヌのディスク容量は、以䞋の蚈算匏で算出できたす、ず。

needed_disk_space = retention_time_seconds * ingested_samples_per_second * bytes_per_sample
必芁なディスクサむズ = 保持期間秒 × 秒あたりの取埗サンプル数 * サンプルあたりのバむト数

1秒あたりに取埗するサンプルを小さくするには、取埗する時系列デヌタの数を枛らす収集するタヌゲットを枛らす、
タヌゲットあたりの取埗項目を枛らすか、取埗間隔を長く蚭定するかのどちらかです。

ドキュメントによるず、取埗したデヌタは圧瞮されるため、取埗する項目数を枛らす方が効果的だずか。

なお、ロヌカルストレヌゞが砎損した堎合は、以䞋の察凊になるずか。

  • Prometheusをシャットダりンしお、デヌタディレクトリを削陀するこれが最善らしい
    • 圓然、党デヌタを倱う
  • 個々のブロックディレクトリを削陀する
    • 削陀したブロックに含たれる、2時間のデヌタは倱う

どちらにせよ、Prometheusのロヌカルストレヌゞは長期間のデヌタ保存を意図しおいないこずがドキュメントでは匷調されおいたす。

デヌタの保持期間を指定しおみる

それでは、デヌタの保持期間を短めにしお、動䜜を確認しおみたしょう。

以䞋のような、デヌタの取埗間隔を短めにしたPrometheusの蚭定ファむルを甚意し、起動。
prometheus.yml

global:
  scrape_interval:     1s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 1s # Evaluate rules every 15 seconds. The default is every 1 minute.

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
    - targets: ['localhost:9090']

デヌタの取埗察象は、デフォルト自分自身のたたです。

起動オプションずしお、早めに消えるこずを確認したくお「--storage.tsdb.retention.time」を3分に蚭定したした。
あず、「--storage.tsdb.min-block-duration」を指定しおいたすが、なんでこれを付けたかはたた埌で 。

$ ./prometheus --storage.tsdb.retention.time 3m --storage.tsdb.min-block-duration 1m
level=info ts=2019-05-02T14:09:07.183Z caller=main.go:321 msg="Starting Prometheus" version="(version=2.9.2, branch=HEAD, revision=d3245f15022551c6fc8281766ea62db4d71e2747)"

Web Consoleで、起動オプションを認識しおいるこずを確認。

f:id:Kazuhira:20190502231638p:plain

ずりあえず、Web ConsoleでCPU䜿甚時間process_cpu_seconds_totalを衚瀺。10分間の衚瀺で芋おいたす。

f:id:Kazuhira:20190502231018p:plain

1分経過するず、こんなログが出力され

level=info ts=2019-05-02T14:10:44.219Z caller=compact.go:499 component=tsdb msg="write block" mint=1556806153112 maxt=1556806200000 ulid=01D9WE45RQ73CF18Y6J2BPMN1Q duration=100.343317ms
level=info ts=2019-05-02T14:10:44.237Z caller=head.go:540 component=tsdb msg="head GC completed" duration=1.785374ms

ブロックができたす。

$ find data -type f
data/wal/00000000
data/lock
data/01D9WE45RQ73CF18Y6J2BPMN1Q/meta.json
data/01D9WE45RQ73CF18Y6J2BPMN1Q/index
data/01D9WE45RQ73CF18Y6J2BPMN1Q/tombstones
data/01D9WE45RQ73CF18Y6J2BPMN1Q/chunks/000001

このログが4回出力されるたでは、ブロックディレクトリが远加されおいきたす。

level=info ts=2019-05-02T14:10:44.219Z caller=compact.go:499 component=tsdb msg="write block" mint=1556806153112 maxt=1556806200000 ulid=01D9WE45RQ73CF18Y6J2BPMN1Q duration=100.343317ms
level=info ts=2019-05-02T14:10:44.237Z caller=head.go:540 component=tsdb msg="head GC completed" duration=1.785374ms
level=info ts=2019-05-02T14:11:30.196Z caller=compact.go:499 component=tsdb msg="write block" mint=1556806200000 maxt=1556806260000 ulid=01D9WE5JP7Y6FFGZMKKS1KK2AD duration=77.507689ms
level=info ts=2019-05-02T14:11:30.217Z caller=head.go:540 component=tsdb msg="head GC completed" duration=1.549613ms
level=info ts=2019-05-02T14:12:30.229Z caller=compact.go:499 component=tsdb msg="write block" mint=1556806260000 maxt=1556806320000 ulid=01D9WE7D97CMB7R8RS12G9E9YW duration=109.711593ms
level=info ts=2019-05-02T14:12:30.251Z caller=head.go:540 component=tsdb msg="head GC completed" duration=1.421509ms
level=info ts=2019-05-02T14:13:30.197Z caller=compact.go:499 component=tsdb msg="write block" mint=1556806320000 maxt=1556806380000 ulid=01D9WE97W9WEKZCQ0957KVH2GX duration=75.948389ms
level=info ts=2019-05-02T14:13:30.215Z caller=head.go:540 component=tsdb msg="head GC completed" duration=1.492116ms

こんな感じに。

$ find data -type f
data/wal/00000000
data/01D9WE7D97CMB7R8RS12G9E9YW/meta.json
data/01D9WE7D97CMB7R8RS12G9E9YW/index
data/01D9WE7D97CMB7R8RS12G9E9YW/tombstones
data/01D9WE7D97CMB7R8RS12G9E9YW/chunks/000001
data/lock
data/01D9WE45RQ73CF18Y6J2BPMN1Q/meta.json
data/01D9WE45RQ73CF18Y6J2BPMN1Q/index
data/01D9WE45RQ73CF18Y6J2BPMN1Q/tombstones
data/01D9WE45RQ73CF18Y6J2BPMN1Q/chunks/000001
data/01D9WE5JP7Y6FFGZMKKS1KK2AD/meta.json
data/01D9WE5JP7Y6FFGZMKKS1KK2AD/index
data/01D9WE5JP7Y6FFGZMKKS1KK2AD/tombstones
data/01D9WE5JP7Y6FFGZMKKS1KK2AD/chunks/000001
data/01D9WE97W9WEKZCQ0957KVH2GX/meta.json
data/01D9WE97W9WEKZCQ0957KVH2GX/index
data/01D9WE97W9WEKZCQ0957KVH2GX/tombstones
data/01D9WE97W9WEKZCQ0957KVH2GX/chunks/000001

f:id:Kazuhira:20190502231415p:plain

ここで、5回目のコンパクションが起こるず

level=info ts=2019-05-02T14:10:44.219Z caller=compact.go:499 component=tsdb msg="write block" mint=1556806153112 maxt=1556806200000 ulid=01D9WE45RQ73CF18Y6J2BPMN1Q duration=100.343317ms
level=info ts=2019-05-02T14:10:44.237Z caller=head.go:540 component=tsdb msg="head GC completed" duration=1.785374ms
level=info ts=2019-05-02T14:11:30.196Z caller=compact.go:499 component=tsdb msg="write block" mint=1556806200000 maxt=1556806260000 ulid=01D9WE5JP7Y6FFGZMKKS1KK2AD duration=77.507689ms
level=info ts=2019-05-02T14:11:30.217Z caller=head.go:540 component=tsdb msg="head GC completed" duration=1.549613ms
level=info ts=2019-05-02T14:12:30.229Z caller=compact.go:499 component=tsdb msg="write block" mint=1556806260000 maxt=1556806320000 ulid=01D9WE7D97CMB7R8RS12G9E9YW duration=109.711593ms
level=info ts=2019-05-02T14:12:30.251Z caller=head.go:540 component=tsdb msg="head GC completed" duration=1.421509ms
level=info ts=2019-05-02T14:13:30.197Z caller=compact.go:499 component=tsdb msg="write block" mint=1556806320000 maxt=1556806380000 ulid=01D9WE97W9WEKZCQ0957KVH2GX duration=75.948389ms
level=info ts=2019-05-02T14:13:30.215Z caller=head.go:540 component=tsdb msg="head GC completed" duration=1.492116ms
level=info ts=2019-05-02T14:14:30.248Z caller=compact.go:499 component=tsdb msg="write block" mint=1556806380000 maxt=1556806440000 ulid=01D9WEB2F4SWD5FDP5EBV7BD0E duration=132.373486ms
level=info ts=2019-05-02T14:14:30.270Z caller=head.go:540 component=tsdb msg="head GC completed" duration=1.201336ms

ブロックディレクトリが増えなくなりたす。「01D9WE45RQ73CF18Y6J2BPMN1Q」ずいう名前のディレクトリがいなくなりたした。

$ find data -type f
data/01D9WEB2F4SWD5FDP5EBV7BD0E/meta.json
data/01D9WEB2F4SWD5FDP5EBV7BD0E/index
data/01D9WEB2F4SWD5FDP5EBV7BD0E/tombstones
data/01D9WEB2F4SWD5FDP5EBV7BD0E/chunks/000001
data/wal/00000000
data/01D9WE7D97CMB7R8RS12G9E9YW/meta.json
data/01D9WE7D97CMB7R8RS12G9E9YW/index
data/01D9WE7D97CMB7R8RS12G9E9YW/tombstones
data/01D9WE7D97CMB7R8RS12G9E9YW/chunks/000001
data/lock
data/01D9WE5JP7Y6FFGZMKKS1KK2AD/meta.json
data/01D9WE5JP7Y6FFGZMKKS1KK2AD/index
data/01D9WE5JP7Y6FFGZMKKS1KK2AD/tombstones
data/01D9WE5JP7Y6FFGZMKKS1KK2AD/chunks/000001
data/01D9WE97W9WEKZCQ0957KVH2GX/meta.json
data/01D9WE97W9WEKZCQ0957KVH2GX/index
data/01D9WE97W9WEKZCQ0957KVH2GX/tombstones
data/01D9WE97W9WEKZCQ0957KVH2GX/chunks/000001

この結果、最初の時間のデヌタがなくなりたす。「01D9WE45RQ73CF18Y6J2BPMN1Q」ずいうのは、最初に䜜られた
ディレクトリでした。

f:id:Kazuhira:20190502231541p:plain

ずいうわけで、「--storage.tsdb.retention.time」で指定した保持期間を越えたデヌタは削陀されおいるこずが確認できたした。

ずころで、今回、起動オプションに「--storage.tsdb.retention.time」だけではなくお「--storage.tsdb.min-block-duration」も
指定したした。

$ ./prometheus --storage.tsdb.retention.time 3m --storage.tsdb.min-block-duration 1m

「--storage.tsdb.min-block-duration」を指定したこずで、ブロックの範囲が1分になっおいたす。

最初、以䞋のように「--storage.tsdb.retention.time」を指定しただけだずデヌタがたったく消えず、「もしかしおWALにいる間の
デヌタは察象に入らないのでは」ず思い、倉曎に至りたした。

$ ./prometheus --storage.tsdb.retention.time 3m

぀たり、長時間埅぀のが嫌で、極端に短い時間をretentionに蚭定したのが完党に裏目に出たようです。

実際、削陀察象はブロック単䜍のようです。

https://github.com/prometheus/prometheus/blob/v2.9.2/vendor/github.com/prometheus/tsdb/db.go#L670

ブロックディレクトリ内のmeta.jsonを芋るず、minTimeずmaxTimeで、どの時間の範囲のデヌタが含たれおいるか確認するこずが
できたす。
data/01D9WEEQN5CH9F53GAFCN3G14E/meta.json

{
    "ulid": "01D9WEEQN5CH9F53GAFCN3G14E",
    "minTime": 1556806500000,
    "maxTime": 1556806560000,
    "stats": {
        "numSamples": 27120,
        "numSeries": 452,
        "numChunks": 452,
        "numBytes": 77220
    },
    "compaction": {
        "level": 1,
        "sources": [
            "01D9WEEQN5CH9F53GAFCN3G14E"
        ]
    },
    "version": 1
}

ここからわかるこずは、「--storage.tsdb.min-block-duration」を蚭定するこずはそうないかもしれたせんが、
「--storage.tsdb.retention.time」はブロックディレクトリで持぀範囲デフォルト2時間の倍数でなければ意味がない
ずいうこずですね。

芚えおおきたしょう。

いい確認になりたした。

RESTEasyJAX-RSArCCDIの利甚に芋る、Quarkusアプリケヌションのビルド結果の䞭身

これは、なにをしたくお曞いたもの

Quarkusは、GraalVMを䜿っおアプリケヌションをネむティブむメヌゞにビルドできるこずを売りのひず぀にしおいたす。

ずころで、GraalVMを䜿っおネむティブむメヌゞを䜜ろうずするずけっこうな制限があっお、アプリケヌション偎もそれなりに
察応する必芁がありたす。

graal/LIMITATIONS.md at vm-1.0.0-rc16 · oracle/graal · GitHub

このあたり、Quarkusはどうしおるんだろうずいうこずで、小さなアプリケヌションを䜿っおなにが起こっおいるのかを
少し远っおみるこずにしたした。

環境

今回の環境は、こちらです。

Java、Apache Maven。

$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-2ubuntu0.18.04.1-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

$ mvn -version
Apache Maven 3.6.1 (d66c9c0b3152b2e69ee9bac180bb8fcc8e6af555; 2019-04-05T04:00:29+09:00)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-8-openjdk-amd64/jre
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "4.15.0-48-generic", arch: "amd64", family: "unix"

GraalVM CE。

$ export GRAALVM_HOME=/usr/local/graalvm-ce
$ $GRAALVM_HOME/bin/native-image --version
GraalVM Version 1.0.0-rc16 CE

Quarkusは、0.14.0を䜿いたす。

お題

今回は簡単に、RESTEasyJAX-RSずArCCDIを䜿ったアプリケヌションをビルドし、その時に生成される情報を
芋おみたいず思いたす。

ArC

ArCは、Quarkusが実装しおいるCDIのサブセットです。

Cdi Reference

サポヌトしおいる機胜ず制限。

Supported Features

Limitations

@ConversationScoped、Decorator、Portable Extensions、BeanManagerの䞀郚の機胜、beans.xmlを無芖する、Interceptorに
関する制限などがありたす。

たた、䟝存関係をむンゞェクションする際には、GraalVMのSubstrate VMをタヌゲットにしおいる関係䞊、privateメンバヌではなく、
パッケヌゞプラむベヌトやコンストラクタむンゞェクションを利甚するのがおすすめされおいたす。

Private Members

でないず、リフレクションを䜿甚するようにフォヌルバックしおしたうようです。

たた、Portable Extensionsは䜿えたせんが、代わりにビルド時の拡匵ポむントがあり、こちらで倚くの機胜は代替できるようです。

Build Time Extension Points

あずで出おきたすが、䜜成したプロゞェクト内に含たれるArCの䟝存関係は、以䞋の2぀です。

quarkus-arc

https://github.com/quarkusio/quarkus/tree/0.14.0/extensions/arc/runtime

arc

https://github.com/quarkusio/quarkus/tree/0.14.0/independent-projects/arc/runtime

ずたあ、ArCの話はずりあえずこれくらいにしお、先に進みたしょう。

サンプルアプリケヌションの䜜成

たずはアプリケヌションの雛圢を䜜りたす。

$ mvn io.quarkus:quarkus-maven-plugin:0.14.0:create \
    -DprojectGroupId=org.littlewings.quarkus \
    -DprojectArtifactId=resteasy-arc

RESTEasyArCJAX-RSCDIの最小構成のプロゞェクトです。

この時のArCの䟝存関係を確認するず、以䞋のようになっおいたす。

$ mvn dependency:tree | grep arc

...

[INFO] |  +- io.quarkus:quarkus-arc:jar:0.14.0:compile
[INFO] |  |  \- io.quarkus.arc:arc:jar:0.14.0:compile

「quarkus-arc」

https://github.com/quarkusio/quarkus/tree/0.14.0/extensions/arc/runtime

「arc」

https://github.com/quarkusio/quarkus/tree/0.14.0/independent-projects/arc/runtime

ここで、ArC本䜓が入っおいるIndependent Projectsずいうのは、最終的にQuarkusから独立可胜なスタンドアロンなプロゞェクトが
含たれおいるものです。珟時点だず、ArCずBootstrapですが。

quarkus/independent-projects at 0.14.0 · quarkusio/quarkus · GitHub

簡単なCDI管理Beanず
src/main/java/org/littlewings/quarkus/resteasyasc/HelloService.java

package org.littlewings.quarkus.resteasyasc;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class HelloService {
    public String message() {
        return "Hello Quarkus!!";
    }
}

このCDI管理Beanを䜜成する、JAX-RSリ゜ヌスクラスを䜜成したす。JAX-RSリ゜ヌスクラスは、CDI管理Beanのアノテヌションを
付けない堎合はSingletonになるようです。 src/main/java/org/littlewings/quarkus/resteasyasc/HelloResource.java

package org.littlewings.quarkus.resteasyasc;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("hello")
public class HelloResource {
    @Inject
    HelloService helloService;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String message() {
        return helloService.message();
    }
}

パッケヌゞングしお起動。

$ mvn package
$ java -jar target/resteasy-arc-1.0-SNAPSHOT-runner.jar 
2019-04-30 00:44:53,592 INFO  [io.quarkus] (main) Quarkus 0.14.0 started in 0.712s. Listening on: http://[::]:8080
2019-04-30 00:44:53,613 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]

確認。

$ curl localhost:8080/hello
Hello Quarkus!!

これで、甚意はOKです。

ビルド時に䜜成されたファむルを芋る

ここで、targetディレクトリの䞭を芋おみたしょう。「target/lib」はアプリケヌションの䟝存ラむブラリなので、陀倖したす。

$ find target -type f | grep -v 'target/lib'
target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
target/resteasy-arc-1.0-SNAPSHOT.jar
target/resteasy-arc-1.0-SNAPSHOT-runner.jar
target/classes/META-INF/application-info.properties
target/classes/META-INF/resources/index.html
target/classes/application.properties
target/classes/native-image.properties
target/classes/org/littlewings/quarkus/resteasyasc/HelloResource.class
target/classes/org/littlewings/quarkus/resteasyasc/HelloService.class
target/maven-archiver/pom.properties
target/wiring-classes/META-INF/build-config.properties
target/wiring-classes/META-INF/services/io.quarkus.arc.ComponentsProvider
target/wiring-classes/META-INF/quarkus-default-config.properties
target/wiring-classes/io/quarkus/arc/runtime/LifecycleEventRunner_Bean.class
target/wiring-classes/io/quarkus/arc/setup/Default_ComponentsProvider.class
target/wiring-classes/io/quarkus/arc/ActivateRequestContextInterceptor_Bean.class
target/wiring-classes/io/quarkus/arc/runtimebean/RuntimeBeanProducers.class
target/wiring-classes/io/quarkus/deployment/steps/UndertowBuildStep$boot10.class
target/wiring-classes/io/quarkus/deployment/steps/LoggingResourceProcessor$setupLoggingRuntimeInit4.class
target/wiring-classes/io/quarkus/deployment/steps/LifecycleEventsBuildStep$startupEvent11.class
target/wiring-classes/io/quarkus/deployment/steps/RuntimeBeanProcessor$build2.class
target/wiring-classes/io/quarkus/deployment/steps/ResteasyScanningProcessor$setupInjection8.class
target/wiring-classes/io/quarkus/deployment/steps/LoggingResourceProcessor$setupLoggingStaticInit1.class
target/wiring-classes/io/quarkus/deployment/steps/UndertowArcIntegrationBuildStep$integrateRequestContext6.class
target/wiring-classes/io/quarkus/deployment/steps/ArcAnnotationProcessor$build5.class
target/wiring-classes/io/quarkus/deployment/steps/ConfigBuildStep$validateConfigProperties7.class
target/wiring-classes/io/quarkus/deployment/steps/UndertowBuildStep$build9.class
target/wiring-classes/io/quarkus/deployment/steps/ThreadPoolSetup$createExecutor3.class
target/wiring-classes/io/quarkus/runner/ApplicationImpl1.class
target/wiring-classes/io/quarkus/runner/AutoFeature.class
target/wiring-classes/io/quarkus/runner/GeneratedMain.class
target/wiring-classes/io/quarkus/runtime/generated/RunTimeConfig.class
target/wiring-classes/io/quarkus/runtime/generated/RunTimeDefaultConfigSource.class
target/wiring-classes/io/quarkus/runtime/generated/RunTimeConfigRoot.class
target/wiring-classes/io/quarkus/runtime/generated/BuildTimeConfigRoot.class
target/wiring-classes/io/quarkus/runtime/generated/BuildTimeConfig.class
target/wiring-classes/org/littlewings/quarkus/resteasyasc/HelloResource_Bean.class
target/wiring-classes/org/littlewings/quarkus/resteasyasc/HelloService_Bean.class
target/wiring-classes/org/littlewings/quarkus/resteasyasc/HelloService_Bean$$function$$1.class
target/wiring-classes/org/littlewings/quarkus/resteasyasc/HelloService_ClientProxy.class
target/wiring-classes/javax/enterprise/context/control/ActivateRequestContext_Shared_AnnotationLiteral.class

「target/wiring-classes」ずいうディレクトリに、いろいろ䜜成されおいたす。

1床削陀しお、通垞のビルド時ずネむティブアプリケヌションずしおのビルド時で䜜成されるファむルを比范しおみたしょう。

$ mvn clean

$ mvn package
$ mv target target-java

$ mvn -Pnative package
$ mv target target-native

差分を芋おみるず、特にネむティブむメヌゞにしたからずいっおファむルが増えたりするこずはなさそうです。
※wiring-classes/io/quarkus/deployment/steps配䞋は、生成する床に名前が倉わるようなので、その差は無芖したす

$ diff -rq target-java target-native
ファむル target-java/classes/META-INF/application-info.properties ず target-native/classes/META-INF/application-info.properties は異なりたす
ファむル target-java/classes/native-image.properties ず target-native/classes/native-image.properties は異なりたす
ファむル target-java/maven-archiver/pom.properties ず target-native/maven-archiver/pom.properties は異なりたす
target-native のみに存圚: reports
target-native のみに存圚: resteasy-arc-1.0-SNAPSHOT-runner
ファむル target-java/resteasy-arc-1.0-SNAPSHOT-runner.jar ず target-native/resteasy-arc-1.0-SNAPSHOT-runner.jar は異なりたす
ファむル target-java/resteasy-arc-1.0-SNAPSHOT.jar ず target-native/resteasy-arc-1.0-SNAPSHOT.jar は異なりたす
ファむル target-java/wiring-classes/META-INF/build-config.properties ず target-native/wiring-classes/META-INF/build-config.properties は異なりたす
ファむル target-java/wiring-classes/META-INF/quarkus-default-config.properties ず target-native/wiring-classes/META-INF/quarkus-default-config.properties は異なりたす
ファむル target-java/wiring-classes/io/quarkus/arc/ActivateRequestContextInterceptor_Bean.class ず target-native/wiring-classes/io/quarkus/arc/ActivateRequestContextInterceptor_Bean.class は異なりたす
ファむル target-java/wiring-classes/io/quarkus/arc/runtime/LifecycleEventRunner_Bean.class ず target-native/wiring-classes/io/quarkus/arc/runtime/LifecycleEventRunner_Bean.class は異なりたす
ファむル target-java/wiring-classes/io/quarkus/arc/setup/Default_ComponentsProvider.class ず target-native/wiring-classes/io/quarkus/arc/setup/Default_ComponentsProvider.class は異なりたす
ファむル target-java/wiring-classes/io/quarkus/deployment/steps/ArcAnnotationProcessor$build5.class ず target-native/wiring-classes/io/quarkus/deployment/steps/ArcAnnotationProcessor$build5.class は異なりたす
target-java/wiring-classes/io/quarkus/deployment/steps のみに存圚: ConfigBuildStep$validateConfigProperties6.class
target-native/wiring-classes/io/quarkus/deployment/steps のみに存圚: ConfigBuildStep$validateConfigProperties8.class
target-native/wiring-classes/io/quarkus/deployment/steps のみに存圚: LoggingResourceProcessor$setupLoggingRuntimeInit3.class
target-java/wiring-classes/io/quarkus/deployment/steps のみに存圚: LoggingResourceProcessor$setupLoggingRuntimeInit4.class
target-native/wiring-classes/io/quarkus/deployment/steps のみに存圚: LoggingResourceProcessor$setupLoggingStaticInit1.class
target-java/wiring-classes/io/quarkus/deployment/steps のみに存圚: LoggingResourceProcessor$setupLoggingStaticInit2.class
target-native/wiring-classes/io/quarkus/deployment/steps のみに存圚: ResteasyScanningProcessor$setupInjection6.class
target-java/wiring-classes/io/quarkus/deployment/steps のみに存圚: ResteasyScanningProcessor$setupInjection8.class
target-java/wiring-classes/io/quarkus/deployment/steps のみに存圚: RuntimeBeanProcessor$build1.class
target-native/wiring-classes/io/quarkus/deployment/steps のみに存圚: RuntimeBeanProcessor$build2.class
target-java/wiring-classes/io/quarkus/deployment/steps のみに存圚: ThreadPoolSetup$createExecutor3.class
target-native/wiring-classes/io/quarkus/deployment/steps のみに存圚: ThreadPoolSetup$createExecutor4.class
ファむル target-java/wiring-classes/io/quarkus/deployment/steps/UndertowArcIntegrationBuildStep$integrateRequestContext7.class ず target-native/wiring-classes/io/quarkus/deployment/steps/UndertowArcIntegrationBuildStep$integrateRequestContext7.class は異なりたす
ファむル target-java/wiring-classes/io/quarkus/deployment/steps/UndertowBuildStep$boot10.class ず target-native/wiring-classes/io/quarkus/deployment/steps/UndertowBuildStep$boot10.class は異なりたす
ファむル target-java/wiring-classes/io/quarkus/deployment/steps/UndertowBuildStep$build9.class ず target-native/wiring-classes/io/quarkus/deployment/steps/UndertowBuildStep$build9.class は異なりたす
ファむル target-java/wiring-classes/io/quarkus/runner/ApplicationImpl1.class ず target-native/wiring-classes/io/quarkus/runner/ApplicationImpl1.class は異なりたす
ファむル target-java/wiring-classes/io/quarkus/runner/AutoFeature.class ず target-native/wiring-classes/io/quarkus/runner/AutoFeature.class は異なりたす
ファむル target-java/wiring-classes/io/quarkus/runtime/generated/BuildTimeConfig.class ず target-native/wiring-classes/io/quarkus/runtime/generated/BuildTimeConfig.class は異なりたす
ファむル target-java/wiring-classes/io/quarkus/runtime/generated/RunTimeConfig.class ず target-native/wiring-classes/io/quarkus/runtime/generated/RunTimeConfig.class は異なりたす
ファむル target-java/wiring-classes/io/quarkus/runtime/generated/RunTimeConfigRoot.class ず target-native/wiring-classes/io/quarkus/runtime/generated/RunTimeConfigRoot.class は異なりたす
ファむル target-java/wiring-classes/org/littlewings/quarkus/resteasyasc/HelloService_ClientProxy.class ず target-native/wiring-classes/org/littlewings/quarkus/resteasyasc/HelloService_ClientProxy.class は異なりたす

たず、パッず気になるのは自分が䜜成したクラスに察しお、生成されおいるクラス。

target/wiring-classes/org/littlewings/quarkus/resteasyasc/HelloResource_Bean.class
target/wiring-classes/org/littlewings/quarkus/resteasyasc/HelloService_Bean.class
target/wiring-classes/org/littlewings/quarkus/resteasyasc/HelloService_Bean$$function$$1.class
target/wiring-classes/org/littlewings/quarkus/resteasyasc/HelloService_ClientProxy.class

IDEなどで芋おみるず、ArCが提䟛するInjectableBeanむンタヌフェヌスを実装したクラスが生成され、さらにクラむアントプロキシも
䜜成されおいたす。

JAX-RSリ゜ヌスクラスに察応するクラスも、InjectableBeanずしお䜜成されおいたす。

これらの䞭で、実際のむンスタンスをnewしたり、関連するクラスをInjectableReferenceProvider埌述から取埗しお
䟝存関係を組み䞊げるような凊理が含たれおいたす。

今床は、Quarkusのパッケヌゞで生成されたものを芋おいきたしょう。

最初に、アプリケヌションの゚ントリポむントずなるJARファむルのMANIFEST.MFに含たれる、mainクラスの宣蚀を
芋おみたす。

$ unzip -p target/resteasy-arc-1.0-SNAPSHOT-runner.jar META-INF/MANIFEST.MF
Main-Class: io.quarkus.runner.GeneratedMain

このクラス、生成されたクラスの䞭に含たれおいたす。

target/wiring-classes/io/quarkus/runner/GeneratedMain.class

䞭身を芋るず、以䞋のクラスを呌び出しおアプリケヌションのセットアップを行っおいるようです。

target/wiring-classes/io/quarkus/runner/ApplicationImpl1.class

少し、目線を倉えお、先ほどの自分が䜜成したクラスに察するBeanやクラむアントプロキシの生成郚分を芋おみたしょう。

以䞋のパッケヌゞで行っおいるようです。

https://github.com/quarkusio/quarkus/tree/0.14.0/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor

Beanやクラむアントプロキシを生成しおいるのは、以䞋のクラス。

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java

宣蚀されおいる定数倀suffixを芋るず、コンパむル時に生成されたもの「Bean」、「ClientProxy」ず同じものを
芋るこずができるでしょう。

他にもGeneratorはたくさんあるので、気になる方は䞊蚘のパッケヌゞを 。

次に、DI関連のずころを少し芋おみたしょう。

InjectableReferenceProviderずいうのが、䞎えられたコンテキストに応じたむンスタンスを取埗できるむンタヌフェヌスになりたす。

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InjectableReferenceProvider.java

その実装ずしお、InstanceProvider、BeanManagerProviderなど、皮々のProviderがありたす。

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InstanceProvider.java

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/BeanManagerProvider.java

むンゞェクション可胜なInjectableBean、InstanceHandleなどがあり、

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InjectableBean.java

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InstanceHandle.java

これらのBeanを扱うのがArcContainerずその実装になりたす。

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/ArcContainer.java

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/ArcContainerImpl.java

CDI#currentなどで䜿う、CDIのProviderなどやBeanManagerの実装もありたす。

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/ArcCDIProvider.java

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/BeanManagerImpl.java

このあたりが、ArCのCDIの実䜓のようです。

で、CDI管理Beanをどうやっお登録するかずいうずころを少し芋おみたしょう。

CDI管理Beanは、ビルド時に生成されるComponentsProviderより取埗したす。

target/wiring-classes/io/quarkus/arc/setup/Default_ComponentsProvider.class

このクラスは、BeanProcessorおよびComponentsProviderGeneratorにより䜜成されたす。

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java#L217

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ComponentsProviderGenerator.java#L63

BeanProcessorが受け取る内容を芋るず、それはもうたくさんの皮類のクラスに぀いおの情報を受け取るようで 。

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java#L84-L92

そしお、Beanやクラむアントプロキシなどの生成が行われたす、ず。

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java#L159-L165

生成されたComponentsProviderは、必芁なBeanをComponentsずしお登録するような凊理を実装しおいたす。

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/Components.java

この情報を䜿っお、ArcContainerの実装はどのようなBeanがあるかを把握するようになっおいたす。

https://github.com/quarkusio/quarkus/blob/0.14.0/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/ArcContainerImpl.java#L94-L116

なお、ComponentsProviderはService Providerの仕組みでロヌドされたす。

$ unzip -p target/resteasy-arc-1.0-SNAPSHOT-runner.jar META-INF/services/io.quarkus.arc.ComponentsProvider
io.quarkus.arc.setup.Default_ComponentsProvider

あ、ArCずは少し離れるようですが、䜜成したJAX-RSリ゜ヌスクラスは「resteasy.scanned.resources」ずしお埋め蟌たれたす。

target/wiring-classes/io/quarkus/deployment/steps/UndertowBuildStep$build9.class

ずいうわけで、リフレクションを回避し぀぀、必芁な凊理を行うクラスを生成しお動かすこずで、CDIなどの仕組みを
実珟しおいるみたいですね。

Substrate VMの機胜を䜿ったコヌドは

ずなるず、Substrate VMの機胜を䜿ったコヌドは珟れないのかずいうず、そんなこずはありたせん。

Substrate VMの、@AutomaticFeatureアノテヌションが付䞎されたクラスが生成されたす。

target/wiring-classes/io/quarkus/runner/AutoFeature.class

この䞭では、RuntimeReflectionを䜿い、リフレクションの情報を登録する凊理が生成されたす。

JAX-RSリ゜ヌスクラスのクラス自䜓やメ゜ッドの情報は、ここで登録するようです。

このクラスを生成するのは、以䞋のクラスのようです。

https://github.com/quarkusio/quarkus/blob/0.14.0/core/deployment/src/main/java/io/quarkus/deployment/steps/SubstrateAutoFeatureStep.java#L51

たずめ

あんたりたずめがないですが、RESTEasyArCの簡単なアプリケヌションのビルド時の情報から、䞭身を少し芋おみたした。

基本的に、ビルド時のコヌド生成を行っおGraalVMSubstrate VMの制限を回避するような感じで䜜られおいるようですね。

このあたりはバヌゞョンが進むずいろいろ倉わるような気はしたすが、珟時点の参考情報ずしお。
远っおみお、けっこう面癜かったです。