ホーム > タグ > by eizono

by eizono

自作RPMの作成とレポジトリ1〜RPM管理のススメ〜

長々書けていなかったのですが、表題の件、何回かに分けて投稿していこうと思います。

rpmパッケージを作って、自作レポジトリに登録するための下準備

まず、rpmパッケージを作成するためには、下準備として次のことが必要です。

 「rpmのコンパイル環境作り」

 「rpmのパッケージにサインするための暗号キー作成」

 「作りたいrpmのソースを持ってくる」

 「SPECファイルを書く」

下の二つは、パッケージを作るたびに毎回行う作業ですが、上の二つは、はじめに一回だけ行う内容です。

rpmのコンパイル環境づくり

至って簡単です。注意するべき点は、コンパイルはrootで行わないことです。rootでコンパイル作業を行うと、テストコンパイルとかを行った際(SPECファイルに書く内容がわからないときなど。特にルートディレクトリの指定方法とかがわからないときなど)、そのPC自体にインストールされてしまいます。パッケージングしたいのに、ふつうのmake installがかかってしまうと元も子もないので、rootは使わない方が賢明です。

とりあえず、naviというユーザーで作ることにします。

# useradd navi
# su navi
# cd

ユーザーつくって、ユーザーのホームディレクトリにいくだけです。

# vi .rpmmacros
%_topdir /home/navi/mkrpm

「.rpmmacros」というファイルをホームディレクトリの直下に作って、上記の内容を一行書いておきます。

これは、rpmbuildコマンドが使うトップディレクトリを指定しています。

# mkdir mkrpm
# cd mkrpm
# mkdir SPECS BUILD RPMS SOURCES SRPMS

実際に作業するディレクトリ群を作ります。これで、環境作成は一段落。

実際の使い方

これで、最低限のrpmを作るための環境は完成です。自分で書いたspecファイルを/home/navi/mkrpm/SPECSの下に置いて、そこから読むソースtarballを/home/navi/mkrpm/SOURCESに置いて、/home/navi/mkrpm/SPECS内で

# rpmbuild -ba ***.spec

でコンパイル/rpmパッケージングが行われて/home/navi/mkrpm/RPMSの下にrpmが出来上がります。ただし、このrpmファイルは、サインされていないので、yumとかで使うには少々難ありです。次回は、rpmファイルにサインをつける方法いきます。その次ぐらいに、specファイルの書き方をかければいいなぁ。(まだ、僕も一部わかっていないところあるんですが・・)

PHPのオーバーライドは仮想関数

PHPでオーバーライドの挙動について調べてみました。

 もともとC++とかでプログラムしていたので、PHPの継承系の挙動について勉強していたところ、次のような疑問を感じました。

「継承元でクラス内別メソッドが使用している関数をオーバーライドするとどっちが呼ばれるのか?」

というわけで、実践・実践っと。


//継承元の実行
print "[継承元結果]";
$m_test = new test_base();
//継承後の実行
print "[継承後結果]";
$m_test = new test1();
exit;

class test_base{
	public function __construct(){
		if($this->printdata()){
			print "Printdata is true\n";
		}else{
			print "Printdata is false\n";
		}
	}

	protected function printdata(){
		return false;
	}
}

class test1 extends test_base{
	protected function printdata(){
		return true;
	}
}

継承元(test_base)のコンストラクタでprintdata関数を呼び出しています。このクラスを継承して、printfata関数をオーバーライドしたのが、test1クラスです。さて、実行するとどうなるのかな?

C++のvirtualで指定した関数みたいに動くならば、継承後はTrue、継承前は当然Falseになりそうです。

また、virtual指定してない関数みたいに動くならば、継承後も継承前もFalseになりそうです。

では、実行結果。

[継承元結果]Printdata is false
[継承後結果]Printdata is true

あ、virutual指定しているときの動作だ。そんな訳で、phpでは、オーバーライドは仮想関数扱いで動くみたいです。

$thisで指定されているものは、継承後は、継承後のメソッドを優先して使用し、オーバーライドメソッドがない場合は継承元の関数を呼ぶ。みたいな感じでしょうか。

 これはこれで困る場合もあるので、(勝手に継承元の挙動が変わったら困る挙動まで変わるおそれがある)ので、オーバーライドで継承元のメソッドが使用されるようにしたいときはどうすれば?と思ったのですが、そんな関数はそもそもオーバーライドさせないようにしてしまったらいいと思いました。

 うだうだ書いたのですが、上記のprintdata関数は、「挙動を変えられたくない」関数である場合は、宣言するときに

final protected function printdata()

みたいに宣言してしまえば、オーバーライドできなくなり、解決!みたいな感じです。

DSRとは

DSRとは

