2013年6月21日

超簡単! GITでローカルファイル管理(RCSから移行)

ホームプロジェクトのソースコードの管理には永らくRCSを使ってきた。もう20年以上になる。とっくに時代遅れのツールだということは分かっていたが、オンリーワンのデベロッパーでコンカレンシーは要求されないので…と言い訳をしつつ、ソースコードだけでなくサーバの各種設定ファイルなどもすべてRCS上にある。

Androidのカーネルの仕事をやっていたときは当然GITで、その使いよさには同意していたものの、「アップストリーム」「多人数」というイメージが先行して何となくホームプロジェクトとは別物としてきた。

ここに来て、本業のLinuxカーネルの開発の仕事がパッチ指向になってきて、パッチファイルを生成しなければならなくなり、ローカルにRCSで管理して自作ツールでRCSからパッチファイルを生成するようにしてきたが、どうも使い辛さだけが目立つようになったので思い切ってローカルのファイル管理にもGITを使うことにした。

新規プロジェクト

新規プロジェクトでGITリポジトリの作成は至って簡単。
h@spice:~$ mkdir projects/my_project 
h@spice:~$ cd projects/my_project
h@spice:~/projects/my_project$ git init
これだけ。.gitという隠しディレクトリが作られる。

もし既に管理対象のファイルがあればaddコマンドでステージングしてcommitコマンドでコミットする。
h@spice:~/projects/my_project$ git add .
(addingのメッセージ)
(「git status」でファイルの状況を確認し、不要なファイルがあれば「git rm -r --cached .svn」のようにしてステージングから削除)
h@spice:~/projects/my_project$ git commit

旧来のプロジェクト

すでに存在するプロジェクトのファイルをヒストリを保持したままRCSからGITに変換するのはちと面倒。いくつかの方法があるようだが、こちらのサイトでCVSからGITへの移行を紹介しているのでパクらせていただき、RCSから一旦その発展形であるCVSに変換してからGITに変換する。

ここでは「tv」というディレクトリの下のプロジェクトのRCSファイルを同じレベルの「tv.cvs」を経由して「tv.git」に変換する。

まず、tv.cvstv.gitの準備。
h@spice:~/projects$ mkdir tv.cvs tv.git 
h@spice:~/projects$ cvs -d `pwd`/tv.cvs init
h@spice:~/projects$ (cd tv.git; git init)

