Web サーバーのクラスターをセットアップする 5 つの簡単なステップ

Linux Virtual Server と Linux-HA.org の Heartbeat でクラスターを稼動させる

Linux Virtual Server と Heartbeat v2 を使用した 5 つの簡単なステップで、複数の物理または仮想 Linux® サーバーにまたがる高可用性 Apache Web サーバー・クラスターを構成してください。

Eli M. Dow (emdow@us.ibm.com), Software Engineer, IBM Japan, Software Group

Eli M. Dow は、ニューヨーク州ポキプシーにある、IBM Test and Integration Center for Linux のソフトウェア技術者です。Clarkson University にてコンピューター・サイエンスと心理学で学位を、またコンピューター・サイエンスで修士号を取得しました。彼は、Clarkson Open Source Institute の卒業生です。関心のある分野は、GNOME デスクトップ、マン・マシン・インターフェース、仮想化、および Linux システム・プログラミングなどです。「IBM Redbook Linux for IBM System z9 and IBM zSeries」の共著者です。



Frank LeFevre, Senior Software Engineer, IBM

Frank LeFevre は、ニューヨーク州ポキプシーにある IBM Systems and Technology Group のシニア・ソフトウェア・エンジニアです。IBM メインフレーム・ハードウェアおよびオペレーティング・システムでの経験は 28 年以上に及びます。現在は、Linux Virtual Server Platform Evaluation Test チームのリーダーを務めています。



2007年 8月 22日

さまざまなソフトウェア・リカバリー手法を組み合わせて複数のプロセッサーに作業負荷を分散させると、RAS (信頼性・可用性・保守性) すべてが強化された可用性の高い環境を実現することができます。このような環境では、予期しない障害から素早くリカバリーできるだけでなく、計画停止によるエンド・ユーザーへの影響を最小限に抑えられるという利点もあります。

この記事を役立てるには、Linux と基本的なネットワーキングについての知識があること、そして Apache サーバーが構成済みであることが必要です。ここで説明するサンプルは標準 SLES10 (SUSE Linux Enterprise Server 10) システムをベースにしていますが、これ以外のディストリビューションで十分な経験を積んでいれば、記事で説明する手段をそのディストリビューションに適応させることができるはずです。

この記事で説明するのは、6 つの Apache サーバー・ノード (ただし概説する手順に従うにはノードが 3 つあれば十分です) と 3 つの LVS (Linux Virtual Server) ディレクターを備えた堅牢な Apache Web サーバー・スタックです。6 つの Apache サーバー・ノードを使用した理由は、より高い作業負荷をかけたときのスループットのテストを行うことによって、より規模の大きなデプロイメントをシミュレートするためです。ここで紹介するアーキテクチャーは、リソースが許容する限り、ディレクターとバックエンド Apache サーバーの数を大幅に増やしても対応できるはずですが、これ以上大規模なデプロイメントはまだ試していません。図 1 は、LVS と linux-ha.org コンポーネントを使用した実装です。

図 1. LVS と Apache
LVS と Apache

図 1 に示すように、外部クライアント (External clients) はトラフィックを単一の IP アドレスに送信します。このアドレスは、いずれかの LVS ディレクター・マシン上に存在するものです。ディレクター・マシンは、作業の中継先とする Web サーバーのプールを頻繁にモニターします。

作業負荷は、図 1 の左から右に向かって進むことに注意してください。このクラスターでは、浮動リソース・アドレスが常時、LVS ディレクター・インスタンスのいずれかに存在することになります。サービス・アドレスは、グラフィカル構成ユーティリティーを使って手動で移動することも、(もっと一般的には) LVS ディレクターの状態に応じて自己管理させることもできます。ディレクターが 1 つでも (接続の切断、ソフトウェア障害などにより) 不適格になると、そのサービス・アドレスは適格なディレクターに自動的に再配置されます。

いずれかの物理マシンが抜けても動作し続けるようにするためには、浮動サービス・アドレスが複数の個別ハードウェアのインスタンスにまたがっていなければなりません。この記事で説明する構成決定に従えば、それぞれの LVS ディレクターが、物理的にどのように配置されているか、あるいは浮動サービス・アドレスを提供するアクティブ・ディレクターとどの程度近くにあるかに関わらず、実際のどの Apache Web サーバーにでもパケットを転送できるようになります。この記事では、動作可能なバックエンド・サーバーにのみ要求が送信されるように、すべての LVS ディレクターに頻繁に Apache サーバーをモニターさせる仕組みを説明します。

この構成では、浮動サービス・アドレス (通常は、http および https による Web 要求) で有効になっているサービスのコンシューマーへの提供を中断することなく、すべての Linux インスタンスを停止させることことができました。

LVS 用語の解説

LVS ディレクター: LVS ディレクターとは、任意の着信トラフィックを受け取って、そのトラフィックを任意の数の実サーバーに渡した後、実サーバーからの応答を受け取って要求を開始したクライアントに返すシステムのことです。LVS ディレクターは、実サーバーで実際の作業負荷の処理を行っていることがクライアントにはわからないような透過的な形式でタスクを行う必要があります。

LVS ディレクター自体に必要なのは、単一障害点にならないようにディレクター間でリソース (具体的には、ディレクターが着信トラフィックをリッスンする仮想 IP アドレス) を浮動させることです。そのため、LVS ディレクターは LVS の Heartbeat コンポーネントを利用して IP アドレスの浮動を行います。これにより、Heartbeat を実行する構成済みディレクターのうちの 1 つのディレクターのみが、着信要求にサービス提供する仮想 IP アドレスを要求できるようになります。

サービス IP アドレスを浮動させるだけでなく、ディレクターは実際の作業負荷を処理している実サーバーをモニターできる必要もあります。ディレクターは常に、どの実サーバーが処理に対応可能かどうかについて、最新の情報を保持していなければならないためです。実サーバーをモニターするには、mon パッケージが使用されます。詳しくは、Heartbeat の構成方法および mon の構成方法を読んでください。

実サーバー: これらのシステムは、HA サービスを提供する実際の Web サーバー・インスタンスです。HA にしたいサービスを提供する実サーバーが複数揃っていることが絶対条件となります。この記事の環境では 6 つの実サーバーを実装していますが、残りの LVS インフラストラクチャーを整えてしまえば、さらに多くの実サーバーを追加するのもわけありません。

この記事では、実サーバーはいずれも Apache Web Server を実行しているという前提ですが、これ以外のサービスも同様に簡単に実装できるはずです (実際、ここで紹介する方法のテストに SSH を追加するのも実に簡単なことです)。

使用する実サーバーは平凡な Apache Web サーバーですが、ただし注目に値する例外として、これらのサーバーが応答する際には、ディレクターで使用する浮動 IP アドレスから応答しているかのように、あるいはLVS ディレクターの浮動 IP アドレスに相当する仮想ホスト名から応答しているかのように見えるように構成されています。このように構成するには、Apache 構成ファイルでたった 1 行変更するだけで済みます。

この構成とまったく同じ構成にするには、linux-ha.org が提供する Heartbeat 技術コンポーネントからなる完全なオープン・ソースのソフトウェア・スタック、そして mon および Apache によってモニタリングを行うためのサーバーを使用してください。前述したように、この構成は SUSE Linux Enterprise Server を使用してテストしました。

LVS シナリオで使用するマシンはすべて同じサブネットに配置して、NAT (Network Address Translation) 構成を使用しています。Linux Virtual Server の Web サイトでは他にも多数のネットワーク・トポグラフィーについて説明していますが、私たちは簡潔さを理由に NAT を支持しています。さらにセキュリティーを強化するには、ファイアウォールを介したトラフィックを、LVS ディレクター間で渡される浮動 IP アドレスだけに制限してください。

Linux Virtual Server スイートでは、透過的 HA バックエンド・インフラストラクチャーを実現するための方法を何通りか用意していますが、いずれの方法にしても長所と欠点があります。LVS-NAT はディレクター・サーバー上で、構成で指定されたポートを宛先とする着信パケットを取得し、パケット・ヘッダーの宛先アドレスを動的にリライトします。ディレクターはパケット自体のデータ・コンテンツを処理する代わりに、パケットを実サーバーへ中継します。パケットはその宛先アドレスがクラスターから特定の実サーバーへのポイントにリライトされた上で、ネットワークに戻されて実サーバーに転送されるため、実サーバーはこの処理を認識することはありません。実サーバーにとっては、外部から直接要求を受け取っただけに過ぎないというわけです。続いて実サーバーからの応答がディレクターに送り返されます。ディレクターは、クライアントの場所を示す浮動 IP アドレスを送信元アドレスに持つように応答をリライトしてから、元のクライアントに送信します。