Direct Server Returnのことです。一般にはNAT(Network Address Translation)との対称みたいな感じで使われていることが多い気がします。先ほどの例であった同一IP帯内でのDNAT戻りパケットの処理を少し変えた感じです。

・DSRの例

「クライアント(192.168.0.10:5000)」→(192.168.0.20:80あて)→「DSRサーバー(受付:192.168.0.20)(宛先IPは書き換えず、パケットの送付先のみ変更。192.168.0.30のNICあて)」→「192.168.0.30:80」

上記例は、先ほどのDNATとほとんど同じです。ただし、今回は、192.168.0.30に届くパケットの送信先は192.168.0.20のままです。このパケットが192.168.0.30に届くのは、パケットの宛先MACアドレスのみが、192.168.0.30のMACアドレスに書き換わっているからです。ゲートウェイにIPパケット送るときと同じです。

(MACアドレスとパケットについて)通常のTCP/IP通信では、次のようになっています。(それぞれの部分でパケットを一定サイズに分割しています。)

  「アプリケーションが送りたいデータ」→「TCPパケットで包み込む(ポート番号付加)」→「IPパケットで包み込む(送信/受信IPアドレス付加)」→「ethernetフレームで包み込む(宛先/送信元MACアドレス付加)」→「ネットワーク上を流れる」

もちろん、届いたパケットは逆順で元に戻されてデータとなります。(TCPの輻輳処理は省略)上記のDSR例のように送信先が異なっていても、パケットが届くのは、ethernetフレームでのMACアドレスが(192.168.0.30)のMACアドレスに設定されていたためです。

 話を戻しまして、通常であれば、DSRのパケットは、(192.168.0.30)では処理されません。当然、IPアドレスが自分のIPでないためです。iptablesとかで、Forwarding処理を許可していれば、(192.168.0.30)に転送してしまうかもしれません。(かもです。ARPチェックではねられるのかな?誰か教えてください。)

 DSRを行うには、このパケットを受け取れるようにしなければなりません。最も簡単な手段が、「透過型プロキシ設定」を行うことです。透過型プロキシを利用すると、自分のIPで無いIPアドレスのデータを受け取って、返信を、あたかもそのIPアドレスからのように返すことができます。これを行うのは、非常に簡単で、iptablesで次のように設定するだけです。

{{{

# iptables -t nat -A PREROUTING -d *.*.*.*(受け取りたいIP) -j REDIRECT

}}}

詳しい説明は省略しますが、REDIRECTターゲットを使うと、自分自身のIPアドレスにリダイレクトして、その結果を返すときは、もともと来たIPアドレスに書き換える処理をする、という動作をしてくれます。まんま「透過型プロキシ」ですね。

iptablesにこの動作を加えることによって次のような流れができあがります。

・DSRの例

「クライアント(192.168.0.10:5000)」→

→(元:192.168.0.10:5000、先:192.168.0.20:80)→

→「DSRサーバー(受付:192.168.0.20)(宛先IPは書き換えず、パケットの送付先のみ変更。192.168.0.30のNICあて)」

→(元:192.168.0.10:5000、先:192.168.0.20:80 ※送信先MACアドレスは192.168.0.30のNIC)

→「192.168.0.30のiptablesが、192.168.0.20あてのパケットを受け取って192.168.0.30:80にリダイレクト」

→(元:192.168.0.10:5000、先:192.168.0.30:80)→

→「192.168.0.30:80で待ち受けているアプリが処理して返答」

→(元:192.168.0.30:80、先:192.168.0.10:5000)→

→「192.168.0.30のiptablesが送信元を192.168.0.20に書き換えて192.168.0.10:5000に返信」

→(元:192.168.0.20:80、先:192.168.0.10:5000)→

→「クライアント(192.168.0.10:5000)が受信」

こんな感じの流れになります。略すと

[クライアント]→[DSRサーバー]→[バックエンドサーバー]→[クライアント]

となり、行きはDSRサーバーを通るが、戻りは直接返す、という行動になります。このため、DSR(Direct Server Return)というみたいです。

DSRの利点は、DSRをロードバランサと置き換えると、わかりやすくなります。

 「ロードバランサは、リクエストをバックエンドに分配はするが、返答はバックエンドが直接行う。このため、ロードバランサは返答の処理の必要性が無くなり、処理が軽くなる。

いいですね〜★。帯域も無駄遣いしなくて済みますし。

※ ふつうのロードバランサは、大抵次のような行動をします。(NATのところで少し触れましたが・・)

[クライアント]→[ロードバランサ]→[バックエンドサーバー]→[ロードバランサ]→[クライアント]

この動作を行うため、ロードバランサは、バックエンドとの通信時に送信元と送信先のIPをどちらも替えています。

