Google Compute Engine での信頼性の高いタスクのスケジューリング

Google Compute Engine インスタンスのネットワークなどの分散型システムでは、オートスケーリングやネットワークのパーティショニングによって個々のインスタンスを使用できなくなる場合があるため、タスクを確実にスケジューリングすることは困難です。

Google App Engine では、Cron サービスを使用できます。このサービスをスケジューリングに使用し、Google Cloud Pub/Sub を分散メッセージングに使用することで、一連の Compute Engine インスタンス全体にタスクを確実にスケジューリングするアプリケーションを作成できます。

この記事は、以下の 3 つのパートに分かれています。

Google Compute Engine で確実にタスクをスケジューリングする方法

Cron は、Unix システムで反復タスクをスケジューリングするための標準ツールです。作成するシステムの複雑さが増し、分散型になっていくと、cron を実行している 1 台のコンピュータでさえ、重大な障害点になる可能性があります。このインスタンスはオートスケーリングで停止する可能性があり、ネットワーク セグメントは、通信する必要のあるシステムから分割される可能性があります。

ロードバランサの背後にあるインスタンスのグループをスケールアウトするなど、可用性を高めるためのその他の対策は、システム全体によって一度しか実行する必要のないイベントをスケジューリングする場合には機能しません。たとえば、4 台のサーバーが同じ 1 群の cron ジョブを毎時実行する場合、リソースの競合が発生し、結果が重複する可能性があります。

分散型システムでタスクをどのようにスケジューリングするかという問題を解決するのは簡単ではありません。この問題の解決策の 1 つとして、Apache Mesos 用の Chronos フレームワークがありますが、膨大な量のセットアップと管理を行う必要が生じます。

App Engine には Cron Service が用意されています。アプリケーションが App Engine で実行されている場合、単に App Engine ハンドラを作成してイベントをスケジューリングすると、Cron Service がアプリケーションに代わってイベントを開始し、対応するイベント ハンドラを呼び出します。Cron Service イベントへの応答として Compute Engine インスタンスでタスクを実行するには、Cron Service イベントをこれらのインスタンスにリレーする必要があります。

これを行う方法はいくつかあります。以下に例を示します。

  1. App Engine の URL フェッチ サービスを使用して、Compute Engine インスタンスで実行している HTTP エンドポイントを呼び出します。
  2. Google Cloud Datastore トランザクションを使用して、タスクとロックをオーケストレートします。
  3. メッセージング サービスを使用して、App Engine で実行されているイベント ハンドラから Compute Engine インスタンスにメッセージを渡します。

このサンプルは、3 番目の設計パターンを示しています。これは、Cloud Datastore でロックとタスクの状態を管理する場合よりも、実装が単純です。また、HTTP リクエストを Compute Engine インスタンスに送信する場合は、タスクが完了する前にネットワーク接続が停止したり、失われたりする可能性があるため、この方法のほうが信頼性があります。

次の図は、この設計パターンの構造を簡単に示しています。

アーキテクチャの概要図

この実装では、App Engine アプリケーションが Cron Service でイベントをスケジューリングし、Google Cloud Pub/Sub を使用してそれらのイベントを Compute Engine インスタンスに送信します。Cloud Pub/Sub は完全に管理されたクラウド サービスであり、アプリケーション間の堅牢な、多対多の非同期メッセージングを行えます。

Compute Engine インスタンス上のユーティリティ サービスは Cloud Pub/Sub トピックにサブスクライブし、それらのトピックからプルダウンするイベントへの応答で cron ジョブを実行します。このユーティリティは標準スクリプトを実行します。つまり、このサンプルでは、それらを使用するために現在の cron スクリプトを変更する必要はありません。

Cloud Pub/Sub を使用して Compute Engine でコマンドを実行しているロジックからタスク スケジューリング ロジックを分離することで、App Engine アプリケーションを更新または再デプロイせずに、必要に応じて cron スクリプトを更新できます。また、Compute Engine インスタンスでユーティリティを更新しなくても、タスク スケジュールを変更できます。

割り当て

通常、cron ジョブは数が少なく、時間、週、または日単位で実行されるため、この設計パターンは容量の大きいオペレーション用に設計されているサービス割り当てを超過してはいけません。超過する場合、アプリケーション コードから直接タスクのタイミングを管理する方法など、他の適用パターンを検討してください。

費用

この設計パターンのランニング コストは、1 か月 1 ドル未満です。この費用分析は、1 つの cron ジョブが 12 時間ごとに実行され、f1 マイクロ インスタンスごとに毎回 1 時間を要することを前提としています。この見積もりの費用内訳と、独自のユースケースでの費用計算方法については、Google Cloud Platform 料金計算ツールを参照してください。

App Engine では、1 日あたり 8 インスタンスの無料バックエンド割り当てが提供されます。他のアプリケーションでこの割り当てを使用していない場合、この設計パターンを実行するための App Engine の費用は 0 ドルであり、費用はさらに削減されます。

たとえば、次のセクションのサンプル実装を 1 時間実行した後、その Google Cloud リソースを削除すると、費用はおよそ 1 セントになります。

