戰(zhàn):不改代碼提升OpenSSL加解密性能)
1. 從一次壓測說起為什么業(yè)務(wù)代碼沒動QPS 卻翻了倍去年幫一個朋友排查他們網(wǎng)關(guān)服務(wù)的性能瓶頸場景很典型一臺搭載鯤鵬處理器的服務(wù)器跑著 Nginx 做 TLS 卸載業(yè)務(wù)側(cè)用的是標(biāo)準(zhǔn)的 OpenSSL 庫。壓測的時候單核 RSA 2048 簽名操作只能跑到幾千次每秒CPU 的sys和usr占用都很高但吞吐就是上不去。當(dāng)時第一反應(yīng)是加機(jī)器、換更強(qiáng)的 CPU但成本擺在那里而且他們已經(jīng)在用鯤鵬了再往上堆硬件性價比不高。后來翻鯤鵬的官方文檔發(fā)現(xiàn)了一個叫KAEKunpeng Accelerator Engine鯤鵬加速引擎的東西。它把加解密、壓縮這類計(jì)算密集型的操作卸載到專用的硬件加速單元上最關(guān)鍵的一點(diǎn)是——業(yè)務(wù)代碼一行都不用改。你原來怎么調(diào) OpenSSL現(xiàn)在還怎么調(diào)只是底層悄悄換了一條路走。這個特性對運(yùn)維和開發(fā)來說太友好了。因?yàn)楦臉I(yè)務(wù)代碼意味著重新測試、重新上線、可能引入新 bug而 KAE 的接入方式是在 OpenSSL 的 ENGINE 機(jī)制層面做文章屬于換引擎不換車的思路。這篇文章就把 KAE 硬件加速的來龍去脈、接入方式、實(shí)測效果和踩過的坑完整地講一遍。適合正在用鯤鵬平臺、對 TLS 性能或加解密吞吐有要求、又不想大動業(yè)務(wù)代碼的工程師參考。2. KAE 到底加速了什么先搞清楚它的能力邊界2.1 鯤鵬加速引擎的硬件底座KAE 不是一個軟件庫它是鯤鵬處理器內(nèi)置的一組硬件加速模塊的統(tǒng)稱。在鯤鵬 920 這類芯片上集成了幾個專門的協(xié)處理器單元分別負(fù)責(zé)對稱加密、非對稱加密和壓縮解壓。你可以把它理解成 CPU 旁邊專門雇了幾個專業(yè)工人CPU 自己不用再干這些重復(fù)性極高的體力活把任務(wù)派給它們就行。具體來說KAE 主要覆蓋三類運(yùn)算對稱加密AES 系列的加解密包括 AES-128/192/256 的 CBC、CTR、ECB 等常見模式以及 SM4 國密算法。非對稱加密RSA 的簽名與驗(yàn)簽、DH 密鑰交換以及 SM2 國密算法。壓縮解壓zlib 和 gzip 格式的壓縮與解壓加速。這三類正好是 TLS 握手和數(shù)據(jù)傳輸過程中最耗 CPU 的部分。TLS 握手階段大量使用 RSA 或 ECDHE 做密鑰交換和簽名數(shù)據(jù)傳輸階段用 AES 做對稱加密HTTP 壓縮又涉及 zlib。所以 KAE 的加速點(diǎn)選得很準(zhǔn)基本覆蓋了 Web 服務(wù)性能瓶頸的主要來源。2.2 為什么不改代碼這件事如此重要很多人第一次聽到硬件加速會以為要調(diào)用一套全新的 API把原來的RSA_sign、AES_encrypt全部替換掉。如果真是這樣那遷移成本就太高了。KAE 的設(shè)計(jì)巧妙之處在于它通過OpenSSL 的 ENGINE 機(jī)制來接入。OpenSSL 從很早的版本就支持 ENGINE 概念允許第三方實(shí)現(xiàn)一個加密引擎注冊到 OpenSSL 里。當(dāng)應(yīng)用程序調(diào)用RSA_sign這類函數(shù)時OpenSSL 會檢查當(dāng)前是否有可用的 ENGINE如果有就把實(shí)際運(yùn)算委托給這個 ENGINE 去做。KAE 就是實(shí)現(xiàn)了這樣一個 ENGINE把運(yùn)算轉(zhuǎn)發(fā)到硬件加速單元。所以整個接入過程對業(yè)務(wù)代碼是透明的。你的程序還是#include openssl/rsa.h還是調(diào)RSA_sign只是運(yùn)行環(huán)境里多了一個 KAE 的 ENGINE 庫并且通過配置讓它生效。這就是標(biāo)題里不改一行業(yè)務(wù)代碼的真正含義——改的是運(yùn)行環(huán)境和配置不是源碼。2.3 KAE 和純軟件實(shí)現(xiàn)的性能差距在哪純軟件做 AES 加密CPU 需要一條條執(zhí)行指令即使有 AES-NI 這類指令集優(yōu)化單核吞吐也是有上限的。而 KAE 的硬件單元是專門為這類運(yùn)算設(shè)計(jì)的電路并行度高、延遲低。在鯤鵬平臺上實(shí)測AES-256-CBC 的吞吐可以提升數(shù)倍RSA 2048 簽名的性能提升更明顯因?yàn)榉菍ΨQ運(yùn)算本身就是計(jì)算密集型的重災(zāi)區(qū)。不過要注意KAE 的加速效果和具體算法、數(shù)據(jù)塊大小、并發(fā)模型都有關(guān)系。小數(shù)據(jù)塊的頻繁調(diào)用加速比可能沒那么夸張因?yàn)槿蝿?wù)派發(fā)本身也有開銷。大數(shù)據(jù)塊的批量運(yùn)算加速效果才明顯。這個在后面實(shí)測部分會詳細(xì)說。3. 接入 KAE 的完整路徑從環(huán)境檢查到 ENGINE 生效3.1 先確認(rèn)你的平臺到底支不支持不是所有鯤鵬機(jī)器都帶 KAE。KAE 需要處理器本身集成對應(yīng)的加速單元而且需要操作系統(tǒng)和內(nèi)核驅(qū)動的配合。所以第一步是確認(rèn)硬件和系統(tǒng)環(huán)境。檢查硬件是否支持可以看/proc/cpuinfo里的特性標(biāo)志或者直接查鯤鵬的型號文檔。一般來說鯤鵬 920 系列是支持的。操作系統(tǒng)方面主流的企業(yè)級 Linux 發(fā)行版在較新的版本里已經(jīng)集成了 KAE 的驅(qū)動和用戶態(tài)庫。一個比較穩(wěn)妥的檢查方式是看系統(tǒng)里有沒有 KAE 相關(guān)的設(shè)備節(jié)點(diǎn)和內(nèi)核模塊# 查看內(nèi)核模塊是否加載 lsmod | grep -i kae # 查看設(shè)備節(jié)點(diǎn) ls -l /dev/ | grep -i kae # 查看 OpenSSL 是否已經(jīng)識別到 KAE 引擎 openssl engine -t -c如果openssl engine的輸出里能看到kae或類似的引擎名稱說明環(huán)境基本就緒。如果看不到可能需要安裝 KAE 的用戶態(tài)驅(qū)動包或者升級 OpenSSL 到支持 KAE 的版本。提示不同發(fā)行版和不同版本的 KAE 驅(qū)動包名和設(shè)備節(jié)點(diǎn)名稱可能不一樣。有的叫kae有的叫hisi_sec之類。以實(shí)際系統(tǒng)的文檔為準(zhǔn)不要死記命令。3.2 安裝 KAE 用戶態(tài)庫和 OpenSSL 引擎KAE 的軟件棧通常分兩層內(nèi)核態(tài)的驅(qū)動和用戶態(tài)的庫。內(nèi)核驅(qū)動負(fù)責(zé)和硬件通信用戶態(tài)庫提供 OpenSSL 能調(diào)用的 ENGINE 接口。很多企業(yè)級發(fā)行版已經(jīng)把這兩層都打包好了直接通過包管理器安裝即可。以常見的做法為例需要安裝的包大致包括 KAE 的內(nèi)核模塊包、用戶態(tài)庫包以及一個專門給 OpenSSL 用的引擎包。安裝完之后引擎的動態(tài)庫文件通常是.so文件會被放到 OpenSSL 的引擎目錄下比如/usr/lib64/engines/或/usr/lib/aarch64-linux-gnu/engines-3/這類路徑。安裝完成后再次執(zhí)行openssl engine -t -c應(yīng)該能看到 KAE 引擎出現(xiàn)在列表里并且狀態(tài)是 available。如果顯示 unavailable通常是驅(qū)動沒加載或者權(quán)限問題。3.3 讓 OpenSSL 真正用上 KAE 引擎引擎裝好了不代表就會自動生效。OpenSSL 默認(rèn)不會主動加載第三方引擎需要顯式指定。有兩種方式方式一命令行臨時指定。在執(zhí)行 openssl 命令時加上-engine kae參數(shù)比如openssl speed -engine kae -evp aes-256-cbc這種方式只對當(dāng)前命令生效適合測試驗(yàn)證。方式二配置文件全局生效。修改 OpenSSL 的配置文件通常是/etc/ssl/openssl.cnf或/etc/pki/tls/openssl.cnf在文件開頭加入引擎配置openssl_conf openssl_init [openssl_init] engines engine_section [engine_section] kae kae_section [kae_section] engine_id kae dynamic_path /usr/lib64/engines/kae.so default_algorithms ALL init 1這樣配置之后所有使用這個 OpenSSL 配置的程序都會自動加載 KAE 引擎。對于 Nginx、Apache 這類服務(wù)它們啟動時會讀取系統(tǒng) OpenSSL 配置所以配置改完重啟服務(wù)即可生效業(yè)務(wù)代碼完全不用動。注意dynamic_path的路徑要根據(jù)實(shí)際系統(tǒng)里 kae.so 的位置來寫不同發(fā)行版差異很大。寫錯了引擎加載會失敗但 OpenSSL 通常會靜默回退到軟件實(shí)現(xiàn)不會報(bào)錯所以一定要用openssl engine驗(yàn)證是否真的加載成功。3.4 驗(yàn)證加速是否真的生效配置完之后怎么確認(rèn)運(yùn)算真的走了硬件而不是軟件回退最直接的辦法是用openssl speed做基準(zhǔn)測試對比開啟和關(guān)閉引擎時的性能差異。# 不指定引擎走軟件實(shí)現(xiàn) openssl speed -evp aes-256-cbc # 指定 KAE 引擎 openssl speed -engine kae -evp aes-256-cbc如果 KAE 生效后者的吞吐數(shù)字應(yīng)該明顯高于前者。如果兩者差不多說明引擎沒真正起作用需要回頭檢查配置。另一個驗(yàn)證角度是看 CPU 占用。硬件加速生效時同樣的運(yùn)算量下 CPU 的usr占用會下降因?yàn)橛?jì)算被卸載到了加速單元。這個在壓測場景下觀察top或mpstat的輸出就能看出來。4. 實(shí)測數(shù)據(jù)與性能表現(xiàn)加速比到底有多少4.1 對稱加密的加速效果在鯤鵬 920 平臺上用openssl speed實(shí)測 AES-256-CBC軟件實(shí)現(xiàn)和 KAE 加速的對比大致是這樣的具體數(shù)字因平臺和版本而異這里給的是量級參考算法與塊大小軟件實(shí)現(xiàn)吞吐KAE 加速吞吐提升倍數(shù)AES-256-CBC 16字節(jié)塊基準(zhǔn)值約 2-3 倍中等AES-256-CBC 1024字節(jié)塊基準(zhǔn)值約 4-6 倍明顯AES-256-CBC 8192字節(jié)塊基準(zhǔn)值約 6-8 倍顯著可以看到一個規(guī)律數(shù)據(jù)塊越大加速比越高。原因是硬件加速的任務(wù)派發(fā)和回收有固定開銷小數(shù)據(jù)塊時這個開銷占比高攤薄了加速收益。大數(shù)據(jù)塊時計(jì)算本身占主導(dǎo)硬件并行優(yōu)勢就體現(xiàn)出來了。這對實(shí)際應(yīng)用的啟示是如果你的業(yè)務(wù)是大量小包加解密比如高頻的短連接 TLS 握手加速效果可能沒有想象中那么夸張如果是大文件傳輸、批量數(shù)據(jù)加密加速收益會非??捎^。4.2 非對稱加密的加速效果RSA 這類非對稱運(yùn)算才是 KAE 真正的主場。軟件實(shí)現(xiàn) RSA 2048 簽名單核每秒大概幾千次用 KAE 加速后可以提升到數(shù)倍甚至更高。驗(yàn)簽的提升幅度通常比簽名更大因?yàn)轵?yàn)簽的運(yùn)算量相對小硬件加速的固定開銷占比更低。DH 密鑰交換的加速也很明顯這對 TLS 握手性能影響很大。TLS 握手階段如果 RSA 或 DH 運(yùn)算慢握手延遲就高直接影響首字節(jié)時間。KAE 加速后握手階段的 CPU 時間大幅縮短同等硬件下能支撐更高的新建連接速率。4.3 壓縮解壓的加速效果zlib 壓縮在 Web 服務(wù)里很常見尤其是返回 JSON 或 HTML 的場景。KAE 對 zlib 的加速主要體現(xiàn)在壓縮和解壓的吞吐上。實(shí)測下來壓縮的加速比通常比解壓更明顯因?yàn)閴嚎s的計(jì)算量更大。不過壓縮加速有個前提數(shù)據(jù)量要夠大。如果只是壓縮幾百字節(jié)的小響應(yīng)加速收益有限。對于大響應(yīng)體或者批量日志壓縮效果才明顯。4.4 并發(fā)場景下的整體收益單次運(yùn)算的加速比是一回事實(shí)際服務(wù)在并發(fā)場景下的整體收益又是另一回事。在 Nginx 做 TLS 卸載的場景下開啟 KAE 后同等硬件配置下 QPS 提升幅度取決于業(yè)務(wù)模型如果是短連接、頻繁握手RSA/DH 加速帶來的握手性能提升會直接轉(zhuǎn)化為 QPS 提升。如果是長連接、大數(shù)據(jù)傳輸AES 加速帶來的吞吐提升更關(guān)鍵。如果兩者都有綜合提升會更明顯。需要提醒的是KAE 加速單元本身也有吞吐上限。當(dāng)并發(fā)量超過加速單元的處理能力時性能曲線會走平甚至因?yàn)榕抨?duì)而下降。所以不是開了 KAE 就無限快還是要根據(jù)實(shí)際壓測找到最優(yōu)并發(fā)點(diǎn)。5. 踩過的坑那些文檔里不會寫的細(xì)節(jié)5.1 引擎加載了但沒生效的靜默回退這是最容易踩的坑。OpenSSL 的 ENGINE 機(jī)制有個特點(diǎn)如果引擎加載失敗它不會報(bào)錯而是靜默回退到軟件實(shí)現(xiàn)。你配置里寫了個不存在的路徑或者引擎初始化失敗程序照樣跑只是沒用上加速。結(jié)果就是你以為開了加速實(shí)際性能一點(diǎn)沒變。排查方法就是前面說的用openssl engine -t -c確認(rèn)引擎狀態(tài)再用openssl speed對比性能。不要只看配置文件寫了什么要看實(shí)際運(yùn)行結(jié)果。5.2 多版本 OpenSSL 共存導(dǎo)致的引擎路徑混亂系統(tǒng)里如果裝了多個 OpenSSL 版本比如系統(tǒng)自帶的加上自己編譯的引擎的搜索路徑可能不一致。你給系統(tǒng) OpenSSL 裝了引擎但業(yè)務(wù)程序鏈接的是另一個版本的 OpenSSL那引擎自然用不上。排查思路是確認(rèn)業(yè)務(wù)程序?qū)嶋H鏈接的 OpenSSL 庫路徑ldd /path/to/your/binary | grep ssl然后確認(rèn)這個 OpenSSL 版本的引擎目錄在哪把 KAE 引擎裝到對應(yīng)位置。這個問題在容器環(huán)境里尤其常見因?yàn)槿萜骼锏?OpenSSL 可能是鏡像自帶的和宿主機(jī)不是一套。5.3 容器環(huán)境下的設(shè)備透傳問題KAE 是硬件加速容器里要用的話需要把設(shè)備節(jié)點(diǎn)透傳進(jìn)容器。如果容器啟動時沒有映射/dev/下的 KAE 設(shè)備節(jié)點(diǎn)容器內(nèi)的 OpenSSL 即使裝了引擎也訪問不到硬件會回退到軟件實(shí)現(xiàn)。這個問題的隱蔽性在于容器里openssl engine可能顯示引擎 available但實(shí)際運(yùn)算時因?yàn)樵L問不到設(shè)備而失敗回退。所以容器場景下除了裝引擎還要確認(rèn)設(shè)備節(jié)點(diǎn)透傳和權(quán)限配置。5.4 特定算法組合不支持導(dǎo)致的回退KAE 不是支持所有 OpenSSL 的算法和模式。比如某些冷門的加密模式、某些密鑰長度可能硬件不支持OpenSSL 會自動回退到軟件實(shí)現(xiàn)。這種情況下整體性能是混合的部分運(yùn)算加速、部分沒加速。要精確知道哪些運(yùn)算走了硬件可以開啟 OpenSSL 的調(diào)試輸出或者用 KAE 提供的統(tǒng)計(jì)工具查看加速單元的調(diào)用計(jì)數(shù)。如果發(fā)現(xiàn)某個關(guān)鍵算法沒被加速可能需要調(diào)整業(yè)務(wù)側(cè)的算法選擇換成 KAE 支持的組合。5.5 升級 OpenSSL 后的兼容性有時候?yàn)榱诵弈硞€安全問題需要升級 OpenSSL升級后 KAE 引擎可能因?yàn)?ABI 不兼容而加載失敗。這種情況在跨大版本升級時尤其容易發(fā)生。建議升級前先確認(rèn)目標(biāo) OpenSSL 版本有對應(yīng)的 KAE 引擎包升級后立即用openssl engine驗(yàn)證。6. 把 KAE 用好的幾個實(shí)戰(zhàn)建議6.1 先壓測再上線別憑感覺KAE 的加速效果和業(yè)務(wù)模型強(qiáng)相關(guān)別人的數(shù)據(jù)只能參考不能照搬。上線前一定要在自己的業(yè)務(wù)場景下做對比壓測同樣的硬件、同樣的并發(fā)模型開 KAE 和不開 KAE 各跑一輪看 QPS、延遲、CPU 占用的實(shí)際差異。只有數(shù)據(jù)說話才能判斷值不值得上。6.2 關(guān)注加速單元的飽和度KAE 加速單元是共享資源多個進(jìn)程或線程同時用的時候會競爭。壓測時要觀察加速單元的利用率如果已經(jīng)接近飽和再加并發(fā)也不會有收益反而可能因?yàn)榕抨?duì)導(dǎo)致延遲上升。這時候要么優(yōu)化業(yè)務(wù)減少加解密調(diào)用要么考慮多機(jī)分?jǐn)偂?.3 算法選型配合硬件能力如果業(yè)務(wù)側(cè)有算法選擇的靈活性盡量選 KAE 支持得好的算法組合。比如對稱加密優(yōu)先用 AES-CBC 或 AES-CTR非對稱優(yōu)先用 RSA 或 DH這些是加速效果最成熟的。國密場景下 SM4 和 SM2 也有支持但具體效果要實(shí)測。6.4 配置管理要納入版本控制OpenSSL 的配置文件改動、引擎庫的安裝這些都應(yīng)該納入配置管理而不是手工在機(jī)器上改。否則換一臺機(jī)器、重裝一次系統(tǒng)很容易漏掉某個步驟導(dǎo)致加速悄悄失效。建議把 KAE 的安裝和配置寫成腳本或配置管理項(xiàng)保證環(huán)境一致性。6.5 監(jiān)控加速是否持續(xù)生效上線不是終點(diǎn)。系統(tǒng)升級、OpenSSL 打補(bǔ)丁、容器重建都可能導(dǎo)致 KAE 引擎失效。建議在監(jiān)控里加一項(xiàng)定期用openssl engine或性能基準(zhǔn)測試確認(rèn)加速仍然生效。一旦發(fā)現(xiàn)性能回退能第一時間定位到是引擎失效還是業(yè)務(wù)變化。7. 關(guān)于 KAE 與 OpenSSL 配合的幾點(diǎn)個人體會我在實(shí)際項(xiàng)目里用 KAE 最大的感受是它的價值不在于快了多少倍這個數(shù)字而在于接入成本極低。很多性能優(yōu)化方案要么改代碼、要么改架構(gòu)動輒幾周的工作量。KAE 這種通過 ENGINE 機(jī)制透明接入的方式基本上半天就能驗(yàn)證完風(fēng)險(xiǎn)可控。但低接入成本也帶來一個副作用容易被忽視。因?yàn)椴桓拇a很多人配完就忘了系統(tǒng)升級、環(huán)境遷移時很容易漏掉導(dǎo)致加速悄悄失效還沒人發(fā)現(xiàn)。所以我現(xiàn)在的習(xí)慣是凡是這種透明優(yōu)化一定要配一個可觀測的驗(yàn)證手段定期跑一下確保它一直在工作。另外一點(diǎn)KAE 不是銀彈。它解決的是加解密和壓縮這類特定運(yùn)算的性能問題如果你的瓶頸在別的地方比如數(shù)據(jù)庫查詢、網(wǎng)絡(luò) IO上 KAE 也沒用。先定位瓶頸再選工具這個順序不能反。最后鯤鵬平臺上的 KAE 生態(tài)還在持續(xù)完善不同發(fā)行版、不同 OpenSSL 版本的支持程度有差異。遇到問題多查對應(yīng)版本的官方文檔別拿一個環(huán)境的經(jīng)驗(yàn)硬套另一個環(huán)境。硬件加速這東西細(xì)節(jié)都在版本和配置里。