2009/12/30

Anakia 1.0: 出力フォーマットのTips

Anakiaに限らずVelocityを使うと、テンプレートと出力されたファイルの間に予期しない改行コードが埋め込まれるなどの違いが生まれます。 今回はVelocityの MLに投稿された記事を元に対策をメモしておきます。

問題の現象について

MLなどでも度々問題になっているようですが、Velocityでは命令の前に埋め込まれた空白などがそのまま出力されてしまいます。

例えばanakiaを使っているとして、次のようなテンプレートを準備します。

vslテンプレート

<?xml version="1.0" encoding="UTF-8" ?>
<html>
#document()
</html>
#macro (document)
  #set ($allSections = $root.getChildren())
  #foreach ( $section in $allSections )##
    #if ($section.getName().equals("p"))##
    <p>
      $section.getContent()
    </p>
    #end
  #end
#end

出力は次のようになります。

入力ファイル

<?xml version="1.0" ?>

<blog>
  <p>sample messages</p>
</blog>

vslテンプレートを適用した出力例

<?xml version="1.0" encoding="UTF-8" ?>
<html>
            <p>
        sample messages
      </p>
      </html>

期待する出力は、pの開始タグと閉じタグの先頭が縦に揃っていて、内部にコンテンツが少し字下げされて表示されているものなのですが、少し違います。

期待する出力

<?xml version="1.0" encoding="UTF-8" ?>
<html>
      <p>
        sample messages
      </p>
</html>

VTL命令行にある空白の出力抑制

Velocityのテンプレート言語(VTL)の命令はスクリプト言語なので、 書く時には適当にインデントを追加しないと人間には読みずらくなります。

しかし、そのインデントに使われた空白がそのまま出力されてしまうと意図しない空白となる場合があります。

修正版のvslテンプレート

出力をみながらテンプレートを修正します。

修正版 vslテンプレート

<?xml version="1.0" encoding="UTF-8" ?>
<html>
#document()
</html>
#macro (document)
#set ($allSections = $root.getChildren())
#foreach ( $section in $allSections )
#if ($section.getName().equals("p"))
      <p>
        $section.getContent()
      </p>
#end
#end
#end

出力例

<?xml version="1.0" encoding="UTF-8" ?>
<html>
      <p>
        sample messages
      </p>
</html>

出力は意図したようになりましたが、 書かれたテンプレートはかなり人間には読めないものになってしまいました。

vslファイルの自動生成

人間に読めなくて大変なら、人間が編集するファイルとanakiaが使うファイルを分ければ良いという発送で、'#'で始まる行は先頭の空白を取り除くようにしようと思います。

build.xmlは次のようになっていて、 prop/blog.vsl ファイルを自動生成します。

build.xmlファイルから抜粋

<anakia basedir="${docs.src}" 
   destdir="${docs.dest}"
   extension=".html"
   style="prop/blog.vsl"

簡単にできるのでMakefileを使う事にしました。

Makefileの内容

all: prop/blog.vsl build

prop/blog.vsl: prop/blog.vsl.template
	sed -e 's/^ *#/#/' $< > $@

build:
	ant -f prop/build.xml

blog.vsl.template はインデントを使って編集して、makeコマンドで blog.vsl を生成しています。 Makefileに限定する必要もないのですが、 blog.vslblog.vsl.template より新しい場合には生成過程がスキップされるので無駄が省く事ができます。 大した時間じゃないですけどね。

改行コードの出力を抑制する

さて今回使ったのは簡単な例ですが、場合によっては本来1行に収めたいものを複数行に渡って書きたい場合があるかもしれません。 その場合には"##"を行末に入れる事で、改行の出力を抑制する事ができます。

vslテンプレート

<?xml version="1.0" encoding="UTF-8" ?>
<html>
#document()
</html>
#macro (document)
  #set ($allSections = $root.getChildren())
  #foreach ( $section in $allSections )
    #if ($section.getName().equals("p"))
      <p>##
        $section.getContent()##
      </p>
    #end
  #end
#end

改行を抑制した例

<?xml version="1.0" encoding="UTF-8" ?>
<html>
      <p>        sample messages      </p>
</html>

これはp開始タグと getContent() が出力された後の改行が抑制されています。 sample messages前後の空白を抑制したい場合には次の2つのパターンが考えられると思います。

例1

      <p>$section.getContent()</p>

例2

      <p>##
$section.getContent()##
</p>

まとめ

今回は2つのTipsを書きましたが、Velocityを使うと出力されるHTMLコードが汚なくなると感じた場合には有効だと思います。

これぐらいならVelocityのTutorialにあっても良さそうなものですが、 知らないとどうしようもないですよね…。 出力に気を配る人はそんなにいないのかなぁ。

alixをブロードバンドルーターにする

いままで使っていたCoregaのブロードバンドルーターに問題はなかったのですが、 時々動作が不安定になる事があって自分でBフレッツと自宅LANとのゲートウェイを作ってみる事にしました。

目的は、とりあえずalixをブロードバンドルーター代りにしてみることと、ポートスキャンの様子を観察する事です。

参考資料

今回は次の資料を主に参考にしました。 特に作成したiptablesの設定については、参考資料1,2の2つを合わせてベースとして使わせて頂いています。

最終的なiptablesのリファレンスとしては参考資料3を参考にしました。

全体の構成

使ったalixはalix2c3で、3つのethernet portを持っています。実験ネットワーク(192.168.10.0/24)は無線LANのgatewayを置く事を考えていて、内部からInternetへのアクセスを許可して、192.168.1.0/24には許可しない構成を考えています。

  • eth0(ppp0): global IP address (BフレッツとのPPoE接続用)
  • eth1: 192.168.1.1 (192.168.1.0/24 主ネットワーク用)
  • eth2: 192.168.10.1 (192.168.10.0/24 実験ネットワーク用)

将来的には別のalixを使って新たにネットワークを作りOSPFの実験なども行ないたいと思っています。

フィルターポリシー

eth1,eth2の間は特にルーティングを制限する事はいまのところ考えていませんが、将来的には制限を加える可能性があります。 またeth0については参考にさせて頂いた設定例を参考に、ppp0を通るパケットについて制限を加える事にしました。

具体的な制限の内容は、Internetからの入力については基本的に不許可として、弾力的にssh, bittorrentなどの通信を許可する事ができる仕組みを準備する事としました。 その他に通常使っているpingやtracerouteといったコマンドは外部から拒否しつつも、内部からは使えるようにする事としています。

iptablesの使い方