設計パターンのサンプル実装

この設計パターンのサンプル実装であるサンプル: Google Compute Engine での信頼性の高いタスクのスケジューリングは、GitHub で入手できます。

このサンプルには、次の 2 つのコンポーネントが含まれています。

  • App Engine Cron Service を使用して cron メッセージを Cloud Pub/Sub トピックにリレーする App Engine アプリケーション。

  • Compute Engine で実行されるユーティリティ。このユーティリティは Cloud Pub/Sub トピックをモニタリングします。新しいメッセージを検出すると、サーバー上で対応するコマンドをローカルで実行します。

サンプルに含まれている readme ファイルには、サンプルの詳細情報と、Google Cloud Platform でサンプルコードを実行する方法の説明があります。

設計パターンとサンプルでのビルド

App Engine Cron サービスを使用すると、設定作業と費用を最小限に抑えながら、Compute Engine インスタンス上に分散型 cron を簡単に設定できます。

前のサンプルには、App Engine Cron Service を使用して、Compute Engine 用の信頼性のあるスケジューリング ソリューションを実装する 1 つの方法が示されていました。これは、Compute Engine インスタンスでコマンドを実行するロジックからスケジューリング ロジックを分離することで、スケジューリング ロジックを更新しなくてもタスクのロケーションや実行を変更できるという点で、便利な設計パターンと言えます。

次の図は、このサンプルでの cron メッセージのフローを示しています。所定のトピックをサブスクライブするインスタンスを指定することで、cron ジョブが単一のインスタンスまたは複数のインスタンスのどちらで実行されるのかを制御できます。

詳細なアーキテクチャの図

このアーキテクチャのもう 1 つの利点は、どのように cron ジョブがインスタンスにルーティングされるかを制御できるということです。

Cloud Pub/Sub のトピック A および C に示されているように、さまざまな cron メッセージを異なるサーバーセットに送信できます。トピック A のタスクは単一のサブスクライバに送信される一方、複数のサーバーがトピック C にサブスクライブします。この方法を使用して、ウェブサーバーでコマンドの 1 つのセットを実行し、他のサーバーで別のセットを実行できます。

さらに、複数あるサーバーのいずれか 1 台のサーバーでコマンドを実行することもできます。これは、トピック B で示されています。この場合、複数のサーバーが単一のサブスクリプションを共有し、トピック B にパブリッシュされるメッセージは最初のサーバーで処理され、そのメッセージと対応するコマンドがそのサーバーでのみ実行されるように要求します。これを使用して、単一のサーバーでのみ実行する必要のある夜間データ解析を実行できます。

次のステップ

サンプルを修正して、独自のアプリケーション用のモデルとして使用できます。作業を開始するときには、以下を参考にしてください。

  • cron.yaml を更新して、使用する cron メッセージを指定します。Cron ジョブのアップロードで説明されているように、cron ジョブを直接更新したり、App Engine アプリケーションを再デプロイして App Engine Cron Service を更新したりすることができます。

  • logger_sample_task.py ではなく、実際のスクリプトが実行されるように test_executor.py を更新するか、独自の Executor ユーティリティを作成します。

  • Compute Engine でユーティリティを手動で起動し、フォアグラウンド プロセスとして実行するのではなく、システム、または systemdSupervisor などのサードパーティ ツールによってデーモンとして自動的に起動できます。

  • App Engine Cron サービスと Cloud Pub/Sub では、どちらも厳密な「正確に 1 回」の配信は保証されていません。可能性は低いものの、メッセージが重複して配信される可能性があります。特定のタスクを複数回実行することで望ましくない結果が生じる場合、Zookeeper などの分散型永続ロッキング ツールを使用して、タスクが単一のインスタンスによって確実に 1 回のみ実行されるようにしてください。

  • 複数のタスクをスケジューリングするときは、cron のベスト プラクティスに従い、タスクが次回実行されるまでに、タスクが処理を完了できるように、各タスクの間隔を十分に取ってスケジューリングします。

  • マイクロ インスタンスでユーティリティを実行し、コスト効率の良い cron ソリューションを作成します。このソリューションはタスクを受け取ると、より強力なインスタンスを開始し、cron タスクをすばやく処理します。タスクが完了したときに、より大きいインスタンス自身が自動的にシャットダウンするように設定できます。これにより、最小限のコストで、イベント時刻のすぐ後にタスクを完了するように、柔軟に対応できます。

  • この実装では、App Engine アプリケーションとともに、cron.yaml が App Engine にデプロイされます。そのため、cron メッセージに何らかの変更を加えると、App Engine アプリケーションの再デプロイが必要になります。これを回避するには、Cloud Storage バケットから YAML ファイルをプルし、ファイルに変更がないかそれにモニタリングさせ、YAML ファイルへの変更が検出されたら App Engine Cron Service を更新するように App Engine アプリケーションを再作成することで、このサンプル実装を拡張できます。

Google Cloud Platform のその他の機能を試すには、チュートリアルをご覧ください。

フィードバックを送信...