2014年1月25日土曜日

自己満足的自宅サーバーディスク構成変更(1)~計画

先日のエントリで会社の開発機に起きた顛末をつらつらと書き連ねましたが、無事復活したことで急に家のサーバーのディスクは大丈夫かいなと思い始めました。ディスクの稼動時間なら会社のやつに負けないよ?いっぱい壊れてるかもよ?w

早速帰宅して調べたところ、すばらしいことにどのディスクも1つも不良セクタはないようです(ディスクが気づいてないだけかもw)。一番長いやつで 3.4 years となっているので3年以上稼動しているようですが。とりあえずそんなひどい状況ではないようです。

自宅サーバーはこんな感じでRAID 1のアレイをいくつか作り、それをLVMで取りまとめていたわけですが、今回LinuxのソフトウェアRAIDの便利さに感銘を受けたので、もっと使い込もうかなと。しかも当時誤解していたのか、ソフトウェアRAIDって0/1/10ぐらいしかできないと思ってたのに5や6もできるんですね。Linuxすばらしい。

現状のディスクはちょっとずつ買ったせいでサイズはまちまちです。
  1. 2.0TB (/boot, swap, /, pv(1))
  2. 2.0TB (/boot, swap, /, pv(1))
  3. 2.5TB (pv(2))
  4. 2.5TB (pv(2))
  5. 3.0TB (pv(3), data)
  6. 2.0TB (pv(3))
/boot, /, pv(1), pv(2), pv(3) はすべてRAID 1、swap は RAID 0、data は素の領域となっています。これをもっと改善できないかな?

/boot や / は何もディスクをケチるほど大きくないので RAID 1 のままでよいとして、他はせっかく6台もあることだし、RAID 5/6 にしたいですよね。私は個人的にはRAID 5が嫌いなので6にしたいかなと。もし/bootや/を2.5TBや3.0TBのディスクに移動できれば、2.0TBx6(RAID 6)=8.0TB になるわけで、現状の約6.0TBから2.0TBも増やせた上に冗長性が高くなります。これはやらない手はない。なぜRAID 5が嫌いかというと、5ってリビルドの負荷がかなり高くて、その間に大丈夫だったディスクがやられちゃうのが怖いのです。 6なら仮にそれがおきてもさらに1台やられない限り大丈夫なので気持ちの余裕が違うかなと。

しかしやるとなっても、システムには既にデータが数TBオーダーで置いてあるので、退避して戻すのは不可能。そんなでかいストレージは他にないです。なのでオンラインで変更できるわけもないし無理かなーと思っていたのですが、できそうなんですよ。確かに言われて見ると、「2台のRAID 5」=「RAID 1」なわけで、それをRAID 5、さらに6と上げていくのは技術的には可能でおかしくはないか。このあたりを読むとRAID 5から6への変更は mdadm コマンドでできるっぽいので、もしかしたらRAID 1から5もできたりしないのかな?

しかし、それができるとわかっても、やっぱりデータが載ってるRAID 1のアレイをリンク先の手順に書いてあるように一度消して作り直すのはちょっと怖い。なので、やるなら以下の方法がいいかなと思ったわけです。
  1. 現状LVMに参加しているアレイの最大サイズ(2.5TB)の分だけなんとか領域をあける
  2. そのアレイをLVMから抜く
  3. そのアレイをRAID5/2(RAID5の2台の意)にする
  4. 再びLVMに参加
  5. 別のアレイをLVMから抜く
  6. そのアレイを壊して3で作ったアレイに追加し、RAID5/2+2(RAID5の2台+スペア2台)
  7. RAID5/2+2をRAID6/4に変更
  8. 最後のアレイをLVMから抜く
  9. そのアレイを壊して7のアレイに追加し、RAID6/6に変更
 いやこれはまた壮大ですねw

しかも理想的には最低サイズ(2.0TB)にあわせてアレイを構築したほうがいいので、 /boot 等のシステム用のパーティションはでかいディスクに移したいわけです。まずはそこから手をつけるか?
  1. 3.0TBが参加しているアレイをLVMから抜く
  2. パーティションを切りなおし、既存の2.0TBのシステム用パーティションにあわせる
  3. RAID 1として参加させる
  4. GRUBをセットアップ
  5. 既存のディスクをアレイから抜く
