チーフエンジニアの加辺です。
今日は珍しいトラブルに出会ったので紹介します。
起こった問題
ある環境ではEC2によりサーバを運用しており、アプリケーションをデプロイサーバでビルドし、その成果物をアプリケーションサーバへコピーすることでデプロイとしていました。
ここで新規サーバを作成していたところ、一部のアプリケーションサーバで見慣れないエラーが発生し、アプリケーションが起動しないという事象が確認されました。調べたところ、デプロイサーバはt3、アプリケーションサーバはt3aファミリーが利用されていることがわかり、アプリケーションサーバをt3ファミリーへ変更したところ、問題が発生しなくなることが分かりました。
さて、これはどのような機序によるものでしょうか。
調査
記事タイトルで答えをほとんど書いてしまっていますし、t3, t3aを知っていれば明らかですが、これはIntelとAMDの違いです。
本アプリケーションはRubyで記述されており、Rubyは以下のようなログを書き出していました。適宜抜粋します。
0x00007f1758f39b10番地に不正な命令があるとのことです。このログだけを見ると libffi に原因があるように見えます。
ところが実際にこの番地が何者かを確認してみると、libffiではありませんでした。
sasscがmapされていました。さらにこの共有オブジェクトの中身を調べ、どんな命令なのか見てみます。
0x00007f1758f39b10 - 0x7f1758eb6000 = 0x83B10です。
ということで、VMOVDQU64という命令であることがわかりました。
新しい命令
これを調べると、AVX-512の命令であることがわかりました。
アライメントされていないメモリー位置からパックド int64 要素をロードします。
通常はAMD64でもIntel64の命令はサポートされているはず…と思ったのですが、AVX-512の命令は2021年3月に発表されたZen-4以降でようやくサポートされたばかりだったようです。
This would make "Zen 4" the first AMD microarchitecture to support AVX-512.
Intel64でsasscをコンパイルした際にAVX-512の命令が入ってしまい、それをAMD64で動かそうとした結果だったようです。
とりうる対応
いくつもありますが、例として以下のような方法が考えられます。
- CPU familyをIntel64に揃える(AVX-512はSkylake-SP以降)
- デプロイサーバをAMD64にする(今回の問題の逆パターンも考えられ、問題が起こる可能性が0になるわけではない)
- ビルドするとき(今回であればgemをbundleinstall)にgccのオプションに --disable-avx512 を付ける
- AWSがAMD EPYCのプロセッサを全てZen-4以降に置き換えてくれるのを待つ
- 各サーバでnativeなバイナリをビルドするようにする
今回、手っ取り早くできる&コストが抑えられる対応として、2つ目の方法をとりました。