Mountain Lion にアップグレードしたら NetBeans 7.1 を起動できなくなりました

というわけで Dock アイコンから起動できなくなってしまったので起動コマンドを直接叩いてみました。
すると・・・

$ /Applications/NetBeans/NetBeans\ 7.1.app/Contents/Resources/NetBeans/bin/netbeans 
Cannot find java. Please use the --jdkhome switch.

Java が見つけられていないようです。
この Mac には Lion の時から Java7 がインストールされていて Mountain Lion にアップグレードした後もちゃんと参照はできるようです。

$ which java                                                                                
/usr/bin/java
$ java -version
java version "1.7.0_05"
Java(TM) SE Runtime Environment (build 1.7.0_05-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.1-b03, mixed mode)

アップグレードによってどこの参照が途切れてしまったのかわからないですがとにかく起動スクリプト内で java が参照できなくなってしまったようなので、設定ファイルに直接 Java Home を指定することにしました。

/Applications/NetBeans/NetBeans 7.1.app/Contents/Resources/NetBeans/etc/netbeans.conf

  ...
#netbeans_jdkhome="/path/to/jdk"
netbeans_jdkhome="/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home"

上記の netbeans_jdkhome="/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home"
が追記した部分です。

これで、無事 Dock からもNetBeans を起動できるようになりました。

TUN/TAPドライバ - パフォーマンス改善までのデバッグ履歴

私はここ数年 Solaris 向けの TUN/TAP ドライバの保守をしているのですが、最近OpenVPNのフォーラムに以下のような書き込みがあるのを見つけました。

http://forums.openvpn.net/topic9542.html

In some sources it was implied that while some performance degradation may be due to OpenSSL operating on small packets, or TCP/UDP flow control mismatches, or MTU and fragmentations, much of the rest can be blamed on the TUN/TAP interface software - which is made differently for different OSes, often by different authors, and performs - well - differently.

投稿した方は Solaris での OpenVPN のパフォーマンスをが出ない事に困っており、TUN/TAPドライバの実装に問題があるのでは無いかと疑って居るようです。実際の投稿の意図としては TUN/TAP ドライバ自体のパフォーマンスの改善では無く、Solaris バンドルの tunnel ドライバを TUN/TAP ドライバの代わりに使えないか?というものでしたが「パフォーマンスが酷く悪い」というのがちょっと気になり調べて見ることにしました。

結論的には調査の結果 Solaris 向け TUN/TAP ドライバにパフォーマンスのボトルネックが見つかり、コードを変更することにより劇的にパフォーマンスを改善できたのですが、そこにいたるまでかなりの道程があったので、後々の備忘録の意味も含めてデバッグ履歴を書き留めておきます。

現在のパフォーマンス測定

パフォーマンスが悪い、といっても遅さの指針となるデータがないと目標も立てられませんし、改善の評価も行えません。
そこで、今回はネットワークパフォーマンス測定ツール netperf を使ってパフォーマンスを測定することにしました。

環境

  • VMWare ESXi 5.0 内の2つの Solaris ゲストOS間の通信。
    • 実H/W: HP Proliant ML110 G5
    • サーバ側(nerserver 実行): OpenIndiana b148
    • クライアント側(netperf実行): Oracle Solaris 11 (11/11)
    • ゲスト間のネットワーク帯域: 1Gbps
    • netperf バージョン: 2.5.0

結果

実ネットワークでの転送速度 1 Gbps
OpenVPN+TAP ドライバ経由での転送 20 Mbps
実際の出力

実ネットワーク

sol11 $ netperf  -H oi148
MIGRATED TCP STREAM TEST from ::ffff:0.0.0.0 (0.0.0.0) port 0 AF_INET to oi148 (192.168.1.3) port 0 AF_INET
Recv   Send    Send                          
Socket Socket  Message  Elapsed              
Size   Size    Size     Time     Throughput  
bytes  bytes   bytes    secs.    10^6bits/sec  

128000  49152  49152    10.00    1028.79  

OpenVPN+TAP 経由

sol11 $ netperf  -H 192.168.30.1
MIGRATED TCP STREAM TEST from ::ffff:0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.30.1 (192.168.30.1) port 0 AF_INET
Recv   Send    Send                          
Socket Socket  Message  Elapsed              
Size   Size    Size     Time     Throughput  
bytes  bytes   bytes    secs.    10^6bits/sec  

128000  49152  49152    10.32      22.88  

なんと実ネットワークと OpenVPN+TAP による仮想ネットワークで46倍ものスループットの差があります。
もちろんソフトウェア的に余計な処理かかなり入っているのである程度がオーバーヘッドがでるのはしょうがないですが、これはあまりにも差が開きすぎています。

というわけで、現状把握とともに目標を

『実ネットワークの10分の1以上(100Mbps以上)まで改善する』

こととしました。

Dtraceによる測定

せっかくSolarisには dtrace(1M) というトレーサーがついているのですから、まずは Dtrace でボトルネック箇所を探してみます。

TAPドライバのトレース。

まずはドライバのボトルネック調査をしてみます。

  • TAPドライバ内の関数の呼び出し回数

count_tuntap.d

fbt:tap::entry
{
        @cntproc[probefunc] = count();
}

tick-1sec { 
        trunc(@cntproc,10);
        printa(@cntproc);
}

実行結果

sol11 $ sudo dtrace -s  count_tuntap.d   
    :
  1  68057                       :tick-1sec 
  tunwsrv                                               678
  tun_unitdata_ind.clone.0                             9334
  tunwput                                             35464
  tun_frame                                           36333

tunwput はTCP/IPから転送されてきたフレームが渡されるエントリポイントで、tun_frame はその先でフレームのルーティング処理を行う関数です。tun_frame の右の数字36,333は転送時間(約10秒)の間に呼び出された回数です。送信するフレームが多くなれば当然呼び出される回数も増えるのでとくにこの結果からは目立ったことはありませんでした。

  • TAPドライバ内の関数のトータル実行時間