次は、実践編。keepalived使って、DSRでmysqlの負荷分散します。

間違いや誤植がありましたら、お教えください。

透過型プロキシとは

iptablesを利用して、透過型プロキシなるものを使って、DSR構成を組んでみました。

その前に、プロキシとかDSRとかって何?みたいなことを簡単にまとめました。

プロキシ・リバースプロキシ・透過型プロキシとは

 プロキシ(代理応答)は、例えばインターネットにアクセスする際に、クライアントPCから直接リクエストを投げるのではなく、一旦別のサーバーにリクエストを投げ、リクエストを受け取ったサーバーが代理でインターネット上のホストにアクセスするといった行動のことです。良く使われる例としては、「グローバルIPが一個しかないけれど、複数のPCからインターネットに接続したい」といったとき、プロキシサーバーを立てて、代理応答させる等に使います。(natでも同様のことを実現できますが、プロキシを使うと、プロキシサーバーにキャッシュさせて応答を高速化したり、プロキシサーバーでアクセス制限とかもできます。natはレイヤ4でアドレス変換するだけなのに対し、プロキシはレイヤ7での行動ができるという感じでしょうか。)

 また、リバースプロキシは、例えばHTTPサーバーを立てている際に使います。(クライアント側でなく、サーバー側。)サーバーあてに来たリクエストを、そのURLとかによってバックエンドサーバーに分配する役目をします。同一URLに来たアクセスをバックエンドに分配するといったもの(この場合、どういった理由で分配するというものが不明確なので、リバースプロキシの役目とは異なります。)はロードバランサで実現します。

 透過型プロキシとは、上記ふたつとは少々異なっていて(これまではレイヤ7での動作が主。)、レイヤ4での動作が主になります。具体的には、IPアドレス自体は自分あてで無いものを、自分宛に書き換えて動作させるといったことを行います。

 「クライアント(192.168.0.10)」 →(リクエスト:123.1.2.3:80あて)→ 「透過型プロキシサーバー(192.168.0.1)のレイヤ4(IP書き換え)」→「透過型プロキシサーバーのレイヤ7(192.168.0.1:80)」

みたいな感じの動作をします。もちろん、戻りパケットは

 「クライアント(192.168.0.10)」 ←(リクエスト:123.1.2.3:80から)← 「透過型プロキシサーバー(192.168.0.1)のレイヤ4(IP書き換え)」←「透過型プロキシサーバーのレイヤ7(192.168.0.1:80)」

と動きます。注意しなければならないのは、

 ・パケットが正しく透過型プロキシに届くようになっているかどうか(よって、普通はゲートウェイ等にします。)

 ・アプリケーションの設定だけでは、動かない。(Linuxでは、iptablesとかの助けが必要。)

となります。

透過型プロキシを使った例としては、

1.クライアントPCのプロキシ設定をせずに、プロキシサーバーがグローバルIPを全部受け取れる(IP書き換えできる)状態にしておけば、クライアントからは、グローバルIPに直接接続しているように見えるが、実際はプロキシサーバーを通っている。

とか、

2.自分とは異なるIPアドレスあてのパケットを取得して、処理し、他のIPのマシンになりすましてパケットを返信する。

といったことがあります。

つぎはNATについてです。

間違いや誤植がありましたら、お教えください。

Linuxで帯域監視

テストでちょっとサイズの大きいデータを沢山扱うサーバーを複数台作成しなければならなかったので、それぞれのサーバーでコネクションあたりの帯域を制限して、「各サーバのNICの帯域監視」を行って、空いているサーバーに振り分けよう!ということになりました。

さて、Linuxでネットワークを経由しての帯域監視をするには・・・あれ?あんまりいいソフトないな。SNMPとかはあるのですが、UDPだから必要なときに確実にデータとるにはちょっと不安だし・・・、sshでつないでゴニョゴニョするのは無意味な転送が多すぎるから、毎秒(または数秒おき)やるのはちょっと・・。

「欲しいのはTCP接続で、/proc/net/devのデータの差を単純に取得したい、しかも余計な転送量喰わない軽いアプリ」なんですが、そんな都合のいいものがみつかりませんでした。

そんな訳で簡単な帯域計測サーバーをつくってみました。次のような仕様です。

「監視したいサーバーで帯域計測サーバーをデーモン起動」

「監視側からアクセスしたら直近の転送量(/sec)が返ってくる」

これだけ。結構あっさりできました。

走らせてみて気付いたのですが、監視側からサーバー接続を1秒おきに繰り返させて、サーバー側でnetstat・・・・

うわ、TIME_WAITが山ほどいる・・

・・当然ですね、TCPだし。

LInuxはTCPコネクション開放が60秒後なので、毎秒やったら、TIME_WAITが60個たまる訳です。TCPの仕様上当然といえば当然なのですが、