iptablesコマンドをそのまま使うというケースはほとんどないと思います。 CentOSなどRHEL系列では/etc/sysconfig/iptablesファイルにその内容を記録しますし、Ubuntuではufwパッケージを使い、/etc/ufw/*.rulesファイルを使う事ができます。

今回はdebianを使っていますが、パッケージを追加する他に適当な方法がありません。 そこで自前のスクリプトを/usr/local/sbin/iptables.shに準備する事にしました。

このスクリプトは起動時ではなく、PPPoEが実行された後で実行する必要があります。 そこで/etc/network/interfacesファイルを使う事にしました。 PPPoEを使うための設定は次のようになっています。

...
auto dsl-provider
iface dsl-provider inet ppp
  pre-up /sbin/ifconfig eth0 up # line maintained by pppoeconf
  provider dsl-provider
  post-up /usr/local/sbin/config_iptables.sh

auto eth0
iface eth0 inet manual
  mtu 1454

このpost-upを使い、ppp0インタフェースが起動した後に必ずiptablesの設定が行なわれるようにしています。 interfacesファイルを使った設定については、$ man 5 interfacesを参考にしてください。

Debian Etchのインストール

以前投稿した記事(Ubuntu 7.10からALIXでブート可能なCFカードを作成する)を参考にしてください。 ここではalixでdebianとsshdが起動している事を前提に進めています。

PPPoEの設定

参考にしたサイトでは手動で設定を入れていますが、pppoeconfパッケージが面倒をみてくれます。

$ sudo apt-get install pppoeconf
$ sudo /usr/sbin/pppoeconf

質問への答えで困るところは特にないはずです。 プロバイダから貰った接続用のID, パスワードを設定すれば基本的には終りです。

PPPoE設定の変更

設定ファイル/etc/ppp/peers/dsl-providerを変更しました。

$ sudo diff -u /etc/ppp/peers/dsl-provider.20091223 /etc/ppp/peers/dsl-provider
38c38
< usepeerdns
---
> #usepeerdns
66c66
< mtu 1492
---
> mtu 1454

usepeerdnsをコメントアウトしたのは、自前のDNSサーバーを持っているからです。 そうでなければ変更する理由はないと思います。

mtuを1454に変更したのは、Bフレッツの設定例に載っているからです。 これに併せて家のネットワークにあるLAN内の機器についても上記eth0と同様にmtu 1454を設定しています。

接続テスト

ponコマンドを使って、ppp0インタフェースが構成されるか確認しましょう。

$ sudo pon dsl-provider
$ /sbin/ifconfig ppp0

テストが終ったら接続を切ります。これを忘れるとppp0, ppp1, ...とインタフェースが増えてしまいます。

$ sudo poff -a
起動時にPPPoEを立ち上げる

電源ONと同時にBフレッツに接続するために、/etc/network/interfacesiptablesの使い方にあるように変更します。

不具合があった場合の対応方法

もしプロバイダから送られてきたIDやパスワードに変更があった場合には、$ sudo pppoeconfを再度実行して必要な変更を加えた方が早いと思います。

そういった場合も含めて、自分で設定を書き換えたい場合には/etc/ppp以下にあるファイルにgrepをかけて必要な情報全体を修正してください。

ID, パスワードの情報はpap-secrets, chap-secretsファイルの他にdsl-providerファイルにも含まれています。

ブロードバンドルーターとしての設定

いまのところalixからInternetへは繋っていますが、LANで繋っている他のPCからはInternetへの通信ができないままです。

つまりeth1のネットワーク(192.168.1.0/24)にあるPCからの通信は、 eth0->ppp0を通ってInternetに出ていかないように設定されています。 そのためppp0,eth1,eth2の各インタフェース間を行き来できるように設定を加えていきます。

sysctl設定

Ubuntuを含むDebianの系統では、systcl関連の設定を/etc/sysctl.confで行ないます。 最低限必要な設定はnet.ipv4.ip_forward=1だけですが、sysctl.confファイル全体の設定は次のようにしました。

$ egrep -v ^# /etc/sysctl.conf | egrep -v ^$
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
net.ipv4.tcp_syncookies=1
net.ipv4.ip_forward=1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
最低限のiptables設定

通常はiptablesのFORWARDポリシーはACCEPTになっているため、一つしかないグローバルIPアドレスを共有するためのNAT(IP MASQUERADE)設定を追加するだけで動くようになります。 この段階での/usr/local/sbin/iptables.shスクリプトの内容は次のようになります。

#!/bin/bash
umask 066
PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH
iptables -F
iptables -t nat -F
iptables -Z
iptables -X
iptables -t nat -X
iptables -t nat -A POSTROUTING -o ppp+ -j MASQUERADE

2010/01/04追記:
このままではスクリプトを複数回実行した時に、ターゲット(-t)に指定したNAT関連の設定が初期化されずに残ってしまいます。 そのため次の2行を追記しました。 iptables -t nat -Fiptables -t nat -X

もちろん、このままでは危なくてInternetに常時接続する事はできません。 最低限確認しておく事はまだあります。

セキュリティ其の一:LISTENポートの確認

Internetのどこかにいる人が、このalixにアクセスする事が可能か確認する事にします。 今回はSheldsUp!ポートスキャンサービスを利用してみます。

リンク先に進むとShieldsUp!が検出したグローバルIPアドレスが表示されます。 Proceedボタンを押すと、サービスの選択画面が表示されます。 All Service Portsを選択して1056ポートをチェックします。

何も設定していないとOpenSSHの22番ポートが赤いOpenにチェックされているのではないでしょうか。 またpingに反応する設定にしていても問題だと認識されます。 外部からICMPパケットを送り込んで内部の状況を観察する方法もあるので、 Firewallの設定は適切に行なう必要があります。

セキュリティ其の二:LISTENポートのクローズ

今やInternetと家のLANとのゲートウェイになったalixが、どのようなネットワークサービスを提供しているのか確認しておく事にします。

$ netstat -na --protocol=inet
稼働中のインターネット接続 (サーバと確立)
Proto 受信-Q 送信-Q 内部アドレス            外部アドレス            状態       
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:6010          0.0.0.0:*               LISTEN     
tcp        0      0 192.168.1.1:22          192.168.1.3:50845       ESTABLISHED
udp        0      0 192.168.1.1:34761       192.168.1.2:123         ESTABLISHED
udp        0      0 192.168.1.1:111         0.0.0.0:*

ここで問題になるのは、状態がLISTENであるもののうち、内部アドレスが0.0.0.0:22になっているSSHのサービスです。 :::22あるいは0.0.0.0:22となっている場合、eth0, eth1, eth2, ppp0のいずれのインタフェースからの接続も許可してしまいます。 そのためポートスキャンサービスでは、Openと検出され警告の対象となります。

Internetからのssh接続は必要ないため、アクセスを制限する事にします。

SSHのポート制限

SSHデーモン(sshd)の制限は、Debianを始め主要なDistributionでは/etc/ssh/sshd_configを通して行なわれます。 変更個所は以下の通りです。

** 変更前 **

ListenAddress 0.0.0.0

** 変更後 **

ListenAddress 192.168.1.1
ListenAddress 192.168.1.1

この変更により、192.168.1.1, 192.168.10.1を宛先とする接続を許可する事ができます。 つまり、クライアントはIPアドレスが192.168.1.0/24, 192.168.10.0/24のいずれかに限定できる事になります。

portmap関連のセキュリティ

"0.0.0.0:111"のように表示されれば、それはportmapが稼働している事を意味しています。 今回はautofsパッケージを導入したため、portmapも稼働する事になっています。

しかしalix以外からの接続を許可する必要はないため、"127.0.0.1:111"と表示されるように変更をします。 まずportmap自身は-i, -lの2つのオプションで、SSHのListenAddressと似たような設定をする事が可能です。 これはportmapの起動時の引数で設定するため、/etc/default/portmapファイルを編集します。

OPTIONS="-l"

しかしportmapをSSHのように192.168.1.1, 192.168.10.1の2つのアドレスで待ち受けるようにする事はできないようです。 また、Linuxに付属するportmap関連のdaemonの中には、上記のようにListenAddressを変更する事ができないものもあります。

そこで、rpc.statdなどの対策のために今回は/etc/hosts.allow, /etc/hosts.denyファイルも編集する事にします。

hosts.allow, hosts.denyの編集

tcpwrapperと呼ばれるライブラリを使うように作成されたプログラムでは、一般的に /etc/hosts.allow, /etc/hosts.denyという2つのファイルを使用してネットワーク接続を制限する事ができるようになります。

さて、どのプログラムがtcpwrapperを使う事ができるのかはリンクされているライブラリから伺い知る事ができます。

$ ldd /usr/sbin/sshd
	linux-gate.so.1 =>  (0xb7f75000)
	 => /lib/libwrap.so.0 (0xb7f68000)
	libpam.so.0 => /lib/libpam.so.0 (0xb7f5d000)

libwrap.so.0が表示されれば、tcpwrapperの機能を含んでいると考えられます。 実際に/sbin/portmapにlddコマンドで確認しても、libwrap.so.0が含まれています。

今回はとにかく家のLANからのアクセスだけを許可したいので、 細かい制御は止めて一括でルールを設定します。

/etc/hosts.allow

ALL: 192.168.1.0/24 192.168.10.0/24

/etc/hosts.deny

ALL: ALL
閑話休題:Linuxでのネットワークアクセス制御

Linuxでは各プログラム独自のアドレス、ポート管理の他に、tcpwarpperというライブラリによるプログラムの制御、iptablesによるカーネルレベルでの管理を行なう事ができます。 低レベルなところで動くiptablesで十分かもしれませんが、iptablesはとびらにつける鍵のようなものです。 LISTENするアドレスを制限する事で、外に向けてとびら自体を作らないという考え方がより堅牢とはいえます。

セキュリティ其の三:iptables

これまでの方法で外部から直接的にシステムに侵入する事は難しくなっています。 しかしまだpingに反応しますし、まだ攻撃対象となる可能性があります。

/etc/network/interfacesファイルから呼ばれる/usr/local/sbin/iptables.shスクリプトの構成について説明します。

この文書の最低限のiptables設定で作成したスクリプトでは、 ざっとみて下記に挙げたようないくつかの観点が抜けています。 いきなり完璧なルールを作る事はできませんが、将来的に拡張可能な小さいルールセットを作成して、 徐々に拡張して行くのが良いと思います。

  • デフォルトポリシーの設定
  • ログの取得による稼働状況の確認
  • インタフェース毎の制御
  • 外部設定ファイルによるinbound接続の許可
  • 不正なIP送信元、送信先パケットの拒否
  • 不正なTCPフラグを持つパケットの拒否

まずは全体的な構成に関わるログの取得のところまでを先に説明します。 各論はまた別のテーマにしたいと思います。

デフォルトポリシーの設定

前にも書きましたが、何のiptablesの設定を行なわないとACCEPTがデフォルトのポリシーになっています。

$ sudo /sbin/iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination  

このままではフィルタールールを設定しても、そこから漏れたものがalixを通過していってしまいます。 そこでiptables -Fなどで初期化した後に、デフォルトルールを設定します。

iptables.sh

...
iptables -F
iptables -Z
iptables -X

iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

iptables -t nat -A POSTROUTING -o ppp+ -j MASQUERADE
...

ポリシーが適用できるのは、この3つのINPUT, OUTPUT, FORWARDだけです。

ログの取得による稼働状況の確認

とりあえずログをとりつつ、許可を与えるルール(logaccept)と、不許可にするルール(logdrop)を準備する事にします。

iptables.sh: ログ取得用コード

...
iptables -P FORWARD DROP

iptables -N logdrop
iptables -A logdrop -m limit --limit 5/minute -j LOG --log-level warn --log-prefix "myfilter drop "
iptables -A logdrop -j DROP

iptables -N logaccept
iptables -A logaccept -m limit --limit 5/minute -j LOG --log-level debug --log-prefix "myfilter accept "
iptables -A logaccept -j ACCEPT
...
インタフェース毎の制御

ここから先はiptablesがどのようにルールを適用させていくか理解しておかないと面倒になってきます。 参考資料3の中にある図で確認するのが良いでしょう。 この図は家からInternetに出ていく方向(outbound)か、Internetから家のLANに入ってくる方向(inbound)かによって、少し頭を切り替えて上部の"Packet in"のところから流れを追い掛ける必要があります。

既に設定されているMASQUERADEによるIPアドレスの変換は、outboundに対するPOSTROUTINGのタイミングで行なわれています。その手前で行なうfilter TableでのFORWARD Chainでの制御は家のLAN内部で通用するIPアドレスを使って制御する事ができます。

逆にinboundでの外部からLAN内部の機器に対するIPアドレス変換を行なう場合は、PREROUTINGのタイミングで設定すると、指定するIPアドレスがLAN内部のアドレスで良くなります。

またこの図からalixでのINPUT, OUTPUT Chainの設定は、ailx自身からの接続に対してだけ適用される事もわかります。単純にalixを通過するだけの通信は、inbound, outbound共にFORWARD Chainが適用される事になります。

alixを通過する通信もalix自身の通信も、個別に制御する必要性は低いと思います。 そのため3つを制御するのではなく、inboundとoutboundで制御する方法を選択する事にしました。

ここではppp0, ppp1などのインタフェース(ppp+)を通過するパケットを、inbound, outboundそれぞれinppp, outpppという名前で制御する事にします。

iptables.sh 続き2

...
iptables -A logaccept -j ACCEPT

iptables -N inppp
iptables -N outppp

iptables -A INPUT   -i ppp+ -j inppp
iptables -A FORWARD -i ppp+ -j inppp
iptables -A OUTPUT  -o ppp+ -j outppp
iptables -A FORWARD -o ppp+ -j outppp
...

eth1, eth2を通過するパケットも同じような名前をつける事で制御が可能ですが、 とりあえず現状では制御はしない事にします。

iptables.sh: 最後でeth1, eth2を許可するルール

...
iptables -A inppp -j logdrop
iptables -A outppp -j logdrop

iptables -A INPUT   -i eth1 -j ACCEPT
iptables -A OUTPUT  -o eth1 -j ACCEPT
iptables -A FORWARD -i eth1 -j ACCEPT

iptables -A INPUT   -i eth2 -j ACCEPT
iptables -A OUTPUT  -o eth2 -j ACCEPT
iptables -A FORWARD -i eth2 -j ACCEPT

FORWARD Chainに-o eth1-o eth2を追加していないのは、Internetを含めた外部からの通信がeth1やeth2を通る可能性があるからで、そのようなパケットはinpppoutpppで許可し、漏れたものは拒否するようにしたかったからです。

外部設定ファイルによるinbound接続の許可

さて少し順序が逆になりますが、外部へwebサーバーを公開するといった手段のために、外部の設定ファイルを使ってinboundの接続を許可しようと思います。

とりあえず外部からデスクトップPC(192.168.1.10)の22番(ssh)ポートへのアクセスを許可するための設定は次のようになります。

参考:外部からのinbound ssh接続を許可するコード

iptables -t nat -A PREROUTING -p tcp -i ppp+ --dport 22 -j DNAT --to-destination 192.168.1.10
iptables -A inppp -p tcp --dport 22 -d 192.168.1.10 -j ACCEPT

PREROUTINGでとりあえず192.168.1.1から192.168.1.10への接続のように変換し、その上で改めて192.168.1.10への接続の許可を与えています。

これを踏まえて、/usr/local/etc/iptables.inboundに次のようなルールを記述しました。

/usr/local/etc/iptables.inbound

tcp,22,192.168.1.210

iptables.sh 設定ファイルを読み込む処理

...
iptables -A FORWARD -o ppp+ -j outppp

OIFS=${IFS}
INBOUND_FILE="/usr/local/etc/iptables.inbound"
if test -f "${INBOUND_FILE}"; then
  IFS=$'\n'
  for line in $(cat "${INBOUND_FILE}")
  do
    ( echo $line | egrep ^# ) > /dev/null && continue
    proto="$(echo $line | awk -F, '{print $1}')"
    dport="$(echo $line | awk -F, '{print $2}')"
    dipaddr="$(echo $line | awk -F, '{print $3}')"
    if test "${proto}" = "tcp" -o "${proto}" = "udp"; then
      iptables -t nat -A PREROUTING -p "${proto}" -i ppp+ --dport "${dport}" -j DNAT --to-destination "${dipaddr}"
      iptables -A into -p "${proto}" --dport "${dport}" -d "${dipaddr}" -j ACCEPT
    fi
  done
fi
IFS=${OIFS}

iptables -A inppp -j logdrop
...

まとめ

とりあえずここまでのコードの断片を繋げると、それなりに動くものができていると思います。

2009/12/29

Alixで動いているCFカード上のDebianを別カードに引っ越してみる

家のDNSサーバーにしているalixのCFカードは実験的に使った古い512MBのカードだったので手狭になってしまいました。 そこで手持ちのTrancend 2GB 266x (SLC)なCFカードに引っ越す事にしました。

TrancendのCFカードは133xがMLC, 266xがSLCですが、hdparm等でスピード自体に違いがある事はわかりますが、メモリ上で動く小さいサーバープロセスでは体感できるほどの速度差はないと思います。

動かすサーバーの種類に依りますが、耐久性以外の理由でSLCなCFカードを選択する必要性は低いのかもしれません。

待ち受けカードを準備

稼働中の512MB CFカードは最後まで抜かずに、新しく置き換える2GBのCFカードを準備する事にしました。 最終的にはrsyncを使って、ファイルをコピーしようと思います。 新しく準備した2GB CFカードの作業はUbuntu 8.04にUSB接続のCFカードリーダーを接続して行なっています。

GRUBのインストール

とりあえず Ubuntu 7.10からALIXでブート可能なCFカードを作成する の手順に従って、Ubuntu上でgrubを導入するところの作業を行なっています。

認識したCFカードが"/dev/sdg1"だとすると、次のようなコマンドになります。

$ sudo /sbin/mke2fs /dev/sdg1
$ sudo /sbin/tune2fs -c 0 -i 0 /dev/sdg1
$ sudo mount /dev/sdg1 /mnt/cf
$ sudo grub-install --root-directory=/mnt/cf /dev/sdg

rsyncを使ったファイルコピー

稼働中のalixサーバーからファイルをコピーしてくるためには、一般ユーザーIDではアクセスできないファイルが出てきます。 そのためrootでのsshアクセスを一時的に許可することにしました。

root@alix:# vi /etc/ssh/sshd_config
PermitRootLogin yes

上記のように"PermitRootLogin"に"yes"を設定します。 続いて、Ubuntu上で作業を続けます。

$ sudo rsync -av root@alix:/. /mnt/cf/.

"root@alix"はユーザーID:root, ホスト名:alixの意味で、"alix"の部分はIPアドレスにする方が良いかもしれません。(e.x. "root@192.168.1.2")

/procやら/sysは不要なので、 --exclude で指定するべきだったのですが手で削除し、作りかえました。

$ sudo rm -rf /mnt/cf/proc
$ sudo mkdir /mnt/cf/proc

CFカードの交換

こんな感じで、とりあえずDNSサーバーなalixを止めてCFカードを交換しました。

CPU, Memoryに余裕があるので、CFカードのサイズが4倍になったことですし、 いろいろパッケージも導入して試していこうと思います。

2009/12/21

Blogger APIでLabelのリストを取得する

xmlファイルからbloggerに投稿できるようにしたまでは良かったのですが、 自分がどんなラベルを設定しているか覚えているのが難しくなりました。 以前は一覧から選択すれば良かったのですけれが…。 そこで今回はBlogger APIを使って"ラベル"の一覧を取得しようと思います。

ラベルを一括取得するAPIはない模様…

どうやらエントリを引き出して、その中に設定されているカテゴリー情報を抜き出す必要があるようです。 BloggerExample.pyを元に次のようなメソッドを追加して呼び出してみます。

def list_labels(self):
  labels = []
  query = gdata.blogger.client.Query(max_results="1000")
  feed = self.client.get_posts(self.blog_id, query)
  print feed.entry[0].updated
  for entry in feed.entry:
    for category in entry.category:
      labels.append(category.term)
      pass
    pass
  
  ## Generating output by two phase sort.
  l = []
  for label in set(labels):
    l.append(label)
    pass
  for label in sorted(l):
    print label
    pass
  pass

この中で Query オブジェクトを使っていますが、デフォルト25件までのところ最大1000件までのブログエントリを取って、その中にあるラべル情報を取り出しています。 1000件の中に含まれていないラベルなら使わなくて良いでしょうけれど、最後までループを回す方法が、このAPIの中にはないので、こういう実装になっています。

実は gdata.clinent.GDClient::get_next() の仕組みを使えばループは出来るんですが、 Query を使うのと大差はないので止めました。

クラシックテンプレートで使えるもう一つの方法

この画面にも右側にラベル一覧が表示されているので、DOM+XPathで解決じゃないかな、 とか思いつつ無視して一気に進みました。

ちなみにこのLabels一覧は Jason Sutter さんの方法でJavaScriptを使って生成しています。 その中でアクセスしているURLからラベルの一覧を取得するURL、 http://www.blogger.com/feeds/USERID/blogs/BLOGID 、を使います。

Ubuntu 8.04に入れている xgrepawk を使って、次のようにラベルのリストをコマンドラインで取る事ができます。

$ wget -O- http://www.blogger.com/feeds/USERID/blogs/BLOGID| xgrep -x '//@term' | awk -F= '/term/ {print $2}'
"趣味"
"プログラミング"
...

解決策はいろいろありますが、まぁこれでラベルをちゃんと付けるようにしましょう。 でもuserIDが漏洩してしまうと、いくつか問題がありますね。 まぁ隠している情報じゃないからいいけれど、そのうちテンプレートを直しましょう。

2009/12/24追記:
クラシックテンプレートを使っているとラベル一覧を表示する方法がなかったので、JavaScriptを使ってラベルを表示していましたが、テンプレートをアップグレードして止めました。 上記URLにはラベル以外の情報も含まれています。 特に隠す情報でもなかったのですが、一般的には良くない方法といえると思います。

誰かの作業内容をモニターするというアイデア

ImpressのGAME Watchの記事の中で SEGAのクローンモニタリングという教育手法について紹介されていました。

これはSEGAがSIGGRAPH ASIA 2009で行なったセッションの中でアートスタッフの新人教育の方法について説明していたもので、メインモニタの出力をスプリットして新人と先輩社員のセカンダリモニタに互いに表示させるという手法です。

原始的な手法ですが、概ね実施当初の成果は良いようです。 慣れてしまうと画面をみないという欠点にも言及されていましたが、新人が熟練者の仕事内容をみることができるというのは良いでしょうね。

自分の中に作っていく成長モデル

私はゲーム業界とは関係ないところで働いていましたが、 一定レベルの技術的スキルが必要とされる現場では、 新人を含めて社員のスキル向上に外部から働きかける事は難しいと感じていました。

若い間は目の前の仕事を片付けるだけで精一杯かもしれませんが、 そこに自分なりの工夫を入れていけると楽しくなると思います。

私は他の人からみてつまらないと思われているような仕事をするときは、 新しいプログラミング言語を使うとか、データ処理用のツールを作るとか、何か1つ自分なりに新しい事を組み込むようにしていました。 「○×縛り」みたいに自分に制約を付けると楽しくはないけれど、辛さは少しだけ軽くなります。 そんな様子は他の人にみえても良かったのかもしれません。

SEやプログラマーの教育には、そのままだと使えないかなとは思いましたが、 何事も新鮮にみえる新人の間に仕事の様子を互いに見えるようにするのは良いアイデアですね。

2009/12/14

ひさしぶりにコンテンツを作ってみた

簡単なXML→HTML変換器を作成したので、調子に乗ってテストを兼ねて気になっていた話題で文書を作成してみました。 実際に使ってみるといろいろ不備がみつかったのですが、その場で修正していって、自分が書きたいものに特化しているけれど、そこそこ使えそうな形になってきたと思います。

きっとベースは同じでも書きたいものによって、少しずつ実装を変化させていくと思います。 書く量を減らしつつ、便利なものを短時間で作っていく、そんな事を目指しています。 対象によってタグが違うのは不便そうですが、記述する対象によってボキャブラリーやタグ付けの粒度は変化すると思うんですよね。

このブログでは読書感想文的なものは単純なリンクを含む程度で、 本文で必要なものは 強調 ぐらいです。 またコードや端末への入力・出力といった技術的な要素を多く含むので、codeタグといったXHTMLへ直接マッピングされるタグの他にコメント用の"note"タグという独自のタグも含んでいます。

今回使ったVelocityベースのAnakiaフレームワークはXSLTのように標準化がされていないけれど、 小さくまとまっていますから自分のツールボックスには入れておこうと思います。

いくつか実装した機能として、本文中に直接anchorとhrefを埋め込む事はせずに、外部のXMLファイルに情報をまとめて、本文からは簡単な名前だけを書き、Anakiaの中で変換するようにしています。 XMLファイルは、build.xmlの中でcontextタグの中で指定して、参照用のマクロをsite.vslで定義しています。 応用すれば、いろいろな情報と本文との結び付きを弱く保つ事ができそうです。

build.xmlでの指定例

<anakia>
...
  <context name="anc" file="../etc/proj_anchor.xml" />
</anakia>

ここで指定した"proj_anchor.xml"ファイルの中身は次のようになっています。

<anchor>
  <item name="ibm.jp" href="http://www.ibm.com/jp"/>
</anchor>

build.xmlの中で指定したcontextには次のようなVelocityマクロを準備しました。

#macro (find_anc $name $value)
  #set ($href = $anc.selectNodes("/item[@name='$name']/@href"))
  <a $href>$value</a>
#end

丁度このブログ記事を生成するツールには、この仕組みを入れていなかったのでバックポートしてみました。 ブログの中にあるリンクを一元管理するのは良いのか、悪いのか、どうなんだろうな。

しかし自分で作成したツールの仕様書をどの程度のレベルでまとめておくのか、少し悩んでいます。 okwaveでも似たような投稿は度々みますね。良い結論に至ったものは、まだなさそうですが。 まぁ基本的な取扱説明書と内部のデータ構造ぐらいは最低限かな。

今回作成したツールで書いた文書はこちら → http://www.yasundial.org/shell_script/

2009/12/12

Apache Velocityの困った挙動

VelocityでXPathを使うと、いろいろ困った事が起ります。 結局XPathを使うのは止めて、JDOMの範囲で処理をするようにしました。

テキストノードが引き出せないXPath

通常はタグの中にあるテキストは $node.selectNode("/element::text()") のような構文で取り出す事ができるのですが、Velocityの場合は text() の部分がエラーになります。 そのためテキストノードに到達するためには、 $node.getChild("element").getContent() のような形でアクセスする他に方法がありません。

この例だとそんなに問題がないように思えますが、特定の要素のアクセスする場合に差が出ます。 XPathの持つ /element[@attr='val'] の構文はループを使わずに特定の要素にアクセスができますが、Velocityの場合は #foreach を使い、 #if('val' == $item.getAttributeValue("attr")) のように条件にマッチする場合に処理を行なう必要があります。

結局要素全体にアクセスしたい場合に限定して使う事になるのですが、後述するように他にも問題があります。 XPathの構文を使わないようにするのも、少し冗長でも分かり易くみえていいのですが、期待してたので少し残念です。

日本語が使えないXPath

次に .selectNode("/chapter[@name='はじめに']") のように式の中に日本語を書くとエラーになります。 antlr-2.7.5.jarがバイト単位で処理しているのが原因なのかなぁ。日本語パッチがあるようなので最近のものには含まれているのかなと思い、jarファイルを最新の"antlr-3.2.jar"に差し替えてみましたが、同じエラーになりました。

最近はUTF-8に対応している字句解析器もありますが、日本語を通すのはやはり難しい場合が多いですね…。

対応自体はXPathを使わずに、JDOMの範囲で $node.getChild("chapter").getAttributeValue("name") == "はじめに" のようなコードを書けば問題なく動きます。 間違いなく意図した要素を指定するには、場合によって少し面倒ですが、丁寧に書きつつコメントで補うしかないかなぁ。

最後に

全体的にxpathはおまけの印象が強いでが、それでもVelocityは便利だと思います。 フレームワークは制限が多少きつめな方が集中できて良いのかもしれないですね。

2009/12/11

Ubuntu 8.04 LTSでGoogle Blogger APIを触ってみる

何はなくとも Blogger API の"client libraries"からpython用のファイルをダウンロードしておきます。 Blogger APIの文書自体は参考になりますが、 サンプルコードに手を加えていけば丁寧に読まなくても先に進めると思います。

パッケージの展開とビルド

Ubuntu 8.04のパッケージに準備されているpython-gdataパッケージはバージョンが1.0です。 そもそも現在の最新版は2.0.5ですから、附属のサンプルは動きません。 ダウンロードしてきたzipパッケージを展開して必要なライブラリをビルドします。

$ unzip -x gdata-2.0.5.zip
$ cd gdata-2.0.5
$ python setup.py build

インストールしてしまうと/usr以下にコピーされてしまいますから、 今回はそれを行なわずにサンプルプログラムを動かす事にします。 まずは附属のサンプルプログラムを起動してみましょう。

$ cd samples/blogger
$ python BloggerExample.py 
Traceback (most recent call last):
  File "BloggerExample.py", line 35, in <module>
    import gdata.blogger.client
ImportError: No module named blogger.client

普通に動かすと"blogger.client"つまり"blogger/client.py"ファイルが見つからないと言われてしまいます。client.pyを探すと…

$ find ../../ -name '*.py' | grep blogger/client
../../build/lib/gdata/blogger/client.py
../../src/gdata/blogger/client.py

今回はbuildディレクトリの方を使うので、コマンドラインを修正します。

$ env PYTHONPATH=../../build/lib python BloggerExample.py
Please choose the authorization mechanism you want to use.
1. to use your email address and password (ClientLogin)
2. to use a web browser to visit an auth web page (AuthSub)
3. if you have registed to use OAuth
: 1
Please enter your username: xxx@gmail.com
....

基本的には BloggerExample.py の中で必要そうな処理はメソッド単位でまとめられているので通して眺めるぐらいで必要な知識は手に入ると思います。

ブログ記事の更新

BloggerExample.py にない機能はブログ記事の更新でしょうか。 でもタイトルの更新は BloggerExample.py に書かれているので、 適当に "Title" を "Content" に書き換えたてうまく動いています。

for article in self.client.get_posts(self.blog_id):
  ...
  article.content = atom.data.Content(type='text', text=content)
  ...

"Developer's Guide: Python"の" Publishing a blog post "には"atom.Content"という記述がありますが、"atom/data.py"を読む限りだと動かないですよね…。

Blogger APIは全体的に動きから予測できるのだけれど、細かいところで初級者に優しい作りではないようです…。

Bloggerへの投稿を自動化してみた

HTMLファイルを生成する事はできるようになったので、Google Data ProtocolのPython Client Libraryに附属するBlogger用のサンプルスクリプトを修正してコマンドラインから投稿できるようにしました。

この投稿自体も自前のXMLファイルから一連のコマンドでブログに投稿しています。 また初投稿した時にタイトル名とblog_post_idを保存しておくようにしたので、 タイトルの変更や記事の更新もできるようになっています。

実行時の様子

動作は2段階でXMLからHTMLに変換する部分( build.sh )と、生成したHTMLファイルをリストして指定した記事を投稿する部分( run.sh )に分かれています。

記事生成スクリプトの実行

HTMLファイルを生成するといっても実体はxhtmlに合わせているのでXML文書です。 ANTタスクの前後にxmlvalidateタスクを実行するようにしているので、少なくともwell-formedなXML文書である事は最低限チェックしています。

Buildfile: prop/build.xml

validate_xml:
[xmlvalidate] 2 file(s) have been successfully validated.

docs:
   [anakia] Transforming into: /home/user01/blogpost/html
   [anakia] Input:  20091211.1.xml
   [anakia] Output: /home/user01/blogpost/html/20091211.1.html
[xmlvalidate] 2 file(s) have been successfully validated.

BUILD SUCCESSFUL
Total time: 0 seconds
投稿スクリプトの実行

[0]	Bloggerへの投稿を自動化してみた
[1]	Apache Anakiaを使った簡易CMSの作成
select article number: 0
find entry: 9106769796720729015
[update_article] file=20091211.1.html,title=Bloggerへの投稿を自動化してみた,labels=[u'web', u'\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0', u'ubuntu']

さいごに

まだ記事を参照するためのリンクの仕組みはなく、編集を楽にするツール類の開発は必要だと思います。でもタイトルの変更と記事の更新ができるようになった事で最低限の機能は実装できました。

しばらく使ってみて、これをネタにアプリケーションの設計や文書化について勉強していこうと思います。

2009/12/10

Apache Anakiaを使った簡易CMSの作成

anakiaについて調べてみたので、このブログに投稿する記事を生成するための簡易CMSを作ってみました。 機能は少ないですし、bloggerにポストするところも手でコピー&ペーストしています。

設計方針

このブログでは端末への入力やその結果を表示したり、 アプリケーションコードの一部を書く事が多いです。

いままでは手動で&などとクォートしていたのですが、 量が多いと面倒な事になります。 リダイレクト(>)なんかは手で書いていると、向きを間違えそうですしね。

コードを記述する場合の例

パラグラフ'p'タグの外で'code'タグやら'kbd'タグを使う場合は、'pre'タグの中に入れています。 この場合は主にCDATAを使って生のコードをそのまま書いています。

変換前:元のテキスト

<code><![CDATA[
<body>
  <h1>title</h1>
  <p>本文</p>
</body>
</code>]] >

変換後:画面には次のように表示される

<body>
  <h1>title</h1>
  <p>本文</p>
</body>

'p'タグの中でも'code'タグや'kbd'タグを使うことが出来るようになっています。 コマンドの入力$ emacs 20091210.1.xml &、コードの断片int i=0;

anakiaを使う場合の注意

Apache Anakiaをダウンロードして中にあるサンプルを変換して試すと思うのですが、 試す前に挫折してしまうかもしれません。 コマンドラインで動かそうとするとCLASSPATHの設定や附属のbuild/build.xmlはanakiaのビルド用でドキュメントの変換用ではなかったりします。

あとはデフォルトの言語が"ISO-8859-1"なので、せめてUTF-8なら良いのですが環境に合わせて適切に設定する必要もあります。

ディレクトリ構造

トップディレクトリは任意で、そこからの相対的なパスは次のようになっています。

  • apache-ant-1.7.1
  • anakia-1.0
  • xml/20091210.1.xml - 記事ファイル
  • build.sh - antを呼び出すラッパースクリプト
  • html - 出力用ディレクトリ
  • prop - 設定用ファイルを配置するディレクトリ
  • prop/build.xml - ant用設定ファイル
  • prop/site.vsl - 変換ルールを記述した
  • prop/project.xml - 今回は使わない共通テンプレートファイル
  • prop/velocity.properties - 日本語を使うならセットアップ必須
build.sh

CLASSPATHの設定が面倒なので、wrapperスクリプトを作成しました。

#!/bin/bash

BASEDIR="$(dirname $0)"
ANT_HOME="${BASEDIR}/apache-ant-1.7.1"
ANAKIA_HOME="${BASEDIR}/anakia-1.0"

## export essential variables
export ANT_HOME
export PATH="$ANT_HOME/bin:$PATH"

CP_ANT=$(find . $ANT_HOME/lib -name '*.jar' | tr '\n' ':')
CP_ANAKIA=$(find . $ANAKIA_HOME -name '*.jar' | tr '\n' ':')
export CLASSPATH=$CLASSPATH:$CP_ANT:$CP_ANAKIA

## main ##
cd "$BASEDIR"
ant -f prop/build.xml
prop/build.xml

マニュアルに附属のものをベースに変更しています。 書き方はいろいろやってみないと良く分からないですね。 "projectFile"は"${docs.src}"からの相対パスになりますが、"style"の方は先頭で指定した"${basedir}"からのパスになったりするのでパスの指定は何回かやり直しました。

anakiaに附属の例では"AnakiaTask"クラスがない場合にエラーにしていましたが、 ここではxmlファイルがwell-formedでない場合にはエラーを出すようにしています。

<project name="build-site" default="docs" basedir="..">
  <property name="docs.src" value="xml"/>
  <property name="docs.dest" value="html"/>

  <target name="validate_xml">
    <xmlvalidate failonerror="yes" lenient="yes" warn="yes"
		 classname="org.apache.xerces.parsers.SAXParser">
      <fileset dir="${docs.src}" includes="**/*.xml"/>
      <attribute name="http://xml.org/sax/features/validation" value="false"/>
      <attribute name="http://apache.org/xml/features/validation/schema"  value="false"/>
    </xmlvalidate>
  </target>
  <target name="docs" depends="validate_xml">
    <taskdef name="anakia"
	     classname="org.apache.anakia.AnakiaTask"/>
    <anakia basedir="${docs.src}" 
	    destdir="${docs.dest}"
	    extension=".html" style="prop/site.vsl"
	    projectFile="../prop/project.xml"
	    lastModifiedCheck="true"
	    velocityPropertiesFile="prop/velocity.properties">
    </anakia>
  </target>