LVS-NAT 方式を使用すれば、実サーバーに必要なのは単純な TCP/IP 機能だけとなります。これ以外の LVS 動作モード、つまり LVS-DR と LVS-Tun に必要なネットワーキングの概念はこれよりも複雑です。LVS-NAT を選んだ大きな理由は、実サーバーの構成をほとんど変更しなくても済むというメリットにあります。実際、LVS-NAT で一番難しいのは、ルーティング命令を忘れずに正しく設定するくらいのことです。

ステップ 1: 実サーバー・イメージを作成する

まず始めに、それぞれが Apache Web サーバーを実行する Linux サーバー・インスタンスのプールを作成し、Web ブラウザーですべての実サーバーの IP アドレスを指定して各サーバーが意図されたとおりに機能していることを確認します。通常、標準インストールでは実サーバー固有の IP アドレス (つまり、実サーバーごとに異なる IP) でポート 80 をリッスンするように構成されることになります 。

次に各サーバーで、ページを提供しているマシンのホスト名が含まれる静的ページを表示するデフォルト Web ページを構成します。このページを構成することで、テスト中に常に、どのマシンに接続されているかがわかるようになります。

万一のため、以下のコマンドを実行して、これらのシステムで IP 転送がオフになっていることをチェックしてください。

# cat /proc/sys/net/ipv4/ip_forward

オフになっていないけれども IP 転送を無効にする必要がある場合は、以下のコマンドを実行します。

# echo "0" >/proc/sys/net/ipv4/ip_forward

実サーバーのそれぞれが http ポート (80) で正しくリッスンしていることを簡単に確かめられる方法は、外部システムを使ってスキャンを実行することです。サーバーに接続可能なネットワークを持つ他のシステムから nmap ユーティリティーを使うと、サーバーがリッスンしているかどうかを確認することができます。

リスト 1. nmap によるサーバーのリッスン状態の確認
# nmap -P0 192.168.71.92