次に、tv.cvstvと同じサブディレクトリ構造を作り、tvのRCSファイル(RCS/*,v)をtv.cvsに「本物ファイル」としてコピーする。「RCS」のディレクトリレベルを削除するところに注意。
h@spice:~/projects$ (cd tv; find -type d -exec mkdir -p ../tv.cvs/{} \;) 
h@spice:~/projects$ for i in `(cd tv; find -name '*,v')`; do cp tv/$i tv.cvs/`echo $i | sed 's|/RCS/|/|'`; done

cvs2gitに各種オプションを与えるファイルをローカルにコピーし、必要箇所を変更する。キモはCVSの在り処を示す「r'test-data/main-cvsrepos'」の部分。
h@spice:~/projects$ cd tv.git
h@spice:~/projects/tv.git$ cp /usr/share/doc/cvs2git-2.3.0/cvs2git-example.options cvs2git.optionsh@spice:~/projects/tv.git$ vi cvs2git.options
(編集して変更)
h@spice:~/projects/tv.git$ diff -c /usr/share/doc/cvs2git-2.3.0/cvs2git-example.options cvs2git.options 
*** /usr/share/doc/cvs2git-2.3.0/cvs2git-example.options    Mon Nov 15 21:48:54 2010
--- cvs2git.options    Thu Jun 20 14:41:31 2013
***************
*** 552,558 ****
      # The filesystem path to the part of the CVS repository (*not* a
      # CVS working copy) that should be converted.  This may be a
      # subdirectory (i.e., a module) within a larger CVS repository.
!     r'test-data/main-cvsrepos',
 
      # A list of symbol transformations that can be used to rename
      # symbols in this project.
--- 555,562 ----
      # The filesystem path to the part of the CVS repository (*not* a
      # CVS working copy) that should be converted.  This may be a
      # subdirectory (i.e., a module) within a larger CVS repository.
!     ##r'test-data/main-cvsrepos',
!     r'/home/h/projects/tv.cvs',
 
      # A list of symbol transformations that can be used to rename
      # symbols in this project.

cvs2gitを実行し、GITにインポートする。
h@spice:~/projects/tv.git$ cvs2git --options=cvs2git.options
h@spice:~/projects/tv.git$ cat cvs2svn-tmp/git-blob.dat cvs2svn-tmp/git-dump.dat | git fast-import

この段階で「git status」で見ると全てのファイルが「deleted」状態でステージされているので、アンステージしてやる。
h@spice:~/projects/tv.git$ git reset HEAD .
h@spice:~/projects/tv.git$ checkout -- .

最後に余計なCVSROOTディレクトリを削除すればでき上がり。一時ファイル置き場のcvs2svn-tmp/とかオプションファイルのcvs2git.optionsも残っているが、これらはGITの管理下にはないので単純にrmで削除すればよい。
h@spice:~/projects/tv.git$ git rm -r CVSROOT/
h@spice:~/projects/tv.git$ git commit

RCSは死んだか?

そうは思わない。CVSやGITはプロジェクトを単位とし対象となる全てのファイルがリポジトリにあることをて想定しているが、例えば/etcの下の各種設定ファイルはほとんど他と独立しており、そういう概念とは無縁だ。そう言うことで、今後はプロジェクトファイルは順次GITに変換していくつもりだが、設定ファイルにはRCSを使い続けると思う。

2013年6月15日

Raspberry PiのGPIOで7セグメントLEDとLCDを使う

Raspberry Piで、NTPでコントロールされたディジタル時計とNCIDサーバからのフィードを受けてCallerID(日本で言う「ナンバーディスプレイ」)の表示できるガジェットを作るというプロジェクトを考えている。ファミリールームのAVラックの上、テレビの前に置いて、普段は時計、電話がかかってきたら発信元を表示させる構想。色々考えたが、時計表示には大型(1.5")の7セグメントLED、CallerID部分は16x2のLCDを使う予定。

まだ構想段階だから何もできていないが、ソフトウェアのリソースを調べた結果を書いておく。

GPIO全般

こちらのサイトに詳細がある。

LEDのドライバ

こちらのサイトにPerlで書いた例がある。Device::BCM2835が必要。下記GPIOドライバと共存できるかどうかはやってみないと分からないが、できるだけコンパイラは使いたくないのでPerlでいけるなら行きたい。

LCDのドライバ

WiringPiというプロジェクトがGPIO全般を扱うライブラリを提供している。LCDはこちら

WiringPiからダウンロードできるソースコートはセルフ開発しか考慮しておらずコンパイラの速度やソースコード管理に問題があるので、クロス開発できるようにするパッチを作った。ダウンロードはこちら
  • 環境変数「RPiCC」にクロスGCCのフルパスをセットしておく
  • buildを編集して以下の変数をカスタマイズする
    • RPi : 最終的にライブラリをインストールするRaspberry Piのホスト名とログインできるユーザ名(root)
    • INSTALL_DIR : 開発ホスト上に作成するインストールイメージのルートディレクトリ(../wiringPi-install
  • おまけとしてbuildスクリプトをそのまま使うMakefileも作った。
LCDのドライバをもう一つ見つけた。こちらはPerlのモジュールDevice::BCM2835:LCD。 上記LEDドライバと同じ系統のようなので、多分こちらを使うことになるだろう。
 

明るさセンサ

ガジェットが家族に受け入れられるためには「プロっぽい」仕様が必要。CdSセンサで周囲の明るさを計測してLEDとLCDのバックライトの明るさを自動調節する機能も計画の一部。Raspberry PiはArduinoと違いアナログポートがなくすべてのGPIOはディジタル入出力しかできないが、こちらのサイトでGPIOのピンの1本を入力と出力に切り替えながら一種の積分型オームメータを実現する方法を紹介している。

Perlのモジュールはパッケージとしてインストールできればよいのだが、apt-cache searchで探してもBCM2835関係は見当たらない。仕方ないのでcpanでインストールするが、ネイティブでコンパイラが走るので数時間かかる。

2013年6月13日

ISOイメージからブート可能なUSBメモリを作る

OSのインストールは、CDやDVDのような12cmの光ディスクを使うのがここ十数年の習わしだった。ベンダが物理メディアを提供していた時代は、一度マスタを起こせば安価に製造・送付できるからだ。

しかし物理メディアを持ち運ぶ「スニーカーネット」が歴史的事実になった現在、このやり方は便利とは言えない。うちの6台のサーバやデスクトップに装着されている光ディスクドライブは何年も使っていない。PCの生涯で数回しかないイベントのために光ディスクドライブを装着しておくのは無駄だし、そもそもダウンロードしたISOイメージを光ディスクに焼くのは手間がかかる。おまけに、書込み可能な光ディスクは寿命があるようで、うちの5年以上前に買ったストックは読出しエラーが頻発するようになった。

SSDのファームウェアのアップデートの際もISOイメージを光ディスクに焼いてそこからブートするように指示されていたが、もう少し便利な方法はないかと思って探したら、UNetBootinというツールを見つけた。ISOイメージをUSBフラッシュメモリに書き込んでブート可能なドライブを作る。Windowsはもちろん、Linux版もあるという優れもの。

USBフラッシュメモリなら書き換えて何度でも使えるし、今時の製品なら容量の心配もない。ポピュラーなOSのインストールイメージを直接選択してダウンロード込みでワンクリックで済ますこともできるし、もちろん別途自分で用意(ダウンロード)したイメージも書き込める。中身はよくわからないが、GRUBのようなブートローダを使って間接的にISOイメージのブートファイルに飛ぶのではないかと想像している。

2013年6月4日

SSDは使えるか?

ファイアウォールとして使っているサーバのHDDが壊れてからしばらくになる。このHDDはRAID1の2台のHDDの片割れなので、全く動作しなくなっていても一応ファイアウォールは動作を続けているが、いつ残りのHDDも壊れるかわからない。もし完全に死ぬと我が家のすべてのインターネットの接続がなくなってしまうので非常に痛い。さっさと新しいHDDを追加してこちらで解説したようにRAIDを修復すればよいのだが、なんとなく躊躇させる事情があった。

その「事情」を延々と書いても仕方ないので本稿に関係するところのみにすると、要するに「最近SSDがだんだん使われてきてているようだが、信頼性と消費電力の点からメカニカルなHDDより有利そうなので、RAIDの修復ではなくSSDに新規インストールしたシステムに置き換えてみっか」ということで、例によってAmazon.comを探し、一番価格対性能比がよさそうなCrucialのV4シリーズの32GB(CT032V4SSD2)を買ってみた。

このファイアウォールという特殊な使われ方のサーバは、パフォーマンスとか記憶容量とかはあまり問題ではない。メイルやウェブサーバのインターネットへのゲートウェイとして動作しているとは言え、インターネットの接続は実測でせいぜい40Mbpsで、ファイアウォール自身はセキュリティのために当然外に見せるファイルとかは持っていないので、4~5年前に中古で買ったSuperMicroのPentium4 3GHz+2GBのマザーボード(ただしオンボードのGBEが2台ついている)に40GBのHDDの半分も使っていないというごく控えめのシステムであり、32GBのSDDで十分なのである。

さて、SSDは使えるか?

結論から言うと

大変失望した。

その顛末をちょっと書く。

システムをそれまでのHDDx2のRAIDから非RAIDのSSDに置き換える方針は決まっていたのだが、いきなりSDDにインストールした新しいシステムを動かすのはちと無謀な感じがした。たかがファイアウォールとは言え、セキュリティを確保するためのパケットフィルタからメイルのフィルタ、自分で作ったいろいろなツールが動いている。それらを検証しながら新しいシステムに再構築しなければならないので、別のマザーボードを使ってOSをインストールし、稼働中のシステムからいろいろなツールを少しづつ転送した。更に、やはりSSDという未知のデバイスなので、念には念を入れるため(と言う言い訳をして、実際にはさぼりながら)エージングと称して立ち上げたまま半年ほどほったらかしにしてあった。

そうこうしているうちに、別のバックアップ専用サーバのマザーボードが寿命を迎え、全く動作しなくなってしまったので、SSDのインストール・検証用に使っていたマザーボード(iCore2 8GB) を転用することになり、したがって「検証段階」を終えて一刻も早くSSDでファイアウォールシステムの更新をしなければならなくなった。

そこで最終テストを実行…したら、何と、SSDからブートしない

「え?そんなはずが…」と調べてみると、どうもgrubの管理する最新のカーネルイメージファイルでSSDの読出しエラーが起こっているようで、 一世代前のカーネルはちゃんと起動した。S.M.A.R.Tで見てみると、セクタの読み出しエラーが何万回か発生していて、エラー回数が増加している。前回のセクタ読出しエラーの修復のワザを使って不良セクタをリマップしようとしたが上手くいかない。どうもSSDの壊れ方のモードはHDDと違うようだ。

買ってから半年少々のSSDなので保証で交換してもらえるかと思い、Crucialのテックサポートに電話をしてみると、ファームウェアのアップデートを勧められた。まぁ、そう簡単に返品だの交換だのは受け付けないと思っていたから、まずはファームウェアのアップデートを試みることにした。当然のことながらSSD上のファイルは全てバックアップする。

 まず、検証用システムをWindowsをインストールした別のHDDでブートし、指示されたウェブページからアップデータのZIPファイルをダウンロードする。SSDはSATAポートに繋いであるがシステムの動作とは関係ないのでアップデータは自由に操作できるはず。と・こ・ろ・が、アップデータは「デバイスが見つかりません」を繰り返すのだ。アップデータの説明にUSBがどうのこうのと書かれていたので、SSDをUSB/SATAのアダプタ2種類に繋いでみたがこれもだめ。

仕方ない、またCrucialのテックサポートに電話したら、何とダウンロードしたアップデータのプログラムはブート可能なUSBドライブを作るためで、そのドライブでシステムをブートしてそこに含まれているアップデータ本体でSSDのファームウェアをアップデートするという。アップデータの説明をもう2回読んでみたが、ブート用のUSBメモリを用意しろなどとはどこにも書かれていない。まぁよく考えてみれば、一般のユーザはSSDをインストールしたままアップデートを実行しなければならないので、外部のブートドライブから起動しなければならないことは当然といえば当然で、こちらが気を聞かせ過ぎたという感じもある(しかしアップデート結果SSD上のデータは全て失われるからSSDを使えるようにするためにはOSから何からを再インストールしなければならない)。文法的に間違いだらけのアップデータのメッセージからしてきっとチュウゴクの下請けが作っているのだろうと想像し、ドキュメントの品質のことは諦めてUSBのブートデバイスを作って再挑戦してみる。

BIOSの認識できるUSBドライブの形式でFAT32ではなくFAT16にしなければならないとかで少々躓いたが、とにかくDOSがブートし、指定されたバッチファイルを走らせると、何やらよさそうなプログレスメッセージ。25%...50%...75%...100%...DONE! で喜んだのもつかの間

SELFTEST FAIL

CT032V4SSD2(32GB SSD)が
2.1MBの訳のわからないドライブに化けた
が…。何だこりゃ?
 
もう一度アップデータを走らせてみると、もうSSDを認識しない。別のHDDからLinuxを起動してS.M.A.R.TでSSDを見ると、何とこれが正体不明のモデル番号の2.1MB(GBではない、めが・ばいと)のHDDに化けているではないか!ファームウェアのアップデートに失敗し壊れたようだ。

はっきり言ってもうこのSSDを使うつもりはないが、一応Crucialのテックサポートに電話してみたところ割とすんなりと動作品に交換してもらえることになったので、交換でき次第中古品として売り飛ばす予定。

結論:SSDはまだ時期尚早の感がある。少なくとも事前にファームウェアのアップデートを施し、十分なエージングをして動作に納得がいってからでないと怖くて使えない。6年前のラップトップのHDDをSSDに置き換えるというプランもあるが、もうCrucialのSSDはご免だ

動作しないRAIDディスクの交換


前回は不良セクタが発見されたRAIDディスクドライブを修復したが、今回は全く動作しなくなってしまったHDDを新しいドライブに交換する。RAID1の場合を示すが、RAID5やRAID6のレベルでも基本的に同じこと。少し古いが、こちらのサイトこちらのサイトが参考になる。
 


まず、/proc/mdstatでRAIDの状態を確認。
h@spice:~$ cat /proc/mdstat
Personalities : [raid1] [raid6] [raid5] [raid4]
md1 : a
ctive raid1 sda2[0]      38398908 blocks super 1.1 [2/1] [U_]
      bitmap: 1/1 pages [4KB], 65536KB chunk

md0 : active raid1 sda3[0]
      204788 blocks super 1.0 [2/1] [U_]
    
unused devices: <none>
/dev/sdbが全く見えていない。すでにmdadmが警告のメイルを送ってきているはず。
 

新しいHDDを用意

ペアになる稼働中のHDDと同じモデルが安心できるが、別にこだわる必要はない。ペアになるHDDより少容量でなければよい。5年ほど前までは、例えば同じ「120GB」を公称するHDDでもメーカにより、あるいは同じメーカの同じモデル名でもリビジョンにより実際の容量が微妙に異なり、運悪く稼働中のHDDより少容量のHDDを手に入れてしまうと使えなかったが、今は業界の申し合わせにより、例えば公称「2TB」のHDDはどのメーカ、どのモデル、どのリビジョンでもすべて同一の容量のはずなので心配事が一つ減った。
 

新しいHDDのパーティション

普段使っているfdiskでいちいち手でやるよりも、sfdiskで稼働中のHDDのパーティションをそっくりコピーする方が失敗がない。/dev/sdaから/dev/sdbにパーティションをコピーする。
h@spice:~$ sudo sfdisk -d /dev/sda | sudo sfdisk /dev/sdb
新しいディスクと古いディスクのジオメトリの違いで「I don't like this partition.」と拒絶されたら、--forceオプションを/dev/sdb側につけて強制的にパーティション情報をコピーする。この時点でパーティションのタイプなどもそっくりコピーされる。

注意:2TB以上の領域を使う場合は、2^32以上の512Bセクタを使うのでMBRのパーティションでは対応できず、従ってsfdiskも使えず、GPTを使う必要がある。この辺はご自分で工夫してください。
 

RAIDに新しいパーティションを加える

パーティションが整ったら、新しいパーティションをRAIDに加えてやれば、RAIDが勝手にリシンクを始める。リシンクは同時に一つのRAIDしか実行されないので、小さなサイズのパーティションから先に加えると先にリシンクが完了するので気持ちがよい。
h@spice:~$ sudo mdadm /dev/md0 --add /dev/sdb3
h@spice:~$ sudo mdadm /dev/md1 --add /dev/sdb2

h@spice:~@ sudo mkswap -U xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /dev/sdb1
h@spice:~$ sudo swapon -a


ブートローダの処理

以上のように、RAIDの修復そのものはさして難しくないが、ブートローダはRAIDの範疇ではなく、新しいHDDにはブートローダが書き込まれておらず、もし以前から動いている方のHDDが全く立ち上がらなくなるとブートできなくなってしまうのでこの手当てが必要。
 
ここではgrubを使っている 。個々のgrubコマンドやgrubの文法は「info grub」を熟読して精通しておくことを勧める。要点は「info grub」の「3.2 Installing GRUB natively」を参照。注意すべきことは、grubでのナンバリングは全てゼロベース、すなわち、
  • (hd0)/dev/sda
  • (hd0,0)/dev/sda1
となること。
ここで行うgrubの作業は以下の二つ
  1. 最初のHDDからのブートが失敗したときの代替HDDを指定する
  2. 代替HDD(新しいHDD)にブートローダを書き込む
上記の/proc/mdstatの出力をもう一度見てほしい。/dev/sda3と/dev/sdb3からなる/dev/md0/bootにマウントされていて、ここにブート用カーネルイメージとRAMディスクがある。

ブートパーティションなどの扱いはディストリビューションにより微妙に異なる。ここではFedoraを使っている。
 
代替HDDの指定のために、/boot/grub/grub.confを編集する。「title ~」で始まるブロックがブート可能なオプション。
(これより前省略)
#boot=/dev/sda
default=0
timeout=0
splashimage=(hd0,2)/grub/splash.xpm.gz
hiddenmenu
title Fedora (2.6.34.9-69.fc13.i686.PAE)
        root (hd0,2)
        kernel /vmlinuz-2.6.34.9-69.fc13.i686.PAE ro root=/dev/mapper/vg_mill2-LogVol00 rd_MD_UUID=d1a49db9:b462d70c:4d734507:a1a49339 rd_LVM_LV=vg_mill2/LogVol00 rd_NO_LUKS rd_NO_DM LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYTABLE=us rhgb quiet
        initrd /initramfs-2.6.34.9-69.fc13.i686.PAE.img

title Fedora (2.6.34.8-68.fc13.i686.PAE)
        root (hd0,2)
        kernel /vmlinuz-2.6.34.8-68.fc13.i686.PAE ro root=/dev/mapper/vg_mill2-LogVol00 rd_MD_UUID=d1a49db9:b462d70c:4d734507:a1a49339 rd_LVM_LV=vg_mill2/LogVol00 rd_NO_LUKS rd_NO_DM LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYTABLE=us rhgb quiet
        initrd /initramfs-2.6.34.8-68.fc13.i686.PAE.img

(これより後省略)
黄色の部分(hd0,2)すなわち/dev/sda3(からなる/dev/md0)に最新のブートファイル(カーネルイメージとRAMディスク)があることを示している。

代替用ブートパーティションを指定する。 水色の部分が追加された行。
(これより前省略)
#boot=/dev/sda

default=0
fallback=1

timeout=0
splashimage=(hd0,2)/grub/splash.xpm.gz
hiddenmenu

title Fedora (2.6.34.9-69.fc13.i686.PAE)
        root (hd1,2)
        kernel /vmlinuz-2.6.34.9-69.fc13.i686.PAE ro root=/dev/mapper/vg_mill2-LogVol00 rd_MD_UUID=d1a49db9:b462d70c:4d734507:a1a49339 rd_LVM_LV=vg_mill2/LogVol00 rd_NO_LUKS rd_NO_DM LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYTABLE=us rhgb quiet
        initrd /initramfs-2.6.34.9-69.fc13.i686.PAE.img

title Fedora (2.6.34.9-69.fc13.i686.PAE)
        root (hd0,2)
        kernel /vmlinuz-2.6.34.9-69.fc13.i686.PAE ro root=/dev/mapper/vg_mill2-LogVol00 rd_MD_UUID=d1a49db9:b462d70c:4d734507:a1a49339 rd_LVM_LV=vg_mill2/LogVol00 rd_NO_LUKS rd_NO_DM LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYTABLE=us rhgb quiet
        initrd /initramfs-2.6.34.9-69.fc13.i686.PAE.img

title Fedora (2.6.34.8-68.fc13.i686.PAE)
        root (hd0,2)
        kernel /vmlinuz-2.6.34.8-68.fc13.i686.PAE ro root=/dev/mapper/vg_mill2-LogVol00 rd_MD_UUID=d1a49db9:b462d70c:4d734507:a1a49339 rd_LVM_LV=vg_mill2/LogVol00 rd_NO_LUKS rd_NO_DM LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYTABLE=us rhgb quiet
        initrd /initramfs-2.6.34.8-68.fc13.i686.PAE.img

(これより後省略)
fallback=1」で「default=0」すなわち/dev/sdaからのブートに失敗した時は/dev/sdbを試すことを意味し、「title ~」は黄色の部分をコピーしたもので、「root (hd1,2)」でその時のブートファイルは/dev/sdb3(からなる/dev/md0、ただし多分デグレードしたRAID)にあることを示す。もし3台以上のHDDのRAIDでブートできる場合は「fallback=1 2」のように代替ドライブを列挙し、「title ~」の部分をコピーと(hd*,3)の変更で増やしてやる。

grub.confの追加が終わったら、grubを起動して/dev/sdbにブートローダを書き込む。

注意すべきは、普段はBIOSのデフォルトでは最初に見つかったブート可能なHDDすなわち/dev/sda、grubの表現では(hd0)からブートするが、もしこのHDDが死んでしまうと 物理的に2番目のHDDが/dev/sdaまたは(hd0)にマップされること。

だから以下の作業では、まず「device (hd0) /dev/sdb」で物理的に2番目のHDDを一時的に(hd0)にマップし、その後のコマンド実行のときにこのHDDが実際に2番目の物理デバイスからブートするときはブート可能な最初のHDDであることをシミュレートする。
h@spice:~$ sudo grub
Probing devices to guess BIOS drives. This may take a long time.


    GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

 [ Minimal BASH-like line editing is supported.  For the first word, TAB
   lists possible command completions.  Anywhere else TAB lists the possible
   completions of a device/filename.]

grub> device (hd0) /dev/sdb
grub> root (hd0,2)
 Filesystem type is ext2fs, partition type 0xfd
grub> setup (hd0)
 Checking if "/boot/grub/stage1" exists... no
 Checking if "/grub/stage1" exists... yes
 Checking if "/grub/stage2" exists... yes
 Checking if "/grub/e2fs_stage1_5" exists... yes
 Running "embed /grub/e2fs_stage1_5 (hd0)"...  15 sectors are embedded.
succeeded
 Running "install /grub/stage1 (hd0) (hd0)1+15 p (hd0,2)/grub/stage2 /grub/grub.conf"... succeeded
Done.
grub> quit
root (hd0,2)」で/dev/sda3(現在は/dev/sdbとして稼働中)にブートファイルがあることを示し、「setup (hd0)」で現在/dev/sdbとして稼働中のHDDにブートローダを書き込む。

全ての作業が終わったら、念のため一旦シャットダウンし、/dev/sda側のHDDのケーブルを外して/dev/sdb側(2番目の物理のHDD)だけで正常にシステムが起動することを確認して終わり。
 

応用

上記の方法を使って、RAIDのHDDをそっくり入れ替えることもできる。
 
うちのファイアウォールに使っているPCのマザーボードはPATAx2とSATAx2の両方が使えるのだが、導入時になぜかPATAしか使えないと勘違いしてPATAx2のRAID1を構築してしまった。その後PATAのHDDの一台が死に、代替品を入れようとしたがPATAのHDDはもう入手困難。いっそのこと新品のSATAのHDD2台にそっくり入れ替える試みをし、成功した。

以下の手順。
  1. PATAのHDD1台で動作している状態(/proc/mdstatが「U_」を表示する)。
  2. 新しいSATAのHDD2台を接続。
  3. SATAHDDのプライマリ側(/dev/sdb)を上記の手順でRAIDに加え、ブートローダもインストールする。
  4. PATAのHDDを外す。
  5. すでにRAIDに加えたSATAのプライマリ側HDDが/dev/sdaになり、片肺飛行のRAIDとして動作している。
  6. セカンダリ側のSATAHDDを上記の手順でRAIDに加え、ブートローダもインストールして完了。











2013年6月1日

不良セクタのあるRAIDディスクの修復

この10年ほどの間に出荷されているHDDにはSMART(Self-Monitoring, Analysis and Reporting Technology)という機能が備わっていて、セクタエラーや温度などのドライブの状態をオンラインあるいはオフラインで監視できる。また、プラッタ上の全セクタが正常であることは期待せず、アクセスできないセクタは予め確保されている(ドライブの公称容量には含まれない)代替セクタにリマップすることもしばらく前から行われている。
 
このリマッピングはHDD内部のファームウェアで行われ、OSは一旦リマップされたセクタを意識することはないが、SMARTで報告されるのはセクタの読み込みエラーで、エラーを起こした不良セクタをリマップするためにはそのセクタに書き込む必要がある。エラーがOSのインストール時などに発生すれば自動的に書込み→読出しエラー→リマップとなるが、インストール後にデータ領域でないセクタで発生(検出)した場合は、そのセクタに書込みが行われるまで読出しエラーが継続して報告される。
 
読出しエラーが検出されたセクタに書き込めばそのセクタはリマップされるが、当然のことながらそのセクタに記録されていた情報を回復することはできない。したがって、一般的にはそのセクタを含むデータをバックアップから回復することになるが、セクタ番号からファイルを特定するのは非常に困難で、また不良セクタがファイルデータではないメタデータの場合はファイルシステムその物が損傷していることになり、お手上げである。

つまり、HDDのセクタレベルの冗長性を生かすためには上位レベルの冗長性が必要ということになり、だからRAIDが必要、という結論になる。

ここでは、Linux上でRAID1(ミラリング)の一台のHDDの不良セクタをリマップして正常に見せる手順を述べる。

まず、smartctlで過去のセルフテストの結果を表示する 。
h@spice:~$ sudo smartctl -l selftest /dev/sdb
smartctl 5.40 2010-10-16 r3189 [i386-redhat-linux-gnu] (local build)
Copyright (C) 2002-10 by Bruce Allen, http://smartmontools.sourceforge.net

=== START OF READ SMART DATA SECTION ===
SMART Self-test log structure revision number 1
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Short offline       Completed: read failure       60%       145         262845373
# 2  Short offline       Completed: read failure       60%       136         262845373
# 3  Short offline       Completed: read failure       60%       130         262845373
# 4  Short offline       Completed: read failure       60%       129         262845373
# 5  Short offline       Completed: read failure       60%       129         262845373
# 6  Short offline       Completed: read failure       60%       129         262845373
# 7  Short offline       Completed: read failure       60%       129         262845373
# 8  Short offline       Completed: read failure       60%       129         262845373
(以下略)
テストの結果は過去21件まで保存され新しいものから順に表示されるらしいが、SSDの中には古いものから表示のものもあるので、「LifeTime」つまり通算通電時間で確認すること。「Remaining」はエラーが発見されたのでテストがやり残しのあるまま終了したということ(エラーがなければ00%になる)。セクタ262845373が不良の気配が濃厚である。

念のためsmartctlでセルフテストをもう一度実行する 。HDDはオンラインのままでよい。
h@spice:~$ sudo smartcl -t long /dev/sdb
smartctl 5.40 2010-10-16 r3189 [i386-redhat-linux-gnu] (local build)
Copyright (C) 2002-10 by Bruce Allen, http://smartmontools.sourceforge.net

=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===
Sending command: "Execute SMART Short self-test routine immediately in off-line mode".
Drive command "Execute SMART Short self-test routine immediately in off-line mode" successful.
Testing has begun.
Please wait 219 minutes for test to complete.
Test will complete after Fri May 31 10:43:36 2013

Use smartctl -X to abort test.

テストが終了すると結果に新しいエントリが追加されるので、「LifeTime」が更新されていることを確認する
h@spice:~$ sudo smartctl -l selftest /dev/sdb
smartctl 5.40 2010-10-16 r3189 [i386-redhat-linux-gnu] (local build)
Copyright (C) 2002-10 by Bruce Allen, http://smartmontools.sourceforge.net

=== START OF READ SMART DATA SECTION ===
SMART Self-test log structure revision number 1
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error

# 1  Extended offline    Completed: read failure       60%       155         262845373

# 2  Short offline       Completed: read failure       60%       145         262845373
# 3  Short offline       Completed: read failure       60%       136         262845373
# 4  Short offline       Completed: read failure       60%       130         262845373
# 5  Short offline       Completed: read failure       60%       129         262845373
# 6  Short offline       Completed: read failure       60%       129         262845373
# 7  Short offline       Completed: read failure       60%       129         262845373
# 8  Short offline       Completed: read failure       60%       129         262845373
(以下略)
やはりセクタ262845373が不良の気配が濃厚である。

修復のため、不良セクタのあるディスクをRAIDとスワップからはずす。hdparmはマウント中のドライブでも動作するようだが、一応念のため。
h@spice:~$ cat /proc/mdstat
Personalities : [raid1] [raid6] [raid5] [raid4]
md1 : a
ctive raid1 sda2[0] sdb2[1]      38398908 blocks super 1.1 [2/2] [UU]
      bitmap: 1/1 pages [4KB], 65536KB chunk

md0 : active raid1 sda3[0] sdb3[1]
      204788 blocks super 1.0 [2/2] [UU]
    
unused devices: <none>

h@spice:~$ sudo mdadm /dev/md1 --fail /dev/sdb2

h@spice:~$ sudo mdadm /dev/md1 --remove /dev/sdb2
h@spice:~$ sudo mdadm /dev/md0 --fail /dev/sdb3
h@spice:~$ sudo mdadm /dev/md0 --remove /dev/sdb3
h@spice:~$ sudo swapoff /dev/sdb1

h@spice:~$ cat /proc/mdstat
Personalities : [raid1] [raid6] [raid5] [raid4]
md1 : a
ctive raid1 sda2[0]      38398908 blocks super 1.1 [2/1] [U_]
      bitmap: 1/1 pages [4KB], 65536KB chunk

md0 : active raid1 sda3[0]
      204788 blocks super 1.0 [2/1] [U_]
    
unused devices: <none>
RAIDからはずすときは、fail、removeの2段階が必要。

さて、一番怖い瞬間を迎える。hdparmを使って直接当該セクタにゼロを書き込んで 強制的にリマップさせる。
h@spice:~$ sudo hdparm --write-sector 262845373 --yes-i-know-what-i-am-doing /dev/sdb

/dev/sdb:
re-writing sector 262845373: succeeded

hdparmはカーネルのキャッシュなどをバイパスして直接HDDの指定されたセクタにゼロを書き込む。

修復ができたら、再びsmartctlでセルフテスト(shortでよい)を実行してその結果を表示させ、すべての不良セクタがリマップされるまで繰り返す。

不良セクタが見えなくなったら、外してあったパーティションをRAIDとスワップに戻す。
h@spice:~$ sudo mdadm /dev/md0 --add /dev/sdb3
h@spice:~$ sudo mdadm /dev/md1 --add /dev/sdb2

h@spice:~$ sudo swapon -a
mdadm --addを使うときは、サイズの小さなパーティションを先に加えた方がリシンクが先に終わって気持ちがいい。

次回は、全くアクセスできなくなったRAID中の不良HDDを新品と交換する方法を…。