CircleCIのOOM対策

技術ネタ

今日の記事はCircleCI Advent Calendar 2018の10日目の投稿になります🌟

Stripeもそうなんですが、CircleCIも導入したてであまり新しいネタは持っておりません(;´∀`)
OOMのネタは探すと結構記事がありますが、あえて自分がうまく行った方法を書いてきます❗

OOMとは

OOMとはOutOfMemoryのことです。
早い話がCircleCIを回す際に、メモリの利用上限をオーバーしましたってエラーです。

* What went wrong:
Execution failed for task ':test'.
> Process 'Gradle Test Executor 1' finished with non-zero exit value 137
  This problem might be caused by incorrect test process configuration.
  Please refer to the test execution section in the user guide at https://docs.gradle.org/4.8.1/userguide/java_plugin.html#sec:test_execution

おそらくテストケースが増えてくると出くわす方は多いのではないでしょうか❓🤔
僕も2回ほど出くわして、解決に結構時間使ってしまいました🤣

発生する要因

要因については、公式も発表してます。
あとこっちのSupportにも書かれてますね。

By default, projects on CircleCI build in virtual environments with 4GB of RAM. This RAM is shared among all processes running in your project: databases, tests, various tools/frameworks, as well as the ravenous Java Virtual Machine (JVM).


CircleCI起動時に立ち上がるコンテナにはデフォルトで4GBのRAM が与えられます。
こちらの記述を見ると、CircleCIで動くプロセス(DB、Test、ToolやFramework、JVM)を全部ひっくるめて4Gまでって感じ。
なので、自動テスト実行中に4G超えると落ちちゃうんですね。

解決策

超えてしまうのであれば、増やすか絞るしか無いのです。
まずは増やす方の解決策

resource_class 構成オプション

CircleCI 2.0からはresource_classというオプションが使えます。
大前提として、DockerExecuterの場合に限ります。
こちらのSupportを見るとdefaultではmediumになっていて、vCPUsが2、RAMが4GBということなので、有料課金で最大vCPUsが8、RAMが16GBまで増やせます。

ただし、こちらから申請制になっているためOOMが起きたからすぐ増築ということは出来ません。
というわけで、だいたいOOM発生の際の対策を調べると絞る対策が出てきます。

まぁ、増やすのは一般的な対策ではないって事です/(^o^)\

 

environmentでメモリ上限指定

というわけで、一般的なやり方の絞る方法です。
こちらもDockerExecuterの場合に限ります。

ここから先は体験談になりますので、OOMでテストが落ちたあとに調べた内容です❗
ビルドはGradleを利用していたので、Gradleベースで話を進めます。

優先順位表

この表を見ると優先順位0と、_JAVA_OPTIONSがダントツTOPなのでこんな感じで絞ります。

docker:
  - image: circleci/openjdk:8u171-jdk-node
    environment:
      _JAVA_OPTIONS: -Xm3G
      TZ: Asia/Tokyo

絞ったところで再度テストを流してみました。

> Task :test
Picked up _JAVA_OPTIONS: -Xmx3G

> Task :test FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':test'.
> Process 'Gradle Test Executor 1' finished with non-zero exit value 137
  This problem might be caused by incorrect test process configuration.
  Please refer to the test execution section in the user guide at https://docs.gradle.org/4.8.1/userguide/java_plugin.html#sec:test_execution


あれ、落ちた・・・・・

というわけで、ここをもう一回よく見直す。

_JAVA_OPTIONS 
This is the most powerful Java environment variable. It’s read directly by the JVM and overwrites any other Java environment variables, as well as any arguments you pass on the command-line (i.e., java -Xmx512m -Xms64m). For this reason, _JAVA_OPTIONS_ isn’t typically recommended — a more focused approach usually gets the job done just as well.


It’s also worth noting that _JAVA_OPTIONS is Oracle-specific, so it won’t work with everything. For example, you’d need to use IBM_JAVA_OPTIONS for IBM’s Java tools.

引用:How to Handle Java OOM Errors

_JAVA_OPTIONS_ isn’t typically recommended — a more focused approach usually gets the job done just as well.

そもそも非推奨だった(;´∀`)

It’s also worth noting that _JAVA_OPTIONS is Oracle-specific


そもそもOracle様専用だった/(^o^)\

今回使用しているイメージはCircleCI提供のOpenJDKなのでそれで落ちたのかも。
無難に_JAVA_OPTIONSは避けて、次に優先順位高いGRADLE_OPTSに変更

docker:
  - image: circleci/openjdk:8u171-jdk-node
    environment:
      GRADLE_OPTS: -Xm3G
      TZ: Asia/Tokyo

これで結果落ちなくなりました!!

他にもbuild.gradleに直でメモリ上限を書く方法とかあるみたいですが、CircleCIのことはCircleCIの設定で片付けたいのでこの設定を採用です。

もう少し細かく

今回は設定でGRADLE_OPTS: -Xm3Gとしましたが、実際はもう少し細かく設定します。
以前Twitterに投げたら、CircleCI Japanの方からお返事を頂いてました。


最終的にはこんな感じになりました❗

docker:
  - image: circleci/openjdk:8u171-jdk-node
    environment:
      GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx2048m"
      TZ: Asia/Tokyo

一応、メモリは2048まで落としました。
Gradleの特性で起動時にヒープで1G持っていくのと、テストTaskはそれとは別のJVMを起動させるという話も聞いたので・・・w

ただ、GRADLE_OPTSの制限でヒープ+テスト時のJVMも含めて全体のメモリ制限の可能性もあります。
それであれば-Xmx3072まで上げてもよいかと

追記:
今日”Xmx3072m“で死んだので、おとなしくXmx2048mが安泰ですね。

追記2:
これに加えて、こちらの情報みて最終的に落ち着いたコマンドがこちら

GRADLE_OPTS: -Xmx2048m -Dorg.gradle.daemon=false

まとめ

OOMは結構よく起こる現象みたいなので、調べれば日本語の対処法は結構出てきます。
ただ、利用するJVM環境やビルド環境で多少対応策が変動するので公式の表の元に対策するのが一番です。


僕の場合は出てきた対処法の_JAVA_OPTIONSに拘って、Oracle専用というところを見落としていたので結構時間かかりました(;´∀`)
しかも、結局は以前Twitterに投げたときにCircleCI Japanの方から頂いたお返事を忘れていたため、ここにたどり着けば速攻で解決した問題でしたw

というわけで、今は安定稼働しております🌟

参考サイト

How to Handle Java OOM Errors
Trying to keep Java’s memory usage under control in a CI environment can be something of a dark art.With the wealth of build frameworks available for Java/Andro...

コメント