Macのバックアップ機能Time Machineは、直接USBで接続したHDDを使うのは簡単ですが、NASのようにネットワーク越しのドライブを使用するには小細工が必要なことで有名です。
本エントリでは、ネットワーク越しにFreeBSDなファイルサーバーをiSCSIでマウントできるようにして、これをTime Machineに使ってみます。
- FreeBSD 8.2-RELEASE
- istgt-20110223
Time Machine用にzfs領域(なんて言うんだっけ?)を確保しておきます。
## __GooglePrettify__ # zfs create -o mountpoint=/timemachine tank3/timemachine
ここで、zfsの設定からshareiscsi=onを設定してみても、FreeBSDではサポートされていなくて残念な思いをします。 なので、portsから以下をインストールします。
## __GooglePrettify__ # cd /usr/local/etc/istgt # cp auth.conf.sample auth.conf # cp istgt.conf.sample istgt.conf
istgt.conf を適宜修正。以下では、ファイルサーバーは10.0.1.1, マウントするマックは10.0.4.1です。
## __GooglePrettify__ --- istgt.conf.sample 2011-04-01 02:04:14.638057161 +0900 +++ istgt.conf 2011-04-02 17:14:28.031064416 +0900 @@ -17,7 +17,7 @@ [Global] Comment "Global section" # node name (not include optional part) - NodeBase "iqn.2007-09.jp.ne.peach.istgt" + NodeBase "iqn.2007-09.net.f99aq8ove.istgt" # files PidFile /var/run/istgt.pid @@ -83,7 +83,7 @@ Comment "SINGLE PORT TEST" # Portal Label(not used) IP(IPv6 or IPv4):Port #Portal DA1 [2001:03e0:06cf:0003:021b:21ff:fe04:f405]:3260 - Portal DA1 192.168.2.36:3260 + Portal DA1 10.0.1.1:3260 # wildcard address you may need if use DHCP # DO NOT USE WITH OTHER PORTALS @@ -95,10 +95,10 @@ [InitiatorGroup1] Comment "Initiator Group1" - InitiatorName "iqn.1991-05.com.microsoft:saturn" + # InitiatorName "iqn.1991-05.com.microsoft:saturn" # special word "ALL" match all of initiators - #InitiatorName "ALL" - Netmask 192.168.2.0/24 + InitiatorName "ALL" + Netmask 10.0.4.0/24 # TargetName, Mapping, UnitType, LUN0 are minimum required [LogicalUnit1] @@ -106,13 +106,13 @@ # full specified iqn (same as below) #TargetName iqn.2007-09.jp.ne.peach.istgt:disk1 # short specified non iqn (will add NodeBase) - TargetName disk1 - TargetAlias "Data Disk1" + TargetName timemachine + TargetAlias "Time machine" # use initiators in tag1 via portals in tag1 Mapping PortalGroup1 InitiatorGroup1 # accept both CHAP and None - AuthMethod Auto - AuthGroup AuthGroup1 + AuthMethod None + #AuthGroup AuthGroup1 #UseDigest Header Data UseDigest Auto UnitType Disk @@ -136,7 +136,7 @@ # LogicalVolume for this unit on LUN0 # for file extent - LUN0 Storage /tank/iscsi/istgt-disk1 10GB + LUN0 Storage /timemachine/tm1 200GB # for raw device extent #LUN0 Storage /dev/ad4 Auto # for ZFS volume extent
また、ファイヤーウォールの 3260 tcp/udp を開けておきます。
## __GooglePrettify__ # echo istgt_enable="YES" >> /etc/rc.conf # /usr/local/etc/rc.d/istgt start # /usr/local/etc/rc.d/istgt start
これでサーバー側の設定は完了。次はマック側の設定です。 globalSAN iSCSI initiator for OS Xをインストールします。
環境設定でglobalSANの設定画面を開いてTargetの追加で
Target Name: iqn.2007-09.net.f99aq8ove.istgt:timemachine
でadd
iSCSI Options... -> Always send "SessionType" when connecting. をチェック
そのあとでIPaddrを登録してconnect
初期化するか聞いてくるので初期化して、このディスクをTimeMachineで使うようにして完成
Twitter Streaming API で流れてくるユーザー情報を保存するのに、初めは MySQL を使っていました。でも、IO が重かった(自宅ファイルサーバ兼用なので……)ので、Cassandra に切り替えてみました。ちょうど、Key-value store っぽい使いかたしかしてなかったし。
もう少しちゃんと実装してテストも書いてから出そうかと思ってたけど、数ヶ月進展しないからとりあえず貼ってみます。
最近、Ubuntu 10.04 (lucid) にアップグレードしたんだけど、grub2 が入ってなかったので、手動で grub2 を入れてみた。
実は、Ubuntu 9.10 から grub2 がデフォルトだったらしいけど、クリーンインストールはしてなかったので、自動では grub2 は入らなかったみたい。
% grub-install -vしたら、0.94だったかな?
grub2 をインストールする
% sudo aptitude install grub2これで、インストールされる。表示されるデフォルトの選択子を選ぶと、grub2 は menu.lst からチェーンロードされる。この状態で grub2 がちゃんと動作することを確認して、
% sudo upgrade-from-grub-legacyしてやれば、直接 grub2 がロードされるようになる。
とまぁ、特に設定してないんで代わり映えはしないけど、とりあえず grub2 になった模様。
% grub-install -v grub-install (GNU GRUB 1.98-1ubuntu6)
とりあえず、hadoop用のアカウントを作ってみた。
## __GooglePrettify__ % sudo adduser hadoop
このユーザー用に、パスワード無しのSSH鍵を作って、ログインできるようにしておく。
予めtarballを手動で/usr/ports/distfilesに入れておいて、
## __GooglePrettify__ % cd /usr/ports/java/diablo-jdk16 % sudo make config # TZUPDATEは無効にしておく(手抜き)
で、JDKのインストール
## __GooglePrettify__ % sudo portinstall java/diablo-jdk16 % sudo portinstall shells/bash
JDKが入ったら、次はhadoopのインストール。http://hadoop.apache.org/common/releases.html#Downloadらへんからダウンロード。
## __GooglePrettify__ % tar zxvf hadoop-0.20.1.tar.gz % cd hadoop-0.20.1/conf
hadoop-env.shのJAVA_HOMEを設定しておく。
## __GooglePrettify__ export JAVA_HOME=/usr/local/diablo-jdk1.6.0
docs/quickstart.htmlを見ながら
conf/core-site.xml
<!-- __GooglePrettify__ -->
<configuration>
<property>
<name>fs.default.name</name>
<value>hdfs://localhost:9000</value>
</property>
</configuration>
conf/hdfs-site.xml
<!-- __GooglePrettify__ -->
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
</configuration>
conf/mapred-site.xml
<!-- __GooglePrettify__ -->
<configuration>
<property>
<name>mapred.job.tracker</name>
<value>localhost:9001</value>
</property>
</configuration>
起動してみる。
## __GooglePrettify__ % cd ../bin % ./hadoop namenode -format # HDFSの初期化 % ./start-all.sh
エラーが出なければ成功。jpsで動作してるか確認できる。
## __GooglePrettify__ % mkdir ~/inputs % cp CHANGES.txt ~/inputs # 適当なファイルを入れて % hadoop dfs -put ~/inputs inputs # HDFSにコピー % hadoop dfs -ls inputs # コピーできたか確認
いよいよ、map, reduceを実行してみる。
## __GooglePrettify__ % hadoop jar hadoop-0.20.1-examples.jar wordcount inputs outputs
ほっとくと、なにやら出力が出て終了。
## __GooglePrettify__ % hadoop dfs -ls outputs Found 2 items drwxr-xr-x - hadoop supergroup 0 2009-09-19 22:58 /user/hadoop/outputs/_logs -rw-r--r-- 1 hadoop supergroup 126351 2009-09-19 22:59 /user/hadoop/outputs/part-r-00000 % hadoop dfs -cat outputs/part-r-00000 # に、単語をカウントした結果が出力される
発端は、2ch で見かけた、この発言 JavaScript.
まずは、こちらをご覧ください。
(注: 例示の JavaScript は SpiderMonkey や Rhino のインタプリターで実行するか、print を alert に読みかえてブラウザーで実行してください。)
// __GooglePrettify__
function func1(a) {
a = {hoge: 2};
}
var b = {hoge: 1};
func1(b);
print(b.hoge); // => 1
実行すると、1 と表示されましたよね。「参照渡し(call by reference)なら、ここで 2 と表示されなければおかしい。これは、値渡し(call by value)じゃないの?」というのがリンク先で投げかけられていた疑問です。
ここで、次の関数を考えてみます。
// __GooglePrettify__
function func2(a) {
a.hoge = 2;
}
var b = {hoge: 1};
func2(b);
print(b.hoge); // => 2
今度は 2 と表示されたと思います。この挙動を指して、参照渡しだと説明された場合は納得できると思います。
ここで、突然ですが、視点を変えて C++ で同様の関数をつくってみましょう。
// __GooglePrettify__
#include <iostream>
using namespace std;
class Hoge
{
public:
int hoge;
Hoge(int a) : hoge(a) {}
};
void func_v1(Hoge a)
{
a = Hoge(2);
}
void func_v2(Hoge a)
{
a.hoge = 2;
}
void func_r1(Hoge &a)
{
a = Hoge(2);
}
void func_r2(Hoge &a)
{
a.hoge = 2;
}
int main(void)
{
{
Hoge b(1);
func_v1(b);
cout << b.hoge << endl; // => 1
}
{
Hoge b(1);
func_v2(b);
cout << b.hoge << endl; // => 1
// まぁ、値渡しですから。
}
{
Hoge b(1);
func_r1(b);
cout << b.hoge << endl; // => 2
// おっ!?
}
{
Hoge b(1);
func_r2(b);
cout << b.hoge << endl; // => 2
// うんうん。これこそ、参照渡しです。
}
return 0;
}
同じような事をするのに、2 倍のパターンが出てきました。さてさて、JavaScript はどちらのパターンでしょう。 結論から言うと後者の _r 付きのパターンにあたります。これが参照渡し(call by refference)です。 _v 付きは、もちろん値渡し(call by value)です。
ここで、よ〜く見ると、func_r1 と func1 の挙動が異なりますね。 ここで更に C++ について学んで行きましょう。
// __GooglePrettify__
#include <iostream>
using namespace std;
class Hoge
{
public:
int hoge;
Hoge(int a) : hoge(a) {}
};
void func_p1(Hoge *a)
{
a = new Hoge(2);
}
void func_p1_(Hoge *a)
{
*a = Hoge(2);
}
void func_p2(Hoge *a)
{
a->hoge = 2;
}
int main(void)
{
{
Hoge b(1);
func_p1(&b);
cout << b.hoge << endl; // => 1
// あらあら (このままだとメモリーリークします)
}
{
Hoge b(1);
func_p1_(&b);
cout << b.hoge << endl; // => 2
// おぁ!?
}
{
Hoge b(1);
func_p2(&b);
cout << b.hoge << endl; // => 2
// うんうん
}
return 0;
}
またしても分裂してしまいました。ここで、func1 と同じものは func_p1 です。func_r1 と同じものは func_p1_ です。(本当は、参照は書き変え不可な所が違いますが。)
func_p1 と func_p1_ の違いのポイントは、代入時の左辺値が a か *a か、という点です。
これによって、処理の対象が "ポインタ" か "ポインタが指し示している先のオブジェクト" か、という違いが生じます。
C++ では参照を用いることで、"*" を使わずに "参照先のオブジェクト" に対して処理を実行することができます。
ところで、先程の C++ の関数はこうも書けます。
// __GooglePrettify__
#include <iostream>
using namespace std;
class Hoge
{
public:
int hoge;
Hoge(int a) : hoge(a) {}
};
void func_r1_(Hoge &a)
{
a.operator=(Hoge(2));
}
int main(void)
{
{
Hoge b(1);
func_r1_(b);
cout << b.hoge << endl; // => 2
// おおっ!?
}
return 0;
}
この書き方のほうが、参照先のオブジェクトの代入演算子を実行している...、参照先のオブジェクトのメンバ関数 operator= を実行しているということがわかりやすいと思います。
ポインタによる参照渡しの例 func_p1 に戻ると、こっちは参照先自体(ポインタ)を入れ替える処理が実行されます。そしてこれは、JavaScript での参照渡しと同様の動作です。
func_p1 と func_r1 の差はここです。
C++ での参照は、一貫して参照先のオブジェクトに○○を適用という感じです。 JavaScript でも、C++ のそれとだいたい同じなのですが、代入の時だけは参照を貼り替えるという動作になります。
なんとなくイメージはつかんでいただけたかなと思うのですが、いかがでしょう?
最後に、C++ では参照先のオブジェクトに対して代入演算子が呼ばれると言いましたが、この動作と同じようなことを JavaScript でやってみましょう。
// __GooglePrettify__
function assign(that){
if (this === that) return this;
for (var i in that) if (that.hasOwnProperty(i)) {
this[i] = that[i];
}
return this;
}
function func1_(a) {
a.assign = assign; // Assign method を生やして...
a.assign({hoge: 2});
}
var b = {hoge: 1};
func1_(b);
print(b.hoge); // => 2
この様に、ちゃんと値が書き変わりました。
おわり。(続編は有りません。)
参考文献-
Effective C++ 原著第3版
私はこれで C++ を学びました ;) 持ってるのは改訂第2版だけど。 -
新訂版 More Effective C++
私はこれ(ry こっちも、新訂版は持ってない! -
Effective STL—STLを効果的に使いこなす50の鉄則
3 冊セットということで、ついでに。 -
JavaScript 第5版
JavaScript といえば、この本。 -
JavaScript: The Good Parts —「良いパーツ」によるベストプラクティス
JavaScript での Effective C++ 的な本。(だと個人的に思う。)
組み込むのは簡単だった。
steps to phantasien インスパイアで、__GooglePrettify__ を埋め込んだ <pre>
対象にしてみた。こうしておくと、FeedReader で読んでる人への目印(?)になる。
これら CSS と JavaScript をロードしておいて、
<!-- __GooglePrettify__ --> <link rel="stylesheet" type="text/css" href="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css" /> <script type="text/JavaScript" src="http://www.google.com/jsapi"></script> <script type="text/JavaScript" src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js"></script>こんなのを実行するようにしておく。
// __GooglePrettify__
(function(){
// jQuery をロードして…
google.load("jquery", "1");
// コールバックは google.setOnLoadCallback で指定
google.setOnLoadCallback(function(){
// __GooglePrettify__ を含む <pre> の class に prettyprint を指定して、__GooglePrettify__ の行を取り除く
$("pre:contains('__GooglePrettify__')").addClass("prettyprint").each(function(i, elm){
elm.innerHTML = elm.innerHTML.replace(/^.*?__GooglePrettify__.*?\n/, "");
$(this).css("background-color", "white");
});
// prettyPrint!!
prettyPrint();
});
})();
/* __GooglePrettify__ */
#include <stdio.h>
int main(void) {
printf("Hello world!\n");
return 0;
}
ZFS で、際限なく延々と snapshot をとるのではなく、古い snapshot は自動で消したいよね、というモチベーション。
そこで、こんな script を書いてみた。(自己の責任にてご利用ください。)
以下のように crontab に書いておくと…
@daily /root/bin/zfs_snapshot.sh tank days_later 7 recursive @weekly /root/bin/zfs_snapshot.sh tank weeks_later 5 recursive @monthly /root/bin/zfs_snapshot.sh tank months_later 12 recursive @yearly /root/bin/zfs_snapshot.sh tank years_later 10 recursive
このような感じで、今日 0days_later だった snapshot が明日には 1days_later ... というふうにローテーションしていきます。
tank/usr 5.23G 137G 2.64G /usr tank/usr@0weeks_later 0 - 2.57G - tank/usr@0months_later 0 - 2.57G - tank/usr@3days_later 29.5K - 2.57G - tank/usr@2days_later 6.22M - 2.59G - tank/usr@1days_later 6.16M - 2.59G - tank/usr@0days_later 6.16M - 2.59G -
既知の問題
- snapshot が規定数に達していない場合は、zfs destroy, zfs rename がエラーになる
- コメントの英語が変
ということで、つけてみた。↓の方参照。
この前は mktemp を書いてましたが、今度は seq を書いてみました。 seq は、shell script でループを書く時によく使ってます。
勉強ということで、Test::Base を使ってみたり、コードは gist に貼ってみたり...。 しかし、これだと feed reader 上で直接コードが読めないところが難点かな。
(ref: Happy Testing Perl:第 2 回 Test::Base の紹介|gihyo.jp ... 技術評論社)
gist: 5103 — GitHub
OpenPGP key (0xF0F5FDCE) fingerprint: 