無駄なので消す方法ないですかね〜。誰か知っている方いらっしゃったら教えてください。

もし、同様なものをお探しな方いらっしゃいましたら、コメントいれといてください。ソース公開します。適当すぎて怒られそうですが・・

ちなみに検証環境はRHEL5、CentOS5(ほとんど一緒ですね)、開発言語はCです。

「〜」は2つある!?

文字コードセットutf-8で開発を行っていたところ、見た目はまったく同じなのに、何故かstrcmpとか正規表現マッチとか試してみても絶対通らないという現象がおこりました。次のような文字列です。

1.「こんにちは〜開発からのお知らせ〜」

2.「こんにちは〜開発からのお知らせ〜」

理由がわからず、2時間ほど悪戦苦闘・・・・。まさかとは思いながら16進数で表示して全部チェックすると、

あれれ

なんか違う・・・

まさか、見た目は同じなのに文字コードが違う??文字コードセットはutf-8に変換しているのに・・

というわけで、一文字ずつ調べていくと、「〜」の文字コードが、0xEFBD9Eと0xE3809Cと異なっていることがわかりました。

ググると、

0xEFBD9E:全角チルダ(表示は「〜」)

0xE3809C:波ダッシュ(表示は「〜」)

らしいです。見た目は同じです。

なぜこのような現象が起こったのかチェックしていくと次のようなことがわかりました。

Windows:日本語の標準文字コード(cp932:いわゆるshift-jisがベース)には、波ダッシュなるものはそもそも存在しない。「〜」=全角チルダ。よって、utf-8のファイルをwinで編集すると0xEFBD9Eで保存される。

Mac:日本語の標準文字コードはshift-jis系のようだが、ことえりはiso-2022-jp寄りっぽい。「〜」=波ダッシュ。よって、utf-8のファイルをmacで編集すると0xE3809Cで保存される。

がーん。同じutf-8なのに、winで打った文字とmacで打った文字は文字コードが異なるなんて。

また、winで作った文字列(全角チルダ)をメールの文章に入れてphpでメール送信を行うと、utf-8 → iso-2022-jp → utf-8の過程で、全角チルダが波ダッシュに変更されてしまうことがわかりました。iso-2022-jpにはどちらもあるようですが、phpのmbstringの変換表で波ダッシュに変換されているようです。(*正確かどうかの検証は行っていません。現象から判断しました。実際のところがどのような理屈か知ってらっしゃる方がいたら、ぜひ教えてください。)

結構有名な話のようですが、初めて知りました。全角ってやっかいだ・・

Pound+KeepAlivedでロードバランサ+冗長化

  • ロードバランサと冗長化のテストを行いました。
  • ロードバランサ2台とバックエンドサーバー3台(下図のような感じ)
      インターネット
        |
        |
 -------------------------------
     |                       |
     | 192.168.100.12        | 192.168.100.13
 ==========              ==========
 |   LB   |              |   LB   |
 ==========              ==========
     |                       |
     |                       |
 ----------------------------------------------------------
     |                       |                           |
     | 192.168.101.5         | 192.168.101.6             | 192.168.101.7
 ===========             ===========                 ===========
 |   web   |             |   web   |                 |   web   |
 ===========             ===========                 ===========

ロードバランサには、L7スイッチを行わせたいのと、HTTPSラッパを行わせたいので、採用する選択肢としては、

  • Apache mod_proxy , mod_proxy_balancer
  • Pound
  • squid
  • Ultra Monkey L7

などがあります。それぞれ、次のような特徴があります。

  • Apacheは、十分な安定性と拡張性があります。ただし、少々重い点があります。
  • Poundは、軽量かつセキュアです。ただし、フェイルオーバー機能が一切無い点と、TCPコネクション回りが少々雑です。
  • squidはProxyなので、バランサ機能に乏しいところが問題です。
  • Ultra Monkeyは、もともとレイヤ4のバランサですが、L7はレイヤ7対応しました。最近はやりな感じですが、まだ新しいので、実例に乏しいのが問題です。

今回は、これらのことを踏まえて、Poundを採用することにしました。課題のフェイルオーバー機能ですが、自分で Keepalived+LVS+Pound構成にして、VRRPによるフェイルオーバー機能を実装することにしました。

レイヤ4スイッチとレイヤ7スイッチのちがい(スイッチというよりバランサ)

レイヤ4スイッチは、「TCP層でのスイッチング」です。TCP層ですので、IPを見て振り分けることができます。また、SYNフラグ等を見て、通信のはじまりや終わりといったことをチェックしたり、それによりパケットブロックを行う等が可能です。ただし、HTTPプロトコル層(アプリケーション層)までは組み立てを行わないので、HTTPのヘッダを見たりして振り分けすることはできません。もちろんHTTPSの変換等も行えません。これに対し、レイヤ7 スイッチは、最上位層のアプリケーション層まで組み立ててから振り分けを行うので、HTTPのヘッダを見たり、URLによる振り分けを行ったりすることができます。

