もち256ログ
About Note Log
Log
published: 2022-03-21, modified: 2022-04-10
k8sクラスタによる並列処理のパフォーマンス検証【1】

■ 2022-04-10 追記

本記事の実験「1. k8sクラスタのジョブ実行で最適な並列数の測定」ですが、実験方法に不備があったため、結果に誤りがあります。

詳細は「k8sクラスタによる並列処理のパフォーマンス検証【2】」をご確認ください。


以前RaspberryPiで構築したk8sクラスタですが、k8sはアプリのデプロイを行う際に複数のPodを起動し、冗長化を行うことができるなど、ITインフラとして様々な用途で使用することができます。

その中で、自分は「ジョブの実行」ができる点に興味を持ちました。
クラスタリングを行った計算機で並列処理や分散処理を行う場合、処理の対象となるデータの計算量に対し、構成する端末を増やすことで、処理速度を速めることができます。

今回はk8sを計算機クラスタとして使ううえで、色々実験を行ってみました。


目的

今回の実験の目的は、下記のものになります。

  • 前回構築した環境(1マスターノード、2ワーカーノード)でジョブの並列実行を行う場合、何並列(何Pod)で実行するのが最適か確認する。
  • またk8sの構築にかかった費用と、同じ価格帯のPC(mouse C1)の処理速度を比べた場合、どちらが速く処理を行えるか。

準備

今回の実験にて、下記のものを用意しました。


方法と結果と考察

0. 計測アプリについて

「準備」の項に記載した計測アプリですが、下記の仕様となっています。

アプリの仕様

  • アプリはサーバクライアントに分かれています。
  • サーバは起動時にカウントの最大値を引数(APP_COUNT_MAX)で取り、サーバにアクセスされる毎に1から順にカウントを行い、引数の値までの数値をクライアントへ返します。
  • サーバのカウントが引数の値を超えた場合、nullが返るようになります。
  • クライアントは起動時に接続先のURLを引数(APP_URL)を取り、対象のURLへアクセスを行い、取得した数値の素数判定を行います。
  • クライアントの素数判定の処理は、サーバからnullが返ってくるまで、逐次実行され続け、nullが返ってきた場合はプログラムを終了します。

計測区間について

  • 処理速度の計測区間は、クライアント実行時の「client_start」というログの出力時から、クライアント終了時の「client_terminated」というログが出力されるまでとします。
  • クライアントの並列実行を行う場合は、最初に起動されたクライアントの「client_start」というログから、最後に実行されていたクライアントの「client_terminated」というログが出力されるまでとします。

1. k8sクラスタのジョブ実行で最適な並列数の測定

実験方法

まずは、構築したk8sクラスタでジョブの並列実行を行う場合、何並列が最もパフォーマンスが出るかの確認になります。

下記の手順で実験をしました。

  1. APP_COUNT_MAXを10000に設定し、app-server.ymlをデプロイする。
  2. 並列数(completions, parallelism)を任意の値に設定し、app-job.ymlをデプロイする。
  3. "kubectl get pods"コマンドで、ジョブのコンテナが全て完了することを確認する。
  4. "kubectl logs {{ ジョブのコンテナ名 }}"を実行し、クライアントのログを取得する。
  5. 最初にクライアントが実行された時間から、最後にクライアントが実行された時間を記録する。
  6. サーバ、ジョブのコンテナを全て終了する。
  7. 並列数を1~5として、手順1~6までを繰り返す。

実験結果

結果として、以下のようになりました。

並列数(個) 処理時間(秒)
1 60.767
2 37.582
3 32.596
4 33.917
5 42.467

結果を図示すると、以下のようになります。


考察

  • 1マスター・2ワーカーノードの構成の場合、3並列が最もパフォーマンスが良いことがわかった。
  • 4~5並列の場合に処理時間が長くなっているが、今回の構成だと同時に起動できるPod(ジョブ)が3つまでとなっていた。
    その3つPodが終了した後に残りのPodが起動され、処理時間が伸びていたことが原因であった。
  • ワーカーノードをさらに増やすことで、処理時間を早められると思うが、もしかしたらカウントを行うサーバの効率化(キューを使うなど)も行う必要があると思われた。

