hitomedia Tech Blog

株式会社ヒトメディアのテクノロジーに関するブログです

ruby-jmeterで負荷テストをした話とTips

こんにちは。id:Tetsujinです。

ヒトメディアでは、いろいろとWebアプリケーションの開発や運用をしていますが、自社のものなのか、お客様向けに納品するのものなのか、などで作るものに求める・求められるレベル感も変わることが多く、アプリケーションの性能要件の決め方や、計る手法が確立していません。

いままでも、シンプルに測定するのはSiegeを使ったり、シナリオを組む場合はJMeterを使うこともあれば、Load Impactを契約して利用したこともありました。

また、テスト結果のレポート提出が必要な場合は、自分たちで実施せずにテスト会社様にお願いをしたりと、都度都度、状況にあわせてやる、やらない、やり方を決めていました。

最近、改めてJMeter(お客様の要望もあり)で負荷テストをすることがあり、これを機にもう少しノウハウを蓄積していこうと取り組んだので、Tipsを共有します。

ruby-jmeterについて

ruby-jmeterを使うと、RubyDSLでテストシナリオを書いてjmx(JMeterの設定ファイル)を生成することができます。

https://github.com/flood-io/ruby-jmeter

  • JMeterGUIでポチポチ操作しなくてよくなる。
  • テストシナリオがコードでバージョン管理できる。
  • いつでもテストシナリオを再生性可能であるという安心感。

ただ、結局JMeter自体の知識は必要となるので、JMeterのドキュメントは読む必要がありますし、それに対応するruby-jmeterのコードってどうなるんだっけ?とDSLの中身を追いかける必要もでてくるので、 敷居が低いのかというと、そうでは無いと感じています。

インストール方法や使い方などはインターネット上に、非常にわかりやすい記事が沢山あります。

Tips

今後も他のアプリケーションに対して、ruby-jmeterを使う時にも流用しようかなと思っているTipsです。

環境変数で環境毎の設定を変更する

direnvdotenvなどを使って環境変数で、テスト対象の環境毎の細かな設定変更をするようにしました。

JMeter的には、 user_defined_variables で変更すべき項目なのかもしれませんが、都度、設定変更するのも面倒なので環境変数で定義しておいて差し替えられるようにしています。

ローカル環境ではBASIC認証をかけていませんが、ステージング環境ではかけていたのでその切り替えや

if ENV["JMETER_AUTH_USER"]
  http_authorization_manager url: ENV["JMETER_BASE_URL"],
                             username: ENV["JMETER_AUTH_USER"],
                             password: ENV["JMETER_AUTH_PASSWORD"]
end

ステージング環境ではJMeterのクライアント側マシンのファイルパスが異なったためその切り替えや

csv_data_set_config filename: "#{ENV["JMETER_DATA_DIR"]}/data1.csv"

direnvを使って、負荷テストを実施する環境に応じてjmxを生成できるようにしています。

.envrc

export JMETER_BASE_URL="https://example.com"
export JMETER_DATA_DIR="$PWD/data"

.envrc.d/staging/.envrc:sh

export JMETER_BASE_URL="https://staging.example.com"
export JMETER_AUTH_USER="user"
export JMETER_AUTH_PASSWORD="password"
export JMETER_DATA_DIR="C:\\jmeter\\data"

Gemfile

source 'https://rubygems.org'
gem 'ruby-jmeter'

コマンドラインから生成します。

# ローカル向け
$ bundle exec ruby generate_jmx.rb

# ステージング向け
$ direnv exec .envrc.d/staging bundle exec ruby generate_jmx.rb

ロジックの共通化

今回のプロジェクトでは、シナリオ毎の共通点が多かったのでログイン処理などは一部、モジュールに切り出して共通化しました。

module/auth.rb

module Auth
  def login
    -> {
      visit name: "ログイン - 表示", url: "#{ENV["JMETER_BASE_URL"]}/login" do
        extract name: "csrf-token", xpath: '//*[@id="_token"]/@value', tolerant: true
      end

      submit name: "ログイン - 実行", url: "#{ENV["JMETER_BASE_URL"]}/login", always_encode: true,
             fill_in: {
               "LoginForm[_token]"   => "${csrf-token}",
               "LoginForm[login_id]" => "${login_id}",
               "LoginForm[password]" => "${password}",
             } do
      end
    }
  end
end

generate_jmx.rb

require 'ruby-jmeter'

require_relative 'module/Auth'
include Auth

test name: 'example' do
  threads name: "シナリオ1", count: 10, loop: 1 do
    csv_data_set_config filename: "#{ENV["JMETER_DATA_DIR"]}/data1.csv",
                        shareMode: "shareMode.group",
                        quotedData: true,
                        recycle: false,
                        stopThread: true,
                        fileEncoding: "Shift-JIS"
    cookies
    login.call
    # 以降、シナリオのコード
    # ...
  end
end.jmx(file: 'example.jmx')

あまりやり過ぎると、シナリオの見通しが悪くなるため、適度にやるのがよさそうです

テストデータの定義