一般的には、レイヤ4での振り分けを行うバランサをロードバランサと呼び、レイヤ7での振り分けを行うバランサをリバースプロキシ+ロードバランサというみたいです。リバースプロキシ自体は、レイヤ7での応答を代理する(※proxy:代理)だけの機能です。通常、プロキシサーバーはイントラネット上のリクエストを代理送信するものですが、逆に、リクエストの受信を代理で受け付けて、必要な処理を行うものを「逆代理」なのでリバースプロキシというようです。

Poundにフェイルオーバー機能を上乗せする

今回はテストですので、単純にいきました。(HTTPSは使わず、HTTPのみで通信。L7スイッチの特徴を使わない。)頭の二台をマスタ+ホットスタンバイ構成でなく、DNSラウンドロビンにより二台とも動かして、リソースを無駄にしない(単にバランサがボトルネックになるのを防ぐ)ために、要求性能としては、次を定義します。

  • 公開側のIPアドレスはどちらのIPでもアクセスがいつでもできる。(片側のバランサが落ちたときは、もう一方がIPアドレスを引き継ぐ)
  • 停止時間はできるだけ短めに(最大5秒)
  • 再起動は単純にできる
  • Poundが応答しないときも、切り替えたい。 IPアドレスの引継があるので、このあたりはKeepAlived+LVSを用いて、公開IPはバーチャルIP(VIP)とすることにしました。

ここで、問題が発生します。

    Poundは、起動時にIP&portにソケットをつなぐ。

    このため、はじめに一方のVIPをListenするようにして起動したら、あとから別のVIPを引き継いでも、PoundはそのIPからのアクセスを受け付けてくれない。

というわけで、VIPを引き継ぐタイミングで、再起動して、両方のIPをListenするようにします。(別のconfigを読ませて立ち上げる。)

これだけでは、最後の「再起動を単純に」が問題となりますので、元に戻るときにも再起動させて(1方のVIPのみ引き離すと、Poundは自動的にシャットダウンする)、Poundに1方のVIPのみをListenさせるように切り替えることにしました。

次に、Poundの応答ですが、KeepAlivedには、VRRP機能でのサービス監視機能がありません。というわけで、自分でサービス監視もスクリプトを書いて行うことにしました。

あとは、実装するだけです。VIPの引継は、KeepAlivedに行わせますので、KeepAlivedからのトリガか、自分で作るサービス監視からのトリガで、Poundを再起動させることにします。

仕様実現のためのまとめ

  • Keepalivedが起動する。
    • 自動的に現状の状態を把握してVIPを誰が持つのか決定する。(KeepAlived(VRRP)+LVS)
    • 自分が持っているVIPから、poundにVIPをListenさせる設定にてpoundを起動する。
    • poundが起動したら、poundが止まっていないかどうかの監視スクリプトを開始する。
  • poundが異常停止してしまったとき
    • 監視スクリプトが異常に気づく。
    • 自分が最後の一台である(すべてのVIPを保持している)場合は、poundを再起動する。
    • 自分が最後の一台でない場合は、keepAlivedとpoundを落として、他のサーバーにまかせる。(keepAlivedが落ちると、他のサーバーのkeepAlivedがフェイルオーバーを始める。)
  • 他のサーバーのkeepAlivedが落ちたとき(VRRP信号が送られてこなくなったとき)
    • KeepAlivedが勝手に気づく。
    • フェイルオーバーを始める。
    • フェイルオーバーが終わったら、poundを、引き継いだVIPを含めた自分が保持するVIPをすべてListenするようにして、再起動する。
    • 以前の監視スクリプトを落とす。
    • poundが起動したら、poundが止まっていないかどうかの監視スクリプトを開始する。

実際の工程