elapsed_sum_tuntap.d

fbt:tap::entry
{
        self->ts = timestamp;
}
                                       
fbt:tap::return
/self->ts > 0/
{
        @ts[probefunc] = sum(timestamp - self->ts);
        self->ts = 0;
}

tick-1sec { 
        trunc(@ts,10);
        printa(@ts);
}

実行結果

sol11 $ sudo dtrace -s  elapsed_sum_tuntap.d 
dtrace: script 'elapsed_sum_tuntap.d' matched 30 probes
CPU     ID                    FUNCTION:NAME
    :
  1  68057                       :tick-1sec 
  tunwsrv                                           1081603
  tun_unitdata_ind.clone.0                         13908076
  tunwput                                          32014047

一番 CPU時間を使った tunwput 関数のトータル実行時間が 32,014,047ns、つまり 0.032秒であったことを示しています。
先ほどの実行回数のところでも tunwput が出てきましたが、結果として10秒あたり 0.032 秒しか使ってないてないということで、TAP ドライバ側で時間がかかっている処理が有るわけでな無いようです。

OpenVPN プロセスのトレース

つづいてユーザプロセスである OpenVPN プロセスをトレースしてます。

  • openvpnプロセス内での関数の呼び出し回数

count.d

pid$target:::entry
{
        @cnt[probefunc] = count()
}

tick-1sec { 
        trunc(@cnt, 30);
        printa(@cnt);
}

実行結果

sol11 $ sudo dtrace -s count.d -p 5511
dtrace: script 'count.d' matched 11218 probes
   :
 BF_decrypt                                          66579
  sha1_block_data_order                              68652
  EVP_CIPHER_CTX_iv_length                            75084
  po_ctl                                              75084
  proto_is_dgram                                      81978
  memset                                             223560
  memcpy                                             243961
  BF_encrypt                                        2032723

実行時間10秒の間に BF_encrypt が 2,032,723回呼ばれた事を示しています。2番目には memcpy が 243,961 回呼ばれています。

  • openvpn内から呼ばれる関数の実行時間

elapsed_sum.d

pid$target:::entry
{
        self->ts = timestamp;
}

pid$target:::return
/self->ts > 0/
{
        @ts[probefunc] = sum(timestamp - self->ts);
}

tick-1sec { 
        trunc(@ts,10);
        printa(@ts);
}

実行結果

sol11 $ sudo dtrace -s elapsed_sum.d -p 5511
dtrace: script 'elapsed_sum.d' matched 22404 probes
CPU     ID                    FUNCTION:NAME
    :
  1  68057                       :tick-1sec 
  SHA1_Update                                     142005693
  memset                                          184523613
  memmove                                         189561457
  EVP_DigestUpdate                                190132463
  memcpy                                          316211424
  BF_encrypt                                     1654880393
  __pollsys                                      3503914650
  _pollsys                                       3538953225
  poll                                           3569561345
  po_wait                                        3600229916

po_wait 関数は openvpn 自身の関数で名前の通り poll を呼び出して待ちます。Stack は以下のとおり。

