HDFSでMissing blocksが出た場合、DataNodeに問題があって外したい場合の対応メモ
手元でちょっとやったのでメモっとく。
Missing blocksが出た場合
HDFSのブロックが読めなくなることがある。手元ではHoop Server経由でappendが高頻度で発生している状況で、さらにHiveのクエリ実行が重なって派手にiowaitが出たときに起きた。
こうなると、そのブロックを含むファイルをMapReduceジョブが読みにいったときにIOErrorでコケてどうにもならなくなる。
状況は以下のコマンドで確認できる。
$ hadoop dfsadmin -report Configured Capacity: 35339596017664 (32.14 TB) Present Capacity: 33745796892986 (30.69 TB) DFS Remaining: 13764055724032 (12.52 TB) DFS Used: 19981741168954 (18.17 TB) DFS Used%: 59.21% Under replicated blocks: 37021 Blocks with corrupt replicas: 0 Missing blocks: 0 ------------------------------------------------- Datanodes available: 9 (9 total, 0 dead) Name: 10.xx.xx.xx:50010 Decommission Status : Normal Configured Capacity: 3905711992832 (3.55 TB) DFS Used: 2237537268559 (2.04 TB) Non DFS Used: 191584869553 (178.43 GB) DFS Remaining: 1476589854720(1.34 TB) DFS Used%: 57.29% DFS Remaining%: 37.81% Last contact: Thu Feb 16 13:38:15 JST 2012
ここの Missing blocks の数を見ていればいい。もしこいつが1以上になった場合は読めなくなっているファイルがあるはず。これを次のコマンドで確認する。これはNameNodeでないと動かない(多分)。
$ hadoop fsck / -files -blocks (中略) Status: HEALTHY Total size: 6540390145448 B Total dirs: 6383 Total files: 104299 Total blocks (validated): 112734 (avg. block size 58016127 B) Minimally replicated blocks: 112734 (100.0 %) Over-replicated blocks: 2542 (2.2548654 %) Under-replicated blocks: 26 (0.02306314 %) Mis-replicated blocks: 0 (0.0 %) Default replication factor: 3 Average block replication: 3.0239325 Corrupt blocks: 0 Missing replicas: 26 (0.00762687 %) Number of data-nodes: 9 Number of racks: 1 FSCK ended at Thu Feb 16 13:47:28 JST 2012 in 2064 milliseconds The filesystem under path '/' is HEALTHY
ここでHEALTHYになってない場合、「(中略)」した部分に、該当のブロックがどのファイルのものなのかが出てくる。出力される行数がすごいので、いったんファイルにリダイレクトしておいて、そこから頑張って grep するのがよい。該当行は以下のような感じ。
/path/to/somewhere/accesslog-20120211-06.log: CORRUPT block blk_-5847475750566557128
あとはこのブロックをどうにかするんだけど、多分復活させる方法はないので、該当ファイルをいったん削除して作り直すなどする。データが読み込めなくても削除はできるので、さくっと hadoop fs -rm するのがよい。
(2/17追記) コメントより、確かに hadoop fsck / -delete というオプションがあって、これやると不整合の出てるファイルを消せます。ただ確認なしにやるのはありえないのでこのオプションなしで実行し、それから対象を確認して実行、とするのがたぶんいい。自分の場合は対象が Hive テーブルに読まれてるブロックだったので、Hive上から alter table drop partition した。
DataNodeに問題があって外したい場合
DataNodeのディスクでI/Oエラーが出てどうにもならない感じなので交換したい、というケース。象本2版(英語版) p315 "Decommissioning old nodes" に書いてある内容。
このノードを外すために mapred.hosts, dfs.hosts で指定する include リスト(ノードのリスト)および mapred.hosts.exclude, dfs.hosts.exclude で指定する exclude リストを使用する。
これも象本(p314)に記載があるが、include ファイルは slaves ファイルとは役割が違う。slavesファイルは start-*.sh や stop-*.sh などのスクリプトが操作対象を決定するためのもので、include/excludeファイルはNameNode/JobTrackerなどが見るためのもの……らしい。なんで区別があるのかは知らん。たぶん何か理由があるんだろう。
なお自分の手元環境ではこれらの設定を *.xml に指定していなかったのでどうするかと思ったが、NameNodeは設定ファイルに追記した上でコマンドを叩けばこれを読み直してくれるようなので再起動の必要はない。JobTrackerは何もジョブが走っていない状態にして(?)再起動すればよい。
また include/exclude ファイルにはNameNode/JobTrackerが各ノードを認識している名前そのままで書く必要がある。NameNodeのWeb UIから辿れる "Live Nodes" あたりに出てくるノード名で書けばたぶん正解。正引きできるDNS名で書いても、名前が一致していないと別ノードとして扱われ、クラスタには1台もいなくなりました、という破滅的な状態になる。JobTrackerであればそういう事態になっても比較的傷が浅いので、まずJobTrackerで試し、結果を確認してからNameNodeにも反映するとよい。(以下、そういう手順を書く。)
具体的な手順は以下の通り。
include/excludeファイルを指定していなかった場合
include/excludeファイルがなければ作成する
- NameNode上の適当なパスに作成する
- まず、全ノードを include ファイルに書いておくこと
- これをやって一度読ませないと正常に Decommissioning になってくれない
hdfs-site.xml および mapred-site.xml に記述を追加する
<property> <name>dfs.hosts</name> <value>/home/hadoop/include</value> </property> <property> <name>dfs.hosts.exclude</name> <value>/home/hadoop/exclude</value> </property>
- mapred-site.xml
<property> <name>mapred.hosts</name> <value>/home/hadoop/include</value> </property> <property> <name>mapred.hosts.exclude</name> <value>/home/hadoop/exclude</value> </property>
- なおここでは mapred と dfs で同じリストファイルを参照しているが、ほとんどのケースではこれで問題ないと思う
これを一度クラスタに読み込ませる
- まず JobTracker に反映させるため、そちらを再起動する
$ stop-mapred.sh $ start-mapred.sh
- JobTracker の Web UI を見てノード数などが変わっていなければOK
- ノードの指定などにおかしい部分があると Nodes が 0 になったりするので、直してまた再起動する
- JobTracker が問題ない状態なのを確認したら NameNode にも読み込ませるため以下のコマンドを叩く
$ hadoop dfsadmin -refreshNodes
-
- NameNode の Web UI を見て、ノード数が変わっていないことを確認すればOK
ノードの除外
続けて、問題のあるノードを外す。まず exclude に登録して Decommissioning 状態にする。この状態になると該当ノードに存在するブロックを強制的に他のノードにレプリケーションする処理を行うため、完了までは待つ必要がある。完了すれば全ブロックのレプリカが他のノードに規定数存在することになり、安全にこのノードを落とすことができるようになる。
exclude ファイルに該当ノードを追記する
- NameNode上の exclude ファイルを編集してしまう
JobTrackerを再起動して、正常に反映されるか確認する
- 以下のコマンドで
$ stop-mapred.sh $ start-mapred.sh
-
- 実行後に JobTracker の Web UI を見ると "Excluded Nodes" のところに該当ノードが入るはずなので、それを確認する
NameNodeにも反映する
$ hadoop dfsadmin -refreshNodes
- 実行すると "Live Nodes" は変わらないままで "Decommissioning Nodes" に該当ノードが入るはず
- 大量のブロックのレプリケーションが開始されるので、この完了を待つ
- (以下まだ実施してないので予定) 完了したら "Decommissioned" となる
該当ノードを落とす
- とりあえずTaskTracker/DataNodeを落とせばいいのかな、たぶん
NameNodeからノードを除去
- include ファイルから該当ノードを削除し、その上でコマンドを叩いてNameNodeに反映させる
$ hadoop dfsadmin -refreshNodes
slavesからも除去する
- start-all.sh とかやっちゃったときに間違えて該当ノードを操作してしまわないように
疑問がひとつ。exclude ファイルからも include から削除するのと同じタイミングで取り除いちゃっていいんだよね? 象本では記述がない。まあ大丈夫だろうと思うんだけど。
あ、もうひとつあった。slaves とか include/exclude ってコメントアウトできないのかな。再セットアップしたら同じ名前でクラスタに戻したいんだけど、いちいち削除とか面倒なんでコメントでどうにかしたいなあ。
(2/17追記) 試してみたけど include から # でコメントアウトしようとしてみたら -refreshNodes した後で 「'#' というノードが見付からない」という大変残念な画面 になった。残念だ。
2/17追記: その後の顛末
"Decomissioning in progress" になってから4時間くらい激しくブロックのレプリケーションが行われたが、それがひと段落ついてからもいっこうに "Decommissioned" にならず22時間が経過した*1ので、腹に据えかねてその後の作業を強行した。その状況を追記しておく。
- 該当ノードのsecondary namenodeおよびdatanodeを停止
- 停止方法がよくわからなかったので kill った
- include から該当ノードを除去して dfsadmin -refreshNodes を実行
- この時点で NameNode のWeb UI上で該当ノードが "Decomissioned" になった
- slavesからも外して処理完了
- しばらく待ってたら該当ノードが "Dead" に移ってた
Decomissioned になるタイミングがよくわからなかった。自動的になるんじゃないの? レプリケーションが終わったっぽいタイミングで自分でやれってことなのかなあ。うーん。新しいブロックの書き込みは Decomissioning になった時点でなくなってたっぽいから多分この状況で実害は無いんだけど、なんかモヤモヤする。
*1:Hoop Serverからの書き込みが連続しているせいかなあ、といったん停止してみたりしたけど、変わらなかった