SCEPの通信をk6でしたらつらかった話

tsurai

この記事はOPTiM TECH BLOG Advent Calendar 2020 12/24の記事です。

はじめまして。オプティムのサービスプロダクト開発部 新卒2年目の中村です。 普段はRubyを書いていることが多いです。今回の記事とは関係ありませんが、明日はRuby3のリリース日ですね。Ractorが楽しみです。

今回は、iOSデバイスをMDMサーバで管理する際に必要となるSCEPの通信を代替する処理をk6で行った際につらかった話をしたいと思います。

何をしようとしたのか

サーバとiOSデバイスとの通信を、実際のデバイスを介さずに、どの程度の通信でどれくらいサーバに負荷がかかるのかを見るためのツールを探していました。そこで、社内でも実績のあったLoad Impactのk6を採用し、k6でiOSデバイスが受け持つ処理を代替しようとしました。

k6とは

Load Impact社のOSSであるk6は、負荷試験を行うためのツールです。

github.com

k6についてはOPTiM TECH BLOGの過去の記事でも紹介しています。

tech-blog.optim.co.jp

また、GitLabもk6を採用しており、各マシンスペックに対するテスト結果やテストシナリオも公開されています。

docs.gitlab.com

SCEPとは

SCEP(Simple Certificate Enrollment Protocol)は、CISCOが仕様を定義した、証明書配信を目的とするプロトコルです。

主な特徴は以下です。

  • 証明書リクエスト形式としてPKCS#10を使用
  • 暗号キー/暗号化されたメッセージを伝えるためにPKCS#7を使用
  • サーバと要求者の間だけで共有する必要がある、証明書署名要求(CSR)内にチャレンジパスワードが必要

管理されるiOSデバイスはMDMサーバに自分自身を認証するためにIDを使用します。このIDはSCEPでデバイス登録を行うなどで生成することができます。

何がつらかったのか

負荷試験コードを実装していく中でつらかったことを挙げました。

利用できないモジュールがあった

  • escape/unescapeメソッドが実装されていなかった
  • osやfsモジュールのようにNode.jsが提供するAPIに依存するパッケージは動作しない
  • windowオブジェクトのようなブラウザ固有のAPIについても同様
    • 上記に該当する処理が実装されているライブラリは使えなかった
  • バイナリを扱える機能が足りなかった

pkcsを手軽に扱えるライブラリがk6にはなかった

  • k6がサポートしている暗号化ライブラリにPKCSを手軽に扱えるものがなかった
    • forgeをimportすることで回避した
      • それでも証明書の作成にむちゃくちゃ時間かかる(約1分半)
        • nodeで同じ実装すると1秒未満
        • 事前に作成できるものは作成しておいて実行時に読む込むなどして回避できる場合もある
      • pkcs#7.verifyが未実装だった(これはforgeの問題)

カスタマイズしたk6をk6 Cloudで使用することができなかった

  • Load Impact社のk6 Cloudを使う前提だったので、k6自体のカスタマイズを行うことができなかった
    • k6 Cloudは負荷をかける側の環境の提供、負荷結果の分析・可視化を行ってくれるプラットフォーム
    • forgeを見つけるまでは暗号周りはGoで解決しようとしていた
    • k6内部はGoで実装されているのでカスタマイズできないことはない
      • しかしk6 Cloudで使用されるk6のイメージを変更できる機能がサポートされていないため不可

デバッグがつらい

  • プリントデバッグぐらいしかできなかった(JavaScript周りの開発環境にあまり慣れていないだけかも)
    • Node.jsで同等の実装をしながらk6に落とし込むやり方のほうが捗った

最終的に

最終的に、k6によるエミュレーションは断念しました。代わりにRubyで通信処理、レスポンスタイム・各ステータスコードの割合などを集計する機能を実装することにしました。実行すると以下のような結果を得ることができます。

+----------------------------------------------+------------------------+------------+-----------------+
| URL             | TOTAL RESPONSE TIME[s] | CALL COUNT | Req Time/Call[s]    | HTTP STATUSES COUNTS |
| 各エンドポイント | 1.3706235239997113     | 4          | 0.34265588099992783 | 200: 4               |
|                 | 0.849753430000419      | 4          | 0.21243835750010476 | 302: 4               |
|                 | 0.6166932179985452     | 4          | 0.1541733044996363  | 200: 4               |
|                 | 0.7185427349995734     | 4          | 0.17963568374989336 | 200: 4               |
|                 | 0.3755187710003156     | 4          | 0.0938796927500789  | 200: 4               |
|                 | 0.545027580000351      | 4          | 0.13625689500008775 | 200: 4               |
|                 | 2.9424243379999098     | 4          | 0.7356060844999774  | 200: 4               |
|                 | 3.356274714000392      | 4          | 0.839068678500098   | 500: 1, 200: 3       |
|                 | 1.1443002809992322     | 4          | 0.28607507024980805 | 200: 4               |
|                 | 1.1406658699988839     | 3          | 0.3802219566662946  | 200: 3               |
|                 | 1.2798034189991085     | 3          | 0.42660113966636953 | 200: 3               |
|                 | 1.1055157859991596     | 3          | 0.36850526199971984 | 200: 3               |
|                 | 1.48988864400053       | 3          | 0.4966295480001766  | 200: 3               |
|                 | 1.209495729000082      | 3          | 0.4031652430000274  | 200: 3               |
|                 | 0.8868289820002246     | 3          | 0.29560966066674155 | 200: 3               |
|                 | 0.9555667079985142     | 3          | 0.31852223599950474 | 200: 3               |
|                 | 1.7509798409992072     | 3          | 0.5836599469997358  | 200: 3               |
|                 | 1.147428360000049      | 3          | 0.38247612000001635 | 200: 3               |
|                 | 0.852724200000921      | 3          | 0.284241400000307   | 200: 3               |
|                 | 0.8892385469998771     | 3          | 0.29641284899995907 | 200: 3               |
|                 | 0.7667950330005624     | 3          | 0.2555983443335208  | 200: 3               |
|                 | 1.1643491119993996     | 3          | 0.38811637066646654 | 200: 3               |
|                 | 46.32250973399823      | 119        | 0.3892647876806574  | 200: 119             |
|                 | 168.79742812300265     | 119        | 1.4184657825462408  | 200: 119             |
+----------------------------------------------+------------------------+------------+-----------------+
Mock finished at 2020-11-16 11:34:01 +0900 .

k6と同様、反復回数または実行期間を指定できるようにし、同時並列数はParallelを使用しました。

k6は頻繁にアップデートされていますが、複雑な処理を行いたい場合にはまだまだ足りない機能があるかもしれません。k6自身のリリースノートをチェックするのはもちろん、gojaの動向も追っていくことも問題解決のヒントになるかもしれません。

テストしたいシナリオを洗い出し、ツールがそのシナリオを満たすことができるかどうかを事前に調査することが重要だと改めて感じました。

さいごに

オプティムではサービスの品質を向上する取り組みを一緒におこなうエンジニアを募集しています!

www.optim.co.jp