sol11 $ sudo dtrace -n 'pid$target:::entry/probefunc=="poll"/{ustack(20)}' -p 5511
dtrace: description 'pid$target:::entry' matched 11217 probes
CPU     ID                    FUNCTION:NAME
  0  77052                       poll:entry 
              libc.so.1`poll
              openvpn`po_wait+0x65
              openvpn`io_wait_dowork+0x120
              openvpn`main+0x35e
              openvpn`_start+0x83

これが全体の中で 3,600,229,916ns、つまり3約秒間とっています。
10秒の転送時間の中でトータル3秒間もデータの到着待ちしているとしたら非効率と言わざるを得ません。

Dtraceのトレース結果からの考察

openvpn プロセスは転送時間の10秒中3秒はTAPドライバからのデータ待ちをしている。これに対して TAPドライバはロック待ちや負荷の高い処理は行なっておらず、余裕がある。
つまり、プロセスに渡すべきデータが上位プロトコル(TCP/IP)から届いていないと思われる。

snoop コマンドのトレース

上位プロトコルからのデータが届いていないのでは?との推測を受けて、snoop コマンドを使って TAP ドライバに流入するパケットをチェックします。

tap0 インターフェースに流れるパケットをファイル(バイナリフォーマット)に落とすコマンド実行例。

# snoop -d tap0 -o /var/tmp/snoop.out

このように snoop コマンドを実行しながら netperf コマンドを実行します。
取得したパケットキャプチャファイルは snoop コマンドでももちろん読めますが、よりグラフィカルにパケットの送受を確認するために Wireshark というパケットキャプチャの表示・解析ツールを使います。

Wireshark は多機能な解析ツールですが、その機能のなかでもパフォーマンス問題を調査するのにお奨めな機能が「Time-Sequence Graph(tcptrace)」です。この機能はキャプチャファイルオープン後、調査をしたいTCPコネクションのパケットを一つクリックした後、メニューの Statictics -> TCP Stream Graph -> Time-Sequence Graph(tcptrace)から起動することができます。

f:id:kaizawa2:20120716200740p:plain

横軸が経過時間を示し、縦軸がそ転送されたデータ量を示しています。平らなっているように見える部分はデータ転送が無い部分です。
デフォルトでは全体を示しているのでわかりづらいのでマウスの左クリク、もしくは「i」キーでズームインします。

f:id:kaizawa2:20120716203423p:plain

図中の「I」の一つが一つのパケットのデータ転送量を表しています。「I」が重なって上に伸びているのは連続してパケットが送信されたことを示しています。逆に「I」と次の「I」の間が開いてしまっているのは何らかの「待ち」が発生していることになります。
図の左上の方に斜め右上に階段上に登っている先は受信側が「Windowサイズ」として通知してきた受信可能なデータ量を示しています。
つまり、実際に「Windowsサイズ」と書かれた縦の赤線の高さくらいまで連続してデータを送ることができるのに、送信側(=クライアント)が送信を自ら止めているのです。
一方右下の階段上の線は受信側(=サーバ)からのとどいた確認応答(ACK)のデータを示しています。ある時間どのデータ量までACKを受けたかが分かります。
これらからわかることは、クライアントは、サーバからのある一定数のACK、実際には3~4つ前に送信したデータのACKが届くのを見届けてから次のデータを送信しているのが分かります。簡単に言うと以下のような動きを繰り返して、ちょっとずつ時間をロスしています。

  1. クライアントが4つのパケットを連続して送信。
  2. サーバからのACKを待つ(1msくらい)
  3. サーバからACKが到着。
  4. 次の4つのパケットを送信

TCP というプロトコルの仕組み上、送信側が通知してきた Window サイズ(図の赤い縦線)分だけのデータはACKを受けること無く、連続して送信することができるはずです。
では、なぜクライアントは 4つのパケットだけしか送信せずにACKを待っているのでしょう?

これはおそらくTCPの「輻輳回避アルゴリズム」が働いているため、と考えることができます。
輻輳回避アルゴリズムは、ネットワーク経路に輻輳が発生しているような場合に、ネットワークに負荷を掛けることによってパケットのロスが発生し、パケットの再送信が必要になる、という悪循環を避けるために、自らパケット送信のスロットルを弱める機能になります。
この場合、このパケット以前に、再送信が頻発するなど、TCPが「輻輳が発生している」と判断する事象が発生していたと考えられます。

では、あらためて、このTCP接続の最初の部分を見てみましょう。

f:id:kaizawa2:20120716211947p:plain

これは、TCP接続の最初の部分です。最初「I」が3つならんで縦に伸びて言いますので、3つのパケットが連続してだされているのが分かります。続いては4つのパケットが連続で、次の次の塊では5つのパケットが連続で送られており、7つめ塊では7つのパケットが連続で送られている様子が見て取れます。先ほど書いた通り、本来はWindowサイズ分だけ一気にデータを連続して送れるはずですが、そうはしていません。なぜでしょう? これも輻輳回避の仕組みの一つで「スロースタート」といわれるTCPの機能です。TCPははじめからフルスロットルでデータの転送を開始すること無く文字通り「スロースタート」で様子を探りながらデータがどれくらいなら再転送せずに連続して送信できるかを確認しているのです。

そして、期待通り(?)問題が発生しています。真ん中付近でデータの再送信が発生している様子が見えます。このTime-Sequence 図では再送信がなければ「I」の高さが低くなることは無いはずで、最後の送信時より高さが低い「I」があったらそれは再送信を示しています。
そして、図の右上にあるようにサーバからの ACK が届かずにデータも送れない、という無通信状態が長く続いている様子が伺えます。

snoop の結果からの考察

データの送信状況をみると、Windowサイズが十分なのにもかかわらず、送信側であるクライアントがデータの送信を制御し、サーバからのACKを待っている様子が見える。また、TCP接続開始直後にスロースタートによって連続したデータ送信数を広げようとしている段階でデータの再送信や長期のACK待ちが発生している。これらにより、このTCP接続においてパケットのロスが頻発し、そのせいでパケットの再送信が発生し、その結果としてTCPの輻輳回避アルゴリズムによってデータの流出コントロールが行われたと考えられる。

コード調査

snoop の調査結果からパケットのドロップが起こっていることが推測されましたが、20Mbps程度のデータ量で NIC や Switch でバッファーオーバーフローが起こることは考えられないので、TAPドライバ自身の内部でパケットのドロップが起こっている可能性が出てきました。

実際には snoop のキャプチャにはパケットが記録できているので、疑わしいのは snoop にパケットを渡す部分と、openvpn プロセスにデータを転送する部分の間あたりだと推測できます。TAPのドライバのコードでその様なことを行なっているのは tun_frame という関数です。

/* Route frames */ ### パケットをルーティングする関数
static void tun_frame(queue_t *wq, mblk_t *mp, int q)
{
     :
  /* Check for the sniffers */ ### snoop 等のパケットキャプチャ向けのルーティング処理
  for( tmp=ppa->p_str; tmp; tmp = tmp->p_next ){
     :
  if( !(str->flags & TUN_CONTROL) ){ ### openvpn プロセスにデータパケットをルーティングする処理
     /* Data from the Protocol stream send it to
      * the Control stream */
     DBG(CE_CONT,"tun: frame %lu -> control str\n", (ulong_t)msgdsize(mp));
     if( canputnext(ppa->rq) ){
         ^^^^^^^^^^^^^^^^^
             putnext(ppa->rq, mp);
             ^^^^^^^^^^^^^^^^^^^ 
     } else {
         if( q == TUN_QUEUE ){
           DBG(CE_CONT,"tun: queueing frame %lu\n", (ulong_t)msgdsize(mp));
           putbq(wq, mp);
           ^^^^^^^^^^^^^
        } else {
           DBG(CE_CONT,"tun: dropping frame %lu\n", (ulong_t)msgdsize(mp));
           freemsg(mp);
           ^^^^^^^^^^^
        }
     }
  } else {

臭う部分がみえて来ました。下線をつけた canputnext(9F)というのは、引数で渡されたキュー(Streams Queue)にパケットが渡せるか?というのを確認するためのカーネルの関数です。ちょっと図で説明します。

f:id:kaizawa2:20120716220720g:plain

今見ているのは図でアプリケーションからTAPドライバをつないでいる赤い矢印の下端部分です。TAPドライバはTCP/IP経由でアプリ(netperf)からのデータを受け取り、それを STREAM HEAD という門を通じて OpenVPN に渡そうかどうしようか、という判断をしている部分になります。
STREAM HEAD はユーザ空間とカーネル空間の間に居る特別なキューです。canputnext(9F) は図のSTREAMS HEAD にデータが渡せるかどうか(キューががいっぱいでないか)を確認するために使われています。
ここで、TAPではちょっと自分でも輻輳制御を行なっています。STREAM HEADのキューが一杯だとしても即座に、freemsg(9F)でパケットをドロップせず、一旦 putbp(9F) で自分自身のキューに保存し、2度めのチャレンジでもSTREAM HEADが一杯だったら初めて freemsg(9F) で破棄する、という動きをとっています。
・・・輻輳制御の仕組みがあるとは言え、非常に疑わしいコードです。デバッグプリントのコードを有効にして再度テストしてみましょう。

 :
Jul 16 22:19:15 sol11 tap: [ID 852863 kern.notice] tun: queueing frame 1389
Jul 16 22:19:15 sol11 tap: [ID 279187 kern.notice] tun: dropping frame 1389
Jul 16 22:19:15 sol11 tap: [ID 852863 kern.notice] tun: queueing frame 1389
Jul 16 22:19:15 sol11 tap: [ID 279187 kern.notice] tun: dropping frame 1389
Jul 16 22:19:15 sol11 tap: [ID 852863 kern.notice] tun: queueing frame 1389
Jul 16 22:19:15 sol11 tap: [ID 279187 kern.notice] tun: dropping frame 1389
Jul 16 22:19:15 sol11 tap: [ID 852863 kern.notice] tun: queueing frame 1389
Jul 16 22:19:15 sol11 tap: [ID 279187 kern.notice] tun: dropping frame 1389
 :

案の定、ドロップの嵐です(笑)
なぜ、こんなにドロップしてしまうんでしょう? そもそも STREAM HEAD のキューのサイズってどれくらいなんでしょう?
チェックしてみましょう。
確認にはデバッガである mdb を使います。mdb に -k オプションを指定してカーネルデバッガとして起動します。(もちろんroot権限が必要です)

sol11 $ sudo mdb -k
Loading modules: [ unix genunix specfs dtrace mac cpu.generic uppc pcplusmp scsi_vhci zfs mpt sd ip hook neti arp usba sockfs fctl kssl random idm fcip cpc crypto lofs ufs logindmux ptm sppp nfs ]
>

ストリームの一覧を表示して TAP ドライバのストリームを探します。

> ::walk stream_head_cache|::stream
  :
+-----------------------+-----------------------+
| 0xffffff015a80f6b8    | 0xffffff015a80f5c0    |
| strwhead              | strrhead              |
|                       |                       |
| cnt = 0t0             | cnt = 0t0             |
| flg = 0x00004022      | flg = 0x00044030      |
+-----------------------+-----------------------+
            |                       ^
            v                       |
+-----------------------+-----------------------+
| 0xffffff014d4988d0    | 0xffffff014d4987d8    |
| tap                   | tap                   |
|                       |                       |
| cnt = 0t0             | cnt = 0t0             |
| flg = 0x00248822      | flg = 0x00208832      |
+-----------------------+-----------------------+

::walk stream_head_cache コマンドは全ての STREAMSをリストし、パイプで ::stream コマンドに渡すことで STREAMSの構造を見やすく図示してくれます。
右上の strrhead (STREAM HEAD の Read サイドキュー)のアドレスから queue のサイズ(q_hiwat)を見てみます。
ここでは queue_t 構造体の q_hiwat メンバを見てみます。

> 0xffffff015a80f5c0::print queue_t q_hiwat
q_hiwat = 0x1400
                 ^^^^^^

::print コマンドは指定した与えられたアドレスをを構造体に当てはめて、指定したメンバを表示してくれるコマンドです。
なるほど、分かりました。キューのサイズは 0x1400=5,120byteです。TCPのペイロード(データ)が1,460byteだとすると、3パケット分しかキャッシュできない計算になります。これでは溢れるのも必至です。

mdb によるカーネルデバッグ結果の考察

TAP ドライバが openvpn に渡すための橋渡しとなる STREAM HEAD のキューのサイズが5,120バイトしかなく、3パケット分しか保持できない。このため、ある程度のデータが流入するようになるとパケットのドロップが発生し、前述の輻輳制御アルゴリズム発動につながると思われる。

コードの変更による対策

これまでの調査から、OpenVPN プロセスがオープンしている STREAM の STREAM HEADのキューのサイズ(q_hiwat)が小さいためにパケットのドロップが起きているのがわかっています。対策は単純で、このキューのサイズを大きくします。
具体的には OpenVPN が STREAM をオープンした際に呼ばれるコードに STREAM HEADのキューのサイズを大きするコードを追加します。

/* Handle IOCTLs */
static void tun_ioctl(queue_t *wq, mblk_t *mp)
{
  :
        /* Set High Water Mark of Stream head */
        if( !(mopt = allocb(sizeof(struct stroptions), BPRI_LO)) ){
            tuniocack(wq, mp, M_IOCNAK, 0, ENOMEM);
            return;
        }
        mopt->b_datap->db_type = M_SETOPTS;
        mopt->b_wptr += sizeof(struct stroptions);
        sop = (struct stroptions *)(void *)mopt->b_rptr;
        sop->so_flags = SO_HIWAT;
        sop->so_hiwat = TUN_SO_HIWAT;
        putnext(ppa->rq, mopt);

so_hiwat に設定している TUN_SO_HIWAT は if_tun.h で以下のように宣言してあります。

#define TUN_SO_HIWAT    1024*1024

1024*1024byte。つまり1Mです。約720個のTCPデータを保持できます。これくらいあれば大丈夫でしょう。

コード変更後のパフォーマンス測定

結果

実ネットワークでの転送速度 1 Gbps
OpenVPN+TAP ドライバ経由での転送 214 Mbps
実際の出力

OpenVPN+TAP 経由

sol11 $ netperf  -H 192.168.30.1
MIGRATED TCP STREAM TEST from ::ffff:0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.30.1 (192.168.30.1) port 0 AF_INET
Recv   Send    Send                          
Socket Socket  Message  Elapsed              
Size   Size    Size     Time     Throughput  
bytes  bytes   bytes    secs.    10^6bits/sec  

128000  49152  49152    10.04     214.29 

どうでしょう? 変更前は 22Mbps 程度だったので約10倍ちかくスループットが向上しています。
実ネットワークとの差も 4.8 倍差と、目標としていた10分の1より縮まっています!!

というわけで、TUN/TAP ドライバのパフォーマンス改善までのデバッグ履歴は以上です。

なお、もし STREAM についてもう少し知りたいと言う方は拙作でありますが以下のページもご参照ください。

http://www.whiteboard.ne.jp/~admin2/index.php

Hadoop 1.0.x の設定

Solaris 11 で Hadoop 1.0.x をセットアップしたときのメモです。

ソースダウンロード

hadoop-1.0.4.tar.gz

インストール(ディレクトリのコピー)

ダウンロードした圧縮ファイルを展開し /usr/local にコピー。今後新しいバージョンに入れ替えるときのためにシンボリックリンクを設定

# cp -r hadoop-1.0.4 /usr/local
# ln -s hadoop-1.0.4 hadoop
# ls -ld /usr/local/hadoop
lrwxrwxrwx   1 root     root          12  1411:47 /usr/local/hadoop -> hadoop-1.0.4

環境変数設定

PATH追加

Hadoop をコピーしたディレクトリの直ぐ下にある bin を PATH にセット

$ export PATH=$PATH:/usr/local/hadoop/bin

HADOOP_HOME解除

以前は HADOOP_HOME という環境変数を設定する必要があったが使われなくなったもよう。念のため unset しておく。

$ unset HADOOP_HOME

設定

/usr/local/hadoop/conf 以下の3つの設定ファイルを編集

  • hadoop-env.sh
  • core-site.xml
  • hdfs-site.xml
  • mapred-site.xml

なお、以下の設定では Hadoop がインストールされているホストのホスト名を sol11 としていますので適時読み替えてください。

hadoop-env.sh

JAVA_HOME を設定

 ...
export JAVA_HOME=/usr/java
 ...

core-site.xml

ファイルシステム名(fs.default.name)を設定

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<!-- Put site-specific property overrides in this file. -->

<configuration>
<property>
  <name>fs.default.name</name>
  <value>hdfs://sol11:9000</value>
</property>
</configuration>

hdfs-site.xml

NameNode のネームスペースとトランザクションログを格納するディレクトリ(dfs.name.dir) と、DataNode がブロックを格納するディレクトリ(dfs.data.dir)を設定

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
<property>
  <name>dfs.name.dir</name>
  <value>/var/tmp/hadoop/dfs/name</value>
</property>

<property>
  <name>dfs.data.dir</name>
  <value>/var/tmp/hadoop/dfs/data</value>
</property>

</configuration>

mapred-site.xml

JobTracker ホストのホスト名(mapred.job.tracker) を設定

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
<property>
   <name>mapred.job.tracker</name>
   <value>sol11:9001</value>
</property>
</configuration>

HDFS のフォーマット

$ hadoop namenode -format
13/01/04 09:48:03 INFO namenode.NameNode: STARTUP_MSG:
/************************************************************
STARTUP_MSG: Starting NameNode
STARTUP_MSG:   host = sol11/127.0.0.1
STARTUP_MSG:   args = [-format]
STARTUP_MSG:   version = 1.0.4
STARTUP_MSG:   build = https://svn.apache.org/repos/asf/hadoop/common/branches/branch-1.0 -r 1393290; compiled by 'hortonfo' on Wed Oct  3 05:13:58 UTC 2012
************************************************************/
13/01/08 09:48:03 INFO util.GSet: VM type       = 32-bit
13/01/08 09:48:03 INFO util.GSet: 2% max memory = 17.81375 MB
13/01/08 09:48:03 INFO util.GSet: capacity      = 2^22 = 4194304 entries
13/01/08 09:48:03 INFO util.GSet: recommended=4194304, actual=4194304
13/01/08 09:48:03 INFO namenode.FSNamesystem: fsOwner=kaizawa
13/01/08 09:48:03 INFO namenode.FSNamesystem: supergroup=supergroup
13/01/08 09:48:03 INFO namenode.FSNamesystem: isPermissionEnabled=true
13/01/08 09:48:03 INFO namenode.FSNamesystem: dfs.block.invalidate.limit=100
13/01/08 09:48:03 INFO namenode.FSNamesystem: isAccessTokenEnabled=false accessKeyUpdateInterval=0 min(s), accessTokenLifetime=0 min(s)
13/01/08 09:48:03 INFO namenode.NameNode: Caching file names occuring more than 10 times
13/01/08 09:48:04 INFO common.Storage: Image file of size 113 saved in 0 seconds.
13/01/08 09:48:04 INFO common.Storage: Storage directory /var/tmp/hadoop/dfs/name has been successfully formatted.
13/01/08 09:48:04 INFO namenode.NameNode: SHUTDOWN_MSG:
/************************************************************
SHUTDOWN_MSG: Shutting down NameNode at sol11/127.0.0.1
************************************************************/

起動

hadoopの各デーモンの起動

sol11 % /usr/local/hadoop/bin/start-all.sh
starting namenode, logging to /usr/local/hadoop-1.0.4/libexec/../logs/hadoop-kaizawa-namenode-sol11.out
localhost: starting datanode, logging to /usr/local/hadoop-1.0.4/libexec/../logs/hadoop-kaizawa-datanode-sol11.out
localhost: starting secondarynamenode, logging to /usr/local/hadoop-1.0.4/libexec/../logs/hadoop-kaizawa-secondarynamenode-sol11.out
starting jobtracker, logging to /usr/local/hadoop-1.0.4/libexec/../logs/hadoop-kaizawa-jobtracker-sol11.out
localhost: starting tasktracker, logging to /usr/local/hadoop-1.0.4/libexec/../logs/hadoop-kaizawa-tasktracker-sol11.out

動作テスト

HDFS のテスト

messages ファイルを HDFS に保存してみる

$ hadoop dfs -copyFromLocal /var/adm/messages /

HDFS上の message ファイルの中を読んでみる

$ hadoop dfs -cat /messages
Apr 15 13:18:49 sol11 sendmail[1102]: [ID 702911 mail.crit] My unqualified host name (sol11) unknown; sleeping for retry
Apr 15 13:19:49 sol11 sendmail[1102]: [ID 702911 mail.alert] unable to qualify my own domain name (sol11) -- using short name

Hadoop のテスト

Hadoop の添付されているサンプルを使って円周率を求める

$ cd /usr/local/hadoop
$ % hadoop jar hadoop\-examples\-1.0.4.jar pi 1 10
Number of Maps  = 1
Samples per Map = 10
Wrote input for Map #0
Starting Job
13/01/08 09:57:20 INFO mapred.FileInputFormat: Total input paths to process : 1
13/01/04 09:57:20 INFO mapred.JobClient: Running job: job_201301080948_0001
13/01/04 09:57:21 INFO mapred.JobClient:  map 0% reduce 0%
13/01/04 09:57:40 INFO mapred.JobClient:  map 100% reduce 0%
13/01/04 09:57:56 INFO mapred.JobClient:  map 100% reduce 100%
13/01/04 09:58:01 INFO mapred.JobClient: Job complete: job_201301080948_0001
13/01/04 09:58:01 INFO mapred.JobClient: Counters: 27
13/01/04 09:58:01 INFO mapred.JobClient:   Job Counters
13/01/04 09:58:01 INFO mapred.JobClient:     Launched reduce tasks=1
13/01/04 09:58:01 INFO mapred.JobClient:     SLOTS_MILLIS_MAPS=19266
13/01/04 09:58:01 INFO mapred.JobClient:     Total time spent by all reduces waiting after reserving slots (ms)=0
13/01/04 09:58:01 INFO mapred.JobClient:     Total time spent by all maps waiting after reserving slots (ms)=0
13/01/04 09:58:01 INFO mapred.JobClient:     Launched map tasks=1
13/01/04 09:58:01 INFO mapred.JobClient:     Data-local map tasks=1
13/01/04 09:58:01 INFO mapred.JobClient:     SLOTS_MILLIS_REDUCES=14567
13/01/04 09:58:01 INFO mapred.JobClient:   File Input Format Counters
13/01/04 09:58:01 INFO mapred.JobClient:     Bytes Read=118
13/01/04 09:58:01 INFO mapred.JobClient:   File Output Format Counters
13/01/04 09:58:01 INFO mapred.JobClient:     Bytes Written=97
13/01/04 09:58:01 INFO mapred.JobClient:   FileSystemCounters
13/01/04 09:58:01 INFO mapred.JobClient:     FILE_BYTES_READ=28
13/01/04 09:58:01 INFO mapred.JobClient:     HDFS_BYTES_READ=238
13/01/04 09:58:01 INFO mapred.JobClient:     FILE_BYTES_WRITTEN=43307
13/01/04 09:58:01 INFO mapred.JobClient:     HDFS_BYTES_WRITTEN=215
13/01/04 09:58:01 INFO mapred.JobClient:   Map-Reduce Framework
13/01/04 09:58:01 INFO mapred.JobClient:     Map output materialized bytes=28
13/01/04 09:58:01 INFO mapred.JobClient:     Map input records=1
13/01/04 09:58:01 INFO mapred.JobClient:     Reduce shuffle bytes=0
13/01/04 09:58:01 INFO mapred.JobClient:     Spilled Records=4
13/01/04 09:58:01 INFO mapred.JobClient:     Map output bytes=18
13/01/04 09:58:01 INFO mapred.JobClient:     Total committed heap usage (bytes)=245891072
13/01/04 09:58:01 INFO mapred.JobClient:     Map input bytes=24
13/01/04 09:58:01 INFO mapred.JobClient:     Combine input records=0
13/01/04 09:58:01 INFO mapred.JobClient:     SPLIT_RAW_BYTES=120
13/01/04 09:58:01 INFO mapred.JobClient:     Reduce input records=2
13/01/04 09:58:01 INFO mapred.JobClient:     Reduce input groups=2
13/01/04 09:58:01 INFO mapred.JobClient:     Combine output records=0
13/01/04 09:58:01 INFO mapred.JobClient:     Reduce output records=0
13/01/04 09:58:01 INFO mapred.JobClient:     Map output records=2
Job Finished in 41.727 seconds
Estimated value of Pi is 3.60000000000000000000

本家設定ページ

Cluster Setup

Solaris 11: リカバリー目的で ZFS を起動する方法

後述のマニュアルにまんま載ってますが一応書いておきます。

  1. Solaris 11 インストール CD から起動する
  2. ターミナルから root pool をインポートする
    # zpool import -f rpool
  3. /a に ZFS ブート環境をマウントする
    # beadm mount solaris /a

How to Boot ZFS for Recovery Purposes 

 

ESXi のゲスト Windows マシン上でネットワークブリッジを使えるようにする

ESXi 上の Windows ゲストでネットワークブリッジの設定をする際にうまく行かず悩んだ点があったのでエントリ書いておきます。


Windows XP 以降には「ネットワークブリッジ」という機能があり、Windows マシン上に繋がった別々のネットワークを1つのネットワークとして結合することができます。
想定しているネットワーク構成のイメージは以下のようなものです。

f:id:kaizawa2:20120102163401p:image:w360

ESXi のゲストとして、HostAHostB という2つの Windows ゲストがあって、それぞれが別々の仮想スイッチ(vSwitch)につながっている状態です。ここで、2つのネットワークアダプタを持ち双方の仮想スイッチに繋がった BridgeHost というWindowsゲストを配置してそれぞれのネットワークを Windows のネットワークブリッジ機能を使って結合し、HostA と HostB が BridgeHost を介して通信できるようにする、というのが目標です。


f:id:kaizawa2:20120102163404p:image:w450


実マシンとスイッチで構成されていれば、上図のように2つのネットワークアダプタを追加したネットワークブリッジを作成*1すれば、期待通りにブリッジされて HostA と HostB は同じネットワークに繋がっているかのように通信することができます。

ところが、ESXi のゲストの場合は、この設定だけでは HostA と Host B は通信することができません。
具体的に言いますとブロードキャストのイーサネットフレームは相手側に到達しますが、双方のネットワークアダプタの Mac アドレス宛のイーサネットフレームが BridgeHost を通過できません。(というより BridgeHost にも到達しません)

例えば HostA から HostB に ping コマンドを打った場合。

  1. HostA は HostB の Mac アドレスを解決するべくブロードキャスト宛に ARP リクエストを投げる
  2. BridgeHost はブロードキャスト宛の ARP リクエストを受け取り、HostB の居る vSwitch1 に ARP リクエストを転送する。
  3. HostB は ARP リクエストを受け取り、HostA の Mac アドレス宛に ARP リプライを送信する。
  4. しかし HostA 宛の ARP リプライは BridgeHost には届かず、したがって HostA にも転送されない。
  5. HostA は ARP リクエストを再送を繰り返すが、リプライは受け取れない…

これは ESXi の vSwitch のセキュリティ設定にて「無差別モード」がデフォルトで拒否になっている為です。つまり vSwtich としては BridgeHost 宛で無い(HostA宛)のイーサネットフレームを BridgeHost のネットワークアダプタに転送しないようになっているのです。

これを変更するためには以下の要領で vSwitch の設定を変更する必要があります。

vSphre Client の構成タブよりハードウェア→ネットワークを選び vSwitch のプロパティクリックします。

f:id:kaizawa2:20120102173237p:image:w500

vSwitch を選び「編集」を押します

f:id:kaizawa2:20120102173238p:image:w500

セキュリテイタブより「無差別モード」を「承諾」に変更して「OK」をクリックします。

f:id:kaizawa2:20120102173239p:image:w360


これで期待通り、HostA 宛、HostB 宛 のイーサネットフレームが BridgeHost を通過することができるようになり、HostA と HostB が通信できる様になります。なお、BridgeHost がブリッジ接続するアダプタに繋がった全ての vSwitch の設定を変更する必要があります。*2

ご参考

vSphere Clinet のオンラインヘルプによると以下のように説明されています。

vSphere 標準スイッチのセキュリティ ポリシーの編集

レイヤー 2 は、データ リンク レイヤーです。レイヤー 2 セキュリティ ポリシーの 3 つの要素は、
無差別モード、MAC アドレス変更、および偽装転送の 3 つです。無差別モード以外では、ゲスト 
アダプタは、自身の MAC アドレスのトラフィックのみを検出します。無差別モードでは、すべて
のパケットを検出できます。デフォルトでは、ゲスト アダプタは無差別モード以外に設定されます。

無差別モードオプション

拒否 ゲスト アダプタを無差別モードに設定しても、アダプタが受信するフレームには影響しません。
承諾 ゲスト アダプタを無差別モードに設定すると、アダプタに接続するポート グループの VLAN ポリシーが許可する vSphere 標準スイッチを通過したすべてのフレームが検出されます。


Windows のネットワークブリッジについてはこちらが詳しいです。
Windows XP ネットワーク ブリッジの動作概念

*1:2つアダプタのアイコンをそれぞれコントロールキーを押しながら選択し、右クリックから「ブリッジ接続」を選べば新しいブリッジが作成できます

*2:上記例では vSwitch0 と vSwitch1

Solaris 11 Early Adopter でのパッケージのインストール

先日 Solaris11 Early Adoperがリリースされました。既存のコードが Solaris11 EA 上でビルド可能か確認しようとさっそくインストールしたのですが、pkg(1M) コマンドを使ったネットワーク経由での gcc や ヘッダファイル等のパッケージのインストールがエラーになってしまい困っておりました。

# pkg install developer/opensolaris/X
Creating Plan /               
pkg install: No matching version of developer/opensolaris/X can be installed:
  Reject:  pkg://solaris/developer/opensolaris/X@0.5.11,5.11-0.151:20101026T181446Z
  Reason:  All versions matching 'require' dependency pkg:/compatibility/ucb are rejected
    Reject:  pkg://solaris/compatibility/ucb@0.5.11,5.11-0.151.0.1:20101104T230637Z
    Reason:  This version is excluded by installed incorporation pkg://solaris/consolidation/osnet/osnet-incorporation@0.5.11,5.11-0.173.0.0.0.0.17656:20110826T144402Z

その事を twitter 上でつぶやいたところ @mayn_lum さんからローカルレポジトリからパッケージのインストールする方法を教えて頂き、さっそく試したところようやく必要なパッケージのインストールできたので、こちらに簡単な手順を紹介しておきます。(@mayn_lum さんいつもありがとうございます!)


なお、ネットワーク経由でのパッケージがインストール失敗する根本の原因(パッケージ名違い?)はわかっていません。
もしうまくいく方法がありましたら教えて下さい。(おそらくEA 版だけの問題だとおもうのですが)

Solaris 11 EA のローカルにパッケージのリポジトリを作成し集積サーバを起動する

パッケージアーカイブの入ったリポジトリイメージをダウンロード

パッケージファイルは2つの zip ファイルに分割して配布されているので Solaris11 EA のページ の「Oracle Solaris 11 Early Adopter Repository Image」という部分から以下の2つのリンクをクリックして、それぞれ sol-11-ea-repo-full-iso-a.zip と sol-11-ea-repo-full-iso-b.zip というファイルをダウンロードする。

 Download Part A SPARC, x86 (2 GB) 
 Download Part B SPARC, x86 (2 GB) 

ファイルの統合

ダウンロードした zip ファイルを解凍した後、cat(1M) コマンドで一つファイルに連結します。
連結後の ISO ファイルのサイズはだいたい 6.1GB ほどです。

$ unzip sol-11-ea-repo-full-iso-a.zip
$ unzip sol-11-ea-repo-full-iso-b.zip
$ cat sol-11-ea-repo-full-iso-a sol-11-ea-repo-full-iso-b > sol-11-ea-repo-full.iso

ISO ファイルをブロックデバイスに関連付け

パッケージのISOファイルの中身をこのあとの集積サーバーで利用するために、lofiadm(1M) コマンドを使って ISOファイルをブロックデバイスに関連付けします。
ここからは root 権限が必要です。

# /usr/sbin/lofiadm -a /repo/sol-11-ea-repo-full.iso /dev/lofi/1

ブロックデバイスをマウント

関連付けを行ったブロックデバイスをマウントします。
ここでは /mnt にマウントしていますが他もディレクトリでも大丈夫です。

# /usr/sbin/mount -F hsfs /dev/lofi/1 /mnt

集積サーバの設定

先ほどマウントしたリポジトリデータの場所を指定して Image Packaging System の集積サーバを設定します。
inst_root には先ほどマウントしたマウントポイント /mnt の下の repo というディレクトリを指定しています。もし /mnt 以外のところにマウントしている場合には inst_root=/repo という具合に指定します。

# /usr/sbin/svccfg -s application/pkg/server setprop pkg/inst_root=/mnt/repo
# /usr/sbin/svccfg -s application/pkg/server setprop pkg/readonly=true

集積サーバの起動

Image Packagin System の集積サーバを起動します。
起動には svcadm(1M) コマンドを使用します。これによって集積サーバはデフォルトで TCP ポート 80 番で接続を待機します。

# /usr/sbin/svcadm refresh application/pkg/server         
# /usr/sbin/svcadm enable application/pkg/server

パッケージ配布元の設定

pkg コマンドに配布元(パブリッシャー)の場所をセットします。
上記の通りに設定した場合には場所は http://localhost になります。

# pkg set-publisher -O http://localhost solaris

これで以降の pkg コマンドの実行時にはパッケージはローカルのパッケージから取得されるようになります。
私もこれで無事 gcc のインストールができました。


このエントリは基本的に下記の記述を参考に書いています。あわせてこちらもご参照下さい。
Oracle SolarisTM 11 Early Adopter Release Repository Image

Nゲージ高架レイアウト作成記2

Nゲージレイアウト作成記第三弾。1400x700 のレイアウトです。

ストラクチャやレールを買うだけ買って進んでなかったのですが先週、今週と作業しました。
今回やったのは以下です。

高架などのストラクチャの墨入れとウェザリング

買ったばかりなのに勿体無い。と思われますがリアルさのため墨入れとウェザリングを行います。
墨入れは、ストラクチャの溝に薄く溶いた黒の塗料を流しこんで、光の明暗を表してリアルに見せるテクニックです。
ウェザリングは風雨にさらされたように見せる塗装のテクニックです。
実は単に墨入れしようとしたら下地と同じ溶剤の塗料でやってしまったためにはみ出た分をきれいに拭き取れずそのままウェザリングということにしてしまったことはないしょです。(笑)

f:id:kaizawa2:20111010144410j:image:w640

写真の階段部分のレンガの筋がはっきり見えているのが墨入れの効果です。看板の周りとかもなんとなく風雨にさらされた雰囲気がでているかなー?と。
ちなみに駅名の「新小山」はJR総武線の新小岩からインスパイアされてます。このレイアウトのモデルが小岩、新小岩なので。
屋根もこんな感じ。息子にはやりすぎと言われました。そうかも。

f:id:kaizawa2:20111010230628j:image:w640

道路となる厚紙の貼付け

今まで作ってきたレイアウトと違い近郊駅のイメージのレイアウトなので基本地面は舗装路になります。もちろん道幅に合わせて貼りつけててもいいのですが、めんどくさいので一気に道路用の厚紙を貼り付けました。貼りつけたのは東急ハンズでかってきた灰色の厚紙。買ったときは大きいなぁとおもっていたんですけど貼りつけてみたら意外に小さかったです。
貼付けは2倍に薄めた木工用ボンドをハケでベースのベニア板に塗っていきます。決して厚紙側に塗ってはダメです。ヘニャヘニャになります。泣 (経験済み)

f:id:kaizawa2:20111010145948j:image:w640

ベニアにボンド水が染みこむので思ったよりボンドが必要で焦りました。
ボンド水をまんべんなく塗ったら厚紙を貼っていきます。しっかり着くように布巾などでゴシゴシこすっていきます。決して素手でやってはダメです。どっかでついたボンドが厚紙について台無しになります。泣 (経験済み)

f:id:kaizawa2:20111010151334j:image:w640

貼ったあとがこちら。貼ってない上側は土手になる予定です。

f:id:kaizawa2:20111010151312j:image:w640

高架盛土用のスタイロフォームの整形

高架線路の反対側は盛土にする予定です。なので土手用にスタイロフォーム(堅い発泡スチロール)を整形します。
なんで盛土にしたかというと変化をつけたかったのと高架線路が高いからです。
こちらが完成したもの。

f:id:kaizawa2:20111010220435j:image:w640
f:id:kaizawa2:20111010220422j:image:w640

発泡スチロールをきれいに切断するのは意外に骨が折れます。
ホントはこんな工具があれば楽なんですけどねー。

白光 電池式スチロールカッター No.251-01

白光 電池式スチロールカッター No.251-01

そんな訳でこちらが現在の全景。

f:id:kaizawa2:20111010233911j:image:w640

駅も結構細かいとこまであるんですよ。シールを張ってちょっと賑やかに。

f:id:kaizawa2:20111010233938j:image:w640

今回買ったもの

最後に今回買ったものたちです。

Nゲージレイアウト ジオコレ一式 3,600
塗料 4,507
ストラクチャ(一軒家) 1,200
両渡ポイントレール 5,292
ポイントレール 1,890
小計 16,489

これまでの出費: 44,248円

ひえ〜両渡りポイントレール高い><