2.処理速度の比較

実験方法

次に、処理速度の比較になります。

「1. k8sクラスタのジョブ実行で最適な並列数の測定」にて、今回用意したk8sの環境だと、3並列で実行することが最もパフォーマンスが良いことがわかりました。

そのため、今回は下記3つの方法で計測アプリを使用した場合、どのような処理速度の差が生じるかを確認します。

  • マスターノード1、ワーカーノード2の構成と同価格帯の「mouse C1」にて、直列でアプリを実行する。
  • k8sクラスタを構成しているRaspberryPiにて、直列でアプリを実行する。
  • k8sクラスタにて、3並列でアプリを実行する。

上記3種類の方法で、素数判定の処理数(APP_COUNT_MAX)を1000100000まで設定し、処理終了までに何秒かかるかを計測します。


実験結果

結果として、以下のようになりました。

処理数(個) mouse C1(秒) RaspberryPi(秒) k8s 3並列(秒)
1000 4.160 6.728 5.355
5000 23.702 33.597 17.533
10000 43.359 68.087 32.303
50000 237.820 366.337 161.320
100000 554.985 800.588 342.183

結果を図示すると、以下のようになります。


考察

  • 3方式を比較し、最終的に最も処理が速かったものはk8sでの実行であった。
  • 処理数が1000の場合、k8sは2番目に速いこととなっているが、これは恐らくPodの起動・終了に時間がかかっていると思われる。
  • RaspberryPiで処理数10000に設定した場合の速度は、k8sクラスタの1並列で処理を行った際より、約8秒遅い結果となっていた
    ※「1. k8sクラスタのジョブ実行で最適な並列数の測定」の項を参照
  • 処理数1000の時にRaspberryPiがk8sより遅くなる点と、上述の処理数10000のk8sの1並列の処理よりもRaspberryPiの処理が遅い点が、不可解であった。
    (k8sで命令を通さない分速くなると思ったが、そうでもなかった。もしかしたら、OSインストール直後の端末で計算を行ったほうが、余計な処理が走っておらず、速くなったかもしれない)

おわりに

今回実験を行った結果、RaspberryPi 4Bの1マスターノード、2ワーカーノードの構成のk8s環境を作成した場合、ジョブは3並列が最もパフォーマンスが良く、同価格帯のmouse C1のノートパソコンよりも、1.5~6倍ほど速く処理が行えることがわかりました。

ただ、実験中に「5つ同時に起動するジョブのPodが、3つまでしか同時に起動しない」ということが起こっていました。

そこで「ワーカーノードの数に対し、同時起動できるジョブの数はいくつなのか(ノード数に対し、比例するのか)」という点が気になりました。

なので、今後ラズパイが追加で手に入りましたら、そこら辺の検証もするかもしれません。


今回は以上になります。

ここまで読んでいただき、ありがとうございました!


実験時のメモ

以下は今回の実験を行った際の、自分向けのメモになります。


Dockerイメージの登録

k8sで計測アプリを実行する際、DockerHubにイメージをpushし、k8sクラスタでpullすることとしました。

下記のコマンドで登録をしたのですが、当時dockerイメージはビルドした端末のアーキテクチャに依存することを知らず、Windows(amd64)でビルドしたイメージをk8sにデプロイしたところ、

「standard_init_linux.go:219: exec user process caused: exec format error」

というエラーが発生し、困ったりしました。
(使用するラズパイでイメージのビルド&pushを行ったことで、今回は解決しています。)

docker build -t throughput_measurement_tools .
docker tag throughput_measurement_tools mochi256/throughput_measurement_tools
docker push mochi256/throughput_measurement_tools


ログ一覧へ戻る