BigAdmin System Administration Portal
Solaris Service Management Facility (SMF) - 予測的自己修復
Print-friendly VersionPrint-friendly Version
一般概念
  • smf(5) サービスモデル

    SMF は、サービスと呼ばれる実行し続けるアプリケーションを提供するためのプログラミングモデルを定義します。 サービスは、一連のプロセス、システム構成パラメタ、またはサービスの統合セットといった、いくつかのソフトウェア機能を表現できます。 Solaris サービスは、管理者が使用可能と指定し、その依存関係すべてが満たされている場合のみ起動されます。

    既存のサービスを smf(5) に変換する時間を惜しまなければ、ハードウェア障害や予期しないサービス障害、あるいは管理エラーが起こった時に自動的に再起動を行う機能を容易に提供することができます。 また、SMF に対応することで、svcs(1) (さらに、今後計画されている GUI ツール) による高い可視性を、svcadm(1M) やその他の各種 Solaris 管理ツールによる管理の容易性を手に入れることができます。 通常であれば、短い XML ファイルを作成し、サービス init スクリプトに数箇所簡単な修正を加えるだけで、この利点が得られます。

  • XML 対リポジトリ

    サービスは通常、サービスとそれに関連するインスタンスが記述された XML ファイルであるサービスマニフェストによって定義されます。 サービスマニフェストは、ブート時または svccfg import サブコマンドを使用することでリポジトリに取り込まれます。 サービスマニフェストの XML フォーマットは、/usr/share/lib/xml/dtd/service_bundle.dtd.1 位置にあるサービス記述の DTD によって指定されます。

    リポジトリは、サービス構成の信頼できるコピーが保存される場所です。 リポジトリでは、システムにインストールされたサービス設定を管理者がカスタマイズすることができます。

    本書では、これ以降、すべてのサービスの配信メカニズムであるサービスマニフェストの作成について説明します。

  • インスタンス対サービス分割

    サービスは、汎用的なサービス定義とそのサービスを実装する 1 つまたは複数のインスタンスから構成されます。 インスタンスのプロパティは、そのインスタンスで明確にオーバーライドされていない限り、サービスから継承されます。

    このため、本書のこれ以降の説明に対する一般的なガイダンスとして、サービスの別のコピー (サービスでサポートされている場合) を実行しても変更されることのないプロパティはすべて、サービスレベルで定義します。

    インスタンスによって実装が異なるサービスの場合 (たとえば、'smtp' は sendmail、postfix、qmail... で実装が可能)、現在の実装に固有のプロパティをサービスではなくインスタンスに定義しなければなりません。

    下記で説明するプロパティはすべて、インスタンスレベルまたはサービスレベルで定義できます。

  • 互換性と注意事項

    smf(5) は、/etc/rc?.d ディレクトリに配置することで init(1M) によって起動されるほとんどのアプリケーション、および inetd.conf に配信されるアプリケーションに対して互換性を保持します。

    ただし、init サービスによっては、ブート時の順序を保持するために、smf に変換しなければならないことがあります。デバイス、ファイルシステム、またはネットワーク構成の初期セットアップなど、他のインフラサービスに影響を及ぼす場合は、init サービスを変換する必要があります。また、ブートプロセスでコンソールからの入力が必要な場合にも、サービスを変換する必要があります (このようなサービスはできる限り避けるようお勧めします)。

