負け組アーキテクトの憂鬱

メモしておきたいことや読書の記録を淡々と書く。

引っ越しと再開

さくらインターネットのレンタルサーバで自前で構築したMovableTypeからlivedoor Blogに引っ越しました。

引っ越してて気づいたけど、2年も前の記事か…

引っ越さなくてもよかったかも。

ようやくMTを3.35にしてみた(がDBのUpgradeで手こずる)

気まぐれ更新。

以前3.3にアップグレードした時と同じ事象になった。

 failed to execute statement alter table mt_session modify session_id varchar(80) not null primary key: Multiple primary key defined at lib/MT/Upgrade.pm line 1190.

というエラーで先に進めない。うちはMySQLなんだけど、MySQLの人はみんな悩まされてるのかしら?

ad hocな対応として、とりあえずmt_sessionのprimary keyを一旦削除してみた。

 mysql> show fields from mt_session;
 +---------------+----------------+------+-----+---------+-------+
 | Field         | Type           | Null | Key | Default | Extra |
 +---------------+----------------+------+-----+---------+-------+
 | session_data  | mediumblob     | YES  |     | NULL    |       |
 | session_email | varbinary(255) | YES  |     | NULL    |       |
 | session_id    | varbinary(80)  |      | PRI |         |       |
 | session_kind  | binary(2)      | YES  | MUL | NULL    |       |
 | session_name  | varbinary(255) | YES  |     | NULL    |       |
 | session_start | int(11)        |      | MUL | 0       |       |
 +---------------+----------------+------+-----+---------+-------+
 6 rows in set (0.00 sec)

おおかた、session_idがPKなのに、Upgrade.pmの中でさらにPKを指定しようとしていたんでしょう。

 alter table mt_session drop primary key;

として、session_idのKeyからPRIが消えているのを確認。

 mysql> show fields from mt_session;
 +---------------+----------------+------+-----+---------+-------+
 | Field         | Type           | Null | Key | Default | Extra |
 +---------------+----------------+------+-----+---------+-------+
 | session_data  | mediumblob     | YES  |     | NULL    |       |
 | session_email | varbinary(255) | YES  |     | NULL    |       |
 | session_id    | varbinary(80)  |      |     |         |       |
 | session_kind  | binary(2)      | YES  | MUL | NULL    |       |
 | session_name  | varbinary(255) | YES  |     | NULL    |       |
 | session_start | int(11)        |      | MUL | 0       |       |
 +---------------+----------------+------+-----+---------+-------+
 6 rows in set (0.00 sec)

この状態で、再度アップグレード用のcgiを実行すると今度はうまくいった。もういっかいfieldsを見てみる。

 mysql> show fields from mt_session;
 +---------------+----------------+------+-----+---------+-------+
 | Field         | Type           | Null | Key | Default | Extra |
 +---------------+----------------+------+-----+---------+-------+
 | session_data  | mediumblob     | YES  |     | NULL    |       |
 | session_email | varbinary(255) | YES  |     | NULL    |       |
 | session_id    | varbinary(80)  |      | PRI |         |       |
 | session_kind  | binary(2)      | YES  | MUL | NULL    |       |
 | session_name  | varbinary(255) | YES  |     | NULL    |       |
 | session_start | int(11)        |      | MUL | 0       |       |
 +---------------+----------------+------+-----+---------+-------+
 6 rows in set (0.00 sec)

おお、元通り。よかったよかった(のか?)。

SunSutidioに同梱されるccで64bitバイナリを生成する

SunSutudio11しか使っていないけれど、たぶんSunStudio12でもいっしょ。

インストーラの指示に従ってccへのpathを通したら、あとはジャンジャンコンパイルすればいいんだけれども、デフォルトではUltraSPARC拡張命令を含まない32bitオブジェクトが出力される。