基本これでいけるはず。じゃあアレイの構築とか相当時間がかかると思うのでのんびり行きますか。

2014年1月23日木曜日

再びディスクの話

おっと気づいたら2014年初エントリですね。つまりこんな感じで頻繁に書くものではないですが、今年もよろしくお願いします。

突然ですが、私はソフトウェア開発を生業としております。従って会社でもプログラムを書く仕事をしていて、机にはいわゆる「開発機」という開発専用のマシンを所有して(もちろん正確には貸与されて)います。会社は社員にノートPC(Windows)を貸与しているので日々の業務アプリケーション等は Windows が基本ですが、WindowsでないとできないものはノートPCでやればいいので、開発機は各自の自由な管理となっています。大半の人はWindowsですけどね。私はもちろんLinux - Red Hat Enterprise Linux を入れて活用しています。

その開発機もディスクが飛んですべて吹っ飛ぶのはいやなので、家のサーバーのように RAID 1 で冗長化しています。正確には、悲しいかな会社で使うマシンはうちのマシンより高いくせに貧弱(失礼)で、ディスクも 1.5TB/1.0TB/500GB を後生大事に使い続けるという状態なので、大事なデータを RAID 1 (1.5TB のうち 1.0TB と 1.0TB)と RAID 0 (1.5TB のうち 500GB と 500GB)で運用しています。しかもその1.5TBと1.0TBは前のマシンから転用してきたので結構前からあるやつです。

それが先週ふと GNOME の上のバーのところに、ディスクのアイコンに「!」がついてるアイコンがでていることに気づきました。最初は「何だろ?ディスクいっぱいにでもなったかな?」と思ったのですが、クリックしてみると

「ディスクが壊れかけています」(うろ覚え)

!!!
これはまずいと思いさらにクリックだかダブルクリックだかでディスクユーティリティーなるものが開くので見てみます。(余談ですが Linux もずいぶん便利になりましたよね。)すると、1.0TBのディスクの表示が既に「故障しています」になっているではないですか。 orz

どうも SMART の情報に何かあるということでクリックしてみると、"Reallocated Sector Count" という項目に赤が。ダメになって別のところが使われたと判定されたセクタの数ということですね。警告の出る閾値が確か100ぐらいだったんですがなんと2000越え。これは危険だ。仕方ないので交換用のディスクを注文し、翌日見ると3000越え。その翌々日見たら4000越えと日増しに増えてました。

とはいえ、上に書いたようにこのディスクは基本 RAID 1 のメンバーなので、最悪吹き飛んでもいいわけです。なのでのんびり見てたんですが、さすがにこんだけ増えてくるとなるともう外したほうがいいかと思って物理的に外しました。/proc/mdstat を見るとしっかり全部 degrade 状態に変化します。

秋葉原で買ってくれば即日なのに会社ってのは面倒なもんで、指定した業者から納品されたのは1週間後ぐらいでしたか。秋葉原で買ってくれば半額ぐらいなのに。 早速つないで RAID を再構築です。全く簡単な話で、既にあるアレイと同じか大きいパーティションを作成し、
# mdadm --add /dev/md127 /dev/sdb1
などとするだけです。しばらくして resync が終われば冗長性も完全復活です。