</project>
prop/velocity.properties

linux上で動かしているのでUTF-8に設定しています。 WindowsであればShift_JISなどに変更するのが良いのでしょうね。

input.encoding=UTF-8
output.encoding=UTF-8

さいごに

anakiaは便利なんですけれど、含まれているsite.vslファイルの中では'source'タグを処理する時に'pre'開始タグがなくて、'/pre'閉じタグだけがあったり、急いでリリースしたのかなぁと思ったりします。

入出力の言語設定はvelocityの部分なので、anakiaのマニュアルには記述がありません。 それでもコードをみてvelocity.propertiesだなと当りが付けられるので、OSSで良かったです。

Velocityを直接操作しても、この程度のものは作れると思いますが、 このanakiaを触り始めて、この記事を書き上げるところまで睡眠時間を含めて通算15時間ぐらいですから、 まぁ自分の時間は節約できたと思います。 シンプルだけれど、フレームワークとしては初心者向けのはずなのに、説明不足な点が少しばかり残念です。

2009/12/10追記: 後から付けたすためのノートのテスト。

2009/12/07

Ubuntu Enterprise Cloudに附属のイメージが起動しない

結局のところUbuntu Enterprise Cloud(UEC)で"euca-debian-5.0-x86_64.tar.gz"は起動しませんでした。

