inductor's blog

nothing but self note :)

RubyでYJITを有効にする場合はメモリ消費量に気をつけよう、という話(on Kubernetesでハマった)

TL; DR

  • YJITをDocker/Kubernetesでで有効にするには RUBY_YJIT_ENABLE=1 or RUBYOPT=--yjit
  • YJITはデフォルトで256MBのメモリ領域を確保するため、全体のメモリ消費量が上がる
  • Docker/Kubernetesでメモリ量を制御したい場合、RUBYOPT=--yjit-exec-mem-size=128のようにメモリ確保量を変更すればよい

Ruby 3.1から使えるYJIT

Ruby on Railsで動くアプリケーションでRubyのMJITが効果的でないことは色んなところで書かれていて、Ruby 3.1から実験的に導入されたShopify製のYJITでは対照的にRailsでのパフォーマンス改善が見込めるという期待があります。

ただしこのYJIT、動かすときには(JITなのでそりゃそうですが)デフォルトで256MiBのメモリを確保しようとします。Kubernetesのように細かいサービスを水平に並べる仕組みを採用する場合、メモリ消費量がある程度重なると"ちりつも"でかなりリソースを圧迫することにも繋がります。

困ったこと

とあるRailsアプリケーションで、以下のようなspecを書いていたのですが、Rubyを3.1に上げてYJITを有効にしただけだと起動後数分でOOMKillerが走ってPodの再起動が散見されるようになりました。

    resources:
      limits:
        cpu: "1"
        memory: 512Mi
      requests:
        cpu: 800m
        memory: 256Mi

解決策

Ruby-jp Slackの皆様のお陰で(主に@ko1さん、ありがとうございました)、YJITではデフォルトで256MiBのメモリを確保する点、それからこれをいじるには起動オプション--yjit-exec-mem-sizeを指定すれば良い点がわかりました。

RubyのDockerイメージではRUBY_YJIT_ENABLERUBYOPTが使えるため、DockerfileまたはKubernetes YAMLにはRUBY_YJIT_ENABLE=1RUBYOPT=--yjit-exec-mem-size=128と言った形でENVの指定を入れてあげれば良いということがわかりました。これはDocker Composeでも同じですね。

完全に当時はスルーしてましたが、@mameさんと@ko1さんが書かれたRuby 3.1リリースブログにも起動時のメモリ確保の件は書かれていました。一次情報大事。

techlife.cookpad.com

今日は短いですがこのへんで。