これを変更するには、''-xarch''オプションを指定する。-xarch=v9bといった具合。ここに指定出来るパラメータで主なものは、以下のようなかんじ。

  • v8:SPARC V8アーキテクチャ向け。32bitバイナリ。
  • v8plus:SPARC V9アーキテクチャの32bitサブセット向け。32bitバイナリ。
  • v8plusa:v8plusにVIS (Visual Instruction Set)1.0とUltraSPARC拡張を加えたもの。32bitバイナリ。
  • v8plusb:v8plusaにVIS 2.0とUltraSPARC-III拡張を加えたもの。32bitバイナリ。
  • v9:SPARC V9アーキテクチャ向け。64bitバイナリ。
  • v9a:v9にVIS 1.0とUltraSPARC拡張を加えたもの。64bitバイナリ。
  • v9b:v9aにVIS 2.0とUltraSPARC-III拡張を加えたもの。64bitバイナリ。

UltraSPARC-III対応のCPUであればv8plusbとv9bの使い分けでいいだろう。ただし、これらのバイナリはUltraSPARC以前のCPUアーキテクチャでは実行できない。

また、64bitでコンパイルする場合、リンクするライブラリもすべて64bit対応している必要がある。64bitバイナリから32bitバイナリはリンクできない。/usr/sfwとか/opt/sfwにインストールされるものの多くは32bitライブラリしか用意されていなかったりするので注意が必要。

32bitと64bitの使い分けは単純に「膨大な数を扱うか否か」で判断して良いと思う。よほど膨大なデータを扱うとか、メチャメチャ沢山コネクションを張るとか。その際はプロセスあたりのリソース上限値の調整も忘れずに(limitやunlimitコマンドで)。

ちなみに、64bitにしたからといってパフォーマンスが劇的に上がることはほとんどない。よほどのことが無い限りない(下がることはある)。当たり前と言えば当たり前の事なんだけれども、よく誤解している人も多い。

個人的には、一昔前の次世代ゲーム機論争のときにNintendo64は64bitだから性能いいんだぜとか、セガサターンは32bitのCPUを2つ積んでいるから64bit級だぜとか、そういう言われ方が多かったことも原因の一つだと思うんだけどまあどうでもいいや。

SunStudio12がリリースされている

http://developers.sun.com/sunstudio/downloads/index.jsp

まだインストールはしていないんだけれども。

最近のSolarisにはgccが添付されているとは言え、gccよりも質の良いコードを吐いてくれるそうなのでインストールしておいて損はないと思う。SDMにメンバ登録する必要はあるけれども、無償で利用できる。ただし、IDE等もまとめて入るので、かなりのディスク容量は食ってしまう。

x86やx64でのコードの質はどうなんだろうか。

mod_proxy_balancerのstickysessionは大文字小文字を区別する

何事もなかったように記事再開。

で、何が困ったかというと、apache httpd-2.2.4に添付のmod_proxy_balancerの挙動。

mod_proxy_balancerについてはこちら。要はmod_proxy系の各種モジュールでproxy先をバランシングできるわけですな。mod_proxy_balancer自体はかなり汎用性のあるモジュールなんだけど、今回困った用途はmod_proxy_ajpとの組み合わせ、つまりmod_jk(2)の代用として使ったときのこと。

たとえばweb層をapache(+mod_ajp +mod_proxy_balancer)の1台、ap層をtomcatの2台構成にしたとき、かつstatefulに振り分けをする必要があって、かつtomcatでセッションレプリケーションが使えない時にstickysessionの機能を使う。

httpd.conf中ではこんなかんじ。

 ProxyPass /webapp/ balancer://testcluster/webapp/ stickysession=ss
 <Proxy balancer://testcluster>
        # r0
        BalancerMember ajp://192.168.100.10:8009 loadfactor=1 route=r0
        # r1
        BalancerMember ajp://192.168.100.11:8009 loadfactor=1 route=r1
 </Proxy>

この設定はssという変数中にr0という文字列が含まれた場合(正しくはss=hogehhuga.r0 のようにピリオド+文字列で終わった場合)にはajp://192.168.100.10:8009に、ajp://192.168.100.11:8009にr1という文字列が含まれた場合にはajp://192.168.100.11:8009にproxyするというもの。変数とは具体的にはCookie、CGI環境変数、urlが利用できるスグレモノだ。