同じくWebインタフェースの"Extras"からダウンロードした"euca-ubuntu-9.04-x86_64.tar.gz"は、無事に起動しています。 Googleで検索すると似たような現象に遭遇している方はいるようですが、やはり動く環境と動かない環境があるという事のようです。

[    6.393022] sd 2:0:0:0: Attached scsi generic sg1 type 0
Done.
Gave up waiting for root device.  Common problems:
 - Boot args (cat /proc/cmdline)
   - Check rootdelay= (did the system wait long enough?)
   - Check root= (did the system wait for the right device?)
 - Missing modules (cat /proc/modules; ls /dev)
ALERT! /dev/sda1 does not exist. Dropping to a shell!

このエラーメッセージの中にあるように"rootdelay"を設定してみましたが、busy-boxが立ち上がるまでの時間が長びくだけで、問題は解決しませんでした。

kernelのcmdlineを変更する方法

Eucalyptusの仕組みでkernelのコマンドラインを変更して"rootdelay=90"を追加しようとしたのですが、 よくわかりませんでした。 そこでNode Controller上で直接ファイルを編集する事にしました。(フロントエンド側ではありません)

$ diff /usr/share/eucalyptus/gen_kvm_libvirt_xml.20091207 /usr/share/eucalyptus/gen_kvm_libvirt_xml
95c95
<         <cmdline>root=/dev/sda1 console=ttyS0</cmdline>
---
>         <cmdline>root=/dev/sda1 rootdelay=90 console=ttyS0</cmdline>

効果はありませんでしたが、インスタンスを起動すると反映されています。

$ grep Command /var/lib/eucalyptus/instances/admin/i-42C40825/console.log
[    0.000000] Command line: root=/dev/sda1 rootdelay=90 console=ttyS0

この問題は自分でイメージを作成するとか、直接解決する方法は簡単には見付からなそうです…。

@IT::Ubuntuで始めるクラウドコンピューティング

@ITの記事の中でUbuntuで始めるクラウドコンピューティングが公開されています。

これに従えばUbuntu Enterprise Cloud (UEC)環境は作れるのですが、ちょっと使いづらいかなと思いました。 単純にインスタンスを動かしたいだけであれば、”euca-bundle-image”、"euca-upload-bundle"などは使わずにWebインタフェースの"Store"メニューを使うのが簡単そうです。 参照::STEP 6: Install an image from the store

手順自体はEucalyptusにあるドキュメントと同じ構成ですが、シェル変数が多用されているために、何をやっているか読み解くのに時間がかかりそうです。 ただクラウド環境は自動化が肝ではあるので、いろいろな作業を自動化するためにこういう方法は必要で、最終的には慣れる必要があると思います。

事前準備

以下ではUECの"Extras"から”euca-debian-5.0-x86_64.tar.gz”をダウンロードした前提で、どのようにイメージを作成して行くのか試してみようと思います。 tar.gzファイルは展開しておきます。

$ tar xvzf euca-debian-5.0-x86_64.tar.gz
euca-debian-5.0-x86_64/
euca-debian-5.0-x86_64/xen-kernel/
euca-debian-5.0-x86_64/xen-kernel/vmlinuz-2.6.27.21-0.1-xen
euca-debian-5.0-x86_64/xen-kernel/initrd-2.6.27.21-0.1-xen
euca-debian-5.0-x86_64/kvm-kernel/
euca-debian-5.0-x86_64/kvm-kernel/vmlinuz-2.6.28-11-generic
euca-debian-5.0-x86_64/kvm-kernel/initrd.img-2.6.28-11-generic
euca-debian-5.0-x86_64/debian.5-0.x86-64.img

シェルにeucarcファイルを読み込んでおく事と、事前にいくつか準備しておくシェル変数は次のようになっています。設定して別の端末ウィンドウに移ってしまってはシェル変数は引き継がれないので、以下の操作は全部同じ端末ウィンドウの中で実施します。

$ . ~/.euca/eucarc
$ TS=$(date +%Y%m%d%H%M%S)
$ echo $TS
20091207164000

変数を展開した形の解説 〜 EKI変数の設定まで

*http://www.atmarkit.co.jp/flinux/special/eucaly/eucalyc.html より抜粋
# euca-bundle-image -i $UEC_KERNEL -r $IARCH --kernel true
# euca-upload-bundle -b $BUCKET_KERNEL -m /tmp/$UEC_KERNEL.manifest.xml
# EKI=$(euca-register $BUCKET_KERNEL/$UEC_KERNEL.manifest.xml | grep "^IMAGE" | awk '{print $2}') && echo $EKI

"euca-debian-5.0-x86_64.tar.gz"を展開したところから、上記と同じようにコマンドを打つと次のようになります。

$ euca-bundle-image -i euca-debian-5.0-x86_64/kvm-kernel/vmlinuz-2.6.28-11-generic -r x86_64 --kernel true
x86_64
Checking image
Tarring image
Encrypting image
Splitting image...
Part: vmlinuz-2.6.28-11-generic.part.0
Generating manifest
$ euca-upload-bundle -b "k-$TS" -m /tmp/vmlinuz-2.6.28-11-generic.manifest.xml
Checking bucket: k-20091207164000
Creating bucket: k-20091207164000
Uploading manifest file
Uploading part: vmlinuz-2.6.28-11-generic.part.0
Uploaded image as k-20091207164000/vmlinuz-2.6.28-11-generic.manifest.xml
$ euca-register k-$TS/vmlinuz-2.6.28-11-generic.manifest.xml
IMAGE eki-864C132D
$ EKI=eki-864C132D

最後のところは、中身を見せるために少し変更して自分でEKI=eki-864C132Dと代入しています。 "eki-864C132D"の部分は、実行する人によって変化してきます。

変数を展開した形の解説 〜 ERI変数の設定まで

*http://www.atmarkit.co.jp/flinux/special/eucaly/eucalyc.html より抜粋
# euca-bundle-image -i $UEC_INITRD -r $IARCH --ramdisk true
# euca-upload-bundle -b $BUCKET_INITRD -m /tmp/$UEC_INITRD.manifest.xml
# ERI=$(euca-register $BUCKET_INITRD/$UEC_INITRD.manifest.xml | grep "^IMAGE" | awk '{print $2}') && echo $ERI

今回も最後のERI変数への代入のところは分けて実行しています。

$ euca-bundle-image -i euca-debian-5.0-x86_64/kvm-kernel/initrd.img-2.6.28-11-generic -r x86_64 --ramdisk true
x86_64
Checking image
Tarring image
Encrypting image
Splitting image...
Part: initrd.img-2.6.28-11-generic.part.0
Generating manifest
$ euca-upload-bundle -b "r-$TS" -m /tmp/initrd.img-2.6.28-11-generic.manifest.xml
Checking bucket: r-20091207164000
Creating bucket: r-20091207164000
Uploading manifest file
Uploading part: initrd.img-2.6.28-11-generic.part.0
Uploaded image as r-20091207164000/initrd.img-2.6.28-11-generic.manifest.xml
$ euca-register r-$TS/initrd.img-2.6.28-11-generic.manifest.xml
IMAGE eri-C36C141F
$ ERI=eri-C36C141F

変数を展開した形の解説 〜 EMI変数の設定

*http://www.atmarkit.co.jp/flinux/special/eucaly/eucalyc.html より抜粋
# euca-bundle-image -i $UEC_IMG.img -r $IARCH --kernel $EKI --ramdisk $ERI
# euca-upload-bundle -b $BUCKET_IMAGE -m /tmp/$UEC_IMG.img.manifest.xml
# EMI=$(euca-register $BUCKET_IMAGE/$UEC_IMG.img.manifest.xml | grep "^IMAGE" | awk '{print $2}') && echo $EMI

ここまでで"eucarc"ファイルの中で設定されているもの以外に、次のようなシェル変数を設定しています。

TS
20091207164000
EKI
eki-864C132D
ERI
eri-C36C141F
$ euca-bundle-image -i euca-debian-5.0-x86_64/debian.5-0.x86-64.img -r x86_64 --kernel $EKI --ramdisk $ERI
x86_64
Checking image
Tarring image
Encrypting image
Splitting image...
Part: debian.5-0.x86-64.img.part.0
Part: debian.5-0.x86-64.img.part.1
Part: debian.5-0.x86-64.img.part.2
Part: debian.5-0.x86-64.img.part.3
Part: debian.5-0.x86-64.img.part.4
Part: debian.5-0.x86-64.img.part.5
Part: debian.5-0.x86-64.img.part.6
Part: debian.5-0.x86-64.img.part.7
Part: debian.5-0.x86-64.img.part.8
Part: debian.5-0.x86-64.img.part.9
Part: debian.5-0.x86-64.img.part.10
Part: debian.5-0.x86-64.img.part.11
Part: debian.5-0.x86-64.img.part.12
Part: debian.5-0.x86-64.img.part.13
Part: debian.5-0.x86-64.img.part.14
Part: debian.5-0.x86-64.img.part.15
Part: debian.5-0.x86-64.img.part.16
Part: debian.5-0.x86-64.img.part.17
Part: debian.5-0.x86-64.img.part.18
Part: debian.5-0.x86-64.img.part.19
Generating manifest
$ euca-upload-bundle -b "i-$TS" -m /tmp/debian.5-0.x86-64.img.manifest.xml
Checking bucket: i-20091207164000
Creating bucket: i-20091207164000
Uploading manifest file
Uploading part: debian.5-0.x86-64.img.part.0
Uploading part: debian.5-0.x86-64.img.part.1
Uploading part: debian.5-0.x86-64.img.part.2
Uploading part: debian.5-0.x86-64.img.part.3
Uploading part: debian.5-0.x86-64.img.part.4
Uploading part: debian.5-0.x86-64.img.part.5
Uploading part: debian.5-0.x86-64.img.part.6
Uploading part: debian.5-0.x86-64.img.part.7
Uploading part: debian.5-0.x86-64.img.part.8
Uploading part: debian.5-0.x86-64.img.part.9
Uploading part: debian.5-0.x86-64.img.part.10
Uploading part: debian.5-0.x86-64.img.part.11
Uploading part: debian.5-0.x86-64.img.part.12
Uploading part: debian.5-0.x86-64.img.part.13
Uploading part: debian.5-0.x86-64.img.part.14
Uploading part: debian.5-0.x86-64.img.part.15
Uploading part: debian.5-0.x86-64.img.part.16
Uploading part: debian.5-0.x86-64.img.part.17
Uploading part: debian.5-0.x86-64.img.part.18
Uploading part: debian.5-0.x86-64.img.part.19
Uploaded image as i-20091207164000/debian.5-0.x86-64.img.manifest.xml
$ euca-register i-$TS/debian.5-0.x86-64.img.manifest.xml
IMAGE emi-25881166
$ EMI=emi-25881166

登録したイメージのテスト

いままでに他のイメージを起動するなどしていなければ、keypairの作成とルーティングを作成しておきます。 既に実行されているかは”euca-describe-keypairs”を実行して"mykey"が登録されているかどうか、"euca-describe-groups"を実行して"... ALLOWS tcp 22 22 FROM CIDR 0.0.0.0/0"が設定されているかで確認できます。

$ euca-add-keypair mykey > mykey.priv
$ euca-authorize default -P tcp -p 22 -s 0.0.0.0/0

さて、いよいよ起動です。

$ euca-run-instances -k mykey $EMI
RESERVATION r-56B308F1 admin default
INSTANCE i-5EB10A92 emi-25881166 192.168.1.163 172.19.1.4 pending  mykey  0  m1.small  2009-12-07T07:52:16.199Z  yasundial  eki-864C132D  eri-C36C141F

設定されている値やインスタンスの状況を調べたい時には"euca-describe-"で始まるコマンドが使えるようです。 Amazon EC2から引き継いでいるんでしょうけれど、良いネーミングルールですね。

先ほどの"euca-run-instances"の出力では、最初はpendingとなっているままなので、起動していません。 "euca-describe-instances"を定期的に実行して、"running"になる事を確認します。 ubuntu.comのUEC Install手順の中ではUNIX系OSに標準的に存在している"watch"コマンドを使う例が載っています。

$ watch -n5 euca-describe-instances

最終的に失敗してしまう…

running状態にはなるものの、busy-boxに落ちてしまっているので、無事に起動というわけにはいきませんでした。 この原因はまた追い掛けていこうと思います。

その他の注意点

今回導入したイメージの中にはXen用のものも含まれていますが、こちらを使用すると"pending"のステータスから"terminated"となり起動しません。

また"euca-upload-bundle"の"-b"オプションの引数で状況に応じて、"BUCKET_KERNEL","BUCKET_INITRD","BUCKET_IMAGE"の3つの変数を使い分けていますが、 これはprefixと考えて、1つの文字列を使い回すのが良いようです。

ざっとこんな感じで、イメージを起動する事はできましたが、これだけだとあまり活用する目処は立たないですね。 もう少し周辺の情報を集めてみようと思います。

2009/12/06

東京散策 その弐

原宿の他にお台場にも始めて行ってきました。近くの豊洲で働いていたのに、初ゆりかもめ。 事前にGoogleマップで確認していったのですが「ゆりかもめ」じゃなくて「東京臨海新交通臨海線」なんですよね、正式名称は。 とはいえ、行きはりんかい線だったのでまっすぐ徒歩でテレコムセンタービル近くの日本科学未来館へ。

仕分け作業で毛利さんが吊し上げられた曰くつきの建物ですが、 いまある最高ものをみせたい、という趣旨の毛利さんの発言通り、いろいろ触れてすごかったです…。

昔よくいった新潟の自然科学館もけっこう凝った展示をしていたと記憶していますが、期待以上でしたね。

ものによってはちょっと古めかしい感じもしましたが、それでも未来館にいけば自分で動かせる、触れるっていうインパクトはあると思います。 全体的に見せるだけじゃなくて体験できる展示を心がけているのかなぁという印象を強く持ちました。

仕分けの結果で予算の縮減というフレーズだけが取り上げられていますが、 財団を見直すっていう付帯意見が全員から出ているところってあまり取り上げられてないですよね。 それに毛利さんが情熱を持った最高の館長である事に異論はないだろうから、彼をバックアップできる体制をどうやって作っていけるか、仕分けに参加した識者の方々はその方面からも手伝うべきでしょう。

仕分け人も必要だけど、具体的な手段を作っていくためのシンクタンクも必要だなぁ。 それにいまどき修学旅行でTDLに行くぐらいなら未来館にすればいいのに。

きっとまた行くだろうし、その時は一人じゃないでしょう。つまらないから。

2009/12/05

東京散策

今回の旅行ではマイコミジャーナルの記事をみてJohn Squireさんの個展をにいってきました。 というか、原宿という街が自分に合うわけもない…。いたたまれなくなって明治神宮に逃げ込んできました。

さて別にファンっていうわけではないし、以前から注目していた人というわけでもない。 改めてYouTubeでいくつかライブ映像などを見たけれど、自分には合わない音楽だなと思いました。

それでも足を運んだ理由は単純にギターから絵に移っていったという経緯に興味をもったのと、インタビュー記事で率直に発言している様子に魅かれたから。

