cron から Rundeck に乗り換えると決めた話

こんにちは、鈴木です。

cron でバッチの実行制御を行っている環境に、Rundeck というジョブスケジューラの導入を進めています。cron をやめようとした理由や、Rundeck を検証した結果をお話しします。

cron をやめる

cron の安心感

cron は十分に枯れている、実績のあるソフトウェアです。安定して動き、情報も世の中に多くあります。何かを定期実行するときに、とりあえず cron を選んでおけば大丈夫である場合も多いです。

cron をやめる理由

今回 cron をやめようと考えた理由は以下の通りです。

  • ジョブ数が多くて辛くなってきたから。
  • 権限管理やエラー時のリトライ、監査系ログなどの機能が欲しかったから。

ジョブが数十個、数百個の規模になってくると「このジョブはリソースを大量消費するので別サーバで動かそう」、「ジョブの役割ごとにサーバを分けよう」といった具合にサーバ数が増えていきます。そのような状況になると、cron では一元管理することが難しくなります。

他にも「A が終わったら B を実行し、B が終わったら C と D を実行する」といった依存関係のあるジョブの扱いや、エラー時にリトライするといった機能も cron にはありません。

cron の守備範囲を超えた時が、cron をやめて別のジョブスケジューラに乗り換えるタイミングだということです。

ジョブスケジューラを選定する 

 ジョブスケジューラの一般的な特徴

cron をやめると決めました。それでは具体的にどのジョブスケジューラを使うべきでしょうか。ジョブスケジューラについて調べると、いくつか共通する特徴があることが分かります。以下にポイントとなりそうな点を挙げますので、ジョブスケジューラを選ぶときには、最初にどのタイプにするか決めると良いでしょう。

機能面
  • ジョブの定期実行(cron, Rundeck, Digdag 等)*1
  • ジョブのキューイング・非同期実行(Sidekiq, DelayedJob 等)*2
  • パイプライン制御(Airflow, Luigi, Rundeck, Digdag 等)*3
  • ジョブの分散実行(Dataflow, Hadoop ベースのシステム)
構成面
  • プログラムにライブラリとして組み込むタイプ(Luigi 等)
  • 単一サーバ内で動くタイプ(cron 等)
  • 管理サーバと実行ノードが分かれているタイプ(Rundeck, Digdag, Airflow 等)
  • マネージドサービス(Dataflow, AWS Batch 等)

Rundeck を選んだ理由

「私が Rundeck を選ぶたった 1 つの理由」のような分かりやすいものはなく、以下のように選定基準を書き出して、それを地道に検証することを繰り返しました。

  1. 選定基準を書き出し、「必須 / あれば嬉しい / あっても無くても良い」に分類する。
  2. 検索したり周りの人に聞くなどして見つけたジョブスケジューラを 1. の基準で評価する。

1. で大切となるのは、対象とするシステムにおいて必須の機能を明確にすることです。高機能なジョブスケジューラではなく、導入しようとしている環境で必要となる機能を満たすジョブスケジューラを選ばなければなりません。検索して有名っぽいものを「えいやー」と選んでしまうと、あとから「高機能だけど、やりたいことはできなかった...」ということになりかねません。

具体的な選定基準

具体的な選定基準は以下の通りです。「これって必須じゃなくて良いの?」と思われる項目があるかもしれませんが、今回 Rundeck を導入しようとした環境で必要かどうかで分類しています。

  • 【必須】複数のプログラミング言語でジョブを実装できる。*4
  • 【必須】cron のように時間を基準としてスケジュール実行できる。
  • 【不要】incron のように時間以外の条件でジョブを実行できる。
  • 【必須】ジョブをアドホックに即時実行できる。*5
  • 【必須】ジョブをアドホックに予約実行できる。*6
  • 【必須】ジョブがエラー終了した場合に、後続のジョブを起動しない設定ができる。*7
  • 【必須】エラー終了したジョブを手動で再実行できる。*8
  • 【あると嬉しい】ジョブにリトライ回数を設定できる。
  • 【不要】単一のジョブを実行中に停止できる。
  • 【あると嬉しい】依存関係のある一連のジョブを途中で停止できる。*9
  • 【必須】排他制御のための仕組みがある(個別のジョブで排他制御を実装しなくて良い)。
  • 【必須】ジョブの実行時間に上限を設定できる(上限を超えた場合にエラー扱いにできる)。
  • 【必須】個々のジョブが独立して実行される。*10
  • 【必須】異なるホストで実行されるジョブを制御できる。*11
  • 【必須】A → B → C のようなシーケンシャルなジョブの依存関係を定義できる。
  • 【必須】A → (B, C) → D のような分岐/合流があるジョブの依存関係を定義できる。
  • 【必須】ジョブが終了した時に通知できる。*12
  • 【不要】ログ出力のための仕組みを持つ。*13
  • 【不要】ログ集約のための仕組みを持つ。*14
  • 【必須】ジョブの実行時間を取得できる。
  • 【不要】ジョブの実行予定を確認できる。*15
  • 【必須】無停止でジョブを設定変更できる。*16
  • 【必須】認可機能がある。*17
  • 【必須】十分に枯れている。
  • 【必須】継続的に開発(メンテナンス)されている。
  • 【必須】現実的な運用コスト/難易度である。
  • 【必須】ジョブ設定をファイルとしてインポート/エスクポートできる。*18
  • 【必須】API があること。*19
  • 【必須】Web UI があること。*20
  • 【あると嬉しい】世の中での運用実績。
  • 【必須】監査や統制上必要となるログが記録できること。
  • 【必須】スケールアウトできること。*21
  • 【必須】冗長構成が取れること。*22
  • etc...*23

 最後に

