ソフトウェア

「OSの最深部」が垣間見える「ディスクの暗号化」に関するCloudflareのカーネル改善記録


データの暗号化はインターネットでサービスを提供する企業には欠かせませんが、パフォーマンスの低下を嫌って自社サーバーのディスクに暗号化を施していない場合もあります。そんなディスクの暗号化によるパフォーマンス低下を改善した事例をCloudflareのエンジニアであるIgnat Korchagin氏が公開しています。

Speeding up Linux disk encryption
https://blog.cloudflare.com/speeding-up-linux-disk-encryption/

ストレージの構造は「アプリケーション」「ファイルシステム」「ブロックサブシステム」「物理ストレージ」といった層に分けることができ、ネットワークにおけるOSI参照モデルに似ているとのこと。高次元の層に対して低次元の層での処理は抽象化されており、それぞれの層で暗号化が可能です。通常、高次元の層における暗号化は、ファイルごとの暗号化機能など高い柔軟性を持っていますが、マシンリソースを消費しがちです。それに対し、低次元の層での暗号化は、柔軟性は低いもののマシンリソースを比較的消費せず、管理も容易であるとされています。


一般的には、アプリケーションかファイルシステムによる暗号化が柔軟性の観点から好まれますが、Cloudflareのようなクラウド企業では、シンプルさと安全性が重要であり、ファイルごとの暗号化機能といった柔軟性が不要なので、ブロックレベルでディスクをすべて暗号化しているそうです。また、ハードウェアによる暗号化はプロプライエタリなものが多いため、脆弱性を監視できないとのこと。Cloudflareは顧客のデータをプロプライエタリな環境にさらしたくないため、オープンソースのLinuxカーネルを用いたソフトウェアによる暗号化を行っています。

あるとき、Ignat氏はサーバーのディスクの読み書き速度が想定よりも遅いことに気づき、調査を行ったとのこと。その結果、Linuxの暗号化システム「dm-crypt」が原因であることがわかりました。dm-cryptはLinuxカーネルのデバイスマッパーの機能のひとつで、データを書き込みリクエストを暗号化し、読み込みリクエストを復号化します。

dm-cryptのコマンド自体のパフォーマンスを正確に調査するため、個体差のあるHDDではなく、RAM上にエミュレートしたディスク上でベンチマークを行うことに。まずはRAM上に4GBのディスクを作成します。作成したディスクは「/dev/ram0」にマウントされています。


作成したディスクを「cryptsetup」コマンドで暗号化します。


暗号化したディスクを開き「/dev/mapper/encrypted-ram0」に復号化されたディスクをマウント。「/dev/ram0」と「/dev/mapper/encrypted-ram0」の読み書きの速度を比較すれば、dm-cryptのパフォーマンスがわかります。


さっそく比較してみると、暗号化後は暗号化前の7分の1しか読み書き速度が出ていないことが判明。暗号化前の読み書き速度は1126MB/sほどですが、暗号化後は147MB/sほどに落ちてしまっています。


cryptsetupに実装されている「利用可能な暗号化アルゴリズムのベンチマーク」を実行すると、256bitの「aes-xts」が最も速いことがわかりました。しかし、aes-xtsを使っても、暗号化後の読み書き速度は速くならなかったとのこと。理論上は読み書き合計で696MB/s出るはずですが、実際には294MB/sしか出ていない状況です。


そこで、Ignat氏はdm-cryptのパフォーマンスに関連しそうなオプションを試すことに。「IO命令を出したCPUと同じCPUで暗号化する」という、パフォーマンスに関連しそうなオプションを試してみましたが、読み書き速度は改善せず。暗号化後の書き込みを別のスレッドにオフロードする動作を無効にするオプションも、読み書き速度の改善にはつながりませんでした。

dm-cryptのメーリングリストに質問を投稿しましたが、「あなたは暗号化が重い処理であることを理解していない」といった回答しか得られなかったとのこと。「"is encryption expensive"(暗号化は重い処理なのか)」とGoogleで検索すると、自社のブログに投稿されたTLSの記事がヒットし、記事内で「現代のハードウェアでは暗号化のコストは非常に低い」と結論づけられていました。

Ignat氏がdm-cryptのソースコードから処理の流れを確認すると、dm-cryptは単純に暗号化と復号化を行うプロキシのような構造ではなく、何度かキューを経由してデータを処理していることが判明。例えば書き込みの場合は、いったん「kcryptd」というキューにデータが保持され、それからCrypto APIでデータを暗号化し、暗号化されたデータが赤黒木の「write_tree」キューに格納された後、「dmcrypt_write」キューに格納されるという処理が行われます。書き込み時は4つ、読み込み時は3つのキューを経ており、速度向上のためにこのキューを取り除けないか検討を始めたとのこと。


