kmuto’s blog

はてな社でMackerel CREをやっています。料理と旅行といろんなIT技術

また電子のゴミを作りやがって、あるいは面白実験Webサーバー

先日作ったsaba-memo-runnerをデモのためのEC2オートスケールで試してみて、ターゲット追跡でスケールアウト/インすることはできたものの、互いに作用して負荷分散されていくという使い方はやはりできないな、という予想どおりの結論に達した。

kmuto.hatenablog.com

ということで、今度はロードバランサーの裏側に立ててアプリケーションサーバー負荷っぽい動きをすることを目的としたRubyスクリプトをちょっと作ってみた。

github.com

Sinatraで5分クッキング。入力値チェックなどいろいろ雑だが、利用シーンとしてパブリックに出すものではないのでまぁいいだろう。

リクエストURLパスで挙動を変えている。

  • / : Hello Worldを返す
  • /status/数字: 数字に指定したステータスコードHello Worldを返す
  • /sleep/秒数: 指定秒数待機してからHello Worldを返す
  • /stress/CPU数/秒数: CPU数ぶんの負荷を秒数ぶん実行する。stressコマンドを呼び出している

アプリケーションサーバー負荷っぽいのを狙いとしているのは最後のやつね。

Mackerelのホストメモに書いた命令を実行するスクリプトを作ってみた

ホストメモに命令書いておくと、それを見てホストで実行してくれるくん。saba-memo-runnerと名付けた。

github.com

EC2オートスケールにおけるフリートのメトリクスベースでのスケールアウト/インを、ログインしてコマンド叩くことなしに、またオーケストレーションコントロールホスト/クラスタみたいな大掛かりな構成なしに、何かうまく動的に引き起こせないかと考えていた。

EC2はいずれもMackerelの監視下に登録されているものとする。

Mackerelの思想上、Mackerelサーバー側からエージェント側に何か働きかけることはできないが、MakerelのWebコンソール上で任意記入できる箇所に指示を書いておき、これをエージェントを動かしているホスト側からポーリングで拾い出すということは考えられる。

以前にMackerelのカスタムダッシュボードを使ってフィーチャーフラグ切り替えの仕掛けを実装したことがあった。今回は対象が単一のホストなので、よりシンプルにホストのメモ覧を転用してみる。

メモ覧はホストの画面の上部に目立つところにあり、書き換えが簡単なので、うってつけだ。本来はホストについて人間向けの任意の情報を記述することを目的としている。

何でも取り込んで実行するのはさすがに危ないのと、普通のメモとしての使い方を邪魔したくないという思いがあり、「特定の命令名と一致する行があったらそれを実行する」という仕組みにした。命令名は英数字か_で構成された名前で、その命令名に対して実際に何を具体的に実行するかは別途定義するようにしている(Nagios仕草)。

たとえば「stress」という命令名に「touch stress.lock; stress -c 2 -t 59; rm stress.lock」という実際のコマンド群(ここではCPU負荷を59秒かける)を紐付けておく。ロックファイルを作っているのは次のポーリングの際に実行が完了していないときに多重実行するのを防ぐためだ(ロックファイルの存在チェックはスクリプト側で持っている)。

こうして設定した状態で、ホストのメモに「stress」の行を追加する。saba-memo-runnerのポーリングでこれが検出され、CPU負荷が実行される。メモから「stress」を消せばそれ異常のCPU負荷実行はされなくなり、元に戻る。

オーガニゼーションのユーザーが限定的で信頼できる前提ではあるが、たとえばサービスの再起動の類いを書いておいて定型的な障害に対してメモから発行するとか、フィーチャーフラグでログをより詳細に書き出すモードにさせるとか、いろいろ面白い使い方もできるだろう。

余談

過去に作ったダッシュボード経由の制御はRubyで実装していたのだが、今回はエージェントの入っている各ホスト内で実行することが前提のため、できるだけLinux OSの最小構成で動かすべくbashでの実装にした(mkrはさすがに許容。curlでやったところで結局jqがほしくなるわけだし……)。

スクリプト自体は(長さからもわかるとおり)10分くらいで完成していて、2時間くらいsystemd-timerの挙動と挌闘していた。命令名の内容をbash -cのバックグラウンドジョブで実行するという構成は、systemdのプロセスグループの仕組みと相性が悪い。そのままだとsaba-memo-runner.shの作業終了と同時にバックグラウンド化していたジョブも即終了する。RemainAfterExit=yesは上げたserviceをそのままactiveで残すというものなので、これをやると一度serviceの実行が走った後は永遠にtimerが走らないという状況になる。

結局oneshotのままKillMode=processを指定して子プロセスについては行っとけモデルで回避しているが、気持ちが悪いのは否めない。真面目にプロセス管理するにはtimerではなく常駐デーモン化する必要があるが、bashでがんばるのはいささか無理があるだろう。良い点としては、これまでcronしか使ってこなかったけれど、今回でsystemd-timerについて理解が進んだ(systemctl list-timersとか)。

口頭デモで併用して使うことを当初の目的としていたけれども、実際にはオートスケールの反映自体が時間差があるので、話のテンポが悪くなってダメだなという結論ではある。とはいえ、「これがこうなってこうじゃ」という早送り的なスナップショット画面を作るのには便利になった。