AWSのコンテナのベースイメージ何がいいのか


最近職場で、コンテナのベースイメージを何にするのがよいのかという話が立て続けに出ていて、 古くはalpineとか最近だとdistrolessとか、これまでの経験とか一般的に言われていることもあり、それをベースに話していました。

が、外で言われていることをそのまま受けとめてたものもあるので実際に調べてみました。

自分の経験上の話と世間でよく言われていること

有名な話として、alpineはlibc実装としてmuslが使われています。

と良いつつ単語としてしか知らず、自分の実感として、 以前はAlpineでGoを動かすのがとても面倒だった記憶があります。結局glibcを追加で入れる必要があったりとか。 最近はGoにもご無沙汰なので今は改善しているかもしれませんが。

仕事だとJavaが登場することが多いので、だったらAWS上で動かすなら素直にcorrettoを使えばいいのではないかと思ってます。

世間ではmuslは遅いので、軽量コンテナだったらdistrolessが良いのではないかと言われていると思います。

実際に計測してみた

そんなわけで、alpineは遅いと聞いたことがあったものの、そんなに有意差が出るほど時間に差があるのか?と思ったので、実際に動かして測定してみました。

測定対象

昔、SpringBootを使って、指定したパラメータの桁数だけ円周率を計算する適度に負荷がかかるアプリを作っていたので、 これを用いて計測してみました。

https://github.com/grugrut/pi-springboot

環境はECSで、データプレーンにはEC2を採用しました。Fargateは処理性能がガチャになる可能性があること、 ネットワーク条件などを同じにしたかったので避けました。

ベースイメージは以下の4つを用意しました。

イメージリポジトリ/タグサイズ
OpenJDK on Alpinedocker.io/library/openjdk:17-alpine326 MB
OpenJDK on distrolessgcr.io/distroless/java17-debian12:latest227 MB
Amazon Corretto on Amazon Linuxdocker.io/library/amazoncorretto:17-al2023460 MB
Amazon Corretto on Alpinedocker.io/library/amazoncorretto:17-alpine3.19290 MB

こうして見比べるとdistrolessはサイズが小さいですね。

測定方法

細かい条件はどうでもよいと思いますが、今回はjmeterを使って、円周率8000桁を計算する処理をスレッド数を変え(1多重、4多重、8多重)て一定期間流し入れ、レスポンスタイムの平均値を求めました。

測定結果

それぞれのコンテナに対して負荷をかけた結果(レスポンスタイムの平均値)がこちらです。

予想に反して、Alpine上のOpenJDKだけが頭一つ抜けてレスポンスタイムが小さい(=性能がよい)ことがわかります。

最小値、最大値も求めていましたが、値のばらつきもそこまで大きくない、という結果になりました。 もちろん負荷を掛けるjmeter側のリソースには余裕があることは確認していますし、 時間を置いて複数回試してみましたが、だいたい同じぐらいの結果になったのでタイミングによるものではなさそうです。

考察

Alpineは遅いと聞いていましたが、結果はむしろ逆という興味深い結果になりました。 とはいえ、Amazon Correttoについては、Amazon Linux、Alpineでは性能差は見られない、というこれまた不思議な結果です。

実はAlpineを使いつつglibcを使っていて差が出てないのでは?とも思い、コンテナの中を見てみたのですが、 ざっと見た感じはそれぞれ期待するライブラリを使っているように見えました。

# Alpine上のCorretto
# ldd /usr/lib/jvm/java-17-amazon-corretto/bin/java
      /lib/ld-musl-x86_64.so.1 (0x7ffb98d20000)
      libjli.so => /usr/lib/jvm/java-17-amazon-corretto/bin/../lib/libjli.so (0x7ffb98d0b000)
      libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7ffb98d20000)
      libz.so.1 => /lib/libz.so.1 (0x7ffb98cf1000)

# Amazon Linux上のCorretto
# ldd /usr/lib/jvm/java-17-amazon-corretto/bin/java
      linux-vdso.so.1 (0x00007ffeef100000)
      libz.so.1 => /lib64/libz.so.1 (0x00007f50cf00e000)
      libjli.so => /usr/lib/jvm/java-17-amazon-corretto/bin/../lib/libjli.so (0x00007f50ceffe000)
      libc.so.6 => /lib64/libc.so.6 (0x00007f50cedf6000)
      /lib64/ld-linux-x86-64.so.2 (0x00007f50cf031000)

ひとつ考えられることとしては、今回のベンチマーク対象が円周率の計算という割とCPUに負荷がかかる処理だったため、 もっと頻繁にインスタンスを生成・破棄するようなアプリでは違った結果になるかもしれません。

一旦まとめ

今回、各種Javaのベースイメージを用いて性能を比べてみましたが、思っていた以上に差が出る、そしてなぜかAlpineが高速という予想に反する結果となりました。

今度はDBとのやりとりとか違った特性を持つアプリを用意して同じように比べてみたいと思います。

そして最後に、「実際に計測すること、超大事」


See also

comments powered by Disqus