実装されているキューには何か意味があるはずなので、Gitの変更履歴を追うことに。kcryptdは「割り込みコンテキストにおいて復号化するのは賢明ではないため必要である」とコメントされていました。2005年の段階ではCrypto APIは非同期処理を行っていなかったので、当時は意味のある実装であったとのこと。

2006年のパッチにて、kcryptdは暗号化だけでなく、IOリクエストにも使用され始めました。変更の目的はカーネルスタックの使用量を減らすためでしたが、Linuxのカーネルスタックは2014年にx86プラットフォーム用に拡張されたので、現在は問題にならない可能性があるとIgnat氏は推察しています。


kcryptd_ioは2007年に追加されたキューで、メモリ割り当てを待つ大量のリクエストをひとつのキューでさばききることができなかっため、もうひとつキューを追加したようです。しかし、dm-cryptが実装するキューによるパフォーマンス低下に不満を抱いた人が以前にいたようで、2011年には「十分なメモリがあれば、dm-cryptはブロックの読み書きをキューに保持しない」という変更が加えられています。

2015年に、dm-cryptはスタックに送る前に、書き込みをdmcrypt_writeに保持し始めます。マルチコアのプロセッサでは、書き込みデータの暗号化が異なる順序で終了するので、書き込みリクエストを並び替える必要があるとのこと。以前はシーケンシャルディスクアクセスのほうがランダムディスクアクセスよりもはるかに高速で、シーケンシャルアクセスができるようにdm-cryptが順番を乱したリクエストを並べ替えるのは理にかなっていました。write_treeが実装されているのも同じ理由です。しかし、これはHDDにおける話で、最近のNVMeを採用したSSDなどでは、シーケンシャルディスクアクセスとランダムディスクアクセスの速度差は気にする必要があまりないとのこと。かつ、この変更はIO スケジューラのひとつである「CFQ」に最適化されたものですが、2018年にCFQはLinuxのカーネルから削除され、よりパフォーマンスの高いIOスケジューラに置き換わっています。

調査の末、dm-cryptの設計は今のLinuxには適していない部分があるということがわかったので、不要なキューとCrypto APIの非同期動作を取り除き、dm-cryptを「読み書きリクエストを暗号化および復号化する」という、本来の目的に適した形に改善することに。改善にあたっては、dm-cryptに新しくオプションを追加する方法を採用し、キューやスレッドをすべてバイパスできるようにしました。

改善作業を行う中で、いくつか問題がありました。Crypto APIを非同期化するためには、非同期処理型の暗号ドライバーを選択する必要があり、利用可能な暗号ドライバーの中では「__xts-aes-aesni」が、非同期処理型で最も高速なドライバーであることがわかっていました。


しかし、__xts-aes-aesniはCrypto API内部でコードをラップしなければ利用できないことが判明。さらに、dm-cryptにもラップしたコードを呼び出すオプションを追加する必要があります。

また、__xts-aes-aesniが使用する命令セットである「AES-NI」は浮動小数点演算を行うFPUを使用しますが、割り込みコンテキストにおいてFPUを使用できない場合があるとのこと。dm-cryptは割込みコンテキストでコードを実行する可能性があるので、他のプロセスデータを破損させるリスクがあります。

この二つの問題の解決策として、Ignat氏は「FPUが使える状態であれば、__xts-aes-aesniを使い、そうでなければxtsを使う」ようにCrypto APIの内部でコードをラップする形で新しくカーネルモジュール「xtsproxy」を作成しました。

修正したdm-cryptと自作のカーネルモジュールxtsproxyを読み込ませ、暗号化したディスクに設定を適用すると、読み書きともにスループットが劇的に向上しました。


サーバーのキャッシュ応答速度も比較してみます。暗号化していない場合の速度が緑、改善前の暗号化をしている場合の速度が赤、改善後の暗号化をしている場合の速度が青の線で表されており、このテストでも速度の大幅な改善が確認できます。


この事例から、ソフトウェアの構造を再考することがシステムのパフォーマンスを大幅に向上させうるということがよくわかります。Ignat氏はブログ上にて「似たような症状で悩んでいる場合は、Cloudflareが公開しているパッチを当ててみて、フィードバックしてほしい」とコメントしています。

この記事のタイトルとURLをコピーする

・関連記事
Googleの徹底的なシステム障害への対応「SRE」の中身とは? - GIGAZINE

新しいHDDを使用する時に執り行うべき「儀式」とは? - GIGAZINE

プログラムの動作が異常に遅い原因は「ランダムアクセス」かもしれない - GIGAZINE

「プロセッサの例外処理」を用いてマイコンの保護領域を読み出す手順が公開中、ソースコードもあり - GIGAZINE

in ソフトウェア, Posted by log1n_yi

You can read the machine translated English article here.