ということで、cron から Rundeck に乗り換えることに決めました!

最初は Rundeck の冗長構成について検証したことを書こうと思っていたのですが、その前の話だけで長くなってしまいました。冗長化については次回に書こうと思います。

 

*1:「毎日 10:00 に実行する」といったことを実現するための機能。

*2:メインの処理と同期的に実行する必要がない処理を非同期化するために必要な機能。例えば、完了通知メールを送る、外部 API を呼び出す、データのインポート完了まで待つ、といった処理はネットワークの状態やデータ量によっては短時間で終わらない可能性があります。そのような処理を非同期化することで、メインの処理の実行時間を安定させることができます。

*3:「A が終わったら B を起動する、B が終わったら C と D を起動する」といった依存関係を制御するための機能です。

*4:ライブラリとして組み込むタイプのジョブスケジューラの場合、実装言語が限定されることがあります。例えば Luigi は Python モジュールとして利用するため、ジョブも Python で記述しなければなりません。

*5:「定期実行とは別のタイミングで単発実行したい。」という場合に、サーバに ssh ログインして作業するのではなく、API や Web UI などで実行したい。

*6:「定期実行とは別のタイミングで単発実行したいが、それは今すぐではなく 20:00 だ。」という場合に、20:00 まで待つのではなく、予約だけして 18:00 に帰りたい。

*7:「A が終わったら B を実行する」という依存関係があるとして、「A が失敗したら B は実行しない」という設定をしたい。

*8:手段は何でも良いので、Web UI や API などから再実行したい。

*9:例えば、A を実行中に停止指示すると、A の実行完了まで待つが、後続の B は実行されない。

*10:ジョブの実行プロセスが共有されるタイプのジョブスケジューラの場合、あるジョブがクラッシュすると他のジョブも巻き添えでクラッシュすることがあります。

*11:cron のように単一サーバを前提とするのではなく、ジョブの管理サーバと実行ノードが分かれていること。

*12:メールや WebHooks などで通知したい。

*13:ライブラリとして組み込むタイプのジョブスケジューラを想定した選定項目。ライブラリ側が Logger などを提供しているかどうか。

*14:ジョブの管理サーバと実行ノードが分かれているタイプのジョブスケジューラを想定した選定項目。複数の実行ノードのログを集約する手段があれば、実現方法は何でも良い。

*15:実行済みの結果ではなく、直近で実行予定のジョブを確認できる。

*16:ライブラリとして組み込むタイプのジョブスケジューラを想定した選定項目。設定変更のためにリリースしたくない。

*17:ジョブ設定を参照だけできる人、ジョブを実行できる人、のように権限設定できること。単独で実現できなくても別の手段で実現できるならそれでも良い。

*18:ジョブ設定を Git で管理したい。

*19:ジョブの設定変更をブラウザ以外で行いたい。

*20:サーバにログインせずにジョブの設定などを確認したい。

*21:ジョブスケジューラ自体の負荷が高くなった場合に、サーバを増やすという選択肢が欲しい。

*22:ジョブスケジューラ自体に障害が発生した場合に、全体のジョブが止まってしまうと困る。コールドスタンバイでも良いので、別サーバにフェイルオーバーできるように備えたい。

*23:「Web UI 上でジョブをグルーピングできる」などの細かいことは省略しました。