サービスマニフェストの記述
  1. サービスに名前を付けます

    ネーミングのために一般的なサービスカテゴリを提供しています。このカテゴリはシステムで使用されることはありませんが、そのサービスが通常どのように使用されるのか管理者は見分けることができます。

    このカテゴリは /var/svc/manifest に示され、次のようなものが含まれます。

    • application -- apache など、上位アプリケーション
    • milestone -- name-services など、他のサービスの集まり
    • platform -- Dynamic Reconfiguration デーモンなど、プラットフォーム固有のサービス
    • system -- coreadm など、Solaris システムサービス
    • device -- デバイス固有のサービス
    • network -- protocols など、ネットワーク/インターネットサービス
    • site -- サイト固有の記述

    サー ビス名はそのサービスがどのようなものを提供するのかを表すもので、カテゴリの識別子と実際のサービス名の両方から構成され、カテゴリとサービス名の間は '/' で区切られます。サービス名を見れば、管理者がどのようなサービスを提供しているのか簡単に把握できます。

    インスタンス名は、そのインスタンスについて具体的な機能を表すものです。ほとんどのサービスの場合、'デフォルト' のインスタンスが配信されます。しかし、Oracle などのように、選択した管理構成に基づいてインスタンスを作成する場合もあります。

    製品の一部として提供されるサービス、あるいは通常サイト固有の定義という範囲を超えるようなサービスには、一意に識別できるよう、カテゴリまたはサービス名の一部として、ストックシンボルまたは Java ベースの逆ドメイン接頭辞にコンマを付けます。

    上記の命名規則の例として、cron サービスの場合の前処理の指定は次のようになります。

    <service
    
    name='system/cron'
    
    type='service'
    
    version='1'>
    
                    

  2. サービスがインスタンスを複数持つかどうかを特定します

    サービスの複数のバイナリがシステム上に同時に実行され、これが原因でエラーが発生するような場合は、single_instance サービスとして定義しなければなりません。このタグを付けることによって、管理構成がどうなっているかに関係なく、複数のサービスインスタンスを同時に起動しないようにリスタータに指示できます。

    構成およびシステムサービスのほとんどで、single_instance タグが必要になります。複数の構成を同時に実行する可能性がある (異なるデータベースソースを使用したり、異なるポートで実行するなど) web サーバーやデータベースなどのサービスは、single_instance として指定しないでください。

    サービスブロックの後に次のように指定します。

    <single_instance />
    
                    

  3. サービスモデルを特定します

    実行時特性が異なるサービスに再起動機能を提供するために、smf(5) はサービスに多様なモデルを提供します。現時点では、このようなモデルは svc.startd および inetd のリスタータによって提供されます。今後、このようなリスタータまたは更なるリスタータによって追加モデルを提供する予定です。本書では svc.startd(1M) および inetd(1M) のモデルについて説明しますが、提供されるアプリケーションモデルの詳細については、リスタータのマニュアルを参照してください。

    サービスが inetd によって起動される場合は、移行を容易にするためのツールが提供されているため、下記の inetd サービスマニフェストの記述」 を参照してください。

    svc.startd はプロセスベースのリスタータです。サービスプロセスに対して次の 3 種類のモデルが提供されます。

    • transient サービスは通常構成サービスで、サービスを提供するために実行時間の長いプロセスは一切必要ありません。一般的な transient サービスは、ブート時のクリーンアップまたはカーネルへの構成プロパティのロードを担当します。

      ま た、transient サービスは、contract サービスや wait サービスに対して要求されるメソッドの要件に合致する上で生じる問題を解消するために使用されることがあります。ただし、これは推奨される方法ではなく、 当座しのぎの応急策と考えてください。

    • wait サービスは子プロセスが存続している限り実行され、このプロセスが終了すると再起動されます。

    • contract サービスは標準的なシステムデーモンです。サービスを提供するために一度起動されると永久に実行されるプロセスを必要とします。contract サービス内のすべてのプロセスが終了した場合、サービスエラーとみなされ、サービスが再起動されます。

    デフォルトのサービスモデルは contract サービスですが、サービスマニフェストに次のように指定することで、transient サービスに変更できます。

    <property_group name='startd' type='framework'>
    
    <propval name='duration' type='astring' value='transient' />
    
    </property_group>
    
                    

    wait サービスに変更するには、次のように指定します。

    <property_group name='startd' type='framework'>
    
    <propval name='duration' type='astring' value='child' />
    
    </property_group>
    
                    

  4. サービスがどのように起動/停止されるのかを特定します

    smf は、主にその メソッド によってサービスと対話します。svc.startd によって管理されるサービスには stop メソッドと start メソッドを提供しなければならず、これらのメソッドによって、直接サービスバイナリを起動したり、より複雑な設定を処理するためのスクリプトを呼び出すことができます。refresh メソッドは、svc.startd 管理サービスの場合オプションです。リスタータが異なると、必要とされるメソッドも異なることがあります。

    既存の init スクリプトは、サービスメソッドとして簡単に使用できます。svc.startd でサポートされるメソッドに対して次のルールおよびガイダンスが提供されています。

    • すべてのメソッド
      • シェルスクリプトには、convenience 関数にアクセスし、値の定義を返すために、/lib/svc/share/smf_include.sh を取り込みます。

      • 障害は明示的なエラー復帰処理を伴わなければなりません。0 以外の値はすべてエラーとみなされます。SMF_EXIT_* 定義で、リスタータに追加情報 (たとえば、構成エラーによる再起動を回避するための情報など) を提供できます。

      • メソッドは障害の発生時にログメッセージを出力します。このメッセージは svc.startd によってサービスログファイルに記録されるため、管理者は現状を把握することができます。

      • キーワード :kill および :true は、すべてのメソッドの定義に使用できます。

        :true は、単にリスタータに成功を返すだけです。

        :kill は、サービスの start メソッドによって起動されるすべてのプロセスを強制終了します。すべてのプロセスのリストはサービスの contract によって決まります。

    • start メソッド
      • start メソッドは、svc.startd で管理されるすべてのサービスについて必須です。

      • start メソッドが実行されるのは、サービスが使用可能であり、依存関係がすでに満たされている場合だけです。 このため、構成エラーによってサービスがオンラインにならない場合、start メソッドは終了し、 SMF_EXIT_ERR_CONFIG が出力されます。

      • contract タイプのサービスの場合、すべてのプロセスが終了するとサービスが再起動されるため、成功を返した場合は、start メソッドはデーモンを実行状態のままにしておかなければなりません。

      • contract および transient サービスの場合、サービスが提供されるまで、 start メソッドは成功を返しません。これはデーモンにも当てはまることに注意してください。デーモンは fork() 後、初期プロセスから exit() するのではなく、 起動エラーが蓄積されレポート可能となるまで復帰を待機します。 init スクリプトの多くは、これまでシリアルブートでは依存サービスのブートに 「しばらく」時間がかかるのを当てにして、デーモンを起動したら、直ちに復帰していました。しかし、現在は、 start メソッドからサービスが正常に復帰したら、すぐに (ほとんどの場合、同時に) 依存サービスが起動されるようになったため、不正確な方法は許されなくなりました。

        デーモン/サービスのコードを変更できない場合は、成功を返す前に、サービスのポジティブテストが必要です。 他のオプションを使用できない場合は、正常復帰の前に、然るべき長さの sleep() を挿入してください。

    • stop メソッド
      • stop メソッドは、svc.startd で管理されるすべてのサービスについて必須です。

      • stop メソッドは、依存先がオフラインになってしまった場合、サービスで問題が発生した場合、 管理者が使用不可または再起動を要求した場合など、いくつかの異なるシナリオで実行されます。

      • このため、たとえこのメソッドの実行の開始時にサービスが実行中でなくても、 実行の完了後にサービスが実行中でなくなっていれば、stop メソッドは成功を返します。これは、エラーシナリオで stop メソッドが呼び出されることがあるためです。

    • refresh メソッド
      • refresh メソッドは、svc.startd で管理されるすべてのサービスについてオプションです。

      • refresh メソッドが定義されていると、セマンティクスは非常に正確になります。このメソッドの場合、 リポジトリまたはそれ以外の構成ソースからサービスを中断することなく然るべき構成パラメータをリロードします。 contract サービスまたは wait サービスの場合、既存のプロセスが終了されることはありません。

    すべてのメソッドにタイムアウトを設定しなければなりません。 処理速度の遅いシステムまたは負荷が重い場合のメソッドの最長実行時間 (秒単位) をタイムアウトとして定義します。 それよりも長いメソッドは強制終了されます。fsck のような規模の大きいファイルシステムなど、 メソッドの実行に計り知れないほど長い時間がかかるような場合は、無制限のタイムアウトとして '0' を指定できます。

    サービスメソッドの一部としてユーザーとの対話 (つまり、コンソール入力の介入) を求めることがないよう強くお勧めします。stdin/stdout/stderr はサービスメソッドの /dev/console ではないため、このようなスクリプトは変更しなければ機能しません。

    一般的に使用されるプロパティ値に対するメソッド指定で使用できるように、一連のメソッドを提供しています。 詳しいリストは smf_method(5) に提供されています。

    デフォルトのメソッド環境は init(1M) から継承され、 PATH/usr/sbin:/usr/bin に設定されます。 SMF_ で始まる変数はフレームワークの使用のために予約されています。 smf_method(5) で定義されている SMF_ 変数はすべてのメソッドに提供され、これには SMF_FMRISMF_METHODSMF_RESTARTER などが含まれます。

    最後に、メソッドの実行中に使用されるシステムおよびセキュリティーの属性を定義するために、各メソッドで メソッドコンテキスト を指定できます。実行に時間がかかるサービスは、できる限り、少ない特権レベル、 安全な uid および gid で起動するようにお勧めします。

    start メソッドの指定例を下記に示します。

    <exec_method
    
    type='method'
    
    name='start'
    
    exec='/lib/svc/method/svc-cron'
    
    timeout_seconds='60'>
    
    <method_context>
    
     <method_credential user='root' group='root' />
    
    </method_context>
    
    </exec_method>
    
     
  5. 無視すべき障害を決定します

    サービス自体の動作不良またはサービスによって動作不良のサブプロセスが生成される可能性がある場合、 特定のエラーを予期し、サービス障害とならないようにリスタータに通知できます。

    サブプロセスからのコアダンプをエラーとみなさないようにしたり、外部からの kill 信号をエラーとしないように指定できます。そのどちらもエラーとしないように指定する例を下記に示します。

    <property_group name='startd' type='framework'>
    
    <propval name='ignore_error' type='astring' value='core,signal' />
    
    </property_group>
    
     
  6. 依存関係を特定します

    ほとんどの依存関係は明示的に指定されないため、これはサービス変換の中でもっとも難しい作業です。依存関係には、 ファイル 依存と サービス 依存の 2 つのタイプがあります。

    まず、これ以外のどのサービスを起動する必要があるのかを特定します。たとえば、サービスでネットワークを plumb する必要があるのか、ローカルデバイスを構成する必要があるのか、 ネームサービスを使用可能にする必要があるのか検討します。

    サービスがどのサービスに依存するのか依存先を決定したら、障害伝播モデルを指定する必要があります。 依存関係ごとに、次のような場合にサービスを再起動するかどうかを決定します。

    1. none -- 起動に限りこの依存関係が要求されます。 障害または管理処理では再起動は必要ありません
    2. fault -- 依存関係に障害 (コアダンプ、システム障害など) がある場合に再起動が行われます
    3. restart -- 依存関係が再起動された場合に、サービスを再起動します
    4. refresh -- 依存関係がリフレッシュされた (構成が変更された) 場合に、 サービスを再起動します

    これらの値は、指定されている依存関係の restart_on プロパティによる再起動機能と同じです。

    依存関係はグループごとに指定できます。可能なグループは次のとおりです。

    1. require_all -- 依存関係が起動される前に、 グループ内のすべてがオンラインであるか、または縮退されていなければなりません
    2. require_any -- 依存関係が起動される前に、グループ内のいずれかの 1 つのサービスがオンラインであるか、または縮退されていなければなりません
    3. optional_all -- サービスが使用可能であり、 メンテナンスモードでなく実行できる状態でである場合、依存関係が起動される前に、サービスがオンラインであるか、 または縮退されていなければなりません。
    4. exclude_all -- サービスが使用可能であり、 オンラインであるか縮退されている場合、依存関係は起動されません

    実行されているレガシースクリプトにサービスが依存している場合は、自分自身でそのレガシースクリプトを smf(5) サービスに変換するか、またはベンダーに変換を依頼することを強くお勧めします。 それ以外には、そのスクリプトが属する マイルストーン に対する依存関係を指定できます。 これによって、レガシーサービスからエラーが伝播されることがなくなるため、 restart_on=none 依存関係としてのみ意味をなすことになります。

    最後に、なぜ特定の依存関係が必要であるのかを決定するという困難な作業を行なったので、今後のメンテナンス担当者のためにコメントを作成しておいてください!

    <!-- Must be able to resolve hostnames. -->
    
    <dependency
    
    name='nameservice'
    
    type='service'
    
    grouping='require_all'
    
    restart_on='none'>
    
     <service_fmri value='svc:/milestone/name-services' />
    
    <dependency>
    
     
  7. 依存関係を特定します

    配信しない別のサービスの依存関係であるサービスを配信したいという場合は、 自分自身のマニフェストにこれを指定して、自分が所有していないマニフェストを変更する必要はありません。つまり、依存関係を指定することは、Sun で配信されるサービスの前に自分のサービスを実行させる簡単な方法と言えます。

    依存サービスのすべてを変換していない場合、レガシースクリプトに対する依存を指定する方法が他にないため、これも変換する必要があります。

    競合を防ぐために、依存名の前にサービス名を付けるようにお勧めします。

    たとえば、syslog の前に起動しなければならないサービス (mysvc) を配信する場合は、次のように指定します。

    <dependent
    
    name='mysvc_syslog'
    
    grouping='optional_all'
    
    restart_on='none'>
    
     <service_fmri value='svc:/system/system-log' />
    
    <dependent>
    
     
  8. サービスを マイルストーン に挿入します

    サービスがあらかじめ rc?.d ディレクトリに配信され、 他のサービスがこのサービスに依存している可能性がある場合は、既存の配信位置に対応する マイルストーン を依存関係にします。

    たとえば、サービスがすでに実行レベル 2 で起動されているとした場合、次のように指定することで、サービスが起動されるまで、実行レベル 2 は完了とみなされることはありません。

     <dependent
    
    name='mysvc_multi-user'
    
    grouping='require_all'
    
    restart_on='none'>
    
    <service_fmri value='svc:/milestone/multi-user' />
    
     <dependent>
    
     
  9. 該当する場合に、デフォルトのインスタンスを作成します

    サービスがはじめて起動される前に構成に対して管理者の介入がさらに必要でない場合は、 サービスにデフォルトのインスタンスを設定します。

    インスタンスにサービスと構成の違いがなければ、次のように指定するだけです。

    <create_default_instance enabled='false' />

    または、インスタンスを明示的に定義することもできます。

    <instance name='default' enabled='false'>
    
    <!-- instance-specific properties, methods, etc. go here. -->
    
    </instance>
    
     

    システムブートに不可欠である場合を除き、インスタンスはすべて使用不可として配信するようお勧めします。 このようにしておけば、管理者またはプロファイル (他のいずれかの場所に記述) によるカスタマイズが可能です。

  10. サービスを記述するためのテンプレート情報を作成します

    C ロケールおよびマニュアルページリファレンスに少なくとも 1 つの共通名を記述します。 共通名は次の条件を満たすものとします。

    • 短いこと (40 文字以内)
    • Solaris のような商標を除き、大文字は使用しないこと
    • 句読点は使用しないこと
    • ワードサービスは避けること (ただし、クライアントとサーバーは区別する)
    • この情報は、サービスに関する概要やより詳しい技術情報の参照先を管理者に提供するために、各種の形式の svcs(1) によって示されます。なお、共通名はローカライズしてもかまいません。

      <template>
      
      <common_name>
      
       <loctext xml:lang='C'>
      
       Solaris fault manager
      
       <loctext>
      
      <common_name>
      
      <documentation>
      
       <manpage title='fmd' section='1M' manpath='/usr/share/man' />
      
      <documentation>
      
      <template>
      
       
    • 管理コマンドの記述/更新

      サービスを停止、起動、または再起動する管理コマンドがサービスにすでに含まれている場合は、svcadm(1M) または libscf コールを使用するように更新します。 管理コマンドによって smf(5) の外部で明示的にデーモンが起動される場合、smf(5) フレームワークでは他のデーモンが実行中であることを認識できません。 デーモン間の競合、間違った規約、svcs(1) を使用した不十分な可視性などの問題が発生します。

    • /etc/rc?.d 位置および /etc/init.d からのスクリプトの削除

      init スクリプトを削除しないと、依然としてレガシーモードで実行されることになります。