まずは、2台のみという形で設定を行います。三台以上にする場合は、ちょっと工夫が必要かもしれません。

  • KeepAlived.conf (192.168.100.12をMasterとして持つ側のもの)
  • ! Configuration File for keepalived
    # フェイルオーバーした際のalertメールを送るための設定(無くても可)
    global_defs {
        # メールの送り先
        notification_email {
            hogehoge@hoge.co.jp
        }
        # メールの送信元アドレスと、メールサーバー
        notification_email_from KeepAlived@hogehoge.co.jp
        smtp_server 1.2.3.4
        smtp_connect_timeout 30
        router_id LVS_DEVEL
    }
    
    # VIP:192.168.100.12の設定
    #[vrrp_instance ][好きな名前]
    vrrp_instance VI_1 {
        # マスタサーバー
        state MASTER
        # VIPを割り当てるNIC
        interface eth0
    # ネットワークが混雑していて、スイッチがARP信号を処理できない場合は、ARPを少し送らせて送信する設定。
    # 無くても動く。動作が変な場合(VIPを引き継いだのに、外部から認識できない現象が出たら、こいつを設定。)
    # 弊害として、VIPの引継完了が遅くなる。(デフォルトは引き継いだら即ARP信号の送信。)
    #    garp_master_delay 1
        # 仮想ルーターのID(VRRP信号を共有する他サーバーのインスタンス間で共通にする。)
        virtual_router_id 51
        # 優先度(MasterはBACKUPよりも高く設定。)
        priority 101
        # なんだろこれ?
        advert_int 1
        # VRRP信号を受け取る際のパスワード等(同じvirtual_router_idならば、同じにする。)
        authentication {
            auth_type AH
            auth_pass k@l!ve1
        }
        # このインスタンスで管理するVIP(同じvirtual_router_idなら、同じにする。※NICは同じ必要ない。)
        virtual_ipaddress {
            192.168.100.12/28 dev eth0
        }
    # マスタノードは、何も要らない。
    #    notify_master "/usr/local/lbcheck/poundstart.sh 1"
    }
    
    # VIP:192.168.100.13の設定
    vrrp_instance VI_2 {
        state BACKUP
        interface eth0
    #    garp_master_delay 1
        virtual_router_id 52
        priority 100
        advert_int 1
        authentication {
            auth_type AH
            auth_pass k@l!ve2
        }
        virtual_ipaddress {
            192.168.100.13/28 dev eth0
        }
        # notify_masterは、マスターノードになったときに起動するスクリプトを指定。
        notify_master "/usr/local/lbcheck/poundstart.sh all"
        # notify_backupは、バックアップノードになったときに起動するスクリプトを指定。(初期起動時は、上の設定よりBACKUPなので、必ずこれが起動する。)
        notify_backup "/usr/local/lbcheck/poundstart.sh 1"
    }
    
  • KeepAlived.conf (192.168.100.13をMasterとして持つ側のもの)
  • ! Configuration File for keepalived
    
    global_defs {
        notification_email {
            hogehoge@hoge.co.jp
        }
        notification_email_from KeepAlived@hogehoge.co.jp
        smtp_server 1.2.3.4
        smtp_connect_timeout 30
        router_id LVS_DEVEL
    }
    
    # VIP:192.168.100.12の設定(BACKUPなので、priorityを下げ、stateを変える。)
    vrrp_instance VI_1 {
        state BACKUP
        interface eth0
    #    garp_master_delay 1
        virtual_router_id 51
        priority 100
        advert_int 1
        authentication {
            auth_type AH
            auth_pass k@l!ve1
        }
        virtual_ipaddress {
            192.168.100.12/28 dev eth0
        }
        notify_master "/usr/local/lbcheck/poundstart.sh all"
        notify_backup "/usr/local/lbcheck/poundstart.sh 2"
    }
    
    # VIP:192.168.100.13の設定
    vrrp_instance VI_2 {
        state MASTER
        interface eth0
    #    garp_master_delay 1
        virtual_router_id 52
        priority 101
        advert_int 1
        authentication {
            auth_type AH
            auth_pass k@l!ve2
        }
        virtual_ipaddress {
            192.168.100.13/28 dev eth0
        }
    }
    

監視スクリプト(含pound起動スクリプト)例

  • 今回は、4つにわけました。
    • 192.168.100.12の応答のみを監視するスクリプト(応答が無かったら、keepalivedを落とす)
    • 192.168.100.13の応答のみを監視するスクリプト(応答が無かったら、keepalivedを落とす)
    • 両方のVIPの応答を監視するスクリプト(応答が無かったら、poundを再起動する。)
    • 上記3つのスクリプトを停止したり起動したりするスクリプト
  • 特に最後のスクリプトを作った理由は次の通りです。
  • 監視スクリプト自体は、3つのうち1つだけしか起動しないはずの、排他的無限ループ永久監視なので、起動する際に、他の2つが起きていたら、落とさなければならない。ただし、もしなんらかの原因で、自分自身が多重起動していた場合、落とすことができないので、無限ループでない起動スクリプトから起こして、必ず二重起動をさせないようにする。

  • また、両方のVIPの監視スクリプトは、フェイルオーバーされて、2VIPを持った場合のための監視です。この場合、フェイルオーバーさせる相手がいないので、自分だけで解決しなければならないことから、pound再起動を行うようにしました。
  • なお、poundは、2つ起こして、各VIPを担当させる手もあったのですが、poundはコネクション管理がうまくないことから、二重起動があまり好ましくないと判断したため、必ず1つだけ起動させるようにしました。(二重起動のメリットは、フェイルオーバーしても一方のコネクションは切れずに済むことです。このタイプでもいいかもしれません。また、二重起動の際は、pound停止スクリプトを自分で書かなければいけません。)
  • 一方の応答のみを監視するスクリプト(dpoundstart1.sh)