負荷テストのシナリオで利用するテストデータは csv_data_set_configCSVを読み込んでいます。

csv_data_set_config filename: "#{ENV["JMETER_DATA_DIR"]}/data1.csv",
                    shareMode: "shareMode.group",
                    quotedData: true,
                    recycle: false,
                    stopThread: true,
                    fileEncoding: "Shift-JIS"

今回のプロジェクトでテストに必要なデータは、1レコードにつき100列ほど定義が必要だったため、CSVを直接管理するのは無理だ。と感じて結果的にExcel管理にしました。

もう少し良い管理方法はありそうなのですが、お客様に「このデータでテストします〜」という提示、ファイルの受け渡しも必要だったので容易さも考えてExcelで。

こんな感じでシナリオ毎のデータをシートを分けて定義しておき、CSVに変換します。 f:id:Tetsujin:20170722155227p:plainf:id:Tetsujin:20170722155235p:plain

1列目のヘッダが変数名となり、参照できるようになります。

submit name: "ログイン - 実行", url: "#{ENV['JMETER_BASE_URL']}/login", always_encode: true,
       fill_in: {
         'LoginForm[_token]'   => '${csrf-token}',
         'LoginForm[login_id]' => '${login_id}',
         'LoginForm[password]' => '${password}',
       } do
end

さいごに

というわけで、今回プロジェクトでruby-jmeterを使ってみましたが、なんでもかんでもruby-jmeterでシナリオ書いて負荷テストをするか?と言うと、そうは考えてません。

以下のような場合は、ruby-jmeterが有用だと思っています。

  • テスト対象のアプリケーションの仕様をコードレベルで把握している。
  • 使い捨てじゃなくて、継続的にテストを管理・実行していくつもりがある。

ですが、テスト対象のアプリケーションの仕様をあまり把握はしてないけどテストをすると言う場合は、無理にruby-jmeterは使わず、JMeterのHTTP Proxyをブラウザに通して、ブラウジングを記録させてシナリオを作っていた方が、効率的なんじゃないかなと思っています。

参考: http://inokara.hateblo.jp/entry/2015/08/25/060119

オフィスの空気をモニタリングして改善する話

今年、35歳エンジニア定年説の年齢になり
体調に不調をきたし、もっぱら瞑想と体調管理の本ばかり読んでいるid:kenjiskywalkerです。

最近のおすすめは

この辺です。

オフィスの改善

今回の話はオフィスの空気(物理)を改善するための施策を行ったお話です。

前提

オフィスで働いているメンバーから、オフィスの空気(物理)が
悪い印象があるという声が多くありました。

技術勉強会でこの話をした時に参加者に「空気(物理)が悪いと感じたことがありますか」
とインタビューしたところ、8割以上のメンバーが感じたことがある。と回答していました😅

もし、執務室の二酸化炭素濃度が高く、メンバーのクリエイティビティが
下がっている可能性があるのなら、そこは改善しようという運びとなりました。

参考

チームラボオフィスの空気環境(CO2)を測定して改善している話:tks(高須 正和)のブロマガ - ブロマガ

課題選択

まずは本当に空気が悪いのかモニタリングをすることにしました🌡

解決方法

MH-Z19BME280を利用し
Raspberry Piにて計測しようと思ってチャレンジしてみましたが
MH-Z19がうまく二酸化炭素濃度を測れずあきらめました😇

Netatmo

https://www.netatmo.com/ja-JP/product/aircare/homecoach

利用したのはこちら

なにができるか

これらを計測できるものです。

いくらするの?

21,795円🙄

会社の環境改善ということで経費で購入させてもらいました🙇

ということで

netatmoを利用して

を計測するようにしました

f:id:kenjiskywalker:20170629164823p:plain

取得したデータをどのようにモニタリングするか

都度Webで確認するのも手間なので
APIからデータを取得してSlackに通知するようにしました⛩

  • APIの参考URL

Oauth

https://dev.netatmo.com/resources/technical/guides/authentication/clientcredentials

データ取得 https://dev.netatmo.com/resources/technical/reference/weatherstation/getstationsdata

参考コード

  • 設定した閾値を超えたら通知するくん🚀

https://gist.github.com/kenjiskywalker/4d5fcb9a9edf6dd754c6594d830351a4

どこで動かしている?

社内においてあるRaspberry Piのcronで動かしています😉

どうやって通知している?

こんな感じで通知されます

f:id:kenjiskywalker:20170629165247p:plain

結果どうだった?

たまに二酸化炭素濃度が1600pmを超えるタイミングもありましたが
その度に主導で窓を開けたりして対応し、二酸化炭素濃度は落ち着きました。

また、加湿器が動いていなく
冬の時期に湿度が30%を切るタイミングに気づけたり、
勉強会でのアンケートで、botが動くようになってから
「会社の空気(物理)が良くなったか?」というアンケートで 9割を超えるメンバーが「良くなったと実感した」という回答をしてくれて

21,795円🙄を無駄金にせずにすみました。
今後はIoTで自動で窓の開閉までできると良いなと考えています☺️