これをtomcatが付加してくれるjsessionidとjvmRouteと組み合わせるとstickysessionを実現できるようになる。2台のtomcatのjvmRouteにそれぞれr0, r1を指定すると、UAがCookieを使える設定になっていればCookieにJSESSIONID=hanamogerahogehoge.r0、Cookieが使えないUAにはURLパラメータとして;jsessionid=gegehogegehoge.r0といった具合に値を付加してくれる。ここでhttpd.confで以下のように設定しておけば、JSESSIONIDにr0が入っていればtomcat(r0)に、r1が入っていればtomcat(r1)にajp接続してくれる。一つのsessionは同じコンテナに接続される、という仕組み。

 ProxyPass /webapp/ balancer://testcluster/webapp/ stickysession=JSESSIONID
 <Proxy balancer://testcluster>
        # r0
        BalancerMember ajp://192.168.100.10:8009 loadfactor=1 route=r0
        # r1
        BalancerMember ajp://192.168.100.11:8009 loadfactor=1 route=r1
 </Proxy>

ただし、stickysession=JSESSIONIDではJSESSIONIDが大文字の場合にしかmatchしない。しかも良くないことにtomcatはCookieではJSESSIONIDと大文字、urlではjsessionidと小文字で変数名を指定する。たとえばCookieを有効にしたUAのみを対象にしたサイトでは問題ないが、Cookieの使えない携帯電話用サイトなども混在差せる場合これでは対応できない。しかも今の仕様ではstickysessionを複数指定することもできないようだ。