#!/bin/bash
####
#とりあえずフェイルオーバースクリプト。
####
MyVIP="192.168.100.12"
CheckFile="index.html"
LBCheckDIR="/usr/local/lbcheck"
cfgFile="pound1.cfg"

#または、
#MyVIP="192.168.100.13"
#CheckFile="index.html"
#LBCheckDIR="/usr/local/lbcheck"
#cfgFile="pound2.cfg"

#まずは、poundを再起動。
/etc/rc.d/init.d/pound stop
sleep 1
/usr/sbin/pound -f $LBCheckDIR/$cfgFile
#起動して直後の監視は、失敗することがあるので、少し待つ。
sleep 2

#次に、自身のpoundが動いているかどうかチェック。止まっていたら、後ろを実行。
m_URL="http://$MyVIP/$CheckFile"

while true ; do
    # 1秒おきに監視を実行
    sleep 1
    AP=`/usr/bin/curl -s --head $m_URL | head -n 1 | cut -f 2 -d ' '`
    if [ "200" != "$AP" ]; then
        /etc/rc.d/init.d/keepalived stop
        /etc/rc.d/init.d/pound stop
        exit
    fi
done

o 両方の応答を監視するスクリプト(dpoundstartall.sh)

#!/bin/bash
####
#一台が止まっている危険状態でのスクリプト。(pound停止時は、poundを再起動)
####
MyVIP1="192.168.100.12"
MyVIP2="192.168.100.13"
CheckFile="index.html"
LBCheckDIR="/usr/local/lbcheck"
cfgFile="poundall.cfg"

#しばらく置く。
#まずは、poundを再起動。
/etc/rc.d/init.d/pound stop
#ネットワークが安定するまで待つ
sleep 1
/usr/sbin/pound -f $LBCheckDIR/$cfgFile
#起動して直後の監視は、失敗することがあるので、少し待つ。
sleep 2

#次に、自身のpoundが動いているかどうかチェック。止まっていたら、後ろを実行。
m_URL1="http://$MyVIP1/$CheckFile"
m_URL2="http://$MyVIP2/$CheckFile"
while true ; do
    #スクリプトは1秒おきにチェック。
    sleep 1
    AP1=`/usr/bin/curl -s --head $m_URL1 | head -n 1 | cut -f 2 -d ' '`
    AP2=`/usr/bin/curl -s --head $m_URL2 | head -n 1 | cut -f 2 -d ' '`
    if [ "200" != "$AP1" ] ; then
        #poundを再起動。
        /etc/rc.d/init.d/pound stop
        /usr/sbin/pound -f $LBCheckDIR/poundall.cfg
    elif [ "200" != "$AP2" ] ; then
        #poundを再起動。
        /etc/rc.d/init.d/pound stop
        /usr/sbin/pound -f $LBCheckDIR/poundall.cfg
    fi
done

o 監視スクリプトを起動したり停止したりするスクリプト(強制Kill)

#!/bin/bash
LBCheckDIR="/usr/local/lbcheck"