作品をみてタイトルとイメージが重なったものは多くはなかったけれど、心に残るものも何点かありました。それでも全般的には「やっぱり分からん」って感じでしたけど、好きな雰囲気で良い時間を過ごせました。

作品に付けられた値段はとても高いと思いましたが、好きな人が買えているのかが気になります。

ここからは、また別の話し…

そんなこんなで絵以外で印象に残っていたのが、片隅に置かれた古いiMacでのBBCインタビュー映像の一コマ。音楽を作っていた頃の話しの中で出た"de-construct"と"steal"という言葉。 文脈は関係なくて、この言葉が妙にひっかかっているんですよね。

一般的に音楽を分析的に扱えば似ている点は抽出できるし、 実際に一度コピーしてからメロディーやリズムをいじったのが分かるようなものも世の中には溢れています。

楽曲を分析して批評する人もいるけれど、時々手法が強引で、高次ではまったく違うものを低次に投影して同じといっているような違和感を感じる事があります。

作り手にしたら何がオリジナルなのか、その条件に悩むものなのかもしれません。 経験もなく無から何かを作るっていうのは有り得ない行為ですからね。

絵は有形なもので制約が強いように思えるけれど、無形な音よりも自由な世界なのかもしれない、そんな風に感じました。

2009/11/30

News Corp.がGoogle検索を拒否へ−Microsoft「Bing」と提携の動き - Enterprise Watch
- Reference: http://enterprise.watch.impress.co.jp/docs/series/infostand/20091130_332341.html

News Corp.がGoogle検索を拒否へ−Microsoft「Bing」と提携の動き - Enterprise Watch

なんだかGoogleニュースやらGoogleのキャッシュに記事を掲載されている新聞社が掲載拒否を申し立てようとしている模様です。

まぁ好きにすれば良いんだと思います。 Googleニュースの記事一覧は全文を載せていないし、読みたければクリックしてその記事に飛びますからね。

大手の新聞社が撤退すれば、Googleが記事を集めるために地方の独自性を持っているけれども小さい新聞社まで手を広げるかもしれないし、 その方がインターネットらしくて良いなぁ…。

Ubuntu 9.10でgit経由のカーネル再構築

ついでなので、gitからUbuntuカーネルコードをダウンロードする方法も試す事にしました。 VMWareで新しいCloneを作ったので、i386 Desktop版をインストールして最新の状態にアップデートした状態からのスタートです。

参考資料

後半の手順は前回のYet Another Diary::Ubuntu 8.10 (interpid)以降でのsystemtapと同じです。

あいかわらず本家のKernelCompileです。

そこからUbuntuのバージョン毎にgitを使った方法を載せているHow to compile a kernel for Ubuntu Karmicに飛んでいます。

事前準備

$ sudo apt-get install fakeroot kernel-wedge build-essential makedumpfile
$ sudo apt-get build-dep linux
$ sudo apt-get install git-core libncurses5 libncurses5-dev

カーネルコードの展開

今回はVMWare上の/homeに30GB分の別のHDDイメージを作成しました。"rsync -av"でデータを移してから/etc/fstabを編集して再起動しています。

$ cd
$ git clone git://kernel.ubuntu.com/ubuntu/ubuntu-karmic.git source
$ cd source
$ git checkout Ubuntu-2.6.31-15.50 -b rebuildtest

"debian.master/changelog"ファイルをみて、"2.6.31-15.50"が直近のリリースされたバージョン、"2.6.31-15.51"が作業中のUNRELEASEDバージョンだと分かります。

最後の"-b"オプションに続ける"rebuildtest"の部分は適当な名前をつけます。 これで~/sourceにkernelのgitリポジトリが構築できました。

試しに$ git branchを実行すると、自分が作成した"rebuildtest"プロジェクトの他に"master"というブランチ名が確認できます。 カーネル開発をする分けではないですが、最新のカーネルコードを取得したい場合には、まず"master"ブランチを最新に更新し、それから自分用の"rebuildtest"ブランチのコードに更新をかけるのが良いと思われます。

2009/12/09追記:
最新のカーネルコードにするためには、"git branch master"で一旦branchを変更してから"git pull"でリポジトリからコードを持ってきます。 参考資料にある手順は自分でカーネルコードに変更を加えている前提で書かれているため、マージの手順が面倒になっています。
単純に新しいカーネルコードをビルドするだけであれば、新しいブランチ名で"git checkout"の部分をやり直すのが簡単だと思います。

あとは前回と同じ

注意するところは、まだセットアップが完了していないので前回debian/rules updateconfigsを実行する代りに下記のコマンドで"debian/control.d"などを作成する必要があります。

$ debian.master/scripts/misc/kernelconfig oldconfig

この後で$ fakeroot debian/rules clean$ time AUTOBUILD=1 fakeroot debian/rules binary-mygenericのように進めていきます。

*.deb, *.ddebパッケージの作成完了まで90分ほどかかる見込みです。

linux-image-debugパッケージがない事の損失

linux-image-debugパッケージがapt-getできなくても、どこかのFTPサーバーに入っていれば良いのですが、残念ながらそうなっていないようです。

今回のような手順で"generic"パッケージを作っても、apt-getした"linux-image"と手元で作成した"linux-image-debug"は異なるものなので、systemtapをフルに使うためには今回作成したパッケージと一緒にインストールする必要があります。

systemtap以前はデバッグパッケージは開発者が文字通りデバッグ用に使うぐらいしか用途がなかったと思います。 しかし、これから普及が進めば問題が発生した場合、ある程度はサービスを停止せずに問題を絞り込む事ができると期待しています。

systemtap自体はこれだけで問題を解決するには難しいですけれど、server/clientの枠組みもできて、 原因を追求するために問題が起っている、そのマシンの状態を把握できるというのは必要なことです。

もしカーネル開発者が自分たちの視点だけで、肥大化するカーネルパッケージのサイズを絞り込む方法を考えるなら最初からビルドしないという選択肢もありそうですが、今後はせめて本番サーバーとして使われそうなLTSだけを対象にしてdebugパッケージをダウンロード可能にして欲しいところです。

2009/11/29

Ubuntu 9.10で$HOME直下の日本語名ディレクトリを英語名に変更

日本語localeでデスクトップ環境を使っている以上あたりまえですが、 ホームディレクトリに'文書'やら'音楽'やらのディレクトリが作成されています。

VMWare上に環境を作って気がついて、日本語名のディレクトリを英語名に変換してみました。

変更前

ダウンロード  テンプレート  デスクトップ  ドキュメント  ビデオ  ピクチャ  ミュージック  公開
$ cd ~/.config
$ rm user-dirs.locale
$ env LANG=en_US xdg-user-dirs-gtk-update

user-dirs.localeを削除すると、ログイン時にlocaleに合わせて英語名から日本語名への変換などをするか二度と聞いてこなくなります。 あとからやっぱり日本語に戻したいという場合には、$ env LANG=ja_JP xdg-user-dirs-gtk-updateとするか、$ echo en_US > ~/.config/user-dirs.localeとしてログイン仕直すと良いでしょう。

変更後

Desktop  Documents  Downloads  Music  Pictures  Public  Templates  Videos 

今回編集したuser-dirs.localeといったファイルについて知っていたわけではないのですが、 たまたま'文書'といった特徴的な単語があったので、次のようにfindを使って設定ファイルを探すのは楽でした。

$ find . -type f -exec grep 文書 {} \; -print

確認すること

"~/.config/user-dirs.dirs"ファイルの中身が対応する英語名に変更されているか確認しましょう。

$ cat .config/user-dirs.dirs
...
# 
XDG_DESKTOP_DIR="$HOME/Desktop"
XDG_DOWNLOAD_DIR="$HOME/Downloads"
XDG_TEMPLATES_DIR="$HOME/Templates"
XDG_PUBLICSHARE_DIR="$HOME/Public"
XDG_DOCUMENTS_DIR="$HOME/Documents"
XDG_MUSIC_DIR="$HOME/Music"
XDG_PICTURES_DIR="$HOME/Pictures"
XDG_VIDEOS_DIR="$HOME/Videos"

user-dirs.dirsを直接編集する

例えば'Videos'や'Music'を'Multimedia'といった名前にまとめたい場合には、まず$ mkdir ~/Multimediaのようにディレクトリを作成します。

次に"~/.config/user-dirs.dirs"を直接編集し、'Music'、'Videos'といった単語をMultimediaに変更します。

その上で'Music'、'Videos'にあるファイルを'Multimedia'に移動し、空になったディレクトリを削除すれば良いのですが、ちょっと面倒な感じもします。

作業自体は楽だし、すっきりしているのだけれど…

まぁこういう低レベルなところが制御できるのは心の底から嬉しいのですが、 その反面、徹底してGUIで制御しないことで、肝心な操作は端末で実行する必要があり、 初心者が嫌がるようなのも分からないではありません。

UNIXらしさはCUI環境やらemacs全画面表示やらにあるような気もしますが、 その”らしさ”をユーザーが決められる自由度がUNIX系OSに魅きつけられる理由だと自分では考えています。

そういう"自分らしさ"を持たないが故に初心者と呼ばれる人々を招き入れるために、Windowsライクな環境がエミュレートできる事は必要だと思います。 しかしGnomeみたいに使いやすい環境が簡単には作れるようになっていないのは残念ですね。

いじれるところはThemeぐらいかぁ。 内部のパラメータとGUIの設計が密になり過ぎているんですよね。 GUIの設計自体がそういうものだし、パフォーマンス優先だからしょうがないけれど、 ベースになる土台はGnomeもKDEも同じなのに見栄えと協調させる制御システムが別っていう世界にはならないよね。 ええ、そうでしょうとも。

2009/11/27

スパコンの仕分けにおける議論について

いまさらな感じもしますが、理化学研究所が神戸に建設しているスーパーコンピュータ(スパコン)に対して、来年度の予算計上をどのようにするか仕分けの議論としていろいろな意見がニュースに載っています。

そもそもスパコンの議論に限らず国家の予算を使う以上は、一定の成果の見込みや波及効果についての最低限のアセスメントや事業者に対してコミットメントを求める事はしなければいけない事だと思っています。

それが純粋に学術的な目的だとしても、そういうものだと皆が認識して進めていれば良いのだと思います。

今回の騒動で良かったこと

なにげに理化学研究所のハードルは上がっていると思います。 話題に上がったことで、スパコン作って終りみたいな事では済まされなくて、成果を求められているとプレッシャーを与えたでしょう。そうであって欲しいです。

ただNECの撤退とか、新規のインターコネクトと純粋なスカラ型スパコンで10PFLOPSを目指すように見えているとか、心配な点があります。

政府の姿勢を批判する事は簡単ですが、なぜ必要なのか、それが科学立国の面子を保つだけというのなら必要ないでしょう。 偉い人を出してきた方がマスコミも喜ぶんだろうけれど、「必要」というだけなら必要なくて、現場の人達はもっと語れると思うのですよ。 一連の騒動は、現場に重〜いプレッシャーを与えただろうし、この機会に上から下まで意識を合せて頑張って欲しいと思います。

今回は官僚もプロジェクトの必要性について、ちゃんと理解しているのか疑問でしたが、そういうことを語る必要ができたというのは、とても良い事だったといえるでしょう。 まぁ公開裁判にならないようなルール作りは必要ですが、透明性を高める事でプレッシャーを与えて、なおかつ、より高い投資対効果を生み出す事ができるはずです。

スパコンに求められるものとは

確かにスパコンを作る事自体が一つの重要な事業ではありますが、日本が目指す1位という判断基準もいろいろあります。

TOP500に載る事で一定の評価を得る事もできますが、実世界の問題を解決する高い実性能を叩き出す事も必要です。 地球シミュレーターはTOP500に載るためのベンチマーク性能では順位を落していますが、実計算では高い効率が売りの一つでもあります。 [ 地球シミュレーター 性能 ]

電力消費が尋常じゃないという指摘も地球シミュレーターにはありますが、 世界に誇る成果を挙げてきた事も事実ですし、それが世界1位だったという事実よりも重視するべき事ではないでしょうか。

何のためにスパコンを作るのか、スパコン自体が目的であればそれは一定の批判を受けてしかるべきと思います。

日本が核兵器開発をするなら、効率なんか無視してスカラ型のスパコンを作れば良いと思います。 きっといろいろな会社が買ってくれるでしょう。 流体系のモデルを使うなら、たぶんベクタ型スパコンを極める道が良いでしょう。

両方のタイプのスパコンを作るという話しならすっきりしますが、理化学研究所のスパコンはそもそもNECが撤退したことで、ベクタ型プロセッサを搭載するはずだった計画の変更を余儀無くされています。

NECの撤退で求めるスパコンを作れるのか

ある程度はスパコンを作る際に実世界に存在するどのような問題を解こうとしているのか、その効果がどの程度か考慮してしかるべきでしょう。

そこそこの問題を解くだけであれば、そこそこの性能を持つスパコンを複数作る方が経済的です。 一定の性能を持つスパコンを発注する企業に対する補助なんてのも考えられる案ではあります。

理化学研究所は次世代スパコンプロジェクトの説明の中で、いろいろな情報を公開していますが、その説明文の中の画像イメージがつぶれたままで判読不能だったり、やる気あるのかなぁという雰囲気を醸し出しています。 たぶん良く取って、売り込みが苦手なんですよね、きっと。

21個挙げられているターゲットアプリケーションをみると、現状で地球シミュレータやNEC SX-6で数〜数十GFLOPSの実効性能を出しているアプリケーションをずっと高いレベルに引き上げようとしている事がわかります。

こういった問題を解くために必要だと、それが出来て実世界にどのようなインパクトを与えるのか、そういう点で議論をして欲しいと思います。 学術的な成果だけでも良いと思うんですよ。それはそれで必要でしょ?って。

Ubuntu 8.10 (interpid)以降でのsystemtap

systemtapはUbuntu 8.10以降でもapt-get経由で提供されていますが、使うには少し問題がありそうです。

Ubuntu 9.10にsystemtapをapt-getで導入しましたが、linux-image-debug-*パッケージは提供されていませんでした。 systemtapの提案パッケージの中にはlinux-debug-2.6がありますが、リポジトリには存在していません。 関連する情報はいくつかUbuntuのバグデータベースなどにありました。

手元で確認したのは9.10だけですが、8.10からdebugパッケージがなくなっているようです。 理由として、デバッグ情報を含むことでパッケージリポジトリのサイズが大きくなってしまう事が挙げられています。

開発版では最新のdebug情報付きのカーネルが配布されていますが、1世代分しか提供されていないようです。 LTSでは提供して欲しいけれど、どうなるか情報はみつけられていません。 kernel teamのMLログを漁るしかないか…。

Ubuntu 9.10でのカーネル再構築

とりあえず手元の9.10でsystemtapを試すために、デバッグ情報付きのカーネルを作成する事にしました。 手順はKernelCompile [help.ubuntu.com] に従います。