Starting nmap 3.70 ( http://www.insecure.org/nmap/ ) at 2006-01-13 16:58 EST
Interesting ports on 192.168.71.92:
(The 1656 ports scanned but not shown below are in state: closed)
PORT    STATE    SERVICE
22/tcp  open     ssh
80/tcp  filtered http
111/tcp open     rpcbind
631/tcp open     ipp

nmap のようなポート・スキャン・ツールを使用することに難色を示す組織もあるので、このツールを使用する場合は必ず事前に組織の同意を得るようにしてください。

次に、Web ブラウザーでそれぞれの実サーバーの実際の IP アドレスを指定し、それぞれの実サーバーが該当するページを提供することを確認します。この確認作業が終わったら、ステップ 2 に進んでください。


ステップ 2: LVS ディレクターをインストールして構成する

今度は、必要な 3 つの LVS ディレクター・インスタンスの構成作業に取り掛かります。LVS ディレクターごとに新しく SUSE Linux Enterprise Server 10 をインストールする場合は、初期インストール中に、ハートビート、ipvsadm、そして mon 関連の高可用性パッケージを確実に選択してください。インストール済みの場合は、基本インストールが完了していれば、いつでも YAST などのパッケージ管理ツールを使って、これらのパッケージを追加することができます。ぜひお勧めするのは、実サーバーのそれぞれを /etc/hosts ファイルに追加することです。そうすれば、着信要求に対応する際に DNS 関連の遅延が発生することはありません。

この時点で、すべてのディレクターがすべての実サーバーに対して適切なタイミングで ping を実行できることをダブルチェックしてください。

リスト 2. 実サーバーへの ping
# ping -c 1 $REAL_SERVER_IP_1

 # ping -c 1 $REAL_SERVER_IP_2

 # ping -c 1 $REAL_SERVER_IP_3

 # ping -c 1 $REAL_SERVER_IP_4

 # ping -c 1 $REAL_SERVER_IP_5

 # ping -c 1 $REAL_SERVER_IP_6

ping が完了したら、サーバー上でネイティブ・パッケージ管理ツールを使って ipvsadm、Heartbeat、mon をインストールします。前述したように、Heartbeat はディレクター間通信に使用し、mon はディレクターが実サーバーそれぞれのステータスに関する情報を保持するために使用します。


ステップ 3: ディレクターに Heartbeat をインストールして構成する

以前 LVS を使用したことがある方は、SLES10 での Heartbeat バージョン 2 の構成方法は SLES9 で Heartbeat Version 1 を構成したときとは多少違っていることに注意してください。Heartbeat のバージョン 1 では /etc/ha.d/ ディレクトリーに保存されているファイル (haresources、ha.cf、authkeys) を使用する一方、バージョン 2 では新しい XML ベースの CIB (Cluster Information Base) を使用します。アップグレードする場合に推奨する方法は、haresources ファイルを使って新規 cib.xml ファイルを生成することです。リスト 3 に、典型的な ha.cf ファイルの内容を記載します。

これは、SLES9 システムの ha.cf ファイルの最後にバージョン 2 用の 3 行 (respawnpingd、および crm) を追加したものです。既存のバージョン 1 の構成がある場合は、これと同じようにして構いません。新しくインストールしたものに、これらの命令を使う場合には、リスト 3 をコピーし、実動環境に合わせて内容を変更してください。

リスト 3. サンプル /etc/ha.d/ha.cf 構成ファイル
 # Log to syslog as facility "daemon"
 use_logd on
 logfacility daemon

 # List our cluster members (the realservers)
 node litsha22
 node litsha23
 node litsha21

 # Send one heartbeat each second
 keepalive 3

 # Warn when heartbeats are late
 warntime 5

 # Declare nodes dead after 10 seconds
 deadtime 10

 # Keep resources on their "preferred" hosts - needed for active/active
 #auto_failback on


 # The cluster nodes communicate on their heartbeat lan (.68.*) interfaces
 ucast eth1 192.168.68.201
 ucast eth1 192.168.68.202
 ucast eth1 192.168.68.203

 # Failover on network failures
 # Make the default gateway on the public interface a node to ping
 # (-m) -> For every connected node, add <integer> to the value set
 #  in the CIB, * Default=1
 # (-d) -> How long to wait for no further changes to occur before
 #  updating the CIB with a changed attribute
 # (-a) -> Name of the node attribute to set,  * Default=pingd
 respawn hacluster /usr/lib/heartbeat/pingd -m 100 -d 5s

 # Ping our router to monitor ethernet connectivity
 ping litrout71_vip

 #Enable version 2 functionality supporting clusters with  > 2 nodes
 crm yes

respawn ディレクティブは、実行してその動作をモニターするプログラムを指定するために使用します。指定したプログラムの終了コードが 100 以外であれば、プログラムは自動的に再起動されます。最初のパラメーターはプログラムを実行するユーザー ID で、2 番目のパラメーターは実行対象のプログラムです。-m パラメーターでは pingd 属性を現行のマシンから到達可能な ping ノード数の 100 倍に設定し、-d パラメーターでは CIB の pingd 属性を変更するまでの遅延時間を 5 秒に設定しています。ping ディレクティブは、Heartbeat に対して PingNode を宣言するためのものです。そして最後の crm ディレクティブで、Heartbeat に 1.x 形式のクラスター・マネージャーを実行させるか、または 3 ノード以上をサポートする 2.x 形式のクラスター・マネージャーを実行させるかを指定します。

このファイルはすべてのディレクターで同じでなければなりません。そして絶対不可欠なのは、hacluster デーモンがこのファイルを読み取れるような許可を適切に設定することです。このようにしないと、ログ・ファイルに大量の警告が記録されてデバッグが困難になります。

リリース 1 形式の Heartbeat クラスターでは、haresources ファイルでノード名とネットワーキング情報 (浮動 IP、関連インターフェース、およびブロードキャスト) が指定されます。このファイルは変更せずに、以下のままにしておきます。

litsha21 192.168.71.205/24/eth0/192.168.71.255

このファイルは、cib.xml ファイルを生成するためだけに使用することになります。

authkeys ファイルが指定するのは、ディレクターが相互に通信できるようにするための共有秘密鍵です。共有秘密鍵は単なるパスワードで、すべてのハートビート・ノードはこのパスワードを使って相互に通信します。秘密鍵によって、望ましくない人がハートビート・サーバー・ノードに干渉できないようにするわけです。このファイルも同じくそのままにします。

auth 1

1 sha1 ca0e08148801f55794b23461eb4106db

次に説明するステップは、バージョン 1 の haresources ファイルをバージョン 2 の新しい XML ベースの構成フォーマット (cib.xml) に変換するためのものです。ファイル変換の出発点としてリスト 4 の構成ファイルをそのままコピーして用いることは可能ですが、ファイルの内容を理解してデプロイメントに合わせて構成を調整するよう強くお勧めします。

ファイル・フォーマットをデプロイメントで使用する XML ベースの CIB (Cluster Information Base) ファイルに変換するには、以下のコマンドを実行します。

python /usr/lib64/heartbeat/haresources2cib.py /etc/ha.d/haresources > /var/lib/heartbeat/crm/test.xml

上記のコマンドによって、リスト 4 のような構成ファイルが生成され、var/lib/heartbeat/crm/test.xml に配置されます。

リスト 4. サンプル CIB.xml ファイル
 <cib admin_epoch="0" have_quorum="true" num_peers="3" cib_feature_revision="1.3"
  generated="true" ccm_transition="7" dc_uuid="114f3ad1-f18a-4bec-9f01-7ecc4d820f6c"
  epoch="280" num_updates="5205" cib-last-written="Tue Apr  3 16:03:33 2007">
    <configuration>
      <crm_config>
        <cluster_property_set id="cib-bootstrap-options">
          <attributes>
            <nvpair id="cib-bootstrap-options-symmetric_cluster"
                   name="symmetric_cluster" value="true"/>
            <nvpair id="cib-bootstrap-options-no_quorum_policy"
                   name="no_quorum_policy" value="stop"/>
            <nvpair id="cib-bootstrap-options-default_resource_stickiness"
                   name="default_resource_stickiness" value="0"/>
            <nvpair id="cib-bootstrap-options-stonith_enabled"
                   name="stonith_enabled" value="false"/>
            <nvpair id="cib-bootstrap-options-stop_orphan_resources"
                   name="stop_orphan_resources" value="true"/>
            <nvpair id="cib-bootstrap-options-stop_orphan_actions"
                   name="stop_orphan_actions" value="true"/>
            <nvpair id="cib-bootstrap-options-remove_after_stop"
                   name="remove_after_stop" value="false"/>
            <nvpair id="cib-bootstrap-options-transition_idle_timeout"
                   name="transition_idle_timeout" value="5min"/>
            <nvpair id="cib-bootstrap-options-is_managed_default"
                   name="is_managed_default" value="true"/>
          <attributes>
        <cluster_property_set>
      <crm_config>
      <nodes>
        <node uname="litsha21" type="normal" id="01ca9c3e-8876-4db5-ba33-a25cd46b72b3">
          <instance_attributes id="standby-01ca9c3e-8876-4db5-ba33-a25cd46b72b3">
            <attributes>
              <nvpair name="standby" id="standby-01ca9c3e-8876-4db5-ba33-a25cd46b72b3"
                     value="off"/>
            <attributes>
          <instance_attributes>
        <node>
        <node uname="litsha23" type="normal" id="dc9a784f-3325-4268-93af-96d2ab651eac">
          <instance_attributes id="standby-dc9a784f-3325-4268-93af-96d2ab651eac">
            <attributes>
              <nvpair name="standby" id="standby-dc9a784f-3325-4268-93af-96d2ab651eac"
                     value="off"/>
            <attributes>
          <instance_attributes>
        <node>
        <node uname="litsha22" type="normal" id="114f3ad1-f18a-4bec-9f01-7ecc4d820f6c">
          <instance_attributes id="standby-114f3ad1-f18a-4bec-9f01-7ecc4d820f6c">
            <attributes>
              <nvpair name="standby" id="standby-114f3ad1-f18a-4bec-9f01-7ecc4d820f6c"
                     value="off"/>
            <attributes>
          <instance_attributes>
        <node>
      <nodes>
      <resources>
        <primitive class="ocf" provider="heartbeat" type="IPaddr" id="IPaddr_1">
          <operations>
            <op id="IPaddr_1_mon" interval="5s" name="monitor" timeout="5s"/>
          <operations>
          <instance_attributes id="IPaddr_1_inst_attr">
            <attributes>
              <nvpair id="IPaddr_1_attr_0" name="ip" value="192.168.71.205"/>
              <nvpair id="IPaddr_1_attr_1" name="netmask" value="24"/>
              <nvpair id="IPaddr_1_attr_2" name="nic" value="eth0"/>
              <nvpair id="IPaddr_1_attr_3" name="broadcast" value="192.168.71.255"/>
            <attributes>
          <instance_attributes>
        <primitive>
      <resources>
      <constraints>
        <rsc_location id="rsc_location_IPaddr_1" rsc="IPaddr_1">
          <rule id="prefered_location_IPaddr_1" score="200">
            <expression attribute="#uname" id="prefered_location_IPaddr_1_expr"
                   operation="eq" value="litsha21"/>
          <rule>
        <rsc_location>
        <rsc_location id="my_resource:connected" rsc="IPaddr_1">
          <rule id="my_resource:connected:rule" score_attribute="pingd">
            <expression id="my_resource:connected:expr:defined" attribute="pingd"
                   operation="defined"/>
          <rule>
        <rsc_location>
      <constraints>
    <configuration>
  <cib>

構成ファイルが生成されたら、test.xml を cib.xml に上書きして所有者を hacluster に変更し、グループを haclient に変更します。その上で、ハートビート・プロセスを再開します。

これでハートビートの構成は完了したので、各ディレクターでブート時にハートビートが開始されるように設定します。それには、ディレクターごとに以下のコマンド (またはディストリビューションに合わせた同等のコマンド) を実行します。

# chkconfig heartbeat on

各 LVS ディレクターを再起動して、ブート時にハートビート・サービスが正しく開始されることを確認します。最初に浮動 IP アドレスを持ったマシンを停止すると、他の LVS ディレクター・イメージがクォーラムを確立し、新しく選ばれた 1 次ノードで数秒のうちにサービス・アドレスをインスタンス化する様子を見ることができます。停止したディレクター・イメージをオンライン状態に戻すと、全マシンがすべてのノードでクォーラムを再確立し、その時点で浮動リソース IP が戻されます。このプロセスには数秒間しかかからないはずです。

さらにこの時点で、ハートビート・プロセスのグラフィカル・ユーティリティー、hb_gui (図 2 を参照) を使用して各種のノードをスタンドバイまたはアクティブ状態に設定することにより、手動でクラスター内の IP アドレスを移動させることができます。この手順を何度も繰り返して、アクティブまたは非アクティブのさまざまなマシンを無効、または再度有効にしてください。前に選択した構成ポリシーでは、クォーラムが確立して少なくとも 1 つのノードが適格になる限り、浮動リソース IP アドレスは動作可能な状態を維持するはずです。テストの際には単純な ping を使用して、パケット損失が発生しないことを確認できます。実験し終えたときには、この構成がいかに堅牢であるかを確信できるはずです。浮動リソース IP の HA 構成に慣れた上で、手順を続けてください。

図 2. ハートビート・プロセスのグラフィカル構成ユーティリティー、hb_gui
ハートビート・プロセスのグラフィカル構成ユーティリティー、hb_gui

図 2 はログインすると表示されるグラフィカル・コンソールで、ここには管理リソースと関連構成オプションが示されます。アプリケーションを最初に起動するときログインするのは、hb_gui コンソールであることに注意してください。使用されるクレデンシャルは、デプロイメントによって異なります。

図 2 では、クラスター内のノード、litsha2* システムがそれぞれ実行状態であることに注目してください。litsha21 というラベルが付いたシステムは、その直下にインデントでリソース (IPaddr_1) が追加表示されていることから、現在アクティブなノードであることがわかります。

また、「No Quorum Policy (クォーラムなしポリシー)」フィールドの値が「stop」となっていることも要注意です。この値は、分離されたノードは所有するリソースを解放することを意味します。この設定に含まれる意味は、クォーラム (つまり、過半数への投票) を確立するためには常に 2 つのハートビート・ノードがアクティブ状態でなければならないということです。アクティブで 100% 動作可能なノードとそのピア・システムとの接続がネットワーク障害のために切断されてしまった場合でも、あるいはアクティブでない両方のピア・システムが同時に停止すると、リソースは自動的にリリースされます。


ステップ 4: ipvsadm コマンドで LVS ルールを作成する

次のステップは、浮動リソース IP アドレスに基づいた動作を構築することです。LVS はリモート Web ブラウザー・クライアントに対しては透過的であるよう意図されているので、すべての Web 要求はディレクターを介して実サーバーのいずれかに渡されなければなりません。さらに要求の結果はすべてディレクターに中継される必要があります。するとディレクターが、Web ページの要求を開始したクライアントに応答を返します。

この要求と応答の流れを実現するには、まず以下のコマンドを実行して、IP 転送を有効にするようにそれぞれの LVS ディレクターを構成します (これにより、要求が実サーバーに渡されるようにします)。

# echo "1" >/proc/sys/net/ipv4/ip_forward

# cat /proc/sys/net/ipv4/ip_forward

すべてが正常に完了すると、2 番目のコマンドによって「1」という出力が端末に返されます。これを固定的に追加するには、以下の内容を追加します。

'' IP_FORWARD="yes"

上記を追加する先は /etc/sysconfig/sysctl です。

次に、ipvsadm コマンドを使用して、ディレクターに着信 HTTP 要求を HA 浮動 IP アドレスから実サーバーに中継するよう指示します。

まず始めに、以下のコマンドで古い ipvsadm 表をクリアしてください。

# /sbin/ipvsadm -C

新しい表を構成するにはまず、作業負荷を分散するのにどのような方法をLVSディレクターで使用するかを決定しなければなりません。クライアントから接続要求を受信した時点で、ディレクターは「スケジュール」に応じて実サーバーをクライアントに割り当てるので、このスケジューラーのタイプを ipvsadm コマンドを使って設定します。使用できるスケジューラーには以下があります。

  • ラウンドロビン (RR): 新しい着信接続要求はそれぞれの実サーバーに順に割り当てられます。
  • 重み付けラウンドロビン (WRR): RR スケジューリングに、追加 CPU や追加メモリーなど、実サーバーの機能の違いを補正するための重み係数を追加したスケジューリング方式です。
  • 最小接続 (LC): 新しい接続要求は接続数が最も少ない実サーバーに割り当てられます。これは最もビジーでない実サーバーである必要はありませんが、その傾向になります。
  • 重み付け最小接続 (WLC): LC に重みを加えた方式です。

RR スケジューリングは確認しやすいため、テストには RR スケジューリングを使用するのが得策です。テスト・ルーチンに WRR および LC を追加して、これらの方式が正常に機能するかどうかを確認することもできます。この記事のサンプルでは RR スケジューリングとそのバリエーションを前提とします。

次に、実サービスへの ipvsadm サービス転送を有効するスクリプトを作成し、そのコピーを各 LVS ディレクターに配置します。このスクリプトは、この後自動的にアクティブな実サーバーをモニターするように mon を構成すると不要になりますが、それまでは ipvsadm コンポーネントをテストする際に役立ちます。スクリプトを実行する前に、ネットワークが適切であること、すべての実サーバーに http/https で接続できることをダブルチェックするのを忘れないようにしてください。

リスト 5. HA_CONFIG.sh ファイル
 #!/bin/sh

 # The virtual address on the director which acts as a cluster address

 VIRTUAL_CLUSTER_ADDRESS=192.168.71.205

 REAL_SERVER_IP_1=192.168.71.220

 REAL_SERVER_IP_2=192.168.71.150

 REAL_SERVER_IP_3=192.168.71.121

 REAL_SERVER_IP_4=192.168.71.145

 REAL_SERVER_IP_5=192.168.71.185

 REAL_SERVER_IP_6=192.168.71.186

 # set ip_forward ON for vs-nat director (1 on, 0 off).

 cat /proc/sys/net/ipv4/ip_forward

 echo "1" >/proc/sys/net/ipv4/ip_forward

 # director acts as the gw for realservers

 # Turn OFF icmp redirects (1 on, 0 off), if not the realservers might be clever and

 #  not use the director as the gateway!

 echo "0" >/proc/sys/net/ipv4/conf/all/send_redirects

 echo "0" >/proc/sys/net/ipv4/conf/default/send_redirects

 echo "0" >/proc/sys/net/ipv4/conf/eth0/send_redirects

 # Clear ipvsadm tables (better safe than sorry)

 /sbin/ipvsadm -C

 # We install LVS services with ipvsadm for HTTP and HTTPS connections with RR

 #  scheduling

 /sbin/ipvsadm -A -t $VIRTUAL_CLUSTER_ADDRESS:http -s rr

 /sbin/ipvsadm -A -t $VIRTUAL_CLUSTER_ADDRESS:https -s rr

 # First realserver

 # Forward HTTP to REAL_SERVER_IP_1 using LVS-NAT (-m), with weight=1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:http -r $REAL_SERVER_IP_1:http -m -w 1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:https -r $REAL_SERVER_IP_1:https -m -w 1

 # Second realserver

 # Forward HTTP to REAL_SERVER_IP_2 using LVS-NAT (-m), with weight=1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:http -r $REAL_SERVER_IP_2:http -m -w 1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:https -r $REAL_SERVER_IP_2:https -m -w 1

 # Third realserver

 # Forward HTTP to REAL_SERVER_IP_3 using LVS-NAT (-m), with weight=1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:http -r $REAL_SERVER_IP_3:http -m -w 1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:https -r $REAL_SERVER_IP_3:https -m -w 1

 # Fourth realserver

 # Forward HTTP to REAL_SERVER_IP_4 using LVS-NAT (-m), with weight=1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:http -r $REAL_SERVER_IP_4:http -m -w 1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:https -r $REAL_SERVER_IP_4:https -m -w 1

 # Fifth realserver

 # Forward HTTP to REAL_SERVER_IP_5 using LVS-NAT (-m), with weight=1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:http -r $REAL_SERVER_IP_5:http -m -w 1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:https -r $REAL_SERVER_IP_5:https -m -w 1

 # Sixth realserver

 # Forward HTTP to REAL_SERVER_IP_6 using LVS-NAT (-m), with weight=1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:http -r $REAL_SERVER_IP_6:http -m -w 1

 /sbin/ipvsadm -a -t $VIRTUAL_CLUSTER_ADDRESS:https -r $REAL_SERVER_IP_6:https -m -w 1

 # We print the new ipvsadm table for inspection

 echo "NEW IPVSADM TABLE:"

 /sbin/ipvsadm

リスト 5 を見るとわかるように、このスクリプトは ipvsadm サービスを有効にし、それから実質上同じスタンザを使って Web および SSL 要求を個別の実サーバーに転送しているだけです。ここでは -m オプションで NAT を指定し、重みを 1 (-w 1) に設定してすべての実サーバーの重みを同等にしています。指定したこの重みは、標準ラウンドロビン・スケジューリングを使用する場合には不要ですが (デフォルトの重みは常に 1 であるため)、重み付けラウンドロビンを選択する場合に備えて記載しました。重み付けラウンドロビンを使用するには、ラウンドロビンの使用に関するコメントの下にある 2 行で rrwrr に変更します。もちろん、重みも適宜調整してください。各種スケジューラーについての詳細は、ipvsadm のマニュアル・ページを参照してください。

これで、各ディレクターが浮動サービス IP への着信 Web 要求と SSL 要求をリライトして実サーバーに作業を順に渡すことによって、要求を処理するように構成できました。しかしトラフィックを実サーバーから戻し、逆のプロセスを行ってから要求を処理して該当するクライアントに返すには、ディレクター上でのネットワーキングの設定を多少変更しなければなりません。ネットワーキングの設定変更が必要なわけは、LVS ディレクターと実サーバーをフラット・ネットワーク・トポロジー (つまり、すべてが同じサブネットに常駐) で実装するように決定したためです。実サーバーそのものに直接応答するのではなく、ディレクターを介して Apache の応答トラフィックを返すようにするには、以下のコマンドを実行します。

echo "0" > /proc/sys/net/ipv4/conf/all/send_redirects
echo "0" > /proc/sys/net/ipv4/conf/default/send_redirects
echo "0" > /proc/sys/net/ipv4/conf/eth0/send_redirects

上記をコマンドを実行することによって、実サーバーと浮動サービス IP を直接対話させ (同じサブネット上にあるため)、アクティブな LVS ディレクターが TCP/IP ショートカットを使用しないようにしました。ネットワーク接続に不要な仲介を取り除いてパフォーマンスを向上させるリダイレクトは通常は有益ですが、この場合は、リダイレクトを行うと、クライアントに対して透過的になるように応答トラフィックをリライトできなくなってしまいます。実際、リダイレクトが LVS ディレクターで無効になっていなければ、クライアントは実サーバーからクライアントに直接送信されるトラフィックを予期しないネットワーク応答とみなすため、トラフィックは破棄されてしまいます。

今度は、実サーバーそれぞれのデフォルト・ルートがサービスの浮動 IP アドレスを指すように設定し、すべての応答がディレクターに返され、パケットがリライトされてから要求を開始したクライアントに渡されるようにします。

ディレクターでリダイレクトを無効に設定し、実サーバーがすべてのトラフィックを浮動サービス IP によってルーティングするように構成したら、続いて HA LVS 環境をテストします。これまでに行った作業をテストするため、リモート・クライアント上の Web ブラウザーで LVS ディレクターの浮動サービス・アドレスを指定してください。

どのブラウザーでも十分に必要を満たすはずですが、私たちがラボでのテストで使ったのは Gecko ベースのブラウザー (Mozilla) です。正常にデプロイされたことを確認するには、ブラウザーでのキャッシングを無効に設定し、更新ボタンを数回クリックします。更新するごとに、実サーバーごとに構成された自己識別をするための Web ページのいずれかが表示されるはずです。RR スケジューリングを使用している場合は、ページにそれぞれの実サーバーが順番に循環して表示されます。

ここで、LVS 構成がブート時に自動的に起動することを確かめたいと思うかもしれませんが、まだその段階ではありません。実サーバーを頻繁にモニターするには (そして、それによって作業要求に対応するのに適格な Apache ノードの動的リストを維持するには)、ステップがもう 1 つ必要です (ステップ 5)。


ステップ 5: LVS ディレクターに mon をインストールして構成する

ここまでのところで、高可用性サービスの IP アドレスを確立して実サーバー・インスタンスのプールに割り当てました。しかし、個々の Apache サーバーが常に動作可能であると過信はできません。RR スケジューリングを選択したことにより、特定の実サーバーが無効になったり、あるいはネットワーク・トラフィックにタイムリーに応答しなくなってしまうと、HTTP 要求の 6分の1 は失敗することになります。

そこで、サービス・プールの実サーバーを動的に追加または削除するために、すべての LVS ディレクターに実サーバーをモニターする機能を実装することが必要となります。このタスクに最適なのが、同じく有名なオープン・ソースのパッケージ、mon です。

LVS の実ノードをモニターするためには、一般的に mon ソリューションが使用されます。mon は比較的簡単に構成することができ、シェル・スクリプトの作成に慣れている人々にとって極めて拡張性に優れています。すべてを機能させるために不可欠な 3 つの主要なステップは、インストール、サービス・モニタリングの構成、アラートの作成です。mon をインストールするにはパッケージ管理ツールを使用してください。インストールが完了した後に必要なのは、サービス・モニタリングの構成を調整し、いくつかのアラート・スクリプトを作成することだけです。アラート・スクリプトは、モニターが実サーバーがオフライン状態であると判断したとき、またはオンライン状態へ復帰したと判断したときに起動されます。

Heartbeat v2 システムでは、すべての実サーバー・サービスをリソースにすることで、実サーバーをモニターすることが可能になります。あるいは Heartbeat ldirectord パッケージを使用するという方法もあります。

mon は複数のモニター・メカニズムが使用できる状態にデフォルト設定されて配布されます。私たちは /etc/mon.cf にあるサンプル構成ファイルを変更して、HTTP サービスを利用するようにしました。

mon 構成ファイルで、ヘッダーに正しいパスが反映されていることを確認してください。SLES10 は 64 ビットの Linux イメージですが、出荷時のサンプル構成はデフォルト (31 または 32 ビット) の場所に合わせてあります。このサンプル構成ファイルではアラートとモニターが /usr/lib にあるという前提になっていたため、この記事でのシステムには不適切でした。そのため、以下のようにパラメーターを変更しました。

alertdir = /usr/lib64/mon/alert.d
mondir = /usr/lib64/mon/mon.d

ご覧のように、liblib64 に変更しただけです。お使いのディストリビューションでは、このような変更は必要ない可能性もあります。

構成ファイルで次に行った変更は、モニターする実サーバーのリストを指定することです。それには、以下の 6 つのディレクティブを使いました。

リスト 6. モニター対象の実サーバーの指定
 hostgroup litstat1 192.168.71.220 # realserver 1

 hostgroup litstat2 192.168.71.150

 hostgroup litstat3 192.168.71.121

 hostgroup litstat4 192.168.71.145

 hostgroup litstat5 192.168.71.185

 hostgroup litstat6 192.168.71.186 # realserver 6

他にも追加したい実サーバーがあれば、上記にエントリーを加えるだけで追加することができます。

すべての定義が完了したら、mon に障害の監視方法、そして障害時に行う措置を指示します。それには以下のモニター・セクションを追加してください (実サーバーごとに 1 つのセクション)。追加し終わったら、mon 構成ファイルとアラートの両方をすべての LVS ハートビート・ノードに配置し、各ハートビート・クラスター・ノードが独立してすべての実サーバーをモニターできるようにします。

リスト 7. /etc/mon/mon.cf ファイル
 #
 # global options
 #
 cfbasedir    = /etc/mon
 alertdir     = /usr/lib64/mon/alert.d
 mondir       = /usr/lib64/mon/mon.d
 statedir     = /var/lib/mon
 logdir       = /var/log
 maxprocs     = 20
 histlength   = 100
 historicfile = mon_history.log
 randstart    = 60s
 #
 # authentication types:
 #   getpwnam      standard Unix passwd, NOT for shadow passwords
 #   shadow        Unix shadow passwords (not implemented)
 #   userfile      "mon" user file
 #
 authtype = getpwnam
 #
 # downtime logging, uncomment to enable
 #  if the server is running, don't forget to send a reset command
 #  when you change this
 #
 #dtlogfile = downtime.log
 dtlogging = yes
 #
 # NB:  hostgroup and watch entries are terminated with a blank line (or
 #  end of file).  Don't forget the blank lines between them or you lose.
 #
 #
 # group definitions (hostnames or IP addresses)
 # example:
 #
 # hostgroup servers www mail pop server4 server5
 #
 # For simplicity we monitor each individual server as if it were a "group"
 #  so we add only the hostname and the ip address of an individual node for each.
 hostgroup litstat1 192.168.71.220
 hostgroup litstat2 192.168.71.150
 hostgroup litstat3 192.168.71.121
 hostgroup litstat4 192.168.71.145
 hostgroup litstat5 192.168.71.185
 hostgroup litstat6 192.168.71.186
 #
 # Now we set identical watch definitions on each of our groups. They could be
 #  customized to treat individual servers differently, but we have made the
 #  configurations homogeneous here to match our homogeneous LVS configuration.
 #
  watch litstat1
      service http
         description http check servers
         interval 6s
         monitor http.monitor -p 80 -u /index.html
         allow_empty_group
         period wd {Mon-Sun}
             alert dowem.down.alert -hupalert dowem.up.alert -h
             alertevery 600s
                 alertafter 1
  watch litstat2
      service http
         description http check servers
         interval 6s
         monitor http.monitor -p 80 -u /index.html
         allow_empty_group
         period wd {Mon-Sun}
             alert dowem.down.alert -h
             upalert dowem.up.alert -h
             alertevery 600s
                 alertafter 1
  watch litstat3
      service http
         description http check servers
         interval 6s
         monitor http.monitor -p 80 -u /index.html
         allow_empty_group
         period wd {Mon-Sun}
             alert dowem.down.alert -h
             upalert dowem.up.alert -h
             alertevery 600s
                 alertafter 1
  watch litstat4
      service http
         description http check servers
         interval 6s
         monitor http.monitor -p 80 -u /index.html
         allow_empty_group
         period wd {Mon-Sun}
             alert dowem.down.alert -h
             upalert dowem.up.alert -h
             alertevery 600s
                 alertafter 1
  watch litstat5
      service http
         description http check servers
         interval 6s
         monitor http.monitor -p 80 -u /index.html
         allow_empty_group
         period wd {Mon-Sun}
             alert dowem.down.alert -h
             upalert dowem.up.alert -h
             alertevery 600s
                 alertafter 1
  watch litstat6
      service http
         description http check servers
         interval 6s
         monitor http.monitor -p 80 -u /index.html
         allow_empty_group
         period wd {Mon-Sun}
             alert dowem.down.alert -h
             upalert dowem.up.alert -h
             alertevery 600s
                 alertafter 1

リスト 7 で mon に使用するよう指示している http.monitor は、mon にデフォルトで付属しています。さらに使用するポートにはポート 80 を指定しています。リスト 7 ではまた、要求する特定のページも指定しています。このように、Web サーバーの複雑なデフォルト html ページより小さく効率的な html のセグメントを成功の証として送信することも可能です。

alertupalert の行が呼び出すスクリプトは、構成ファイルの先頭に指定された alertdir に配置されていなければなりません。このディレクトリーは通常、ディストリビューションのデフォルトの設定です (「/usr/lib64/mon/alert.d/」など)。アラートは、LVS に Apache サーバーを適格リストに追加、またはリストから削除するように指示する役目があります (その手段としては、この後説明する ipvsadm コマンドを呼び出します)。

実サーバーのいずれかが http テストに失敗すると、mon は複数の引数を自動的に指定して dowem.down.alert を実行します。同様に、モニターが実サーバーがオンライン復帰したと判断すると、多数の引数が自動的に指定された dowem.up.alert が mon プロセスによって実行されます。このアラート・スクリプトの名前は、独自のデプロイメントに合わせて自由に変更して構いません。

このファイルを保存し、alertdir 内にアラートを (単純なバッシュ・スクリプトを使って) 作成してください。リスト 8 は、実サーバーとの接続が再確立されたときに mon が呼び出す bash スクリプト・アラートです。

リスト 8. 単純なアラート: 接続時
 #! /bin/bash

 #   The h arg is followed by the hostname we are interested in acting on

 #   So we skip ahead to get the -h option since we don't care about the others

 REALSERVER=192.168.71.205

 while [ $1 != "-h" ] ;

 do

         shift

 done

 ADDHOST=$2

 # For the HTTP service

 /sbin/ipvsadm -a -t $REALSERVER:http -r $ADDHOST:http -m -w 1

 # For the HTTPS service

 /sbin/ipvsadm -a -t $REALSERVER:https -r $ADDHOST:https -m -w 1

リスト 9 は、実サーバーとの接続が切断されたときに mon が呼び出す bash スクリプト・アラートです。

リスト 9. 単純なアラート: 接続切断時
 #! /bin/bash

 #   The h arg is followed by the hostname we are interested in acting on

 #   So we skip ahead to get the -h option since we dont care about the others

 REALSERVER=192.168.71.205

 while [ $1 != "-h" ] ;

 do

         shift

 done

 BADHOST=$2

 # For the HTTP service

 /sbin/ipvsadm -d -t $REALSERVER:http -r $BADHOST

 # For the HTTPS service

 /sbin/ipvsadm -d -t $REALSERVER:https -r $BADHOST

上記の 2 つのスクリプトはいずれも ipvsadm コマンドライン・ツールを使用して、LVS 表に対する実サーバーの追加、削除を動的に行います。ただし、この 2 つのスクリプトは完成には程遠いものです。mon が単純な Web 要求の http ポートしかモニターしなければ、ここで概説するアーキテクチャーは、特定の実サーバーが http 要求には正しく動作する一方、SSL 要求には正しく動作しないという状況に対しては脆弱です。このような状況では、問題のある実サーバーが https 候補リストから削除されないことになります。もちろん、mon 構成ファイルでそれぞれの実サーバーに対して 2 つ目の https モニターを有効にするとともに、Web 要求タイプごとに専用の高度なアラートを作成すれば簡単に修正できる問題ですが、この作業は読者の演習として残しておきます。

モニター機能がアクティブになっていることを確認するには、実サーバーのそれぞれで Apache プロセスを順に有効、無効に設定し、これらのイベントに対する各ディレクターの反応を観察します。すべてのディレクターがそれぞれの実サーバーを適切にモニターしていることが確認された場合に限り、chkconfig コマンドを使って mon プロセスがブート時に自動的に起動することを確かめる必要があります。ここで使用したコマンドは chkconfig mon on ですが、それぞれのディストリビューションによって使用するコマンドは異なります。

この最後のステップが終われば、システム間に高可用性 Web サーバー・インフラストラクチャーを構成する作業は完了です。ここからは当然、さらに高度な作業を行うこともできます。例えば、mon デーモン自体はモニターされてないことにお気付きかもしれませんが (ハードビート・プロジェクトではmon をモニターすることが可能です)、この最後のステップにより、このような高度な作業を行うための基礎が築かれたことになります。


トラブルシューティング

アクティブなノードが HA クラスター内で正しく機能しなくなる理由は、ノード自ら機能しなくなったのかどうかに関わらず、さまざまにあります。例えば、該当するノードと他のノードとのネットワーク接続が切断された、ハートビート・プロセスが停止した、そしてさまざまな環境上の問題のいずれかが発生したなどの理由です。アクティブなノードを意図的に停止させるには、そのノードで停止コマンドを実行するか、あるいは hb_gui (純粋な停止) コマンドを使ってスタンバイ・モードに設定するという方法があります。環境の頑強性をテストしようと思ったら、少々アグレッシブになっても構いません。(プラグを力いっぱい引き抜いてください!)

インジケーターとフェイルオーバー

Linux HA ハートビート・システムの構成を担当するシステム管理者が使用できるログ・ファイル・インジケーターには、2 つのタイプがあります。ログ・ファイルは、システムが浮動リソース IP アドレスを受信しているかどうかによって異なるためです。以下のログ結果は、浮動リソース IP アドレスを受信していないクラスター・メンバーの場合です。

リスト 10. リソース保持メンバー以外のログ結果
 litsha21:~ # cat  /var/log/messages

 Jan 16 12:00:20 litsha21 heartbeat: [3057]: WARN: node litsha23: is dead

 Jan 16 12:00:21 litsha21 cib: [3065]: info: mem_handle_event: Got an event

  OC_EV_MS_NOT_PRIMARY from ccm

 Jan 16 12:00:21 litsha21 cib: [3065]: info: mem_handle_event: instance=13, nodes=3,

  new=1, lost=0, n_idx=0, new_idx=3, old_idx=6

 Jan 16 12:00:21 litsha21 crmd: [3069]: info: mem_handle_event: Got an event

  OC_EV_MS_NOT_PRIMARY from ccm

 Jan 16 12:00:21 litsha21 crmd: [3069]: info: mem_handle_event: instance=13, nodes=3,

  new=1, lost=0, n_idx=0, new_idx=3, old_idx=6

 Jan 16 12:00:21 litsha21 crmd: [3069]: info: crmd_ccm_msg_callback:callbacks.c Quorum

  lost after event=NOT PRIMARY (id=13)

 Jan 16 12:00:21 litsha21 heartbeat: [3057]: info: Link litsha23:eth1 dead.

 Jan 16 12:00:38 litsha21 ccm: [3064]: debug: quorum plugin: majority

 Jan 16 12:00:38 litsha21 ccm: [3064]: debug: cluster:linux-ha, member_count=2,

  member_quorum_votes=200

 Jan 16 12:00:38 litsha21 ccm: [3064]: debug: total_node_count=3,

  total_quorum_votes=300

                    .................. Truncated For Brevity ..................

 Jan 16 12:00:40 litsha21 crmd: [3069]: info: update_dc:utils.c Set DC to litsha21

  (1.0.6)

 Jan 16 12:00:41 litsha21 crmd: [3069]: info: do_state_transition:fsa.c litsha21:

  State transition S_INTEGRATION ->

 S_FINALIZE_JOIN [ input=I_INTEGRATED cause=C_FSA_INTERNAL

  origin=check_join_state ]

 Jan 16 12:00:41 litsha21 crmd: [3069]: info: do_state_transition:fsa.c All 2 cluster

  nodes responded to the join offer.

 Jan 16 12:00:41 litsha21 crmd: [3069]: info: update_attrd:join_dc.c Connecting to

  attrd...

 Jan 16 12:00:41 litsha21 cib: [3065]: info: sync_our_cib:messages.c Syncing CIB to

  all peers

 Jan 16 12:00:41 litsha21 attrd: [3068]: info: attrd_local_callback:attrd.c Sending

  full refresh

                    .................. Truncated For Brevity ..................

 Jan 16 12:00:43 litsha21 pengine: [3112]: info: unpack_nodes:unpack.c Node litsha21

  is in standby-mode

 Jan 16 12:00:43 litsha21 pengine: [3112]: info: determine_online_status:unpack.c Node

  litsha21 is online

 Jan 16 12:00:43 litsha21 pengine: [3112]: info: determine_online_status:unpack.c Node

  litsha22 is online

 Jan 16 12:00:43 litsha21 pengine: [3112]: info: IPaddr_1

         (heartbeat::ocf:IPaddr): Stopped

 Jan 16 12:00:43 litsha21 pengine: [3112]: notice: StartRsc:native.c  litsha22

    Start IPaddr_1

 Jan 16 12:00:43 litsha21 pengine: [3112]: notice: Recurring:native.c litsha22

       IPaddr_1_monitor_5000

 Jan 16 12:00:43 litsha21 pengine: [3112]: notice: stage8:stages.c Created transition

  graph 0.

                    .................. Truncated For Brevity ..................

 Jan 16 12:00:46 litsha21 mgmtd: [3070]: debug: update cib finished

 Jan 16 12:00:46 litsha21 crmd: [3069]: info: do_state_transition:fsa.c litsha21:

  State transition S_TRANSITION_ENGINE ->

  S_IDLE [ input=I_TE_SUCCESS cause=C_IPC_MESSAGE origin=do_msg_route ]

 Jan 16 12:00:46 litsha21 cib: [3118]: info: write_cib_contents:io.c Wrote version

  0.53.593 of the CIB to disk (digest: 83b00c386e8b67c42d033a4141aaef90)

リスト 10 からわかるのは、ロールが行われて、クォーラムとして十分なメンバー数が投票できるようになっていることです。投票が行われると、通常の動作が再開されます。再開するために必要なアクションは何もありません。

その一方、浮動リソース IP アドレスを受信したクラスター・メンバーの場合、以下のようなログ結果となります。

リスト 11. リソース保持メンバーのログ結果
 litsha22:~ # cat  /var/log/messages

 Jan 16 12:00:06 litsha22 syslog-ng[1276]: STATS: dropped 0

 Jan 16 12:01:51 litsha22 heartbeat: [3892]: WARN: node litsha23: is dead

 Jan 16 12:01:51 litsha22 heartbeat: [3892]: info: Link litsha23:eth1 dead.

 Jan 16 12:01:51 litsha22 cib: [3900]: info: mem_handle_event: Got an event

  OC_EV_MS_NOT_PRIMARY from ccm

 Jan 16 12:01:51 litsha22 cib: [3900]: info: mem_handle_event: instance=13, nodes=3,

  new=3, lost=0, n_idx=0, new_idx=0, old_idx=6

 Jan 16 12:01:51 litsha22 crmd: [3904]: info: mem_handle_event: Got an event

  OC_EV_MS_NOT_PRIMARY from ccm

 Jan 16 12:01:51 litsha22 crmd: [3904]: info: mem_handle_event: instance=13, nodes=3,

  new=3, lost=0, n_idx=0, new_idx=0, old_idx=6

 Jan 16 12:01:51 litsha22 crmd: [3904]: info: crmd_ccm_msg_callback:callbacks.c Quorum

  lost after event=NOT PRIMARY (id=13)

 Jan 16 12:02:09 litsha22 ccm: [3899]: debug: quorum plugin: majority

 Jan 16 12:02:09 litsha22 crmd: [3904]: info: do_election_count_vote:election.c

  Election check: vote from litsha21

 Jan 16 12:02:09 litsha22 ccm: [3899]: debug: cluster:linux-ha, member_count=2,

  member_quorum_votes=200

 Jan 16 12:02:09 litsha22 ccm: [3899]: debug: total_node_count=3,

  total_quorum_votes=300

 Jan 16 12:02:09 litsha22 cib: [3900]: info: mem_handle_event: Got an event

  OC_EV_MS_INVALID from ccm

 Jan 16 12:02:09 litsha22 cib: [3900]: info: mem_handle_event: no mbr_track info

 Jan 16 12:02:09 litsha22 cib: [3900]: info: mem_handle_event: Got an event

  OC_EV_MS_NEW_MEMBERSHIP from ccm

 Jan 16 12:02:09 litsha22 cib: [3900]: info: mem_handle_event: instance=14, nodes=2,

  new=0, lost=1, n_idx=0, new_idx=2, old_idx=5

 Jan 16 12:02:09 litsha22 cib: [3900]: info: cib_ccm_msg_callback:callbacks.c

  LOST: litsha23

 Jan 16 12:02:09 litsha22 cib: [3900]: info: cib_ccm_msg_callback:callbacks.c

  PEER: litsha21

 Jan 16 12:02:09 litsha22 cib: [3900]: info: cib_ccm_msg_callback:callbacks.c

  PEER: litsha22

                    .................. Truncated For Brevity ..................

 Jan 16 12:02:12 litsha22 crmd: [3904]: info: update_dc:utils.c Set DC to litsha21

  (1.0.6)

 Jan 16 12:02:12 litsha22 crmd: [3904]: info: do_state_transition:fsa.c litsha22:

  State transition S_PENDING -> S_NOT_DC [ input=I_NOT_DC cause=C_HA_MESSAGE

  origin=do_cl_join_finalize_respond ]

 Jan 16 12:02:12 litsha22 cib: [3900]: info: cib_diff_notify:notify.c Update (client:

  3069, call:25): 0.52.585 ->

 0.52.586 (ok)

                    .................. Truncated For Brevity ..................

 Jan 16 12:02:14 litsha22 IPaddr[3998]: INFO: /sbin/ifconfig eth0:0 192.168.71.205

  netmask 255.255.255.0 broadcast 192.168.71.255

 Jan 16 12:02:14 litsha22 IPaddr[3998]: INFO: Sending Gratuitous Arp for

  192.168.71.205 on eth0:0 [eth0]

 Jan 16 12:02:14 litsha22 IPaddr[3998]: INFO: /usr/lib64/heartbeat/send_arp -i 500 -r

  10 -p

 /var/run/heartbeat/rsctmp/send_arp/send_arp-192.168.71.205 eth0 192.168.71.205 auto

  192.168.71.205 ffffffffffff

 Jan 16 12:02:14 litsha22 crmd: [3904]: info: process_lrm_event:lrm.c LRM operation

  (3) start_0 on IPaddr_1 complete

 Jan 16 12:02:14 litsha22 kernel: send_arp uses obsolete (PF_INET,SOCK_PACKET)

 Jan 16 12:02:14 litsha22 kernel: klogd 1.4.1, ---------- state change ----------

 Jan 16 12:02:14 litsha22 kernel: NET: Registered protocol family 17

 Jan 16 12:02:15 litsha22 crmd: [3904]: info: do_lrm_rsc_op:lrm.c Performing op

  monitor on IPaddr_1 (interval=5000ms, key=0:f9d962f0-4ed6-462d-a28d-e27b6532884c)

 Jan 16 12:02:15 litsha22 cib: [3900]: info: cib_diff_notify:notify.c Update (client:

  3904, call:18): 0.53.591 ->

 0.53.592

  (ok)

 Jan 16 12:02:15 litsha22 mgmtd: [3905]: debug: update cib finished

リスト 11 を見るとわかるように、/var/log/messages ファイルにはこのノードが浮動リソースを獲得したことが示されています。ifconfig 行から、サービスを維持するために eth0:0 デバイスが動的に作成されていることがわかります。

リスト 11 でも同じく、ロールが行われ、クォーラムとして十分なメンバー数が投票できるようになっています。投票が行われた後、続いて浮動リソース IP アドレスを要求するために ifconfig コマンドが実行されます。

障害が発生したことを知るには、この方法とは別に、クラスター・メンバーのいずれかにログインし、hb_gui コマンドを実行するという方法もあります。この方法を使用すれば、目視確認により浮動リソースを保持するシステムを判断することができます。

最後に説明しておかなければならないのは、クォーラムがない場合のログ・ファイルの例です。ノードがそのピア・ノードのいずれとも通信できないと、そのノードはクォーラムを失うことになります (3 つのメンバーで投票を行う場合、3分の2 が過半数になるため)。この場合、ノードはクォーラムを失ったことを認識し、クォーラムなしのポリシー・ハンドラーを呼び出します。リスト 12 は、このようなイベントが発生した場合のログ・ファイルの一例です。クォーラムが失われると、ログ・エントリーによってそれが示されます。このログ・エントリーを示すクラスター・ノードは浮動リソースを放棄することになり、ifconfig down ステートメントによってこのリソースは解放されます。

リスト 12. クォーラム損失を示すログ・エントリー
 litsha22:~ # cat /var/log/messages

 ....................

 Jan 16 12:06:12 litsha22 ccm: [3899]: debug: quorum plugin: majority

 Jan 16 12:06:12 litsha22 ccm: [3899]: debug: cluster:linux-ha, member_count=1,

  member_quorum_votes=100

 Jan 16 12:06:12 litsha22 ccm: [3899]: debug: total_node_count=3,

  total_quorum_votes=300

                    .................. Truncated For Brevity ..................

 Jan 16 12:06:12 litsha22 crmd: [3904]: info: crmd_ccm_msg_callback:callbacks.c Quorum

  lost after event=INVALID (id=15)

 Jan 16 12:06:12 litsha22 crmd: [3904]: WARN: check_dead_member:ccm.c Our DC node

  (litsha21) left the cluster

                    .................. Truncated For Brevity ..................

 Jan 16 12:06:14 litsha22 IPaddr[5145]: INFO: /sbin/route -n del -host 192.168.71.205

 Jan 16 12:06:15 litsha22 lrmd: [1619]: info: RA output: (IPaddr_1:stop:stderr)

  SIOCDELRT: No such process

 Jan 16 12:06:15 litsha22 IPaddr[5145]: INFO: /sbin/ifconfig eth0:0 192.168.71.205

  down

 Jan 16 12:06:15 litsha22 IPaddr[5145]: INFO: IP Address 192.168.71.205 released

 Jan 16 12:06:15 litsha22 crmd: [3904]: info: process_lrm_event:lrm.c LRM operation

  (6) stop_0 on IPaddr_1 complete

 Jan 16 12:06:15 litsha22 cib: [3900]: info: cib_diff_notify:notify.c Update (client:

  3904, call:32): 0.54.599 ->

 0.54.600 (ok)

 Jan 16 12:06:15 litsha22 mgmtd: [3905]: debug: update cib finished

リスト 12 からわかるように、特定のノードでクォーラムが失われると、そのノードはリソースを放棄します。これはクォーラムなしのポリシー構成が選択されているためです。クォーラムなしのポリシーを使用するかどうかは、自由に選択することができます。

フェイルバックのアクションおよびメッセージ

適切に構成された Linux HA システムが示す興味深い点の 1 つは、何のアクションを行わなくてもクラスター・メンバーが再インスタンス化されることです。Linux インスタンスを起動するだけで、ノードはそのピア・ノードに自動的に再結合されます。1 次ノード (つまり、浮動リソースを他に優先して取得するノード) が構成されている場合には、1 次ノードが自動的に浮動リソースを再取得することになります。優先されないシステムは単に適格プールに加わり、通常と同じように動作を続けます。

別の Linux インスタンスをプールに再び追加すると各ノードがそれを認識し、可能な場合はクォーラムを再確立します。クォーラムの再確立が可能であれば、いずれかのノードに浮動リソースが再設定されます。

リスト 13. クォーラムの再確立
 litsha22:~ # tail -f /var/log/messages

 Jan 16 12:09:02 litsha22 heartbeat: [3892]: info: Heartbeat restart on node litsha21

 Jan 16 12:09:02 litsha22 heartbeat: [3892]: info: Link litsha21:eth1 up.

 Jan 16 12:09:02 litsha22 heartbeat: [3892]: info: Status update for node litsha21:

  status init

 Jan 16 12:09:02 litsha22 heartbeat: [3892]: info: Status update for node litsha21:

  status up

 Jan 16 12:09:22 litsha22 heartbeat: [3892]: debug: get_delnodelist: delnodelist=

 Jan 16 12:09:22 litsha22 heartbeat: [3892]: info: Status update for node litsha21:

  status active

 Jan 16 12:09:22 litsha22 cib: [3900]: info: cib_client_status_callback:callbacks.c

  Status update: Client litsha21/cib now has status [join]

 Jan 16 12:09:23 litsha22 heartbeat: [3892]: WARN: 1 lost packet(s) for [litsha21]

  [36:38]

 Jan 16 12:09:23 litsha22 heartbeat: [3892]: info: No pkts missing from litsha21!

 Jan 16 12:09:23 litsha22 crmd: [3904]: notice:

  crmd_client_status_callback:callbacks.c Status update: Client litsha21/crmd now has

  status [online]

 ....................

 Jan 16 12:09:31 litsha22 crmd: [3904]: info: crmd_ccm_msg_callback:callbacks.c Quorum

  (re)attained after event=NEW MEMBERSHIP (id=16)

 Jan 16 12:09:31 litsha22 crmd: [3904]: info: ccm_event_detail:ccm.c NEW MEMBERSHIP:

  trans=16, nodes=2, new=1, lost=0 n_idx=0, new_idx=2, old_idx=5

 Jan 16 12:09:31 litsha22 crmd: [3904]: info: ccm_event_detail:ccm.c     CURRENT:

  litsha22 [nodeid=1, born=13]

 Jan 16 12:09:31 litsha22 crmd: [3904]: info: ccm_event_detail:ccm.c     CURRENT:

  litsha21 [nodeid=0, born=16]

 Jan 16 12:09:31 litsha22 crmd: [3904]: info: ccm_event_detail:ccm.c     NEW:

      litsha21 [nodeid=0, born=16]

 Jan 16 12:09:31 litsha22 cib: [3900]: info: cib_diff_notify:notify.c Local-only

  Change (client:3904, call: 35):

 0.54.600 (ok)

 Jan 16 12:09:31 litsha22 mgmtd: [3905]: debug: update cib finished

 ....................

 Jan 16 12:09:34 litsha22 crmd: [3904]: info: update_dc:utils.c Set DC to litsha22

  (1.0.6)

 Jan 16 12:09:35 litsha22 cib: [3900]: info: sync_our_cib:messages.c Syncing CIB to

  litsha21

 Jan 16 12:09:35 litsha22 crmd: [3904]: info: do_state_transition:fsa.c litsha22:

  State transition S_INTEGRATION ->

  S_FINALIZE_JOIN [ input=I_INTEGRATED cause=C_FSA_INTERNAL origin=check_join_state ]

 Jan 16 12:09:35 litsha22 crmd: [3904]: info: do_state_transition:fsa.c All 2 cluster

  nodes responded to the join offer.

 Jan 16 12:09:35 litsha22 attrd: [3903]: info: attrd_local_callback:attrd.c Sending

  full refresh

 Jan 16 12:09:35 litsha22 cib: [3900]: info: sync_our_cib:messages.c Syncing CIB to

  all peers

 .........................

 Jan 16 12:09:37 litsha22 tengine: [5119]: info: send_rsc_command:actions.c Initiating

  action 4: IPaddr_1_start_0 on litsha22

 Jan 16 12:09:37 litsha22 tengine: [5119]: info: send_rsc_command:actions.c Initiating

  action 2: probe_complete on litsha21

 Jan 16 12:09:37 litsha22 crmd: [3904]: info: do_lrm_rsc_op:lrm.c Performing op start

  on IPaddr_1 (interval=0ms,

  key=2:c5131d14-a9d9-400c-a4b1-60d8f5fbbcce)

 Jan 16 12:09:37 litsha22 pengine: [5120]: info: process_pe_message:pengine.c

  Transition 2: PEngine Input stored in: /var/lib/heartbeat/pengine/pe-input-72.bz2

 Jan 16 12:09:37 litsha22 IPaddr[5196]: INFO: /sbin/ifconfig eth0:0 192.168.71.205

  netmask 255.255.255.0 broadcast 192.168.71.255

 Jan 16 12:09:37 litsha22 IPaddr[5196]: INFO: Sending Gratuitous Arp for

  192.168.71.205 on eth0:0 [eth0]


 Jan 16 12:09:37 litsha22 IPaddr[5196]: INFO: /usr/lib64/heartbeat/send_arp -i 500 -r

  10 -p

  /var/run/heartbeat/rsctmp/send_arp/send_arp-192.168.71.205 eth0 192.168.71.205 auto

  192.168.71.205 ffffffffffff

 Jan 16 12:09:37 litsha22 crmd: [3904]: info: process_lrm_event:lrm.c LRM operation


  (7) start_0 on IPaddr_1 complete

 Jan 16 12:09:37 litsha22 cib: [3900]: info: cib_diff_notify:notify.c Update (client:

  3904, call:46): 0.55.607 -> 0.55.608 (ok)

 Jan 16 12:09:37 litsha22 mgmtd: [3905]: debug: update cib finished

 Jan 16 12:09:37 litsha22 tengine: [5119]: info: te_update_diff:callbacks.c Processing

  diff (cib_update): 0.55.607 -> 0.55.608

 Jan 16 12:09:37 litsha22 tengine: [5119]: info: match_graph_event:events.c Action

  IPaddr_1_start_0 (4) confirmed

 Jan 16 12:09:37 litsha22 tengine: [5119]: info: send_rsc_command:actions.c Initiating

  action 5: IPaddr_1_monitor_5000 on litsha22

 Jan 16 12:09:37 litsha22 crmd: [3904]: info: do_lrm_rsc_op:lrm.c Performing op

  monitor on IPaddr_1 (interval=5000ms, key=2:c5131d14-a9d9-400c-a4b1-60d8f5fbbcce)

 Jan 16 12:09:37 litsha22 cib: [5268]: info: write_cib_contents:io.c Wrote version

  0.55.608 of the CIB to disk (digest: 98cb6685c25d14131c49a998dbbd0c35)

 Jan 16 12:09:37 litsha22 crmd: [3904]: info: process_lrm_event:lrm.c LRM operation

  (8) monitor_5000 on IPaddr_1 complete

 Jan 16 12:09:38 litsha22 cib: [3900]: info: cib_diff_notify:notify.c Update (client:

  3904, call:47): 0.55.608 -> 0.55.609 (ok)

 Jan 16 12:09:38 litsha22 mgmtd: [3905]: debug: update cib finished

リスト 13 には、クォーラムが再確立されたことが示されています。クォーラムが再確立される際に投票が行われ、litsha22 が浮動リソースを持つアクティブ・ノードとなっています。


次のステップ

高可用性とはいわば一連の課題であり、この記事で概説したソリューションは最初のステップに過ぎません。このソリューションを基に、環境を拡張する方法は何通りもあります。冗長ネットワーキング、実サーバーをサポートするクラスター・ファイル・システム、あるいはクラスタリングを直接サポートする高度なミドルウェアをインストールすることも一考です。

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux, Open source, Web development
ArticleID=258240
ArticleTitle=Web サーバーのクラスターをセットアップする 5 つの簡単なステップ
publish-date=08222007