if [ $# -ne 1 ] ; then
    exit;
fi

#監視サービスが動いていたら、killする。
C_Service=`ps -ef | grep $LBCheckDIR/dpoundstart | grep -v grep`
if [ -n "$C_Service" ] ; then
    for m_i in `ps -ef | grep $LBCheckDIR/dpoundstart | grep -v grep | awk '{print $2}'` ; do
        kill -9 $m_i
    done
fi
# 引数の指示通り、監視スクリプトを起動する。
if [ "$1" = "1" ] ; then
    $LBCheckDIR/dpoundstart1.sh &
elif [ "$1" = "2" ] ; then
    $LBCheckDIR/dpoundstart2.sh &
else
    $LBCheckDIR/dpoundstartall.sh &
fi

poundのコンフィグファイルについて

  • pound.conf は、3種類用意していますが、ほとんど同じものです。
  • とりあえず、 pound.conf の設定可能内容を先に書いておきます。
  • #User        "nobody"
    #Group       "nobody"
    #RootJail    "/var/pound/jail"
    #次の行は、Deamontoolsで動かすときは1にする。
    #Daemon         1
    #LogLevel       4
    #Alive          30
    #次は、下のリスナ設定で各値に上書きされる。
    #Client         5
    #TimeOut                60
    #Grace          30
    #このへんは使わない?
    #SSLEngine
    #Control
    #LogFacility    LOG_DAEMON
    #DynScale       0
    
    ## HTTPに関する設定
    #受付側(リスナ)
    #ListenHTTP
    #    Address 192.168.100.12
    #    Port    80
    #    Client  5
    #    xHTTP
    #    CheckURL
    #    Err414
    #    Err500
    #    Err501
    #    Err503
    #    MaxRequest 200M
    #    HeadRemove
    #    AddHeader
    #    RewriteLocation
    #    RewriteDestination
    #    Loglevel
    #    Service
    #End
    
    ## HTTPSに関する設定
    #受付側(リスナ)
    #ListenHTTPS
    #    Address 1.2.3.4
    #    Port    443
    #    Cert    "/etc/pound/pound.pem"
    #    Client  5
    #    ClientCert
    #    Ciphers
    #    CAlist
    #    xHTTPVerifyList
    #    CheckURL
    #    CRLlist
    #    NoHTTPS11
    #    Err414
    #    Err500
    #    Err501
    #    Err503
    #    MaxRequest 200M
    #    HeadRemove
    #    AddHeader
    #    RewriteLocation
    #    RewriteDestination
    #    Loglevel
    #    Service
    #End
    
    ##ここから、バックエンドサーバーに関する設定
    ##例と使えるもの一覧
    ## Image server
    #Service
    #    URL ".*.(jpg|gif)"
    ##    HeadRequire
    ##    HeadDeny
    ##    DynScale
    ##    Redirect
    #    Emergency
    #        Address 192.168.0.11
    #        Port 80
    #    End
    #    Session
    ##        Type {IP|BASIC|URL|PARM|COOKIE|HEADER}
    ##        #IP:IPが同じものは同じ場所に振る
    ##        #BASIC:BASIC認証が同じものは、同じ場所に振る
    ##        #PARM:URIパラメーターが同じものは同じ場所に振る。
    ##        #URL:URLに入っているパラメータ(下のIDで指定)が同じものは同じ場所に振る。
    ##        #COOKIE:COOKIEに入っているパラメータ(下のIDで指定)が同じものは同じ場所に振る。
    ##        #HEADER:HEADERに入っているパラメータ(下のIDで指定)が同じものは同じ場所に振る。
    ##        #ID    "sessid"
    ##        #次のTTLは必須。セッションが無効になるまでの時間。
    ##        TTL    180
    #    End
    #    BackEnd
    #        Address 192.168.0.10
    #        Port    80
    #        #Priorityはデフォルト5、最大9。優先したいサーバーは高くする。
    #        #HTTP以外のポートやアドレスでこのサーバの死活監視を行う場合必要。
    #        #HAport 192.168.100.1 50
    #        Priority 5
    #    End
    #End
    
    
  • 実際の pound1.conf
  • User        "nobody"
    Group       "nobody"
    
    ListenHTTP
      Address 192.168.100.12
      Port    80
      Client  5
    End
    
    Service
      BackEnd
        Address 192.168.101.5
        Port 80
      End
      BackEnd
        Address 192.168.101.6
        Port 80
      End
      BackEnd
        Address 192.168.101.7
        Port 80
      End
    End
    
  • 実際の pound2.conf
  • User        "nobody"
    Group       "nobody"
    
    ListenHTTP
      Address 192.168.100.13
      Port    80
      Client  5
    End
    
    Service
      BackEnd
        Address 192.168.101.5
        Port 80
      End
      BackEnd
        Address 192.168.101.6
        Port 80
      End
      BackEnd
        Address 192.168.101.7
        Port 80
      End
    End
    
  • 実際の poundall.conf
  • User        "nobody"
    Group       "nobody"
    
    ListenHTTP
      Address 192.168.100.12
      Port    80
      Client  5
    End
    
    ListenHTTP
      Address 192.168.100.13
      Port    80
      Client  5
    End
    
    Service
      BackEnd
        Address 192.168.101.5
        Port 80
      End
      BackEnd
        Address 192.168.101.6
        Port 80
      End
      BackEnd
        Address 192.168.101.7
        Port 80
      End
    End
    

まとめ

今回は、最も簡単にpound+keepAlivedでのロードバランサ冗長化ですが、意外と簡単にできました。

今度は、この環境がどのぐらいのアクセスをさばけるのか実験してみたいと思います。

実験結果次第では、市販のロードバランサの置換を考えられるかもしれません。次回は、「はじめてのyumレポジトリ作成」を投稿したい思っています。

ほんとは・・たぶん最もよい手段は・・

poundのソースを見て、VRRP機能を付加することだと思います。(ちょっと敷居が高い・・)そのうち考えます。

Home > Tags > by eizono

Search
Feeds

Return to page top