この章では、リソースタイプの設計や実装でデータサービス開発ライブラリ (Data Service Development Library、DSDL) を通常どのように使用するかについて説明します。また、リソース構成を検証したり、リソースの開始、停止、および監視を行なったりするためのリソースタイプの設計についても説明します。さらに、リソースタイプのコールバックメソッドを DSDL を使って実装する方法を説明します。
詳細は、rt_callbacks(1HA)のマニュアルページを参照してください。
これらの作業を行うには、リソースのプロパティー設定値にアクセスできなければなりません。DSDL ユーティリティー scds_initialize() を使用すると、統一された方法でリソースプロパティーにアクセスできます。この機能は、各コールバックメソッドの始めの部分で呼び出す必要があります。このユーティリティー関数は、クラスタフレームワークからリソースのすべてのプロパティーを取り出し、これによって、そのリソースは、scds_getname() 関数群から利用できるようになります。
この章の内容は次のとおりです。
リソースタイプ登録 (Resource Type Registration、RTR) ファイルは、Sun Cluster ソフトウェアに対して、リソースタイプの詳細な情報を指定します。
詳細情報には次の情報が含まれます。
実装に必要なプロパティー
これらのプロパティーのデータタイプやデフォルト値
リソースタイプの実装用のコールバックメソッドのファイルシステムパス
システム定義プロパティーのさまざまな設定
ほとんどのリソースタイプ実装では、DSDL に添付されるサンプル RTR ファイルで十分なはずです。必要な作業は、リソースタイプ名、リソースタイプのコールバックメソッドのパス名などの基本的な要素の編集だけです。リソースタイプを実装する際に新しいプロパティーが必要な場合は、そのプロパティーをリソースタイプ実装の RTR ファイルで拡張プロパティーとして宣言します。新しいプロパティーのアクセスには DSDL scds_get_ext_property() ユーティリティーを使用します。
リソースタイプ実装の Validate コールバックメソッドの目的は、リソースに対する新しいプロパティー設定により指定されるリソースの新しい設定値が、そのリソースタイプにとって有効であるかどうかを検査することにあります。
リソースタイプ実装の Validate メソッドは、次のどちらかの条件のときにリソースグループマネージャー (Resource Group Manager、RGM) によって呼び出されます。
このリソースタイプの新規リソースが作成されつつある。
このリソースまたはリソースグループのプロパティーが更新されつつある。
この 2 つの操作は、リソースの Validate メソッドに渡されるコマンド行オプション -c (作成) と -u (更新) の存在によって区別されます。
Validate メソッドはノード群の各ノードに対して呼び出されます。ノード群は、リソースタイププロパティー Init_nodes の値で定義されます。Init_nodes が RG_PRIMARIES に設定されている場合、Validate は、そのリソースを含むリソースグループを収容できる (その主ノードになりうる) 各ノードに対して呼び出されます。Init_nodes が RT_INSTALLED_NODES に設定されている場合、Validate は、リソースタイプソフトウェアがインストールされている各ノード (通常は、クラスタのすべてのノード) に対して呼び出されます。
Init_nodes のデフォルト値は RG_PRIMARIES です (rt_reg(4)のマニュアルページを参照)。Validate メソッドが呼び出される時点では、RGM はまだリソースを作成していません (作成コールバックの場合)。あるいは、更新するプロパティーの更新値をまだ適用していません (更新コールバックの場合)。
HAStoragePlus リソースタイプによって管理されるローカルファイルシステムを使用している場合は、scds_hasp_check() 関数を使ってそのリソースタイプの状態を検査します。当該リソース用に定義されている Resource_dependencies または Resource_dependencies_weak のシステム属性を使用することによって、当該リソースが依存しているすべての SUNW.HAStoragePlus リソース状態 (オンラインであるか、オンラインでないか) についての情報が得られます。scds_hasp_check() 関数から返される状態コードの完全なリストについては、scds_hasp_check(3HA)のマニュアルページを参照してください。
DSDL 関数 scds_initialize() は、リソースの作成や更新を次のように処理します。
リソースの作成では、scds_initialize() はコマンド行で渡された新しいリソースプロパティーを解析します。これによって、リソースプロパティーの新しい値を、そのリソースががすでにシステムで作成されているかのように使用できます。
リソースやリソースグループの更新では、クラスタ管理者によって更新されているプロパティーの新しい値は、コマンド行から読み込まれます。残りのプロパティー (値が更新されないもの) は、RMAPI を使って Sun Cluster から読み込みます。DSDL を使用する場合は、このような作業を考慮する必要はありません。開発者は、リソースのすべてのプロパティーが使用可能であるものとして、リソースの検証を行うことができます。
リソースのプロパティーの検証を実装する関数は svc_validate() と呼ばれます。この関数は、scds_get_name() 関数群を使って、検証しようとするプロパティーを検査します。リソースの設定が有効ならこの関数から戻りコード 0 が返されるとすると、リソースタイプの Validate メソッドは、次のコード部分のようになります。
int main(int argc, char *argv[]) { scds_handle_t handle; int rc; if (scds_initialize(&handle, argc, argv)!= SCHA_ERR_NOERR) { return (1); /* Initialization Error */ } rc = svc_validate(handle); scds_close(&handle); return (rc); }
さらに検証関数は、リソースの検証が失敗した理由を記録する必要もあります。ただし、詳細は省略することによって、次に示すように、より単純な例である svc_validate() 関数を実装できます (第 8 章サンプル DSDL リソースタイプの実装には検証関数の実際的な取り扱いが記載されています)。
int svc_validate(scds_handle_t handle) { scha_str_array_t *confdirs; struct stat statbuf; confdirs = scds_get_confdir_list(handle); if (stat(confdirs->str_array[0], &statbuf) == -1) { return (1); /* Invalid resource property setting */ } return (0); /* Acceptable setting */ }
このように、リソースタイプの開発者は、svc_validate() 関数を実装することだけに集中できます。
リソースタイプ実装の Start コールバックメソッドは、特定のクラスタノードのリソースを開始するときに RGM によって呼び出されます。リソースグループ名とリソース名、およびリソースタイプ名はコマンド行から渡されます。Start メソッドは、クラスタノードでデータサービスリソースを開始するために必要なアクションを行います。通常、このようなアクションには、リソースプロパティーの取得、アプリケーション固有の実行可能ファイルと構成ファイルの一方または両方の格納先の特定、および適切なコマンド行引数を用いたアプリケーションの起動が含まれます。
DSDL では、リソース構成ファイルが scds_initialize() ユーティリティーによってすでに取得されています。アプリケーションの起動アクションは、svc_start() 関数に指定できます。さらに、アプリケーションが実際に起動されたかどうかを確認するために、svc_wait() 関数を呼び出すことができます。Start メソッドのコード (詳細は省略) は、次のようになります。
int main(int argc, char *argv[]) { scds_handle_t handle; if (scds_initialize(&handle, argc, argv)!= SCHA_ERR_NOERR) { return (1); /* Initialization Error */ } if (svc_validate(handle) != 0) { return (1); /* Invalid settings */ } if (svc_start(handle) != 0) { return (1); /* Start failed */ } return (svc_wait(handle)); }
この起動メソッドの実装では、svc_validate() を呼び出してリソース構成を検証します。検証が失敗する場合は、リソース構成とアプリケーション構成が一致していないか、現在このクラスタノードのシステムに関してなんらかの問題があることを示しています。たとえば、リソースに必要なクラスタファイルシステムが、現在このクラスタノードで使用できない可能性などが考えられます。その場合には、このクラスタノードでこのリソースを起動しても意味がないので、RGM を使って別のノードのリソースを起動すべきです。
ただし、上記の文では svc_validate() が十分に保守的であり、アプリケーションにより絶対に必要なリソースがあるかどうかをそのクラスタノードだけで検査することに注意してください。そうでないと、このリソースはすべてのクラスタノードで起動に失敗し、START_FAILED の状態になる可能性があります。この状態の詳細は、『Sun Cluster データサービスの計画と管理 (Solaris OS 版)』を参照してください。
svc_start() 関数は、このノードでリソースの起動に成功した場合は戻りコード 0 を、問題を検出した場合は 0 以外の戻りコードをそれぞれ返す必要があります。この関数から 0 以外の値が返されると、RGM は、このリソースを別のクラスタノードで起動しようと試みます。
DSDL を最大限に活用するには、svc_start() 関数で scds_pmf_start() ユーティリティーを呼び出して、アプリケーションを PMF (プロセス管理機能) のもとで起動できます。このユーティリティーは、PMF の障害コールバックアクション機能を使って、プロセス障害を検出します。詳細は、pmfadm(1M)のマニュアルページにある -a アクション引数の説明を参照してください。
リソースタイプ実装の Stop コールバックメソッドは、クラスタノードでアプリケーションを停止するときに RGM によって呼び出されます。
Stop メソッドのコールバックが有効であるためには、次の条件が必要です。
Stop メソッドは結果に依存しない命令 (idempotent) でなければなりません。つまり、Stop メソッドは、そのノードで Start メソッドが正常に終了していなくても、RGM によって呼び出されることがあります。したがって、Stop メソッドは、そのクラスタノードでアプリケーションが動作していない場合でも (したがって、特別な処理が必要ない場合でも)、正常に (終了コード 0 で) 終了しなければなりません。
リソースタイプの Stop メソッドが、あるクラスタノードで失敗に終わると (0 以外で終了)、停止中のリソースは STOP_FAILED の状態になります。リソースの Failover_mode 設定によっては、この条件により、クラスタノードが RGM によってハードウェア的に再起動されることがあります。
したがって、Stop メソッドの設計時には、このメソッドがアプリケーションを明示的に停止する手段が必要です。アプリケーションが停止しない場合は、SIGKILL などを使って、アプリケーションを強制的かつ即時に停止する必要があります。
さらに、このメソッドによるアプリケーションの停止は一定の時間内に行われなければなりません。Stop_timeout プロパティーで設定した時間が経過すると、停止が失敗したものとみなされ、リソースは STOP_FAILED の状態になるからです。
ほとんどのアプリケーションには、DSDL ユーティリティー scds_pmf_stop() で十分なはずです。これは、アプリケーションを SIGTERM で「静かに」停止しようとするためです。続いてこの関数は、プロセスに対してSIGKILL を適用します。この関数は、まず、アプリケーションが PMF の scds_pmf_start() で起動されたものとみなします。このユーティリティーの詳細については、「PMF 関数」を参照してください。
アプリケーションを停止するそのアプリケーション固有の関数を svc_stop() とするなら、Stop メソッドは、次のように実装します。
if (scds_initialize(&handle, argc, argv)!= SCHA_ERR_NOERR) { return (1); /* Initialization Error */ } return (svc_stop(handle));
前述の svc_stop() 関数の実装に scds_pmf_stop() 関数が含まれているかどうかは、ここでは関係ありません。scds_pmf_stop() 関数を含めるかの決定は、アプリケーションが PMF のもとで Start メソッドによって起動されているかどうかに依存します。
Stop メソッドの実装では、svc_validate() メソッドは使用されません。システムに問題があったとしても、Stop メソッドは、このノードでこのアプリケーションを停止すべきだからです。
RGM は、リソースの障害モニターを起動する場合に Monitor_start メソッドを呼び出します。障害モニターは、このリソースによって管理されているアプリケーションの状態を監視します。リソースタイプの実装では、通常、障害モニターはバックグラウンドで動作する独立したデーモンとして実装されます。このデーモンの起動には、適切な引数をもつ Monitor_start コールバックメソッドが使用されます。
モニターデーモン自体は障害が発生しやすいため (たとえばモニターは、アプリケーションを監視されない状態にしたまま、停止することがある)、モニターデーモンは、PMF を使って起動すべきです。DSDL ユーティリティー scds_pmf_start() には、障害モニターを起動する機能が組み込まれています。このユーティリティーは、モニターデーモンプログラムのリソースタイプコールバックメソッド実装の場所を表す RT_basedir からの相対パス名を使用します。このユーティリティーは、DSDL によって管理される Monitor_retry_interval 拡張プロパティーと Monitor_retry_count 拡張プロパティーを使って、デーモンが際限なく再起動されるのを防止します。
このユーティリティーでは、モニターデーモンのコマンド行構文には、すべてのコールバックメソッドに対して定義されたコマンド行構文と同じものが使用されます (-R resource -G resource-group -T resource-type) が、モニターデーモンが RGM から直接呼び出されることは決してありません。このユーティリティーでは、モニターデーモン実装自体が scds_initialize() ユーティリティーで独自の環境を設定できます。したがって、主な作業は、モニターデーモン自体を設計することです。
RGM は、Monitor_start メソッドで起動された障害モニターデーモンを停止するために、Monitor_stop メソッドを呼び出します。このコールバックメソッドの失敗は、Stop メソッドの失敗とまったく同じように処理されます。したがって、Monitor_stop メソッドは、Stop メソッドと同じように強固なものでなければなりません。
障害モニターデーモンを scds_pmf_start() ユーティリティーを使って起動したら、scds_pmf_stop() ユーティリティーで停止する必要があります。
RGM は、指定されたリソースについて、クラスタノードがリソースをマスターする能力を持っているかどうかを確認するために、そのノードのリソースに対して Monitor_check コールバックメソッドを実行します。つまり、RGM は、そのリソースによって管理されるアプリケーションがそのノードで正常に動作するかどうかを判別するためにこのメソッドを実行します。
通常、この状況では、アプリケーションに必要なすべてのシステムリソースが本当にクラスタノードで使用可能かどうかが確認されます。「Validate メソッド」で説明されているように、開発者が実装する svc_validate() 関数は、少なくともこの確認が行われなければなりません。
リソースタイプ実装によって管理されている特定のアプリケーションによっては、Monitor_check メソッドでそのほかの作業を行うことがあります。Monitor_check メソッドは、並行して実行中のそのほかのメソッドと競合しない方法で実装する必要があります。DSDL を使用する場合には、リソースプロパティーに対するアプリケーション固有の検証を実装する svc_validate() 関数を Monitor_check メソッドで呼び出す必要があります。
RGM は、リソースタイプ実装の Update メソッドを呼び出して、クラスタ管理者が行なったすべての変更をアクティブリソースの構成に適用します。Update メソッドは、そのリソースがオンラインになっているすべてのノードに対して呼び出されます。
リソースの構成に対して行われた変更は、リソースタイプ実装にとって必ず有効なものです。RGM は、リソースタイプの Update メソッドを呼び出す前に Validate メソッドを呼び出すからです。Validate メソッドは、リソースやリソースグループのプロパティーが変更される前に呼び出されます。したがって、Validate メソッドは新しい変更を拒否できます。変更が適用されると、Update メソッドが呼び出され、新しい設定値がアクティブ (オンライン) リソースに通知されます。
リソースタイプの開発者は、どのプロパティーを動的に変更できるようにするかを慎重に決定し、RTR ファイルでこれらのプロパティーに TUNABLE = ANYTIME を設定する必要があります。通常、障害モニターデーモンによって使用されるリソースタイプ実装のプロパティーは、すべて動的に更新できるように指定できます。ただし、Update メソッドの実装は、少なくともモニターデーモンを再起動できなければなりません。
使用できるプロパティーの候補には次のものがあります。
Thorough_probe_interval
Retry_count
Retry_interval
Monitor_retry_count
Monitor_retry_interval
Probe_timeout
これらのプロパティーは、障害モニターデーモンがサービスの状態をどのようにチェックするかや、デーモンがチェックをどのような頻度で行うか、エラーをデーモンがどのような履歴間隔を使用して追跡管理するか、あるいは、PMF がどのような再起動しきい値を設定するかに影響を及ぼします。DSDL には、これらのプロパティーの更新を行うための scds_pmf_restart() ユーティリティーが備わっています。
リソースプロパティーを動的に更新できなければならないが、プロパティーの変更によって動作中のアプリケーションに影響が及ぶ可能性がある場合は、適切なアクションを行なう必要があります。プロパティーに対する更新が動作中のアプリケーションインスタンスに正しく適用されるようにしなければなりません。現在のところ、DSDL を使用してこのようにリソースプロパティーを動的に更新することはできません。変更されたプロパティーをコマンド行で Update に渡すことはできません (Validate では可能)。
これらのメソッドは、リソース管理 API 仕様の定義による「一度だけのアクション」を行うためのものです。DSDL のサンプル実装には、これらのメソッドの使い方は示されていません。しかし、これらのメソッドを使用する必要がある場合には、DSDL のすべての機能をこれらのメソッドでも使用できます。通常、「一度だけのアクション」を使用するリソースタイプ実装では、Init メソッドと Boot メソッドはまったく同じように機能します。Fini メソッドは、一般に、Init メソッドや Boot メソッドのアクションを「取り消す」ためのアクションを実行します。
DSDL を使用したリソースタイプ実装には、通常、次の役割を実行する障害モニターデーモンがあります。
管理されているアプリケーションの状態を定期的に監視します。モニターデーモンのこの役割は特定のアプリケーションに大きく依存し、リソースタイプによって大幅に異なることがあります。DSDL には、TCP に基づく簡単なサービスの状態を検査するいくつかのユーティリティー関数が組み込まれています。HTTP、NNTP、IMAP、POP3 など、ASCII ベースのプロトコルを使用するアプリケーションは、これらのユーティリティーを使って実装できます。
アプリケーションによって検出された問題を、リソースプロパティー Retry_interval や Retry_count を使って追跡します。 さらに、アプリケーションが完全に異常停止した場合、障害モニターは、PMF アクションスクリプトを使ってサービスを再起動すべきかどうかや、アプリケーションの障害が急速に蓄積されるためにフェイルオーバーを実行する必要があるかどうかを判断する必要があります。DSDL のユーティリティー scds_fm_action() と scds_fm_sleep() は、この機構の実装を助けることを目的としています。
一般に、アプリケーションを再起動するか、リソースを含むリソースグループのフェイルオーバーを試みるなど、適切なアクションを実行します。DSDL ユーティリティー scds_fm_action() には、このアルゴリズムが実装されています。このユーティリティーは、この目的のために、過去の Retry_interval 秒数の間に起った検証障害の現在の累積を計算します。
リソースの状態を更新します。これによって、Sun Cluster 管理コマンドやクラスタ管理 GUI で アプリケーションの状態を知ることができます。
DSDL ユーティリティーの設計では、障害モニターデーモンの主要ループは、この節の最後にある擬似コードで表すことができます。
DSDL を使用して障害モニターを実装する際には、次の点に注意してください。
アプリケーションプロセスの異常停止は、scds_fm_sleep() によって迅速に検出されます。これは、PMF によるアプリケーションプロセス停止の通知が非同期に行われるためです。そのため、障害の検出時間が大幅に短くなり、サービスの可用性が高くなります。別の方法としては、障害モニターが頻繁にスリープから復帰してサービスの状態を検査し、アプリケーションプロセスの停止を検出する方法があります。
RGM が scha_control API によるサービスのフェイルオーバーを拒否すると、scds_fm_action() は、現在の障害履歴を「リセット」(消去) します。この関数が現在の障害履歴をリセットするのは、障害履歴が Retry_count の値をすでに超えているからです。モニターデーモンは、次のサイクルでスリープから復帰したあとに、デーモンの状態検査を正常に完了できないと、scha_control() 関数を再び呼び出そうとします。しかし、前回のサイクルで呼び出しが拒否され状況が依然として残っていれば、この呼び出しは今回も拒否されるはずです。履歴がリセットされていれば、障害モニターは、少なくとも、次のサイクルでアプリケーションの再起動などによってその状況を内部的に訂正しようとします。
再起動が失敗に終わった場合、scds_fm_action() は、アプリケーション障害履歴をリセットしません。これは、状況が訂正されなければ、scha_control() が間もなく呼び出される可能性が高いからです。
ユーティリティー scds_fm_action() は、障害履歴に従って、リソースステータスを SCHA_RSSTATUS_OK、SCHA_RSSTATUS_DEGRADED、SCHA_RSSTATUS_FAULTED のどれかに更新します。その結果、このステータスをクラスタシステム管理から使用できるようになります。
ほとんどの場合、アプリケーション固有の状態検査アクションは、スタンドアロンの別個のユーティリティー (たとえば、svc_probe()) に実装できます。これは、次の汎用的なメインループに統合できます。
for (;;) { /* sleep for a duration of thorough_probe_interval between * successive probes. */ (void) scds_fm_sleep(scds_handle, scds_get_rs_thorough_probe_interval(scds_handle)); /* Now probe all ipaddress we use. Loop over * 1. All net resources we use. * 2. All ipaddresses in a given resource. * For each of the ipaddress that is probed, * compute the failure history. */ probe_result = 0; /* Iterate through the all resources to get each * IP address to use for calling svc_probe() */ for (ip = 0; ip < netaddr->num_netaddrs; ip++) { /* Grab the hostname and port on which the * health has to be monitored. */ hostname = netaddr->netaddrs[ip].hostname; port = netaddr->netaddrs[ip].port_proto.port; /* * HA-XFS supports only one port and * hence obtaint the port value from the * first entry in the array of ports. */ ht1 = gethrtime(); /* Latch probe start time */ probe_result = svc_probe(scds_handle, hostname, port, timeout); /* * Update service probe history, * take action if necessary. * Latch probe end time. */ ht2 = gethrtime(); /* Convert to milliseconds */ dt = (ulong_t)((ht2 - ht1) / 1e6); /* * Compute failure history and take * action if needed */ (void) scds_fm_action(scds_handle, probe_result, (long)dt); } /* Each net resource */ } /* Keep probing forever */