mrubyでApacheのCPU割当てを制御できるモジュール作った

週末の夜なので、突発的に何か面白い物作れないかと、一人ハッカソンがてら新しいApacheモジュールを作ってみました。最初に断っておきますが、mrubyを使ったものの、mod_mrubyとは全く関係ないです。

ApacheのサーバプロセスのCPU割当を制御

まだまだ、あくまで試しに実装しただけというレベルのモノです。一応、名前は「mod_resource_manager」と呼んでいます。何ができるのかというと、Apacheのサーバプロセス上のCPU使用率を制御できるモジュールです。例えば、アクセス集中の場合やCPUを大量に消費するようなスクリプトが動作するプロセスのCPU使用率を制御する事ができます。特徴としては以下になります。

  • サーバプロセスやCGIプロセスのCPU使用率を制御
      • 100%から50%やさらには5%に変更など
      • 仮想ホスト単位で制御可能(FileMatchを使えばファイル単位も可能?)
  • 基本的にはApacheのconfに設定
      • Host名とCPU使用率(20%だけ割り当てるというイメージ)を引数に指定
      • Apacheリスタートが必要
  • mrubyスクリプトでApacheを再起動させずに使用率を即時変更して制御可能
      • mrubyで正規表現等の条件によってCPU割当を動的に変えるような記述が可能
      • アクセスのあったファイルをApacheから取得可能
      • 正規表現等の条件によってレスポンスで返すファイルを変更可能

使い方(Apacheに設定)

まずはGithubからmod_resource_managerをcloneして下さい。例の如くMakefikeがありますので、make reloadで一発でインストールできます。Linux上でcgroupが使えるカーネルで、cgroupがマウントされていて、mrubyがコンパイルされている事が条件になります。詳しくは沢山Webサイトがありますのでそちらをご覧ください。

apacheユーザー用のcgroupグループディレクトリを作っておきます。

mkdir /sys/fs/cgroup/cpu/apache
chown -R apache.apache /sys/fs/cgroup/cpu/apache

例えば、以下のようにApacheに設定します。

LoadModule resource_manager_module modules/mod_resource_manager.so

ResourceManagedCPU 50000 example.com
ResourceManagedmruby /var/www/html/managed.mrb

この設定の場合、VirtualHostで複数ホストを動かしていた場合において、example.comとマッチしたホストは、CPU使用率が50%(50ms)に制限される設定です。また、以降(以降が本番)で詳しく説明しますが、mrubyスクリプトで制御する場合はあらかじめ制御スクリプトのパスを上記のように登録しておきます。

例えば、以下のようなループするCGIスクリプトがあったとします。

#!/bin/sh

while :; do true ; done

こんなCGIにアクセスすると、通常はCPUを100%フルに使ってしまいます。しかし、上記のような設定をしていると、CPU使用率を50%に抑える事ができます。実際にtopをみてみました。

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 1328 apache    20   0  3356  972  848 R 50.0  0.0   0:03.39 while.cgi
    1 root      20   0 16956 3560 1896 S  0.0  0.1   0:04.69 systemd
    2 root      20   0     0    0    0 S  0.0  0.0   0:00.65 kthreadd
    3 root      20   0     0    0    0 S  0.0  0.0   0:03.74 ksoftirqd/0
    6 root      RT   0     0    0    0 S  0.0  0.0   0:00.00 migration/0
    7 root      RT   0     0    0    0 S  0.0  0.0   0:07.72 watchdog/0
    8 root      RT   0     0    0    0 S  0.0  0.0   0:00.00 migration/1

このように、50%に制限されます。

使い方(mrubyスクリプトで制御)

さて、ここからが本番です。

このモジュールはmrubyスクリプトで制御できるように実装されています。そのため、管理用のmrubyスクリプトとして登録したmanaged.mrbを弄る事で、この設定を自由に変える事ができます。mrubyで利用できる拡張メソッドとして、CPU割当の現在の設定を取得、CPU割当を再設定、アクセスのあったファイルパスの取得、レスポンスで返すファイルパスを再設定、を用意しました。

例えば、一時的にもっと制限したい場合は、以下のようにmrubyスクリプトを記述する事で制限を50%から20%に抑え込む事ができます。また、mrubyがスクリプト言語であることを生かして、アクセスのあったファイルによって、CPU割当を動的に変更することができます。

例えば、正規表現を使うと以下のような処理が可能になります。今はまだmrubyで正規表現は動かなかった気がしますが、すぐに実装されるだろうということで想像で書いてみましてみました。こんな事ができちゃいます。

require "Resource"

r = Resource::Manager.new()

# 現在Apacheで設定されているCPUの制限Rate(今回だと50%)を取得
cur = r.cpurate

# この辺に色々条件式を入れると便利
# CPUの制限Rateを20%(20ms)に変更
if /^.*\.cgi/ =~ r.filename
    r.cpurate = 20000
elsif /^.*\.php/ =~ r.filename
    r.cpurate = 10000
elsif /^.*\.pl/ =~ r.filename
    r.cpurate = 5000
elsif /^.*\.rb/ =~ r.filename
    r.filename = "/path/to/tsukaemasen.html"
else
    r.cpurate = cur
end

こう記述する事で、Apacheにアクセスがあるとこのmrubyスクリプトが実行され、.cgiにアクセスがあった場合は設定が50%から20%の変更されると予想されます。これはmrubyの正規表現実装をまってからのお楽しみということで、実際は以下のようなmrubyを動かしてみました。

require "Resource"

r = Resource::Manager.new()

# 現在Apacheで設定されているCPUの制限Rateを取得
cur = r.cpurate

# この辺に色々条件式を入れると便利
# CPUの制限Rateを20%(20ms)に変更
r.cpurate = 20000

この状態で再度同じスクリプトにアクセスすると以下のようになりました。

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 1384 apache    20   0  3356  976  848 R 19.9  0.0   0:03.01 while.cgi
 1385 root      20   0  2888 1116  828 R  0.7  0.0   0:00.03 top
    1 root      20   0 16956 3560 1896 S  0.0  0.1   0:04.69 systemd
    2 root      20   0     0    0    0 S  0.0  0.0   0:00.65 kthreadd
    3 root      20   0     0    0    0 S  0.0  0.0   0:03.75 ksoftirqd/0

20%にCPUが抑えられていますね。

これで、CPUを異常に食うスクリプトが動いても、mrubyスクリプトを一発書き換えてやれば、CPUの割当を即時変更させる事ができます。また、mrubyで記述できる事を生かして、様々な制御の条件やチューニングが可能になるように思います。今はまだまだベータのベータといった所ですが、これがうまく機能してきて柔軟に様々なリソース値を制御できれば、高負荷対応もかなり楽になるんじゃないかと思いますので、是非mrubyで様々な記述をして遊んでみてください。

今後の課題

とりあえず今は実装してみたというレベルなので、コードの書き直しやオーバーヘッドの調査(cgroupの制御そのものがリソースを食ってないか)、詳細なテストや機能追加をやっていこうかなと思っています。

今後はさらに、リソースマネージャーの名前に負けないように、CPUだけでなく色々実装していこうと思っていますので、こうご期待ということで。

「mrubyでApacheのCPU割当てを制御できるモジュール作った」への1件のフィードバック

コメントは受け付けていません。