ビルド環境
  • VMWare Workstation 6.5.3上のVM
  • CPU: 1つ (Host CPU Phenom II X4 940 の1コア)
  • OS: Ubuntu 9.10 i386版
  • Memory: 512MB (後半1.5GB)
  • HDD1: 8GB (/, ext3)
  • HDD2: 30GB (/home, ext3)

Googleで検索すると/usr/srcでlinux-source-2.6.31.tar.bz2を展開して、make-kpkgを使う方法がよくみつかりますが、Ubuntuのお勧めはgitから最新のアーカイブを取得する方法のようですね…。 今回は(2.)の"sudo apt-get build-dep" & "apt-get source"でカーネルのソースコードを展開する事にしました。

少し手順がわかりずらいかも。debian/scripts/misc/oldconfigはないので、$ debian/rules updateconfigsを使うしかないのですが、$ chmod a+x *は本文を良く読めばいいのだけれど、次のようにcdコマンドを使わずに実行できるよう書くべきかもしれません。

chmod a+x debian.master/scripts/misc/*

デバッグ情報を含むdebファイルを作成するために、debian.master/rules.d/i386.mkの中に"skipdbg = false"の設定を加える必要があります。

また"Alternate Build Method: The Old-Fashioned Debian Way"のセクションが同じページに並んでいるので、かなりまぎらわしく、間違ってこの手順も実行してしまわないか心配になります。

実際の再構築手順

まずは事前準備。

$ sudo apt-get install fakeroot kernel-wedge build-essential makedumpfile
$ sudo apt-get build-dep linux
$ sudo apt-get build-dep linux-image-$(uname -r)

いったん準備が終れば、カーネルのソースコードが必要な時にsudoを使わずに自分の権限でファイルを取得&展開できます。 カーネルのコンパイル用に適当なファイルシステムを準備して、任意のディレクトリ(以下、~/mykernel)を作成して移動するのが良いでしょう。

$ mkdir ~/mykernel
$ cd ~/mykernel
$ apt-get source linux-image-$(uname -r)
$ cd linux-2.6.31/
$ chmod a+x debian.master/scripts/misc/*
$ echo "skipdbg = false" >> debian.master/rules.d/i386.mk

最後の一行でi386.mkの最下行に"skipdbg = false"を加えます。 右辺は"false"でなくても"true"以外の文字列なら何でも良いようです。

ただskipdbgオプションは手順には載ってないですね。 このオプションは debian.master/rules.d/2-binary-arch.mk の中で使われていました。

$ cp debian.master/control.d/vars.generic debian.master/control.d/vars.mygeneric
$ tmpf=$(mktemp)
$ sed -e 's/^\(flavours.*\)$/\1 mygeneric/' debian.master/rules.d/i386.mk > $tmpf
$ diff $tmpf debian.master/rules.d/i386.mk
5c5
< flavours        = generic generic-pae 386 mygeneric
---
> flavours        = generic generic-pae 386 
$ cp $tmpf debian.master/rules.d/i386.mk
$ rm $tmpf

diffの引数は"oldfile, newfile"の順番ですが、そのままcpの引数に使えるように またmktempを使ってテンプファイルを作らなくても、"$tmpf"の個所は適当な新規のファイル名にしてもらえれば良いのですが、例として載せてみました。

$ cp debian.master/config/i386/config.flavour.generic debian.master/config/i386/config.flavour.mygeneric
$ cp ./debian.master/abi/2.6.31-15.49/i386/generic ./debian.master/abi/2.6.31-15.49/i386/mygeneric
$ cp ./debian.master/abi/2.6.31-15.49/i386/generic.modules ./debian.master/abi/2.6.31-15.49/i386/mygeneric.modules

$ debian/rules updateconfigs
$ fakeroot debian/rules clean
$ time AUTOBUILD=1 fakeroot debian/rules binary-mygeneric

CPUが複数コアを持っているようであれば、"CONCURRENCY_LEVEL=2"を付ける例にならうと良いでしょう。 make-kpkgを使った場合は、並列コンパイルが難しいようですから、こちらの方法を使う理由になるかもしれないですね。

コンパイルが終ると一つ上のディレクトリにdebパッケージが作成されます。

作成したパッケージの導入

同じバージョンに見えますが、埋め込まれているハッシュ値が違うのでdebugパッケージだけでは十分ではありません。

$ uname -r
2.6.31-15-generic
$ cd ..
$ sudo dpkg -i linux-headers-2.6.31-15-mygeneric_2.6.31-15.50_i386.deb linux-image-2.6.31-15-mygeneric_2.6.31-15.50_i386.deb linux-image-debug-2.6.31-15-mygeneric_2.6.31-15.50_i386.ddeb

あとは再起動をしてsystemtapの稼働を確認します。

systemtapの確認

単純なスクリプトで確認します。

$ sudo stap -v -e 'probe syscall.open { printf("%d %s\n", tid(), execname()) }'
1572 vmware-guestd
1222 hald-addon-stor
....

出力があれば完了です。 ひょっとすると何かの機能を呼び出したタイミングで不具合があるかもしれませんが、いまのところうまく動いているようです。

2009/11/26

"Systemtap tutorial"のまとめ

Systemtapは"DTrace"と"AWK”に触発された、GNU/Linuxカーネル内部を動的にトレースするためのツールです。 オフィシャルサイトで公開されている"Systemtap tutorial"は2007年の日付で、若干古いですが、PDF版の本文12ページの中で基本的な機能についてよくまとめられていると思います。 Reference Manualの方は2009年11月13日の日付で更新されているので、全ての機能について確認する事ができます。

使うだけなら基本的なサンプルだけをコピーして切り貼りしても問題ないと思いますが、 せっかく読んだのでリファレンスやマニュアルページの情報も加えてサンプルだけだと読み解くのが難しそうな点を中心にメモを残しておきます。

全体の構成

チュートリアルは以下のセクションに分けられています。 この流れに沿ってまとめていきます。

  1. Tracing
  2. Analysis
  3. Tapsets
  4. Further information

Tracing

Systemtapはカーネルが処理を行なう途中に"probe"を仕掛けることで、割り込む事ができます。 あたかも電子回路上にオシロスコープのプローブを当てるようなイメージと一致するのだと思います。 使うプローブの種類を変えれば、違うタイミングや内容を知る事ができるといったところでしょうか。

そういった様々なプローブはSystemtapがbuilt-inで提供していますが、その他に、 "tapset"と呼ばれるライブラリスクリプトの中でユーザーが任意のprobeを組み合せて新たなprobeを定義する事が可能になっています。

後でも出てきますが、このライブラリスクリプト群は通常"/usr/share/systemtap/tapset/"以下に配置されています。

例えば、あらかじめ準備されている"probe begin"や"probe end"を使えばstapが処理を始めたり、終えたりするタイミングで変数の初期化やレポートの出力といった処理を行なわせる事ができます。 ただエラーによって終了する場合は"probe end"は呼ばれないため、"probe error"を使う必要があります。

どのようなプローブ(probe points)が存在するのかは、stapprobes manual pageを参照してください。

ユーザーが記述しなければいけないプログラム本体の流れは、1.データの取得、2.若干の加工、3.出力、になると思います。

"データの取得"について、tid()、execname()等いくつかはチュートリアルに書かれていますが、全体はstapfuncs manual pageにまとめられています。

"データの加工"についての具体的な方法は、あまりチュートリアルには書かれていません。 C言語やAWKで一般的な範囲の四則演算やビット演算、条件式は準備されていて、詳細はリファレンスマニュアルにまとめられています。

"出力"については、とりあえずC言語の"printf"関数と似た構文を提供しています。 Ruby等では"%b"というと数値を2進数表記で表示してくれますが、systemtapではバイト列をそのまま表示してくれます。

"%1b"で1バイト分、"%2b"で2バイト分のバイナリを出力する事ができます。 使えるとは思えないけれど、”あ”をUTF8で表示しようと思ったら、次のようなコードになります。

probe begin {
  i = 8552931
  printf("%4b\n", i)
  exit()
}

Analysis

おそらく斜め読みをして、if,while,forなどのループ、式はC言語と似ているなぁというぐらいが分かると思います。

systemtapがawkを強く意識しているだけに、Perl同様に文字列の連結は"."を使って行なう点は少し注意が必要そうです。

あとは使う変数は基本的に全てスコープローカルになるので、probeやfunctionを跨って参照したい変数は、"global"によって宣言する必要があります。 さらに"global"宣言された変数は自動的にread, writeロックがかかるため、不用意に使う事は避けたいところです。

Analysis::Target variables

probeの中でだけ、特殊な変数"Target variables"が参照できます。 これはprobeを仕掛けてた個所のコンテキストにあるカーネル内部の変数にアクセスができるという機能です。 リファレンスマニュアルを見る限りでは、そのコンテキストにあるポインタ(*file)に"$file"のようにアクセスができるようになっています。ローカル変数へのアクセスは難しそうですね。

ここでは"target variables"ではない、"$"を使ってコマンドラインの引数にアクセスする方法が書かれています。 数値の場合は"$1"ですが、文字列の場合は"@1"のように"@"を使って書く必要があります。

// print_name_age.stp
probe begin {
  printf("name: %s, age: %d\n", @1, $2)
  exit()
}
$ sudo stap print_name_age.stp hoge 28
name: hoge, age: 28

"$<pointer>"と"$<数値>"は似ていても、"target variables"は"$<pointer>"だけなので、注意が必要です。

Analysis::Functions

functionについて、ここまで明記されていませんでしたが、probeの中に書く処理をまとめて記述するために使います。

ただし、前記の"Target variables"へのアクセスはできないため、functionの引数に指定するなどして渡す必要があります。functionの引数は値渡し(pass by value)なので必要に応じて結果はfunctionの戻り値として受け取ります。

Analysis::Arrays

次がarrayについてで、連想配列が使えますが、必ずglobalで宣言する必要があり、サイズはコンパイル時に決定されている必要があります。 宣言時に配列のサイズを省略すると、MAXMAPENTRIESのデフォルト値、2048で配列が確保されます。

arrayについての仕様でおもしろそうなところは、foo[4,"hello"]のようにindexに数値や文字列の組み合せが使えるというところでしょうか。 使ってしまえば、特にどうという事はないんですけどね、実用的な仕様だと思います。

わかりずらいのは"foreach ([a,b] in foo)"のところでしょうか。 説明は"simple loop in arbitrary sequence"とありますが、事前にfooがfoo[4,"helloworld"] = barのように宣言されている必要があります。

例1:ちゃんと動くシンプルな例

#!/usr/bin/stap
global ary
function print_ary() {
  foreach (k in ary) {
    printf("ary[%d] => %d\n",k, ary[k])
  }
}
probe begin {
  ary[0] = 3
  ary[1] = 5
  ary[2] = 1
  print_ary()
  exit()
}

例2:チュートリアルと似ていて、なおかつ"l+"と書く事でindexの第2要素を昇順(小さい→大きい)にソートしている。

#!/usr/bin/stap
global ary
function print_ary() {
  foreach ([k,l+] in ary) {
    printf("a[%d,%s] => %d\n", k, l, ary[k,l])
  }
}
probe begin {
  ary[0,"a"] = 3
  ary[1,"d"] = 5
  ary[2,"b"] = 1
  print_ary()
  exit()
}

上記のforeach()にある"k","l","ary"の各変数に"+"や"-"を追加する事で昇順や降順にソートさせる事ができます。具体的に動かして観察するのが良いでしょう。小さい工夫ですが、収集するデータによっては便利だと思います。

Analysis::Aggregates

データ収集用の特殊なデータ型です。 特殊にしたおまけにデータの解析、収集に便利そうな機能が付いてきます。 簡単にまとめると次のようになります。

  • データを格納する変数はarray型を使うため、おのずとglobalで宣言する必要がある
  • データを格納する時に"<<<"オペレータを使う
  • データへのアクセスは"@avg()"など'@'で始まる特殊な関数を使う

リファレンスをみても使える特殊関数は"@count", "@sum", "@min", "@max", "@avg", "@hist_linear", "@hist_log"だけのようです。

Analysis::Safety

いろいろな上限値などに触れられていますが、システムのデフォルト値はstap manual pageの"Safety and Security"の項目に書かれています。

Tapsets

"tap" + "sets"ぐらいの意味だと思いますが、/usr/share/systemtap/tapsetにインストールされているライブラリスクリプトについての解説です。

ユーザーが作成したスクリプトの中に未定義のprobeやfunctionがあると、/usr/share/systemtap/tapset/*.stpフィアルを順番に調べていき、対応する名前をみつけると自動的に読み込みます。 ただし、該当するprobeやfunctionがみつかったファイル全体の内容を読み込みます。

また複数のprobeを組み合せて一つのprobe名にする、aliasの機能があります。 応用すると、前処理をさせるといった目的に使う事もできます。

あとは*.stpファイルの中にC言語のコードを埋め込めるという"Embedded C"の機能でしょうか。 具体的な例はtapsetディレクトリを除く方が確実そうです。他に”embedded C"を使う際の注意点が列挙されています。

便利そうですが、あまりにも怖いのでVMWare上でテスト環境を作って試そうと思います。

Tapsets::Naming conventions

前述のとおりsystemtapはtapsetにあるファイルを全て確認して、必要に応じてファイル全体を取り込みます。 この仕様のため大勢がバラバラに開発したライブラリスクリプトで共通のglobal宣言した変数を使うなどした場合には、名前のバッティングが発生する可能性があります。

そこでガイドラインが掲載されていますが、常識的な個所を除くとだいたい次のような感じです。

  • 作成したtapset固有の名前を付ける (以下、TAPSET、とします)
  • function名など外部からアクセス可能なところは、"TAPSET_"で始まる名前をつける
  • global変数など内部的にアクセスする変数などには、"__TAPSET_"で始まる名前をつける

さいごに

結局はリファレンスに当たらないといけない事ばかりでしたが、systemtapは1.0もでてようやく安定するのでしょうか。

バージョン間の違いには詳しくありませんが、systemtap-1.0/NEWSファイルをみるとUbuntu 8.04 LTSのパッケージとして提供されている0.6.0から多くの機能が加えられ、変更されていることがわかります。

ずっと昔に大学の演習でSunOS 4のパフォーマンスアナライザーを使った時は、C言語用のライブラリだけが提供されていてsystemtapが裏でやっているような事を手作業でやっていたように記憶しています。 教育目的としても手軽に使えそうですね。

2009/11/24

systemtapの情報源

Linux版DTraceともいえるsystemtapのチュートリアルは2007年に作成された日付がありますが、一通り読んでみて入門としてはとても良くできているという印象を受けました。

次に関連する情報はないかなぁとGoogleで調べていたのですが、 そこでひっかかったのが、@ITで連載されていた”Linuxトラブルシューティング”シリーズの記事、「最終回 SystemTapで真犯人を捕まえろ!」でした。

systemtapに限らず全体的に内容が高度に見えて、でも切り口が日常的にどこかで発生していそうな事象を扱っている点が秀逸だと思います。 時間みつけて@ITの記事は抑えておいた方がいいかなぁ。

メモリ周りの思い出

昔の仕事でJavaを使っているときは、そのJavaの世界だけで起こる奇妙な現象には悩まされました。 JVM自体に問題があればsystemtapなんかでも分かるんでしょうけれど、Javaの世界で完結している場合にはGCのログファイルと解析ツールが必須になります。

最近ニコニコ動画でWPFを使ってGCの様子を可視化している動画をみましたが、”Javaの場合”、

  • まだ隙間があるのにいきなりコンパクション走るのか
  • コンパクションはもっと重い処理として表現して欲しい

などと思ったのは私だけでしょうか。

Javaの場合はトータルの空きメモリは10MBあっても、1MB分の配列を宣言するとOutOfMemoryExceptionが発生する場合という事もあり得ます。環境によっては100MBの空き領域でも厳しいかも。 JVMが使うメモリ内部の連続空き領域を考慮しないと、サーバーサイドのJavaプログラミングはできません。

Javaは原理的にはC++のようなdelete()を忘れるタイプのメモリリークは発生しませんが、それでもオブジェクトへの参照が残っていればGCの対象にはなりません。 そもそもコード中のオブジェクト参照を完全にコントロールできれば、C++でもdelete()を忘れるという事は起らないでしょう。

そんなわけでオブジェクトの管理ができていない事で全体のメモリ使用率が上がって、Compaction Phaseで動かせないオブジェクトが残る事とあわさってOutofMemoryExceptionが起ります。 世代別GCも採用が進んで問題は起りにくくなっていますが、Compactionは完璧ではないのにGCに頼ってできる事をしない開発者が増えないか心配です。

GCは開発者をサポートしますが、怠慢をフォローすることはできないでしょう。

ディスクの使用率がなぜか下がらないとか

ほかによくあったのはディスク使用率の警告がでて、サイズの大きなログファイルを削除したのに警告が収まらないというものでした。

ファイルを削除しても、それ以前にopen()されたファイルディスクリプタをclose()しなければいけないのに、ログを吐き続けているプログラムが一旦起動したら停止するまでclose()しない仕様だったりすると最悪です。 Javaなんかだと裏側のライブラリがそういう設計だったりして、必ずしも開発者の単純な落ち度ということでもないのですが、いろいろみていると全体の環境を把握して維持する事の難しさを実感します。

より深刻な問題なのはそういう説明をしても理解できないアプリケーション開発者でしょうか。 プログラミングでJavaだけを教えるのも悪い事ではいのですが、せめてJVMの設計と実装についての講義も必修にして欲しいです。

あれ、systemtapのドキュメントを読んでいたはずなのにJavaの話しになってる…。

2009/11/23

再起動せずにUbuntu 8.04 LTSでsystemtapを使う

systemtapは実行中のkernel内部の情報を取るためのユーティリティです。 RHELでの使い方はSystemTapによるライブカーネルプローブにさらっと載っています。

UbuntuというかDebianでは、debuginfoを含んだ"linux-image-debug-$(uname -r)"パッケージの中にはモジュールについてのデバッグ情報は含まれていません。 そのせいか解説しているWebサイトでは、いきなり"make menuconfig"を実行してカーネルのリコンパイルから説明しているものがあります。

カーネルが実行しているシステムコールだけを知りたかったので、debパッケージをそのまま使うことにしました。

systemtapを導入してもhelloworld.stp以外が動かない

導入するパッケージは2つです。

$ sudo apt-get install linux-image-debug-$(uname -r) linux-headers-$(uname -r)
$ sudo apt-get install systemtap

"$(uname -r)"は”uname -r"部分を実行した結果の文字列"2.6.24-25-generic"に置き換えられて、いま実行しているカーネルのバージョンに応じたデバッグイメージを取り込みます。 もしカーネルが更新されても常にデバッグイメージを最新にしたい場合には"linux-image-debug-generic"等を指定すると良いでしょう。

この状態で"/usr/share/doc/systemtap/examples"以下にあるプログラムを実行してみても、helloworld.stpを除くと実行に失敗するものがあります。

$ sudo stap /usr/share/doc/systemtap/examples/iotime.stp
semantic error: libdwfl failure (missing kernel debuginfo): No such file or directory while resolving probe point kernel.function("sys_open")?

何が悪いのか調べるために"strace"コマンドを使いました。

$ sudo strace stap /usr/share/doc/systemtap/examples/iotime.stp
...
open("/boot/.debug/vmlinux-2.6.24-25-server", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/debug/boot/vmlinux-2.6.24-25-server", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/boot/vmlinux-2.6.24-25-server.debug", O_RDONLY) = -1 ENOENT (No such file or directory)
...

いろいろな場所にあるデバッグイメージを探しに行きますが、見つけられないようです。 導入したdebパッケージ名から、どこにイメージがインストールされているか確認します。

$ dpkg -L linux-image-debug-2.6.24-25-server
/.
/boot
/boot/vmlinux-debug-2.6.24-25-server
...

微妙に名前が違うんですよね。まぁ安易にデバッグ情報にアクセスできるのも考えものなのかなぁ。 どこに置くべきか迷ったのですが、/boot/.debugを作成してリンクを張る事にしました。

$ sudo mkdir /boot/.debug
$ cd /boot/.debug
$ sudo ln -s ../vmlinux-debug-2.6.24-25-server vmlinux-2.6.24-25-server

これでsystemtapが使えるようになりました。

2009/11/21

Ubuntu 9.04 64bit版にCUDA 2.3 SDKを入れてみた

CUDA 2.3がUbuntu 8.04 LTS向けにはリリースされていないので、サブマシンに9.04 Desktop amd64版を新規にインストールしました。

CUDAはNVIDIAのサイトから入れてみましたが、いろいろ2.2 SDKとは構成が違っていて戸惑っています。 OpenCLとディレクトリ構成が同じになったようですね…。

"~/NVIDIA_GPU_Computing_SDK/C/"ディレクトリでmakeが成功するまでには、追加でつぎのようにパッケージの追加が必要でした。

  • $ sudo apt-get install g++
  • $ sudo apt-get install mesa-common-dev libglu1-mesa-dev libxi6-dev libxmu-dev libglut3-dev

これとは別に"openssh-server"パッケージを入れて、リモートから作業できるようにしています。

CUDA 2.2 Toolkitとの見た目の違い

ライブラリが32bit版と64bit版の両方が添付されているので、古いプログラムで/usr/local/cuda/libを"-L"オプションに指定している場合、32bitのライブラリがサーチパスに入ってるので、64bitのオブジェクトコードとリンクできずにエラーになります。おそらくcommon.mkを修正すれば良いと思われます。

この他にも古いCUDAプログラムをダウンロードしてきた場合、そこに含まれる"lib/libcutil.a"のコンパイルに失敗するといった事例が出ています。[ 参考URL::CUDPP 1.1 compile errors (and fixes) (gcc 4.3.3-5ubuntu4) ] いくつかファイルを修正する他に、CUDA 2.3 SDKをコンパイルしていれば、そこに含まれるlibcutil.aなどのライブラリファイルをlibディレクトリにコピーしてくるだけでも解決できます。

この変更のため環境変数LD_LIBRARY_PATHか"/etc/ld.so.conf.d/"以下のファイルで、"/usr/local/cuda/lib"を指定していれば"/usr/local/cuda/lib64"に変更する必要もあります。

2009/11/20

PowerShell 1.0でアプリの一覧を取ってみたものの

東京の寒さを報せるニュースをみていたら、街を歩く人達が自分よりも暖かそうな格好をしている事に気がつきました。うーん、油断してたかなぁ。でもまだここら辺は雪は降ってないし、大丈夫じゃないかなぁと思っています。

たまたまokwaveでWindowsに登録されているアプリケーションの一覧を取る方法について質問されているのをみました。 手元のWindowsXP Home SP3でpowershellを使う方法を試してみたところ、エラーになってしまいました。

PS C:\Documents and Settings\Owner> gwmi win32_product


IdentifyingNumber : {15DA9946-FB08-3742-A385-9F8AAA863D71}
Name              : Microsoft Visual F# Runtime 1.0
Vendor            : Microsoft Corporation
Version           : 10.0.21006
Caption           : Microsoft Visual F# Runtime 1.0

Get-WmiObject : エラーです
発生場所 行:1 文字:5
+ gwmi  <<<< win32_product

"gwmi"は"Get-WmiObject"のエイリアスです。 調べてみたところバグと認識されているものと挙動は同じようです。 [ 荒井省三のBlog::[PS]WMIの取り扱いについて ]

まだBeta版のVisualStudio2010を入れているので、まさかその関連でWMI周りも変ってしまったのかと思い、VMWareのスナップショットをVisualStudioを入れる前に戻して確認してみました。 結果は無事に動きましたが、関連についてはいまいちはっきりしません。

C:\Windows\System32\直下の最近変更されたdllを眺めてみても、wmi.dllとかは変更されていないし、VisualStudio2010が変な情報をレジストリに登録してしまったのかなぁ。そんなことあるんだろうか。

ソフトウェアのインベントリ(台帳)管理のためには、一覧の取得は重要な機能だと思うのですが、レジストリをみなきゃ確実じゃないとしたら、ちょっとインタフェースがいけていないなと思います。

まぁインベントリ管理ソフトウェアを使えという話しになるかもしれませんが、使った事がある人なら、情報整理のための追加で必要な膨大なマンパワーと価格を考えて発狂しそうになったことでしょう。

PowerShell 2.0を試してみる

2.0自体はマイクロソフトのサイトからダウンロード可能です。その際に1.0をアンインストールするように言われますが、プログラムの追加と削除で”更新プログラムの表示”オプションにチェックを入れないと一覧に表示されず、アンインストールできません。更新プログラムの一部なんですね…。

導入が終ると”すべてのプログラム”の"Accesories"フォルダの中にPowerShellのプログラムが導入されていました。昔どこかで試したIntegrated Scripting Environment (ISE)が入っています。

で、早速win32_productにアクセスして試してみると、似たようなエラーが起ります。

VisualStudio 2010 Ultimate beta2をアンインストールしてみる

beta2を入れる前にbeta1を削除する手順はあるのですが、beta2自身をアンインストールする手順はUltimate beta2のリリースノートの中ぐらいしか見当りませんでした。

正しい順番でアンインストールする必要があるならサポートプログラムが欲しいところです。 まだbetaだからしょうがないけど、製品版がでても同じなんだろうなぁ…。 まっさらな環境にbeta2をインストールしている場合のアンインストール順番は以下の通り。

  1. Uninstall all instances of Visual Studio 2010 products (for example, Visual Studio 2010 Ultimate).
  2. Web Deployment Tool (Web配置ツール)
  3. Silverlight 3 SDK (Microsoft Silverlight 3 SDK - 日本語)
  4. SQL Server 2008 Management Objects (SQL Server 2008 R2 Management Objects)
  5. SQL Server CLR Types (SQL Server System CLR Types)
  6. SQL Server 2008 (Microsoft SQL Server 2008)
  7. SQL Server 2008 Native Client (Microsoft SQL Server 2008 Native Client)
  8. SQL Server Compact 3.5 SP2 (Microsoft SQL Server Compact 3.5 SP2 Beta 日本語版)
  9. Visual Studio 2010 Tools for Office Runtime Beta 2 (Visual Studio 2010 Tools for Office Runtime Beta 2 (x86) 及び 同 Language Pack)
  10. The .NET Framework version 4 Language Pack (未導入)
  11. The .NET Framework version 4 Extended (reboot, if prompted) (Microsoft .Net Framework 4 Extended Beta 2)
  12. The .NET Framework version 4 Client (reboot, if prompted) (Microsoft .Net Framework 4 Client Beta 2)

"プログラムの変更と削除”の一覧に表示される時には、日本語の名前で登録されていたり、リストにある名前の通りではなく"Microsoft"が前に付いたりします。 ”SQL Server CLR Types”などは逆に"Microsoft"が前に付かないので、見過してしまいそうです。

また一連の操作の中で"Microsoft SQL Server 2008"を削除する時には、Windowsの再起動処理で失敗マークが付いてしまい、一旦再起動した後でなければアンインストールできませんでした。

明記されていませんが、Visual Studio 2010 Tools for Office Runtime Beta2 (x86)を削除する時には、同 Language Packも削除するべきでしょう。

”Microsoft .NET Framework 4 Beta 2 日本語 Language Pack”は導入されていませんでした。 "Microsoft .Net Framework 4 Extended"と"同 Client"を削除すると、対応するLanguage Packは自動的に削除されました。ここの挙動はOffice Runtimeと違いますね。

まぁBeta版ですからね。いいんですけれど、やっぱり面倒だなぁ。

途中から"gwmi win32_product"が動く

"Web Deployment Tool" (Web配置ツール)を削除して、Silverlight 3 SDKのアンインストールを始めてから、無事にアプリケーションの一覧が表示されるようになりました。

でも全部アンインストールしてから手動でSilverlight 3 SDKとWeb Deployment Toolをインストールしても現象は再現しませんでした。

PowerShellは良いんですけどね、この挙動だと仕事でインベントリ管理にはちょっと使えないかなぁ。

2009/11/15

財団法人が発行しているレポートをチェックしてみた

最近は政府の予算削減のために「仕分け」が行なわれている様子が中継されています。

10億の補助金を配るのに従業員8人の法人が5億の経費をかけているとかいわれて、監督官庁は雑多で定型的な作業なので法人に任せるのが適当とか受け答えしていたり、どこまでがネタかよく分からないですね。

そうはいっても独自に事業を行なっている法人もあるだろうし、どんな仕事しているのかなというわけで今回は「財団法人ソフトウェア情報センター」の平成20年度 調査研究報告書: ソフトウェアの適正取引に関する調査研究報告書 を読んでみました。

確かGoogleで「IT, 運用 ガイドライン」ぐらいのキーワードで探した中に含まれていたと思うのですが、いろいろおもしろかったです。

調査報告書なのに対象読者がいまいち不明

まとめた方々が法律家のようで、想定している読者が経営者なのかSIerのようなシステムを売るベンダなのか不明です。

なにか契約時に注意するべきチェックリストなどといった便利なものはなく、 雰囲気は大学3年生ぐらいが夏休みにまとめたレポートといったところでしょうか。

調査と報告はされていますが、とりたてて注目するべき研究成果はないようです。 まぁこの手の文書は発行する事が目的で注目される事を想定していないでしょうから、 それだけに読むとツッコみどころが満載です。

結論を知らないで読むとがっかりしそう

事実が羅列されていますが、それを踏まえて”ベンダ or ユーザは〜するべき”といった事は、ほとんど書かれていません。少しだけ”免責事項に入れておくと良い”といった記述がありますが、それは責任を回避する事しかできないので、より良いものを作るという根本的な問題解決にはなっていません。

後半にまとめられている結論は「ベンダの説明不足とユーザの勉強不足」という事になっています。それをどうすれば良いかは書かれていないと。

ちょっと編集してチェックリスト的なものにすれば実用的なのですけどね。 まぁ読者の設定もよく分からないし、ベンダが問題を回避するためだけの資料に使われそうです。

技術者が書いたものではないためか、より良いものをいかに作るのか、という話しではないです。

保守サポートは何年間提供するべきか

そもそも保守サポートを提供するべき年数についても、”いろいろ考えて合理的に決めてね”といった事が書かれているだけで、参考にするべきサンプルも事例も紹介されていなかったり、スルー力が大いに鍛えられる構成になっています。

そもそもこの前段は保守部品(HW)を提供しなかった業者に対する判例を紹介していて、後段のソフトウェアの保守性とは整合性が取れていないと思います。

たぶん読んでおいた方がよいページ

まぁ、この報告書は啓蒙以上の効果はなさそうですが、ちょっとgoogleで検索してみるとソフトウェアの契約に関連するトラブルとしては次のような内容をおさえておくのが良いと思います。

発注者側が不利益を被らないために何をするべきか

ソフトウェアほど幅のある物もないわけで、発注する側はどういった効率改善を期待するのか、 どう使うのか明確にイメージした上で発注する必要があります。それが難しいんですけどね。

コンピュータに詳しくない発注者側が面倒になって「とにかく作ってくれ」みたいな流れになると、最終的には全員が泣くことになります。 面倒でも理解できるまでは議論する必要もありますし、業界の常識をベンダに説明する必要もあります。

残念ながらシステム開発についてはベンダ側の実力によって、同じ人手をかけても出来上がってくるものには、とても使えるものから、ギリギリ使えないものまで、かなりの幅があります。

また残念ながらベンダは人手をかけずに最低限の使い勝手で納入する事ができれば利益を最大にできるインセンティブを持つので、信用できないと思えば発注しない事も大切でしょう。 まともなベンダはその事を知っていて顧客の信頼に答える事は何よりも大切だと理解しているはずです。

まともなシステム開発をしたいなら、顧客のパフォーマンスが最大限になるような業務フローの変更といった提案込みで話しをするべきだとも言えます。

発注側は自社できないから外注するのでしょうけれど、少なくとも自社のビジネスとシステムに詳しい人物を確保してコーディネータとして活躍してもらう事がこれからは増えてくるのだと思います。

発注する側はアウトソーシングと思っているかもしれませんが、 勘違いして自社の業務を理解している技術者が不在だとコーディネータになるべき人材がなくて悲惨な事になるでしょうね。

何が必要かって難しいですけれど、 ベンダは仕事をしない事で利益が最大になるインセンティブを持っている事を理解して、 発注者側が求めるものをしっかりイメージして、それを要件として文書できっちり出す事が重要だと理解する事でしょうね。

2009/11/10

Phenom II 940の電圧を少し下げてみたらハングアップ

折角Black Editionを使っているので、Phenom II 940の電圧を少し下げて電力消費が削減できないかなと思いました。

BIOSのメニューからCPU Voltageを1.35Vから0.1Vちょっと落してみたのですが、3.0GHzのままだといくつか不具合が発生しました。

  1. mplayerやvlcで動画を再生すると正しく描画されない
  2. BOINCで計算エラーが発生する
  3. デスクトップ上のアイコンにノイズが乗り、動画同様正確に描画されない
  4. 最終的には動画再生中にシステムがpingも通らなくなりハングアップ

症状と対策

対策は簡単で、元に戻すのも嫌だったので、電圧の低下と同じ比率でクロックを下げて2.8GHzにしています。

症状の方は深刻で、最初はCPU周りの変更で発生したとは考えられませんでした。 いろいろ変更していたのが災いしました。

普通にX11を使っている分にはgnome-terminalやFirefoxはちゃんと動いていたのに、最初は動画だけがうまく描画できないと思ったんですよね。それでも1、2秒間は普通にみえるので、最終的にシステムがハングアップするまで気がつきませんでした。

BOINCの計算結果がエラーになっていたのも気にはなりつつ原因はわからず…。 全てはクロックと電圧を同じ比率で下げて解決しました。

まとめ

オーバークロックしていると動画のエンコーディングに失敗するとブログ等に書かれています。 しかし画面描画の一部だけが悪くなると、ビデオカードやドライバを疑ってしまいそうです。

たぶん動画再生や計算でCPUのコアが一生懸命働いた時にだけ、計算結果がおかしくなってしまったのでしょう。

これはこれで良い経験なのかな…

2009/11/08

Ubuntu 8.04のディスクパフォーマンスは改善されたのか?

5月頃は4台の160GB HDDをRAID-10にしてみたり、RAID-1x2にしてみたりしていたのですが、6月頃にマザーボードとCPUを変更(Phenom II 940 & M4N78 Pro)して、さらにHDDまで500GBx2 Software RAID-1に変更しました。 その時にUbuntu 8.04.3をインストールして、いままで使ってきました。

数ヶ月使った体感としてディスクパフォーマンスが上ったように感じていたのですが、pdumpfsで取っている古い/etcのバックアップを比較した時に/etc/fstabに設定されている"relatime"オプションが以前は設定されていない事に気がつきました。

「おお、これが効いたのか?」と思ってみたものの、設定を外して再起動して、いろいろコンパイルしてみたり、写真をブラウズしてみたりしてみたものの、そんなに違いはないなぁというところに落ち着きました。

交換したHDDのパフォーマンスが良かったっていう事なんだと思います。 atimeを無用に更新する必要はないので、relatimeの設定は戻してそのまま使っています。 "noatime"を設定しても不都合はないけれど、statコマンドはたまに使うし少し過激な設定だと考えて設定していません。

いろいろチューニング

これをきっかけに少しチューニングしてみようか、という事でext3に"journal_data_writeback"オプションを設定してみました。これはジャーナリングをメタデータのみにする設定で、データ領域のジャーナルを省略する事でパフォーマンスを上げようという試みです。

新しいカーネルではext3のデフォルトがwritebackになるようですが、これはext4への移行に備えての準備という側面が強そうですね。パフォーマンスにマイナスになる理由はないのですが、それほどでもないという事でしょうか。

試しに使うにしても'/'に設定するのは少し面倒で、Ubuntuのフォーラムで手順が掲載されているのでそれに従うのが良いでしょう。 menu.lstとfstabに設定を書けば、tune2fsでマウントオプションを設定する必要はないと思うのですが、どれをみても設定する事になっていますね…。なぞだ…。

他のマシンにHDDを持っていってもwritebackモードでmountする必要性はないだろうしなぁ。fstabとtune2fsを使わないと有効にならないとか理由があるのかなぁ。

ちなみに、"mount -o remount,data=writeback"みたいな設定をしようとすると途中からジャーナルの方法は変えられないとエラーになります。'/'の設定を変更する度に再起動が必要になるのは少し面倒かな。

data=writebackの感想

使ってみて速いかというと、気持ち速くなったかもしれない、というのが感想です。 tar-ballの展開なんかも気持ち良く終るけれど、以前と比べてどうかというと、違いがあるかは微妙です。

全体的には改善されているかはともかく快適なのは間違いないですね。 以前ならたまにひっかかるような感じがした写真のサムネイルの更新処理なんかは、いまのところ何ともありません。これはしばらく使ってみないとなんともいえないでしょうね。

仕事用のマシンなら絶対に設定しないけれど、自分用のマシンならこれでも良いかなぁ。

さて、次はCPUのクロックでもいじってみることにしましょう。

2009/11/01

OpenLDAP: access権限の管理

olcAccessディレクティブの管理

slapd.confに代ってslapd.dを使っていると、Access権限(olcAccess)の管理が少しだけ面倒に思えてきます。 それはslapd.confのようにテキストエディタで古いものを削って、新しく付け足すわけではないから。

例えば最上位DNからの一括検索を拒否しつつ、パスワード情報は認証時以外には開示しない設定だとします。 slapd.dディレクトリの中で、アクセス権限は次のように管理されています。

olcAccess: {0}to dn.base="dc=example,dc=org" by * none
olcAccess: {1}to attrs="userPassword" by anonymous auth by * none
olcAccess: {2}to dn.children="dc=example,dc=org" by self read

この状態で下記のldifファイル(update.ldif)に対してldapmodifyを実行したとします。

dn: olcDatabase={1}bdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: to dn.base="dc=example,dc=org" by * read
$ ldapmodify -x -W -D cn=admin,cn=config -f update.ldif

適当なところが一行置き換わるのかなと思いきや、3行だったolcAccessが1行に変更されています。

olcAccess: {0}to dn.base="dc=example,dc=org" by * read

不思議なことは何もないのですが、管理をする上では挙動をチェックして把握しておくのが重要だったりします。

管理用に何かWebベースやGUIなソフトウェアを使うかもしれません。 cn=config以下ならデータサイズが限られているので良いのですが、 データ件数が多いエントリをGUIなどのツールで開く時には、hard limitsに逹っするまでのサーバー側の負荷はもちろんですが、多くのエントリ情報を格納するツール側もそれなりのメモリを消費するためハングアップに気をつける必要があります。

台帳的な管理の手法

既に定義されているolcAccessの間に定義を挿入するというのは、少し面倒そうなので、簡単なシェルスクリプトを作成しました。

ツールは主に2つのパートから成ります。

  1. show_olcAccess.sh: 定義済みolcAccessエントリの確認ツール
  2. gen_access_ldif.sh: 一括登録、削除、更新用のldif生成ツール

どういう風に使えば便利そうかを想定すると…

$ ./show_olcAccess.sh > current.ldif ## olcAccessの行だけをcurrent.ldifに書き出す
$ vi current.ldif ## 追加したいolcAccess行を追加する
$ ./gen_access_ldif.sh < current.ldif | ldapmodify -x -W -D cn=admin,cn=config
## 最後は一気にldif形式のフォーマットを出力してldapmodifyに流し込む

show_olcAccess.sh: 定義済みolcAccessエントリの確認ツール

とりあえずは、"olcDatabase={1}bdb,cn=config"を決め打ちにしてolcAccessの各行を出力するようにします。

#!/bin/bash
ldapsearch -LLL -x -s base -W -D "cn=admin,cn=config" -b "olcDatabase={1}bdb,cn=config" olcAccess | while read line
do
  if /bin/echo -E "$line"|egrep ^olcAccess >/dev/null 2>&1 ; then
    l="$(/bin/echo -E "$line" | sed 's/{[0-9]*}//')"
    echo -E "$l"
  fi
done

これを実行すると次のような表示になります。

$ ./show_olcAccess.sh
olcAccess: to dn.base="dc=example,dc=org" by * none
olcAccess: to attrs="userPassword" by anonymous auth by * none
olcAccess: to dn.children="ou=accounts,dc=example,dc=org" by users read

わざわざ"olcAccess: {0}to"にある"{0}"の部分を取り去っているのは、後から編集する時に邪魔になるからです。 みるだけなら良いのですが、olcAccessを間に1行追加するのに、数字を全部後ろにずらしていくのは不毛でしょう。

gen_access_ldif.sh: 一括登録、削除、更新用のldif生成ツール

次はさきほど1行追加するのに使ったような"changetype: modify"を含むldapmodify用のLDIFファイルを生成します。

#!/bin/bash
db=${1:-"{1}bdb"}
opt="${2:-replace}"
## show header
echo "dn: olcDatabase=$db,cn=config"
echo "changetype: modify"
echo "$opt: olcAccess"
while read line
do
  echo -E "$line"
done

これを実行すると、LDIFファイルを出力します。 入力は”show_olcAccess.sh”で出力されたようなものです。

./gen_access_ldif.sh < access.ldif
dn: olcDatabase={1}bdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: to dn.base="dc=example,dc=org" by * none
olcAccess: to attrs="userPassword" by anonymous auth by self read by * none
olcAccess: to dn.children="ou=accounts,dc=example,dc=org" by users read

もし既存の設定を消したければ、./gen_access_ldif.sh "" delete < access.ldif でできます。 複数のDBがあれば、第一引数に./gen_access_ldif.sh {2}hdbのように指定する事もできます。 次は、olcSuffixとolcDatabaseとの関連を一覧にできれば便利かなぁ。

まとめ

こういうスクリプトって本質的なものじゃないんですけれど、テンプレート+αな操作の場合は準備しておくと、 誰かに頼むとか、手順で置いておかなきゃいけない時に、作業が簡潔になり早くできたりして便利です。

ldapmodifyを実行する部分もスクリプトにしても良いですが、そこは本質的なところではないので。 また、これ以上作り込んでしまうと、トラブルシュートができない。

スクリプト化は勉強にならない、という批判もありそうですが、本物のマニュアル手順を"plan B"として載せておけば良いでしょう。

スクリプトの中で特殊な事はしていませんが、"$1"が未定義の場合に"{1}bdb"をセットするところはダブルクォートなしに書くと、そのまま展開されてしまいます。 ${1:-{1\}bdb}でも良さそうだけれど、ここら辺のドキュメントって見た事ないなぁ。

2009/10/31

Windows7はスルー

日経なんかでも散々Windows7の特集組んだりしてましたが、Inspiron 640mに入っているVista BuisinessをWindows7に移行しない事にしました。

Intelの945GMチップセットを積んでいるので64bit版にしても認識できるメモリ量が増える分けではない、そもそも使いたいアプリ&機能がない、というのが主な理由です。

しかし一番大きな理由は限定出荷されているアップデート版を買うよりも、DSP版の方が安いというところでしょう。もちろんカスタムインストールになるから移行が大変ですけどね。64bit版にするなら同じことですし。

Windows7はRC版を使ってみて、まぁ見た目に違うのとUAC周りで挙動の違いはあるのですが、バックアップ機能とか、壊れたNTFSのチェックディスク機能とか、Vistaと価格差を埋め合わせるほどのメリットはないと感じています。

XPを使っていてハードウェアの性能が十分なら、Windows7にするのはおすすめなんですけどね。 しかしXPのバックアップ周りとかNTFSのチェックディスクの機能なんかは貧弱ですが、 日常的に使う分には何も不具合ないのが困りどころだとは思います。

まぁ新しいもの好きにしても、OS自体はすぐに飽きそうな気がします。 それよりもXPやVistaでIronPythonやPowerShellで遊んでいた方が楽しいかなぁ。

仕事でクライアント環境を管理するようになればVMWare用にパッケージ版を買うかもしれないですけどね。

2009/10/29

LPIC Level3 301 Coreの試験を終えて

無事に試験を終えましたが、途中退室したり体調を崩してしまいました。 試験と名のつくものは苦手ですが、今回もみごとにやられました。 まだ胃の辺りがピクピクしています。

さて、結果はギリギリ合格でしたが、試験前の同意書には試験の具体的な内容について議論しない旨の一筆があり、試験問題について直接的なこと、どんな問題がでた、とかは言えない事になっています。

試験の印象

今回はEnglishのボタンを押す回数は少なくて、2,3回ぐらいでした。 理解できなくはないけれど、直訳っぽくて念のために見たというぐらい。
あとは問題文の意図を理解したり選択肢を選ぶために、実践経験の有無が明暗を分けると思います。

試験についてはこれぐらい。 実戦経験の豊富な人はうかるし、問題集の解説を暗記したような人はうからない。そんな試験でした。

ギリギリだったから偉そうな事はなにも言えない。 いったい自分は受かるべくして受かったのだろうか、いやそうじゃないよなぁ…。