とはいえ、この仕様はapacheのbugzillaにも登録されており、一応不具合として認識はされているようだ。該当のbug。とりあえずここのpatchを、httpd-2.2.4のmod_proxy_balancer.cに当たるようにしたものがこれ(ほとんど変わらないけど)。

 --- mod_proxy_balancer.c.orig   Wed May 23 11:09:16 2007
 +++ mod_proxy_balancer.c        Wed May 23 11:58:50 2007
 @@ -112,9 +112,18 @@
                              const char *name)
  {
      char *path = NULL;
 +    char *session_id = NULL;
 +    int  i;
 
 -    for (path = strstr(url, name); path; path = strstr(path + 1, name)) {
 -        path += strlen(name);
 +    session_id= apr_pstrdup(pool, name);
 +    /* Change 'JSESSIONID' to 'jsessionid' to match the value in the url */
 +    if (isupper(name[0])) {
 +      for (i=0;i<=strlen(session_id);i++)
 +       session_id[i] = tolower(session_id[i]);
 +    }
 +
 +    for (path = strstr(url, session_id); path; path = strstr(path + 1, session_id)) {
 +        path += strlen(session_id);
          if (*path == '=') {
              /*
               * Session path was found, get it's value

このpatchを当てることで、とりあえず無邪気にstickysessionを小文字でもmatchするようにしてくれる。本来の挙動(大文字と小文字を厳密に区別する)を期待する場合にはpatchを当ててはいけない。

本来この設定と挙動はどうあるべきか、という議論は、このへんから始まる一連のthreadで話合われているようなので、次回のリリース(httpd-2.2.5?)では何らかの実装の変更があるだろう。

Microsoft Virtual PC 2004が無償化

Virtual PCのWindows版が無償化された。Mac版はまだのようだ。

ちょっと前にVirtual Serverは無償化されているので、それに追従した形だろう。Virtual Serverに対するVitual PCのメリットはズバリ、「音が出ること」。Virtual Serverはオーディオデバイスのエミュレーションが省かれているので音が出ない。Virtual Serverはサービスとして動作しているから音が出たら出たで困るんだけども。

そんなわけで、Windows98やMeでしか動作しないレガシーなアプリケーションはVirtual Serverで98/Meのインスタンスを作るよりも、Virtual PCで作った方が音も出るし、ゲストOSもホストOSの1アプリとして動くので使いやすいだろう。

ただし、Windowsなどの商用OSをゲストOSとしてインストールする場合、ライセンスには要注意。Virtual Server/PCの仮想マシンも1つのコンピュータとしてカウントされる場合がある。Windows Vistaでは1ライセンスで複数インスタンス(4?)をインストールすることができるらしいが、個人用途や評価開発用途で使いたいのであればMSDN Operating System Subscriptionがオススメだ。商用利用はできないものの、ほぼ全てのMicrosoftのOSが10ライセンスづつ与えられる。サブスクリプションは年毎更新だけれども、利用だけなら有効期間終了後も使用することができる

Movable Type 3.3にアップグレード

3.2-ja-2からのアップグレードに失敗。

MySQLのエラーなんだけど、たぶんこれ、ソフト側の設定云々でどうなる問題ではない気がしてシックス・アパートさんに相談させてもらっております。短絡的に、エラーが出なくなるまでテーブル構造変えたりすればいいんだろうけど、根本解決にはならないので。

どうしようもないので、とりあえず3.2-ja-2に切り戻し、mysqldumpのログからdb再構築するも、新規エントリの追加時にidが0まで巻き戻っていて登録しようとするとまたMySQLがエラー吐く。今度はどこでどんな地雷を踏んでしまったのやら・・・。素直にすんなりインストールしているはずなんだけどなあ。

切り戻して暫く様子を見てみよう、なんて思ってましたが不具合でちゃったらしょうがない。テンプレートのバックアップ取って、エントリの書き出しして、3.2環境削除→3.3新規インストール→チマチマリストアしました。

疲れたので寝ます。おやすみなさい。

第3の冗長化プロトコル、CARPを試す

CARP(Common Adress Redundancy Protocol)とはOpenBSD由来のサーバ冗長化プロトコル。同様なものにCisco SystemsのHSRP、以前試したVRRPがある。なんでまた3つも似たようなプロトコルがあるのかと言えば、乱暴に言ってだいたい以下のような違いがあるようだ。

  • HSRP Cisco Systemsのパテント。
  • VRRP パテントフリーのHSRPのはずだったが、どうもまだパテント部分が残っているらしい。
  • CARP 真のパテントフリーの実装を目指して実装されたもの。

さて、FreeBSDでのCARPの実装だけれども、ユーザランドでの実装とカーネルでの実装があるようだ。ユーザランドでの実装はUCARP。freevrrpdと同じような位置づけだろう(きっと)。これは後日試すとして、今回はOpenBSDのカーネルからポーティングされたと云うカーネルでの実装を試して見たい。結構最近の実装で、対象はFreeBSD 5.4以降のカーネルになる。今回試したバージョンは6月末あたりの6.1-STABLE。

カーネルでの実装、と言うだけあってカーネルの再構築が必要。今のところ標準状態ではLKMも用意されていないようなので、素直に再構築しておこう。以下の行を追加してカーネルを再構築する。

 device	carp

freevrrpdでは共有アドレスするアドレスは物理ネットワークデバイス(fxp0とかrl0とかde0とか)のエイリアスアドレスとして定義されたが、今回はcarp?という専用のネットワークデバイスが用意される。/etc/rc.confに以下のように記述。192.168.1.253を仮想アドレスとした。

masterにしたいホストでの設定例

 cloned_interfaces="carp0"
 ifconfig_carp0="vhid 1 pass hogehoge 192.168.1.253/24"

backupにしたいホストでの設定例

 cloned_interfaces="carp0"
 ifconfig_carp0="vhid 1 advskew 100 pass hogehoge 192.168.1.253/24"

vhidは仮想ホストを構成する実ホストのグループID。freevrrpで言うところのserveridに等しい。

advskewは死活監視への応答時間。1/256秒を掛けた値だけ遅延し、最初に応答した実ホストが仮想ホストを引き継ぐ。実質的な優先度と考えていいだろう。デフォルトで0なので、backupホストのみ指定すればいいだろう

passは通信時のパスワード。グループ内で同じものを指定すれば良い。

advbaseは今回指定していないが、死活監視の間隔。デフォルトで1秒だが、間隔を長くしたい場合は指定する必要がある(最大255)。

/etc/rc.conf以外に、sysctlもいくつかの設定項目がある。必要ならば/etc/sysctl.confで指定する。

 net.inet.carp.allow=1		#carpパケットを受信する。デフォルトは1。
 net.inet.carp.preempt=1	#複数のvhidをまとめてフェイルオーバさせたいとき(localとglobalのフェイルオーバを同期させたいなど)指定。デフォルトは0。
 net.inet.carp.arpbalance=0	#同一リンクで、arpの応答を使ってバランシングできるらしい。両現用にできるっぽいが未検証。デフォルトは0。
 net.inet.carp.log=1		#エラーが起きた時など、ログを記録。デフォルトは1。

ざっとこんなところ。この状態でmaster側でifconfigしてみると、

 carp0: flags=49<UP,LOOPBACK,RUNNING> mtu 1500
         inet 192.168.1.253 netmask 0xffffff00
         carp: MASTER vhid 1 advbase 1 advskew 0

おお、なんかかっこいい。backup側では、

 carp0: flags=8<LOOPBACK> mtu 1500
         inet 192.168.1.253 netmask 0xffffff00
         carp: INIT vhid 1 advbase 1 advskew 100

となっている。実はこの状態ではmasterが通信不能になってもフェイルオーバできない。ifconfigを使ってcarp0をupさせてあげる必要がある。なんか/etc/rc.confで引数にupを指定してもダメっぽい。別のオプションがあるのかもしれないけど探してません。ごめんなさい。

 # ifconfig carp0 up
 # ifconfig carp0
 carp0: flags=49<UP,LOOPBACK,RUNNING> mtu 1500
         inet 192.168.1.253 netmask 0xffffff00
         carp: BACKUP vhid 1 advbase 1 advskew 100

INITがBACKUPにかわった!!これで設定完了。フェイルオーバさせるには、物理的に抜線してもいいが、さきほど同様にifconfigでcarpインターフェイスをup,downさせてやれば良い。

 (master)# ifconfig carp0 down
 (master)# ifconfig carp0
 carp0: flags=8<LOOPBACK> mtu 1500
         inet 192.168.1.253 netmask 0xffffff00
         carp: INIT vhid 1 advbase 1 advskew 0

 (backup)# ifconfig carp0
 carp0: flags=49<UP,LOOPBACK,RUNNING> mtu 1500
         inet 192.168.1.253 netmask 0xffffff00
         carp: MASTER vhid 1 advbase 1 advskew 100

MASTERの交代が行われているようだ。再度master側でcarp0をupさせると、net.inet.carp.preemptが0のときは即座にMASTERが切り戻され、1の時は切り戻しは起きず、backup側がMASTERのままだった(わかりにくいな・・・)。

しかし、ここで問題が一点。syslogを見ると、

 kernel: arp_rtrequest: bad gateway 192.168.1.253 (!AF_LINK)

こんなメッセージが出力されている。ググってみたら「気分悪いけど問題ねーよ」みたいな事が書いてあるが、Gratuitous ARPしようとしてしくじっているように見える。仮想MACに00:00:5e:00:01:01というアドレスを使っているようだが、うちの環境ではどうもこれにスイッチングハブが付いてきていないみたいだ。carp0なんてインタフェイスが出来てるくらいなので、これをrouteに指定してもうまくいかず。実質、物理インタフェイスにalias割り振ったのと同じような動作しかしないようだ。うちでは物理的なスイッチングハブの他に、Virtual Server 2005の仮想スイッチなんかも噛んでるから、切り分けするのがめんどくさくなって今日は終了。次回(いつ?)に続く。

freevrrpdのようにフェイルオーバ時のスクリプトも簡単に指定できるわけではないので(まあ、それがユーザランド実装の強みだしね)、実際に運用するにはいろいろと作り込みが大変そう。ただ、carpのarpバランス機能には興味津々。このへんの期待も込めて、次回はUCARPを試してみるかも。