参考情報

DTD は自己文書化方式になっています。質問の多くは、Solaris 10 システムで /usr/share/lib/xml/dtd/service_bundle.dtd を読むだけで解決できます。

Sun は /var/svc/manifest に多くのマニフェストを配信しています。テンプレートおよび例としてお使いください。 まず代表的なものとして、次のようなものが挙げられます。

  • system/utmp は、シンプルなスタンドアロンデーモンです
  • system/coreadm は、シンプルな transient サービスです

  • network/telnet は、inetd(1M) ベースのデーモンです

まずは、下記のマニュアルページが役に立ちます。smf(5)、 smf_bootstrap(5)、smf_method(5)、 smf_restarter(5)、smf_security(5)、 svc.startd(1M)、inetd(1M)、 inetconv(1M)

最後に、『SMF System Administration Guide Documentation』 および BigAdmin の Predictive Self-Healing サイト も有益な情報源です。

inetd サービスマニフェストの記述

inetconv(1M) をもとにして、テンプレートを追加したり、わかりやすい名前に変更するなど、それ以外の変更を行います。

続いて、次の処理を行います。

  • パッケージ化
  • 事前に変換された inetd.conf サービスの削除

今後の提供予定:
  • パッケージ化
  • サービスのインストール
  • ログファイル
  • サービスのテスト
  • アップグレードに関する検討事項
  • サービス構成に対する smf プロパティの使用
  • action_authorizations -- 特定のサービス管理のためのきめ細かな管理ロールの作成

この記事は、 Solaris Service Management Facility - Service Developer Introductionを翻訳したものです。

BigAdmin
  
 
BigAdmin Upgrade Hub