しかしですね、もうお分かりかと思うんですが、今さら同じサイズ(1.0TB)のディスク買う理由はないのですよ。というわけで今回買ったのは秋葉原なら普通に3.0TBや4.0TBも買えるのになぜか2.0TBの大容量!w つまり1.0TBも多いのです!(遠い目

なので、そのあまった分も RAID に組み入れたい、と思ったわけですが。当然アレイのサイズは変えられないし、別のアレイを作って LVM に追加かなぁと思っていたところ、アレイのサイズは変えられるんですね。Linux えらい。

詳細な手順は間違えているかもしれませんが、覚えている限りやったのは以下です。上の mdadm --add する前、既存のアレイが degraded の状態からです。
  1. 大きくしたいサイズでパーティションを切る(私の場合既存のアレイが800GB ぐらいでしたが、1.3TB ぐらいで切りました)
  2. mdadm --add して追加する
  3. mdadm --detail や /proc/mdstat 等で resync が終わるのを待つ
  4. mdadm --fail や物理的に外すなどで元の小さいパーティションをアレイから除く
  5. パーティションを切りなおす
  6. mdadm --add して追加する
この時点ではまだアレイのサイズは変わりませんが、アレイが乗っているディスクのサイズはどちらも増えた状態ということになります。なのでアレイのサイズを増やすことができます。
  1. mdadm --grow <device> -z max
これで使える最大サイズのアレイとなりました。実行する前と後で mdadm --detail <device> | grep Size などとすれば違いがわかると思います。

私はこのアレイをさらに LVM で使っていたので、PV としても大きくなってもらわなければ意味がありません。が、pvdisplay 等で見てもまだ前のサイズのままです。なのでもうひと手間かけましょう。
  1. pvresize <device>
これで無事 PV としても大きなサイズになりました。めでたしめでたし。

ちなみにパーティションのサイズは完全に一致している必要はありません。追加するほうがちょっと大きいぐらいなら無駄にはなりますが全く問題はありません。が、しかし、小さいとちょっと面倒です。先に大きいほうのパーティションでアレイを構築してしまうと、「アレイのサイズ」=「大きいほうのパーティションのサイズ」となってしまい、後から小さいのを追加すると怒られます。アレイのサイズを小さくすればいいのかもですが、大きくするのに比べて小さくするのはちょっと怖い感じですので・・・。(昔乗ってるファイルシステムよりLVを小さくしてぶち壊した経験ありますw)

さらになんでこのエントリが「自宅サーバー」ラベルなのか?それはこの出来事に触発されて自宅サーバーをいじることにしたからです。それはまた後日。

2013年10月15日火曜日

play-pac4j : Play! Framework 2.2 で OAuth(失敗 -> 2.1 では成功)

ちょっと前のエントリで Play! Framwork で twitter4j を使うのに SecureSocial というのを使ってみましたが、別のものということで今度は pac4j というのを使ってみます。定義によると
pac4j is a Profile & Authentication Client for Java (it's a global rebuilding of the scribe-up library)
だそうです。それをさらに Play! Framework から利用しやすいように play-pac4j というものがあるようですね。早速使ってみます。

まずは dependency の解決です。上のガイドに従うと、Twitter を使うためには、play-pac4j_java と pac4j-oauth が必要なようですね。Maven の central repo では最新はそれぞれ 1.1.1 と 1.4.1 です。以下のように build.sbt に追加します。(このファイル、2.x では project/Build.scala だったはずなんですが、変わったんですね・・・)

libraryDependencies ++= Seq(
  javaJdbc,
  javaEbean,
  cache,
  "org.twitter4j" % "twitter4j-core" % "3.0.4",
  "org.pac4j" % "play-pac4j_java" % "1.1.1",
  "org.pac4j" % "pac4j-oauth" % "1.4.1"
)  

前回やったように、IntelliJ IDEA を一度止めて play idea してから開きなおすのが手っ取り早いようです。ではコードの変更です。
  1. コントローラーが継承しているクラスを Controller から JavaController に
  2. Global.onStart() で TwitterClient を登録
  3. ログインを要求したいコントローラーメソッドに @RequiresAuthentication を追加
  4. routes に callback を追加
具体的には
1
import org.pac4j.play.java.JavaController;

public class Application extends JavaController {
2
public class Global extends GlobalSettings {
    @Override
    public void onStart(Application application) {
        final TwitterClient twitter = new TwitterClient(CONSUMER_KEY, CONSUMER_SECRET);
        final Clients clients = new Clients("http://localhost:9000/callback", twitter);
        Config.setClients(clients);
3
    @RequiresAuthentication(clientName="TwitterClient")
    public static Result top() {
4
# For pac4j
GET   /callback               org.pac4j.play.CallbackController.callback()
POST  /callback               org.pac4j.play.CallbackController.callback()
GET   /logout                 org.pac4j.play.CallbackController.logoutAndRedirect()
おお、結構作業量が少なくていい感じ。早速実行してみましょう。
[info] play - Application started (Dev)
Uncaught error from thread [play-akka.actor.default-dispatcher-7] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem[play]
java.lang.AbstractMethodError
あっさりオワタw
まだ 2.2 がサポートされてないんですね。この方も嘆いてますが、Play! Framework ってバージョン間の互換性がなさすぎる・・・。

仕方ないので Play! Framework を 2.1.5 にしてみます。2.1.5 で play new して作った雛形から、Build.scala をコピーして、build.sbt 内に加えた変更を移動します。あとは念のため diff で差分が出た build.properties とか plugins.sbt とかを直していざテスト。

おっ!
うまくいきました。これは結構簡単。

最後にお約束の twitter4j のインスタンス作り。
    @RequiresAuthentication(clientName="TwitterClient")
    public static Result login() throws TwitterException {
        final TwitterProfile profile = (TwitterProfile) getUserProfile();
        TwitterFactory factory = new TwitterFactory(new ConfigurationBuilder().setOAuthConsumerKey(CONSUMER_KEY).setOAuthConsumerSecret(CONSUMER_SECRET).build());
        Twitter tw = factory.getInstance(new AccessToken(profile.getAccessToken(), profile.getAccessSecret()));
これでうまくいきました。

2013年9月30日月曜日

Heroku を試す(7)~ Play! Framework 2.2 を IntelliJ IDEA で動かしてみる

実は前の記事はだいぶ前に書いていて公開し忘れていたものでした。このたびもう一度使ってみようと思ったので、新しいエントリを書く前にとりあえず公開したものです。で、もう一度やろうとしたらいろいろバージョンも変わっていたので変化部分を。

まず前回はあきらめた git のバージョンですが、ちゃんとバージョンをあげておきました。

$ git --version
git version 1.7.11.3

これなら文句は言われまいw IntelliJ IDEA を起動しましたが、確かに何も警告は出ませんでした。勝った!(←何に?w)これで IDEA のほうも最新バージョンで進めます。

Play! Framework のほうですが、最新は 2.2.0 になっていました。なにやら嫌な予感が・・・。Play! Framework 自体は好きなんですが、バージョンアップ時の挙動が結構変わるのが難儀するんですよね。まあいいか、早速プロジェクトを作ってみます。


orz
こんな予想的中してもうれしくないw まだIDEAのプラグインが対応してないのかな。ここは気を取り直して、Play側から作って IDEA 対応を試みます。

$ play new sample-app
$ cd sample-app
$ play
[sample-app] $ idea

これを IDEA から開くと、無事開けました。

前回なぜはまらなかったのか不明ですが、デフォルトでは起動すると 9000 番ポートにつなぎに行きます。しかし他のアプリケーションで使っていると当然失敗します。直接コマンドラインから指定するときは

$ play run --http.port=9001

のようにすればいいのですが、IDEA からの起動だと play に直接コマンド渡せるわけではないので。結論としては Run/Debug Configurations のところで、Play 2 の起動構成を出し、 JVM Options に -Dhttp.port=9001 のように足せばポートが指定できます。

これで起動、デバッグ、あとは heroku へのデプロイ等は問題なくできました。しかし初回の push は長い・・・。いろんなライブラリの解決してるからっぽいですけど。あとはデータベースへの接続やバージョン管理(GAEで version.application.appspot.com で試せるやつのような)のやり方を学ばねば。

2013年9月29日日曜日

Heroku を試す(6)~ Play! Framework 2.0 を動かしてみる

さてなんとなく感じがつかめてきたところで今度は Play! Framework を使ってみようと思います。せっかくなのでちゃんと対応している(はずの)IntelliJ IDEA を使ってみましょう。Eclipse でやると結局のところ Java 以外のところはテキストエディタとして使うことが多くていまいち便利になった感がないので。

早速起動してプロジェクトを作成・・・ってあれ?前はここに Play! って出てた気がしたんですが・・・。恐らく完全な気のせいで、プラグインを入れなければいけないようです。それじゃ Eclipse と同じとかは思わないことにしますさくっと入れますw すると、おお、確かにでてきましたね!

実は最初何度やってもバージョンを変えてもうまくいかなくて相当悩んだんですが、ここに問題があがっていてこれかと思って普通にコマンドラインから play new してもエラーになったんです。なんだろうと思ったら Play! Framework 自体を /opt (つまり書き込み不可な場所)に入れていたのが原因だそうで。どうせ読み書き可能にしなきゃいけないのならユーザーのディレクトリに入れて指定したところうまくいきました。この思想がいいかどうかは別として。

さてこれを Heroku に上げてみましょう。Heroku のほうPlay! Framework のほうと両方に異なる文書がありますが、スクロールバーが短い(w) Play! のほうにしたがってみます。

まずは Store your application in git の部分。git コマンドをたたけばいいわけですが、IDEA から行きましょう。VCS > Import into Version Control > Create Git Repository で・・・

orz

厳しいっすw

git は rpm 経由で入れていて上げるのはまあまあ面倒なので、12.0.1 を再度引っ張り出してきて構成しました。するとコマンド自体は動いていたのか、既に git の管理下になっているようです。

では heroku の設定を。
$ heroku create sdk0815-sample2
Creating sdk0815-sample2... done, region is us
http://sdk0815-sample2.herokuapp.com/ | git@heroku.com:sdk0815-sample2.git
Git remote heroku added
これでリモートがセットされたはず。 しかし
$ git push heroku master
Warning: Permanently added the RSA host key for IP address '50.19.85.154' to the list of known hosts.
error: src refspec master does not match any.
error: failed to push some refs to 'git@heroku.com:sdk0815-sample2.git'
うーむ。これは何のエラーだろう。いろいろ調べたらどうやら何も push するものがないときに出るエラーみたいですね。 git commit したら確かにコミットされたので commit まではされてなかったんですね。
#IDEA からやるべきだったんですがついコマンド打っちゃいました。プロジェクトを右クリックで Git > Commit Directory... で全部 commit できます。

さて今度は push したらずっと待たされました。いけー。どりゃー。おおおお!

やったー!・・・ってあれ、こんな殺風景なはずは・・・w

どうもフレームワーク動いてないですね、これ。前に作ったときにあった Procfile にあたるものが必要なのかも。と思ったらちゃんと書いてありました。 これを作って再度挑戦です。また待たされました。いつ終わるのかな~。終わった!

変化なしw

よく考えてみると、この "Your new application is ready." という文字列って、ソースコードの中では
@(message: String)

@main("Welcome to Play 2.1") {
    
    @play20.welcome(message, style = "Java")
    
}

となっていて、文字列はコントローラである Application.java から送られてきています。ということはフレームワークは動いているということかな? @play20 というカスタムタグ(?)が動いていないようだと。

まあちょっと玉虫色な感じもしますが、とりあえず動かせるところまではもってこれました。実際に何か作るかは意欲と時間とネタ次第です。

2013年8月2日金曜日

IntelliJ IDEA で maven の dependency を解決する

ほんのちょっとした Tips 的ですが、しばらく悩んだので。

IntelliJ IDEA で Maven Module を指定して Project を作成します。ここまではよし。しかし実際に作成された pom.xml に dependency を追加する方法がわかりませんでした。すると、いくつか方法はあるようですが、そのうちの1つはエディターから Alt+Enter 等で出てくるメニューから "Add Maven Dependency..." を選べばよいと。ふむふむ。出てくるダイアログで文字列検索すれば・・・
 でてこない。 orz
全く出てこないわけではなく、何も入れてないときは ant とか commons なんとかとかそのへんは出てるんですよね。うーむ。どうもローカルにあるリポジトリだけを見てるっぽいですね。

そんなときは File > Settings から Maven > Repositories を選ぶと以下のような画面になるので、central repo を選んで Update を押せばよいようです。
その後同じ操作をすると、
めでたしめでたし。

2013年6月26日水曜日

Play! Framework で twitter4j を使う

久しぶりにちょっと時間ができたので遊んでみました。

表題のとおりです。とりあえず OAuth でログインできるところまで行きたいですね。

まずは twitter4j を追加します。これまでは jar をコピーしていたのですが、そんなことするよりずっと簡単な方法があります。Managed Dependency という項に書かれているように Build.scala に依存を書けば勝手に解決してくれます。フレームワークって大事ですね。
  val appDependencies = Seq(
    "org.twitter4j" % "twitter4j-core" % "3.0.3"
  )
これだけ・・・。便利になりました。

早速コードを書き始めると・・・おや、Play! を再起動しても IntelliJ IDEA では解決してくれないですね。うーむ。File > Invalidate Caches なんかもやってみましたがだめです。どうやら
$ play idea
を実行しないとだめみたいです。このへんはもう少し便利になってくれるとうれしいですけどね。まあこれで特に問題はないです。

では OAuth のページを作成しましょう。

と思ったらなんか便利そうなものを見つけました。 ここに書かれている SecureSocial なるものを使ってみますか。また dependency をつけます。
  val appDependencies = Seq(
    "org.twitter4j" % "twitter4j-core" % "3.0.3",
    "securesocial" %% "securesocial" % "master-SNAPSHOT"
  )

  val main = play.Project(appName, appVersion, appDependencies).settings(
    // Add your own project settings here
    resolvers += Resolver.url("sbt-plugin-snapshots", new URL("http://repo.scala-sbt.org/scalasbt/sbt-plugin-snapshots/"))(Resolver.ivyStylePatterns)
  )
この時点では Play! 2.1 には spapshot を使えとあるのでそうしてみました。これで同じように play idea も実行して解決させておきます。routes は書いてあるものそのままで。play.plugins からは不要な Provider を消します。
9994:securesocial.core.DefaultAuthenticatorStore
9995:securesocial.core.DefaultIdGenerator
9997:securesocial.controllers.DefaultTemplatesPlugin
9998:service.InMemoryUserService
10000:securesocial.core.providers.TwitterProvider
こんな感じで。あ、sample から InMemoryUserService を持ってきました。動作確認に使えます。次に securesocial.conf を作って application.conf から include します。
securesocial {
  onLoginGoTo=/
  onLogoutGoTo=/login
  ssl=false

  twitter {
     requestTokenUrl="https://twitter.com/oauth/request_token"
     accessTokenUrl="https://twitter.com/oauth/access_token"
     authorizationUrl="https://twitter.com/oauth/authenticate"
     consumerKey="****"
     consumerSecret="****"
  }
}
いよいよ実行です・・・。とその前に、ログインしてないとだめなメソッドに @SecureSocial.SecuredAction アノテーションをつけます。
    @SecureSocial.SecuredAction
    public static Result index() {
        Identity user = (Identity) ctx().args.get(SecureSocial.USER_KEY);
        final String userName = user != null ? user.fullName() : "guest";
        return ok(index.render("Logged on as " + userName + "."));
    }
では実行です。/login にアクセスすると
まずはこのようにログインの画面が出ます。ここで t のアイコンをクリックするといつものツイッターの認証画面に飛ばされます。そこで認証を行うと
のようになります。やったー。・・・ツイッターだけだと正直楽になったかどうか微妙ですがw ほかのサービス(facebook とか)でもログインできるのでその手のサービスを作りたいときは便利なのは間違いないですね。

さてこれでログインはできましたが、twitter4j が使えなければ意味がないわけで。ログインした後に実行されるメソッドの中身を変えます。

        String ret = "Unknown";
        Identity user = (Identity) ctx().args.get(SecureSocial.USER_KEY);
        if (user != null) {
            Twitter twitter = new TwitterFactory().getInstance();
            twitter.setOAuthConsumer(Play.application().configuration().getString("securesocial.twitter.consumerKey"),
                    Play.application().configuration().getString("securesocial.twitter.consumerSecret"));
            AccessToken accessToken = new AccessToken(user.oAuth1Info().get().token(), user.oAuth1Info().get().secret());
            twitter.setOAuthAccessToken(accessToken);
            User u = twitter.verifyCredentials();
            ret = "Logged on as " + u.getName() + "(@" + u.getScreenName() + ") followed by " + u.getFollowersCount() + " followers.";
        }
        return ok(index.render(ret));
実行すると・・・
 できたー。