Slide 1

Slide 1 text

Fargate上のJVMからCPUを認識するまで 〜正しく認識されないCPUの謎を追え〜 金川 祐太郎 株式会社ディー・エヌ・エー

Slide 2

Slide 2 text

2 自己紹介 金川 祐太郎 ● 所属: 株式会社ディー・エヌ・エー ● twitter: @orekyuu ● 最近の悩み: コロナ禍に入ってから太ってしまった 久しぶりに登壇で顔を出したらTwitterで「顔のアス比かわったね」

Slide 3

Slide 3 text

3 本日のゴール ● 今回の事例を例に、JVMがどのようにCPU数を認識しているかを知る 非ゴール: ECSでJavaアプリケーションを使うためのコツや知見 OpenJDKのコードを読むためのきっかけになれば幸いです

Slide 4

Slide 4 text

事例の紹介

Slide 5

Slide 5 text

5 環境の紹介 1 ● Quarkus: 2.9.2 ● JDK: amazon corretto 17 ● quarkus-container-image-jibでビルドしたimageをECS fargateへdeploy

Slide 6

Slide 6 text

6 GCのアルゴリズムを指定せず動かすとSerial GCになっている 2 ● 負荷試験でJFRの記録をとったところ、 SerialGCが使われていることが分かった ● ECS Taskのcpuの指定は2048になっていて、 G1 GCが使われるはず

Slide 7

Slide 7 text

7 目次 OpenJDKのGCに関する登場人物 OpenJDKが使うGC選択の流れ AvailableProcessorsの検出の流れ 1 2 3

Slide 8

Slide 8 text

8 OpenJDKのGCに関する登場人物

Slide 9

Slide 9 text

9 OpenJDKのディレクトリの構造 ● src/hotspot以下にosやcpuアーキテクチャ毎の 実装が書かれており、shareは共通の実装 ● share以下は機能ごとにディレクトリが 掘られている 1

Slide 10

Slide 10 text

10 登場人物 1 Arguments GCConfig GCArguments SerialArguments SerialArguments initialize GCの決定 GCアルゴリズムごとの引数 GCアルゴリズムごとにサブクラスを持つ CollectedHeap createHeap

Slide 11

Slide 11 text

11 登場人物 1 Arguments GCConfig GCArguments SerialArguments SerialArguments initialize GCの決定 GCアルゴリズムごとの引数 GCアルゴリズムごとにサブクラスを持つ CollectedHeap createHeap

Slide 12

Slide 12 text

12 OpenJDKが使うGC選択の流れ

Slide 13

Slide 13 text

13 GCアルゴリズムの決定 1

Slide 14

Slide 14 text

14 GCアルゴリズムの決定 1

Slide 15

Slide 15 text

15 GCアルゴリズムの決定 1 起動オプションでGCが 明示的に指定されていない

Slide 16

Slide 16 text

16 GCアルゴリズムの決定 1 サーバークラスマシンなら G1GC そうでないならSerialGCを選んでいる

Slide 17

Slide 17 text

17 GCアルゴリズムの決定 1 active_processor_countが2以上 physical_memoryが約2GB以上 右辺の変数は直前で宣言している

Slide 18

Slide 18 text

18 jcmd VM.infoしてみる 2 initial active 1 になっている?

Slide 19

Slide 19 text

AvailableProcessorsの検出の流れ

Slide 20

Slide 20 text

20 active_processor_countの取得 1 os::active_processor_countはosごとに 実装が異なる 今回のイメージでは linuxの実装になる

Slide 21

Slide 21 text

21 active_processor_countの取得 1 -XX:ActiveProcessorCountが 指定されている場合

Slide 22

Slide 22 text

22 active_processor_countの取得 1 コンテナのときはこちらの処理に入る

Slide 23

Slide 23 text

※ここから2022/08頃のコードです OpenJDKのmasterを見ていましたが、変更が入って処理の内容が変わっているためです

Slide 24

Slide 24 text

24 active_processor_countの取得 1 CgroupSubsystemとは?

Slide 25

Slide 25 text

25 寄り道: cgroups 1 ● linuxの機能で、プロセスの利用するリソースに制限をかけるもの ● dockerではcgroupsを活用しているらしく、コンテナ対応のコードでcgroups の名前が出てきた

Slide 26

Slide 26 text

26 active_processor_countの取得 1 どうやらDockerコンテナのサポート っぽい

Slide 27

Slide 27 text

27 active_processor_countの取得 1 share / 1024がshare_countになる

Slide 28

Slide 28 text

28 active_processor_countの取得 1 最終的にcpu_countとlimit_countの 小さい値がactive_processor_count

Slide 29

Slide 29 text

29 active_processor_countの取得 1 これらのログを確認したい

Slide 30

Slide 30 text

30 ログの確認 2 ● -Xlogオプションを使う ○ 今回の場合はjava -Xlog:os+container=trace -versionを ECSのコンテナ内で実行して検証

Slide 31

Slide 31 text

31 ログの確認 2 CPU Sharesが2になっている?

Slide 32

Slide 32 text

32 active_processor_countの取得 1 share / 1024がshare_countになる

Slide 33

Slide 33 text

33 原因を知る 3 ● 「詳解: Amazon ECS による CPU とメモリのリソース管理」という記事を読 む

Slide 34

Slide 34 text

34 原因を知る 3 コンテナにCPUユニットを設定しない場合、 2を設定します

Slide 35

Slide 35 text

35 原因を知る 3 ● ECSにはTask DefinitionとContainer Definitionの両方にCPUの制限をつけられ る ● JVMが見ているのはContainer DefinitionのCPU制限で、指定をしない場合は2 になる

Slide 36

Slide 36 text

36 まとめ 3 ● ECSにはTask DefinitionとContainer Definitionの両方にCPU制限があり、JVM が見るのはContainerの方なので必ず宣言しよう ● GCは明示的に指定しない場合、意図しないGCになる場合があるので明示的 に宣言しておくと安心 ● -Xlogオプションでログを出力できるので、デバッグする際には活用 してみよう ● JDKのコードを読むのは怖くない!みんな読もう!