Home

tap dev blog

自作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の負荷分散します。

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

NATとは

NATとは

Network Address Translationのことです。発信元(ソース)IPや、送信先(ディスティネーション)IPといったネットワークアドレスを変換します。発信元IPを変換するものをSNAT、送信先IPを変換するものをDNATと呼びます。まず、SNATの例ですが、次のようになります。

・SNATの例

「クライアント(192.168.0.10:5000)」→(123.4.5.6:80あて)→「NATサーバー(GW)(受付:192.168.0.1、GIP:111.123.123.*)(ソースIP書き換え111.123.123.*:5000)」→「123.4.5.6:80」

SNATでは、上記のように、NATサーバーが、ソースIP(発信元IP)を書き換えます。リクエスト先の(123.4.5.6:80)からは、(111.123.123.*:5000)からのアクセスに見えます。よって、リクエスト応答は(111.123.123.*:5000)に返します。NATサーバーは、このポートに返ってきたパケットの宛先IPは、(192.168.0.10:5000)であると記憶しているので、宛先IPを(192.168.0.10:5000)に替えてクライアントに送ります。

このようにして、IPアドレスの書き換え操作のみで、アドレス帯の異なるネットワークと外向けIPひとつで通信できるようになります。

ただし、単にIPアドレスだけを書き換えると問題が発生する場合があります。例えば、上記の例の通信が行われている最中に、クライアント2から、同じ5000番ポートからアクセスが来た場合、NATサーバーは、IPアドレスのみの書き換えを行っていると、(111.123.123.*:5000)宛に戻ってきたパケットをどちらのクライアントに送ればいいのかわからなくなってしまいます。このようなことが起こらないように、port番号も変更する(クライアント2はport:5001を使おう!みたいな感じ。)ものを、「IPマスカレード」といいます。ふつう、natというと、IPマスカレードのことを言うことが多いようです。(この機能は、家庭のブロードバンドルーターとかに標準で入っています。この機能のおかげで、複数台のPCがインターネットに接続できます。)

次に、DNATですが、良く使われる例として、「家庭でサーバー立ち上げたい」ときに使います。

・DNATの例

「クライアント(123.4.5.6:5000)」→(111.123.123.*:80あて)→「NATサーバー(GW)(受付:111.123.123.*:80、内部IP192.168.0.1)(宛先IP書き換え192.168.0.10:80)」→「バックエンドサーバー192.168.0.10:80」

こんな感じで、外部からのアクセスが来た場合、宛先IPを書き換えて、内部のサーバーに投げるときに使えます。バックエンドサーバーは、送信元(123.4.5.6:5000)から来たパケットとして処理を行い、戻りパケットは、ゲートウェイであるNATサーバーに送り、NATサーバーが、送信元IPを書き戻してクライアントに送ります。

 「未知のIP帯→既知のIP帯」:DNAT

 「既知のIP帯→未知のIP帯」:SNAT

みたいな感じです。IPv4のグローバルIPが枯渇してきたために考えられたものですが、よくできていますね。

NATの問題点

NATは万能ではありません。例えば、「既知のIP帯→既知のIP帯(同一IP帯)」で、NATを使用すると、問題が発生します。次の例は、サブネットマスク255.255.255.0で考えてください。

・DNATの例(問題発生)

「クライアント(192.168.0.10:5000)」→(192.168.0.20:80あて)→「NATサーバー(受付:192.168.0.20)(宛先IP書き換え192.168.0.30:5000)」→「192.168.0.30:80」

上記例のように、NATサーバーによって、宛先IPアドレスを書き換えて別のサーバーに転送しています。「こんな必要あるのか?」と思われた方、ときどきあります。特に、サーバー周りですが。

さて、上記のようなDNATに何の問題があるのでしょうか。送信は問題無く行われるのですが、問題は、戻りパケットの方にあります。宛先である「192.168.0.30:80」では、パケットは次のような形になっているはずです。

  「送信元:192.168.0.10:5000、宛先:192.168.0.30:80」

このパケットを返信すると、「送信元:192.168.0.30:80、宛先:192.168.0.10:5000」となります。送信元は、送信先が同一アドレス帯であるので、そのままパケットを宛先に送ります。NATサーバーには返しません。

 このパケットを受け取ったクライアントPCは、「送信元:192.168.0.10:5000、宛先:192.168.0.20:80」で送ったパケットの返答が、(192.168.0.30)と、異なるアドレスから返ってきたので、受け取りを拒否します。(というよりも、そもそも返答と気付きません。)

このように、パケットを何度送信しても、戻ってこないことになり、通信が成立しません。

 また、SNATは同一IP帯では、成立しません。(そもそも初めのパケットが、NATサーバーに届かないため。)

 上記のDNAT問題を解決するパターンは、2つあり、1つはリバースプロキシやロードバランサ等の転送で行われることがある、宛先も送信元も同時に書き換える(パケット自体作り替えるとか・・)です。また、もうひとつが、DSR構成です。

DSRは次回・・

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

透過型プロキシとは

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の変換表で波ダッシュに変換されているようです。(*正確かどうかの検証は行っていません。現象から判断しました。実際のところがどのような理屈か知ってらっしゃる方がいたら、ぜひ教えてください。)

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

TAP事件簿 iPad がやってきた

IMG_0974

他の誰にも触らしてくれません。

Apache2.2 では UseCanonicalName ディレクティブの default 値は OFF

P1030820.JPG

タイトルの通り。PHP で $_SERVER[‘SERVER_NAME’] などを使っている場合ハマります。ハマりました。

apache2.0 と apache2.2 が混在している環境では注意です。

参考)

TAP事件簿 太田さんが大変な事に!

激務に耐えかねた目が、ついに悲鳴をあげました。

お医者様に「本当に帰るんですか?」とドクターストップがかかったのに、振り切って帰ってきたそうです。(本人談)

お大事に・・・。

100514_192029

Home

Search
Feeds

Return to page top