メドピア開発者ブログスマートフォン用の表示で見るメドピア開発者ブログ集合知により医療を再発明しようと邁進しているヘルステックリーディングカンパニーのエンジニアブログです。Tumblrこんにちは。Ruby化をすすめるメドピアをお手伝いしている@willnetといいます。メドピアではPHPからRubyに移行するにあたり、単純に言語を置き換えるだけではなく、言語以外の仕組みについても適宜見直しを行っています。今回はそのうちジョブスケジューラを見直した件について書いていきます。言語を置き換えた話はこちらを参考にしてください。メドピア開発者ブログそもそもジョブスケジューラってなに「毎日1時になったら前日のアクセスログを集計して統計データとしてまとめる」などといった定期的に実行するジョブを登録するためのものです。ウェブサービスを作るときのジョブスケジューラといったらやっぱりcronですよね。メドピアでもこれまでcronを活用していました。サービスが小さいうちはcronでもそれほど問題ないのですが、サービスが育つにつれだんだん問題が顕になってきます。crontabファイルを編集した場合、変更の履歴が残りません。以前のバージョンに切り戻したり、過去の履歴を振り返ったりすることができません。-rでcrontabの設定を消去してしまった経験のある人はチームに一人くらいいるんじゃないでしょうか。間違えやすい記法crontabの記法は一見してわかりづらく、設定ミスをしやすいものになっています。次のようなcronを設定したとします。/your/batch/commandこれは毎月1日の2時5分に/your/batch/commandを実行するcronジョブですが、ぱっと見ただけで理解するのは難しくはないでしょうか。あとは*/10とか1-5,10-20とかの特殊記号を使ったときも、本当にこの書き方で想定通りの時間に実行できるのか不安になります。cronサーバを分散できないウェブサービスで負荷が高まったときには、仮にアプリケーションサーバがボトルネックであれば同じサーバを追加(スケールアウト)することで負荷を分散することができます。cronサーバで同じことをすると複数のサーバで同じジョブが同時に実行されることになってしまいます。そのため大抵の環境においてcronサーバは1台だけで運用されているはずです。開発が進みcronジョブが増えてくると、時間帯によっては複数のジョブが1台のcronサーバで並列に実行されて負荷が異常に高まり、予想しない挙動や障害につながっていきます。デバッグしづらいcronの環境と、普段使っているシェルの環境との違いでジョブが失敗することはよくありますが、特にそれを示唆するような出力はないことが多いです。そのためこの手の経験がないと「手元で実行すると動くのになぜかcronだと動かない!なぜ!?」のようにハマります。ログを追いづらいcronを実行した際の出力はメールで送信されますが、2017年の現在その機能を使っている方はあまりいないのではないでしょうか。普段は自前でログを頑張って出力したものを保持しておき、エラーが起きたときにはAirbrakeなどのエラー管理システムと連携して通知、ログを漁って原因を究明する...というケースが多そうです。ジョブごとに成功/失敗のステータスやログがまとまっていると便利ですが、そこまで自前で実装するのは大変ですね。やって問題を解決するかcronの問題についていろいろ書きましたが、どのようにしたらこれらの問題を解決できるのでしょうか。cronそのものを改善させる方向と、cronをやめる方向で考えてみます。改善させるこれまで挙げたcronの問題のうちいくつかはwheneverというgemを利用することで改善可能です。使っている方も多いのではないでしょうか。wheneverを使うと次のような記法でジョブをファイルに定義し、crontab用の記法に変換して登録することができます。"MyModel.task_to_run_at_four_thirty_in_the_morning"endこのファイルをバージョン管理しておき、デプロイ時に自動でcrontabを更新するようにします。これで、バージョン管理の問題と、記法の問題を解決できました。他の問題は変わらず残っています。やめる調べると、cronを置き換えようとするプロジェクトはたくさんあることがわかります。(enterprise版)cronoresque-schedulerclockwork上記はなるべくRubyで作られたツールの中から選びました。Rubyにこだわらなければもっとたくさんの選択肢があるわけです。全ての問題を完全に解決するツールは存在しなかったので、次のような観点でツールを選定しました。使ったプロジェクトであることメンテナンスが続いていること最悪メンテナンスが停滞しても自分たちでなんとかできること導入のしやすさ結果として、sidekiq-cronを採用することにしました。バックグラウンドジョブを扱うgemであるsidekiqを拡張し、cron的な機能を追加してくれるgemです。uiを用意しているので、ジョブの状態を確認できる*1などがあります。慣れているsidekiqをそのまま使う感覚でいけるのがいいですね。時間を指定する記法はcrontabと同じだったり、詳細なログ出力は独自で頑張る必要がありますが、cron単体の時よりはだいぶ前進できた気がします。将来的にどうするかsidekiq-cronは本番導入されており、今の段階では特に問題はないのですが、遅くても数年したら乗り換えを検討する必要がありそうです。複数のRailsアプリを管理するようになった場合に、複数アプリを横断したジョブスケジューラを管理したいという要望にはsidekiq-cronでは応えられません。ジョブの数が数十〜百になった場合にジョブをどうやって管理するかも悩ましいところですし、ログの出力が弱いことが問題となるケースも今後出てくるでしょう。そんな折に昨年OSS化されたkuroko2を軽く触ってみたところ、なかなか良さそうでした*2。次回乗り換えを検討する際の有力な候補となりそうです。kuroko2の詳細は以下のリンクを参考にどうぞ。Developers.IOまとめ開発しているアプリケーションの規模によって適切なツールは変わってきます。小規模なアプリケーションのジョブスケジューラであればcronはまったく問題ないと思いますが、アプリケーションが成長していくにつれてより適したツールに乗り換えていく必要があるでしょう。このエントリが次のツール選定の参考になれば幸いです。*1:sidekiqの仕様上、WebUI上にログはほぼ残らないので、sidekiq-failuresを利用して失敗したジョブのエラーを閲覧できるようにしています。SREの侘美(たくみ)です。開発事例公開~こんにちは。エンジニアの宮原です。月別アーカイブリンク医師専用サイト「MedPeer」エンジニア採用はこちら!スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。