はてな朗読会第1回公演会
タイトルは嘘です。はてな朗読会というものがあるわけではなく(たぶん。はてなスペースに「朗読」スペースはあります)、はてなユーザーによる朗読会というわけでもないです。朗読者ではてなユーザーはぼくだけです(たぶん。知らないだけでじつは、とかだったら面白いけど)。
主催はふくしま現代朗読会で、第1回公演会のタイトルは「第一回 言葉で奏でるコンサート 宮沢賢治の世界」です。タイトルからもわかるように、「宮沢賢治縛り」でいきます。「やまなし」「いちょうの実」「セロひきのゴーシュ」「雨ニモマケズ」の4編を読みます。ぼくは「いちょうの実」の予定です。朗読教室の生徒(ぼくを含む)が前半2編、プロの話し手が後半2編を読みます。
フライヤーはこんな感じ:
- 日時:2014年9月20日(土)13:30~
- 場所:ホテルハマツ1階ロビー
ホテルハマツって、福島県郡山市でたぶん一番でかいホテルです。
入場無料なので、当日フラッとお寄りいただければ幸いです。
予習はどうするか
宮沢賢治を前もって読んでおいたほうがいいのか、と聞かれたら、聴き手が読んでいなくとも楽しめるように、朗読者はがんばります、というのがたてまえですが、読んでいたらなおいっそう楽しめる、というのが朗読の世界です。
個人的にはちくま文庫からでている全集をおすすめします。新潮文庫とかは省略や修正が多い。それと、ちくま文庫の全集には「異稿」も(研究上明らかになっているものに関しては)入っているので、「宮沢賢治をちゃんと読む」ためには必須ともいえます。
というのは、宮沢賢治の作品は、生前に発表されたもののほうが少く、しかも彼は、しょっちゅう、それもかなりドラスティックに改稿に改稿を重ねる作家でした。「最終稿」とか「決定稿」というものはないと言ってもよいでしょう。具体的には、かの有名な「銀河鉄道の夜」には「第4次稿」まであるのですが、それは4つの原稿用紙の束があるというのではなくて、さまざまな筆記具で、さまざまな用紙を使って、書き加えたり削除したりを繰り返しているプロセスが大雑把に言って4段階ある、というにすぎず、「第4次稿」とされているものも「削除し忘れ」による矛盾点があって(削除されたはずの人物がいきなりセリフを言ったり)、現在我々が「宮沢賢治の作品」として読んでいる「銀河鉄道の夜」は後年に研究者(発掘者)が手直しをしたものでしかないのです。
「やまなし」
宮沢賢治が生前に出版した単著は、童話集『注文の多い料理店』と詩集『春と修羅』(第1集)のみですが、同人誌や新聞・雑誌への寄稿はいくつかあります。この「やまなし」も『岩手毎日新聞』に寄稿されたものです。今回の公演会で朗読される作品で、生前に発表されたものはこれだけです。
ちくま文庫版全集では第8巻に収録されています。第8巻には他に「注文の多い料理店」なども所収。
教科書で読んだよ、という方も多いかと思いますが、むしろ最近だと『花もて語れ』で久しぶりに読んだという方が多いかと。「クラムボン」のあれです(発音は「クランボン」)。
『花もて語れ』はこの記事執筆時点で12巻まで出ていますが(13巻で完結予定)、宮沢賢治作品が頻出なので、いちおう全巻読破しておくと良いかと思います(Kindle版だと32%オフ、現時点でポイントが30%バックなのでお得です。最新刊の12巻はまだKindle版が出てないですが)。
今回の公演会では、この「やまなし」しかかぶってないのですが、「朗読」にとって宮沢賢治は、釣りにとってのフナのようなもので、「賢治にはじまり賢治に終わる」と言ったら……過言かもしれませんが、『花もて語れ』で「宮沢賢治はどう読まれうるか」ということをイメージしていただければ、楽しめるかと思います。
「いちょうの実」
全集では第5巻に収録(「いてふの実」)。他に「貝の火」や「よだかの星」といった、斎藤環がラカンを説明するために引用した童話も所収。それから「風野又三郎」も(有名なのは「風の又三郎」の方)。
『花もて語れ』に「おきなぐさ」の朗読が出てきますが(6~7巻)、「おきなぐさ」の原稿表紙裏に「花鳥童話集」として11篇の童話題名が列挙されていて、その中のひとつが「いてふの実」。
「セロひきのゴーシュ」
全集では第7巻に収録。他に「銀河鉄道の夜」「風の又三郎」も所収。前述したように、「銀河鉄道の夜」がいかにドラスティックに書きなおされたのか、その図解が面白いので、解説も必読。
「セロひきのゴーシュ」は、東百道氏が「朗読がうまくなるプロセスをストーリーにしたもの」(大意)と言っているように、朗読者にとってはひとつの重要な道標ともいえる作品。
「雨ニモマケズ」
全集では第10巻に収録。他に「農民芸術概論」など所収。第10巻は手帳やノートに残されたメモなどを集めた巻で、超マニアック。
「雨ニモマケズ」も手帳に鉛筆書きされたメモで、詩として残されたものではない(一般には詩として受容されていますが)。解釈としてもさまざまで、手帳の右ページで終わりとするか(「サウイフモノニ/ワタシハナリタイ」で終わり)、左ページまでを含むとするか(「南無無辺行菩薩/南無上行菩薩/南無多宝如来/南無妙法蓮華経/南無釈迦牟尼仏/南無浄行菩薩/南無安立行菩薩」で終わり)によって、出版形態が異なったりします(新潮文庫の賢治詩集は「左右ページで終わり」派)。
あと、賢治の詩は無題が多く、これも(そもそも詩として書かれていないので当たり前ですが)無題の作品ですが、たいてい第一行を〔雨ニモマケズ〕のような書き方でタイトルの代わりにすることが多いようです。
本題 広告出稿募集
公演会は入場無料ですが、パンフレットが配られます。印刷代とかを広告でまかないます。ぜひ日本の知性のトップを担うはてなユーザーの皆様方におかれましては、広告枠を購入していただきたく!
パンフレットは100~200部ぐらい配布されます。
カラーです。(1)2分の1ページ・縦122mm×横166mm、(2)4分の1ページ・縦61mm×横166mm、(3)8分の1ページ・縦58mm×横81mm、の3サイズ。
価格は(1)20,000円、(2)10,000円、(3)5,000円。
フォトショ・イラレのCS5対応で350dpi、CMYKで。jpgでRGBでもいいそうです。
ご連絡はGMailにお願いします(@の前がhidex7777)
かなり久しぶりにAndroid SDKに触ったのでメモ(基礎の基礎)
Android SDKが見つかんねーな(自分のPCに)、と思ったら、OSのクリーンインストールをして以来、ダウンロードすらしていなかった!
そんぐらい触ってないと、何をどうしたらddmsとかができるのかとかまで忘れている!
そんなわけで自分用メモに手順書を残しておくぜ・・・
Android SDKをダウンロードする
http://developer.android.com/sdk/index.html
ここの「DOWNLOAD FOR OTHER PLATFORMS」にあるSDK Tools Onlyのzipをダウンロードする。
ADTバンドルとかいらねー。
ダウンロードしたら解凍、android-sdk-windowsとかいうフォルダが生まれた!
こいつをC:\に配置!
パスとかは後回し!
JDKをダウンロードする
http://www.oracle.com/technetwork/java/javase/downloads/index.html
ここの最新版(でいいのかな?)をダウンロードする。
インストール済みのJDKは1.7系統だけど、1.8系統が出ているみたいだ(記事執筆時点)。
ぼくはWindowsの64bit環境だからそのexeをダウンロードすることにした。
JDKのインストールはexeのおおせのままに・・・
PATHを通す
先に入っていたJDK1.7のパスを書き換えてしまうかな・・・
C:\Program Files\Java\jdk1.8.0_05\bin;C:\android-sdk-windows\tools;C:\android-sdk-windows\platform-tools
にパスを通した。platform-toolsはこれからダウンロードすることになるのだ・・・
>java -version java version "1.8.0_05" Java(TM) SE Runtime Environment (build 1.8.0_05-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mi
なかなかによさそうだぞ!
SDK managerを使う
Windows版だと、SDK manager.exeというのがついてくる。
ダブルクリックしてしばらくたつと、GUI画面が立ち上がる。
なにやら通信しているが、しばらくするとDone、とメッセージ。
Toolsにチェック、Androidなんちゃらのチェックははずす、Android Support Libraryのチェックもはずす、Google USB Driverのチェックはいちおう入れたままにしておく。
Install 12 Packagesをクリック!
Accept License→Install!
今回はAndroidなんちゃらをインストールしなかったので、思ったよりも時間がかからなかった。
platform-tools/というディレクトリがつくられて、ここにadbがある。
コマンドプロンプトを再起動して
>adb devices List of devices attached
もしAndroid端末が接続されていて、adb接続もされているのであれば、Listになんかしら表示されるはず。
されないということは、さらになにかが必要なのだ。
Android側の設定
機種によってはGoogle USB Driverでは認識してもらえないことがある。
F-05Dのような極悪非道な充電式カイロなんかがそうだ。
ググって
http://spf.fmworld.net/oss/driver/f-05d/
のようなサイトからダウンロードする。
F-05Dはadb接続を可能にするためにいろいろとめんどくさすぎるぞ!
ドライバをインストールした後もadb_usb.iniがないからandroid update adb
コマンドを打ったり、iniファイルに文字列がないから自分で書き込んだり・・・
menu→本体設定→アプリケーション→開発→USBデバッグにチェック。
USBケーブルでPCに接続。
コマンドddms
。
Dalvik Debug Monitorが開く。
接続されているデバイスを選択して、Device→Screen Captureでスクリーンショットがとれたりする。
Android4.0以降は、デバイス側で簡単にスクショとれるんだけどね。
いろいろ作業が終わったらadb kill-server
でプロセスを終了しておく。
いまさらながらBower入れてGruntでcss-modal
いまフロントエンドのみの案件にとりかかっているのですが、いわゆるモーダルウィンドウが必要なケースが出てきました。
なんとなーく「jQueryでいいよなー」と考えていたのですが、「そーいやCSSだけでできるのがなんかあったよなー」と思い出し、
を試してみることにしました。
bowerインストール
Bowerはnpmみたいなパッケージマネージャです。
そんでBowerはNode.jsのパッケージです。なのでNode.jsのパッケージマネージャであるnpmでインストールできます。
ややこしいですね。
Bowerの基本的なことはfrom scratch様の記事
らへんを参考にさせていただきました。
まずNode.jsのアップデートから始めるのが基本ですが、ぼくの環境はWindowsなのでアップデートするにはインストーラ使うしかないです。
Windowsじゃない方は
$ node -v $ sudo npm cache clean -f $ sudo npm install -g n $ sudo n stable $ node -v
でアップデート。
Windowsだとnコマンドが使えないです(n --help
だけ使えますw)。
この記事を書いている時点で、Windowsで利用できる最新版が0.10.28みたいです。
つぎにBowerのインストール。
(たぶんWindowsだと、Gitがインストールされていることが必須です)
>npm install -g bower >bower -v 1.3.3
bower init
使いたいパッケージと目的が決まってるので、いきなり現行の開発環境に行きたくなりますが、テストから始めるのが良いかと思います。
さっき現行の開発環境で試しながらやってたら、とあるディレクトリがきれいさっぱり消えてなくなりました。
gitがなかったら死んでました。
>mkdir bowertest >cd bowertest >git init .gitignoreファイル node_modules/ bower_components/ >git add . >git commit -m "init bowertest" >bower init
めっちゃいろいろ聞かれますが、
[?] main file: index.js [?] add commonly ignored files to ignore list?: Yes
このふたつの項目は「公開するライブラリ(Bowerにレジストする)なら」重要、とfrom scratch様の記事には書いてあった。
ここでは公開するライブラリの制作目的でなく、プロダクトの制作にのみ使うことを想定していますが、いちおう上記のように答えました。
あと、公開されないようにprivateのマークを付けるか、と聞かれるので
[?] would you like to mark this package as private which prevents it from being accidentally published to the registry? Yes
これもYesにしました。
最後に書きだされるbower.jsonファイルが良さそうかどうか聞かれるのでこれもYesで答えます。
とりまcss-modalをインストール
>bower install css-modal --save-dev
bower.jsonのdevDependenciesにcss-modalが記述されます。
インストールしてみると、./bower_components/css-modal/以下にいろいろとファイルが置かれていますが、このうち必要なのはmodal.scssとmodal.jsだけです。
そんでもってscssファイルは/dev/scssディレクトリに、jsファイルは/dist/jsディレクトリに配置したいとします。
手作業でやるのでは自動化している意味がないので、Gruntでディレクトリ・レイアウトを指定することにします。
grunt-bower-task
bowerでパッケージをインストールすると、ディレクトリ構造ごと持ってくるので、レイアウト的に使いづらい。
レイアウトを使いやすいようにするgrunt-bower-taskをインストールします。
まだgrunt-cliがインストールされていなければ:
>npm install -g grunt-cli
まだカレントディレクトリがGruntで運用されていなければ:
>npm init >npm install grunt --save-dev
package.jsonのdevDependenciesにgruntが記述されます。
>npm install grunt-bower-task --save-dev
終わったら、Gruntが使うpackage.jsonファイルのdevDependenciesにgrunt-bower-taskが記述されています。
Gruntfile.coffeeにbowerの挙動を記述します
module.exports = (grunt) -> pkg = grunt.file.readJSON 'package.json' #config grunt.initConfig bower: install: options: #ターゲットディレクトリを指定します targetDir: './lib' #byTypeでファイルの種類ごとにディレクトリを分けます layout: 'byType' #bower installタスクを実行したいか否か install: true #詳細ログを出すか verbose: true #インストール前にターゲットディレクトリを削除するか cleanTargetDir: true #必要なファイルをターゲットディレクトリにコピーした後にbower_components/を削除するか cleanBowerDir: false #plugin for taskName of pkg.devDependencies grunt.loadNpmTasks taskName if taskName.substring(0, 6) is "grunt-" return
とりあえずこの設定でインストールを実行してみます。
>grunt bower:install
/lib/css-modal/にmodal.jsとmodal.scssがインストールされました!
cleanBowerDirをfalseにしたので、bower_components/も残っています。
bower_components/css-modal/bower.jsonをみると、"main"にmodal.scssとmodal.jsが書いてあるので、必要なファイルのみ/lib/以下にコピーされたわけです。
exportsOverride
layoutをbyTypeにしたのに、ファイルタイプごとに分けられず、/css-modal/以下に2ファイルとも配置されてしまいました。
これはタイプが定義されていないためです。
bower.jsonファイルで定義します:
"devDependencies": { "css-modal": "~1.0.4" }, "exportsOverride": { "css-modal": { "js": "**/*.js", "scss": "**/*.scss" } } }
このように定義してgrunt bower:install
を実行すると、
のようにタイプ分けしてくれます。
今回のケースではcss-modalしかインストールしていないので、よけいにゴチャゴチャしたように見えますが、いろんなパッケージをbowerでインストールしたときは、こっちのほうが便利なケースがあるかもしれません。
ファイルタイプでのレイアウトよりコンポーネントでのレイアウトのほうがいい、という場合は、layoutオプションを'byComponent'にします。
grunt-contrib-copy
もしかしたらgrunt-bower-taskでうまいことやれば、最終目的配置までやれるのかもしれませんが(layoutオプションに関数を定義できます)、めんどくさいので、grunt-contrib-copyを使って、決め打ちでコピーしてしまいます。
どうせファイル名がわかっているので。
>npm install grunt-contrib-copy --save-dev
package.jsonに追記されました。
Gruntfile.coffeeでコピーのタスクを記述します:
module.exports = (grunt) -> pkg = grunt.file.readJSON 'package.json' #config grunt.initConfig bower: install: options: targetDir: './lib' layout: 'byType' install: true verbose: true cleanTargetDir: true cleanBowerDir: true copy: cssModalScss: expand: true cwd: "lib/scss/css-modal/" src: "modal.scss" dest: "dev/scss/" flatten: true filter: 'isFile' cssModalJs: expand: true cwd: "lib/js/css-modal/" src: "modal.js" dest: "dist/js/" flatten: true filter: 'isFile' #plugin for taskName of pkg.devDependencies grunt.loadNpmTasks taskName if taskName.substring(0, 6) is "grunt-" #tasks grunt.registerTask "bowerinstall", [ "bower:install" "copy:cssModalScss" "copy:cssModalJs" ] return
こんどはcleanBowerDirをtrueにしてみました。
(copyの書き方も、もっとうまいことできるかと思いますがめんどくさいので1ファイルごと書いています)
これでgrunt bowerinstall
を実行すると、/bower_components/ディレクトリが消え、/lib/以下に配置された各ファイルが、目的の場所にコピーされました。
css-modal
そういえば、この記事は、CSS-Modalを使うために書き始めたのでした。
完全に忘れていた。
CSS-Modalは、cssのみを使ってモーダルウィンドウを表現するライブラリです。
ただしIE8との互換性やESCキーでモーダルを消す、などの機能のためにmodal.jsも使うことになります(jsがなくても、IE8で機能することは機能するらしいです)。
使い方は簡単で
@import 'modal';
でscssファイルにインポートして、モーダルウィンドウになるパーツに対して
.my-modal-section{ @extend %modal; }
のように適用するだけです。
注意点は、
- modal.scssは_modal.scssにリネームして使ったほうがいいかなー
- _modal.scssの1行目に@charset "UTF-8";を書かないとたぶんエラーが出る
- csslintはオフる
HTMLのマークアップはこんな感じ
<div class="work"> <figure class="workimg"> <a href="#modal-work01"><img src="img/dummy300.png"></a> </figure> <dl> <dt class="worktitle">モーダル作品1</dt> <dd class="workdesc">モーダル作品についての説明です。モーダル作品についての説明です。モーダル作品についての説明です。</dd> </dl> <section class="modal-work" id="modal-work01" tabindex="-1" role="dialog" aria-hidden="true"> <div class="modal-inner"> <div class="modal-content"><img src="img/work_01_image.jpg" alt="作品01"></div> </div> <a href="#!" class="modal-close" title="Close this modal" data-close="Close" data-dismiss="modal">×</a> </section> </div><!-- /.work -->
imgにaのhrefで#modal-work01へのトリガー、モーダルウィンドウ側はhrefに#!でトップに移動しないようにする。
ちょっと気になるのは、CLOSEしたときにURLに#!がついてしまうことかなー。
WindowsでもVagrantでChefしたい、rbenvも(11.12.4-1版)
以前、Chefの導入編の記事を書きましたが、4月に、Ubuntu14.04 LTSのまっさらなVagant Boxをつくったので、Chefのレシピを書いておきたいと思います。
ところで。
心を入れ替えます。
導入編から書きなおしてみます。
方針としては、Chefリポジトリは(1)Git+GitHubで管理する、(2)Vagrantプロジェクトの中に入れる(ディレクトリ構成のはなし)。
「Chefって結局なんなの?」という方は『入門Chef Solo - Infrastructure as Code』を読んで下さい。
- ChefとChef-Soloを(ひとまず)区別しましょう(ローカル開発環境=Vagrantで使うのはChef-Soloです)
- 「冪等性(べきとうせい: Idempotence)」「収束(Convergence)」がキーワードですよ
- NodeのことをClientとよんだりする(厳密にはChefのAPIと通信できるすべての端末のこと、したがってknifeもChef Clientのひとつ)
- knife-soloとはChef-Soloを扱うにさいして便利なツールです
こう書いてみると当たり前のことばかりですが、Chefの概念図だけ頭にあると、こんがらがってきます。
ぼくはこの本を読んで、Chefライフがすべてうまく行き始めた気がしてきました。不思議なマジックをもった本です。
筆者の環境:
- Windows 7 SP1 64bit
- Ruby 2.0.0p451
- gem 2.2.2
- Git 1.9.2(http://msysgit.github.io/)(Git\binにパスを通している)
- Vagrant 1.5.4
- VirtualBox 4.3.10
- 環境変数HOMEにC:\Users\hidex7777をセットしている
- GitHubにchef-repoという空のリポジトリを作ってある
VagrantにはUbuntuサーバ14.04のボックスが登録されている:
>vagrant box list Trusty (virtualbox, 0)
Vagrantのプラグインvagrant-omunibusは便利ですがknife solo prepare
で済むので、個人的には必要ないかな―と思っています。本番環境でVagrant使うかどうかわかんないですし。
あとvagrant-berkshelfは開発が終わるようなので、見送ります。gemでインストールするBerkshelfは終わるわけではないので、gemで入れましょう。
この記事では、rbenvとruby-buildでrubyをインストールするのに使います。
下準備
>pwd /c/Users/hidex7777 >mkdir mychef3 >cd mychef3 >vagrant init Trusty >vim Vagrantfile (Vagrantfileの修正) config.vm.network "private_network", ip: "192.168.33.10" :wq >vagrant up >vagrant ssh $ exit >vagrant ssh-config --host mychef3 >> %HOME%\.ssh\config >ssh mychef3 $ exit
Macとかの場合、ssh-config --host
の出力先は~/.ssh/config
にします。
WorkstationにChefとかをインストール
Step 2: Set up your workstationの"Run the Chef installer"の指示に従ってChef Clientをインストールします(前回、WindowsはGemでインストールするべきだと書きましたが、いまのインストーラならそちらを使うべきかと思います。knifeとかのツールもこれで使えるようになります)。
11.12.4-1がWin7で使える記事執筆時点の最新版みたいです。
インストールが終わったらコンソールを再起動して:
>path C:\opscode\chef\bin;C:\opscode\chef\embedded\bin;
にパスが通っているのを確認。
knifeの設定:
>knife configure
いろいろ聞かれますがぜんぶY
とかEnter
でOK。
便利なツールknife-soloは入れておきます:
>gem install knife-solo
Windowsの場合
ちなみに、Windows版だとないと困るgemを、前もってアンインストールしておきましたが:
gem uninstall win32-process gem uninstall windows-pr
このChefのインストールで、これらも入りました。
さらに、Windowsだとやはりrsyncがないので、cwRsyncのFree Editionをダウンロード、解凍して、中身をぜんぶパスが通っているディレクトリ(ぼくの場合はC:\Users\hidex7777\bin)に入れて、ssh.exeとssh-keygen.exeを削除。
以下の環境変数も追記:
chef-repo作成
>pwd /c/Users/hidex7777/mychef3 >knife solo init chef-repo
- ~/mychef3/chef-repo/.chef/knife.rb
ができていればたぶんOK。
自作のレシピは、つくられたディレクトリのなかのsite-cookbooksに入れます。
とりあえずGitHubにpush
GitHub側であらかじめchef-repoリポジトリをつくってあるとして:
>cd chef-repo >git init >git add . >git commit -m "first commit" >git remote add origin https://github.com/hidex7777/chef-repo.git >git push -u origin master
NodeをChefに対応させる
>knife solo prepare mychef3
- ~/mychef3/chef-repo/nodes/mychef3.json
というファイルができていれば、たぶんOKです。
defsetというcookbookをつくってみる
>knife cookbook create defset -o site-cookbooks
- ~/mychef3/chef-repo/site-cookbooks/defset/recipes/default.rb
ができていればたぶんOK。
/chef-repo/.chef/knife.rbに
cookbook_path ["cookbooks", "site-cookbooks"]
という記述があることを確認します。
(このknife.rbは~/.chef/knife.rbとは別のファイルです)
とりあえずHello Worldをやっておきます。
default.rbをテキストエディタで開いて:
log "hello world"
と記入。
~/mychef3/chef-repo/nodes/mychef3.jsonに:
{ "run_list":[ "recipe[defset]" ] }
と記入。
CookbookをNodeに反映
>pwd /c/Users/hideo64temp/mychef3/chef-repo >knife solo cook mychef3 * log[Hello World] action write
ここまででいちおうコミットしておく:
>git status >git add . >git commit -m "add defset" >git push origin master
ここまでは前回と同じです。
今回は、もうちょっと先に進みたいと思います。
ここから先は、すべてdefsetのdefault.rbに追記していきます。
aptitudeをupdate
してdist-upgrade
する
aptのupgradeはユーザがrecipeで自動化するべきではない、と述べられています。
そのため、これはインストール後、手動でやるべきとされています。
パッケージを最新版にしたければ、packageのリソース内に記述するべきであると。
例:
package "nginx" do action :upgrade end
とはいえ、使い捨てのVagrantにそこまで気を使う必要もない気がするので、次のように書いてしまう:
execute "aptitude-dist-upgrade" do command "aptitude dist-upgrade -y" action :nothing end execute "aptitude-update" do command "aptitude update -y" notifies :run, "execute[aptitude-dist-upgrade]", :immediately end
cookのとき、dist-upgradeのほうはaction :nothingなのでスルーされて、updateが実行されたときにnotifiesでdist-upgradeを:runするようになっています。
なお、「この書き方ではaptシステムを使っているディストリビューションでしか有効でなく、普遍性がないではないか」という批判は正しいのですが、「個人的レシピは徐々に育てる」をモットーにしていきたいと思います。
.bashrcファイル中のHISTSIZEとHISTFILESIZEをそれぞれ90000にする
templateリソースを使います。
ぼくはConsole2を使っているので、chef操作をしているタブとは別タブで、sshログインしておきます。
ssh mychef3
したコンソールのタブで、
cp /home/vagrant/.bashrc /vagrant/.bashrc.erb
でコピーしておく。
Nodeの/vagrantディレクトリは、WorkstationのVagrantfileがあるディレクトリと共有フォルダになっているので、手元にコピーがきます。
このファイルをおもむろにコピーして、/chef-repo/site-cookbooks/defset/templates/defaultディレクトリにペーストします。
この.bashrc.erbファイルをテキストエディタで開き、以下の部分を次のように書き換えます:
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1) HISTSIZE=<%= node['bashrc']['histsize'] %> HISTFILESIZE=<%= node['bashrc']['histfilesize'] %>
サイズは変数にしておきます。ファイル名は拡張子.erb
をつけて.bashrc.erbとして保存。
/chef-repo/nodes/mychef3.jsonをテキストエディタで開き、次のように変数の値(Attribute)を用意しておきます:
{ "bashrc":{ "histsize": 90000, "histfilesize": 90000 }, "run_list":[ "recipe[defset]" ] }
/chef-repo/site-cookbooks/defset/recipes/default.rbにtemplateリソースの記述をします:
template ".bashrc" do path "/home/vagrant/.bashrc" source ".bashrc.erb" mode 0644 end
コンソールのChef操作タブに戻り、
> knife solo cook mychef3
でNodeに反映させます。
-HISTSIZE=1000 -HISTFILESIZE=2000 +HISTSIZE=90000 +HISTFILESIZE=90000
のようにdiffが表示されればOKのはずです。
Vagrantにログインしているタブに切り替えて、ll
コマンドで.bashrcファイルのモードが-rw-r--r--
(つまり0644)になっているのを確認。
vi .bashrc
で、ファイルが適切に書き換えられているのを確認する。
.bash_aliasesファイルにalias eng='export LANG=en_US.UTF-8'
とalias jpn='export LANG=ja_JP.UTF-8'
の行を追記
これはssh接続でなく直接サーバのターミナルを開いたときに、日本語がちゃんと表示されないのを回避するためのエイリアス。
ssh接続のときはjpn
で日本語表示、ターミナルで直接開いたときはeng
で英語表示にする。
これもtemplateを使う。
たぶんインストールしたばかりのサーバには.bash_aliasesファイルはないので、Workstation側で作る。
alias eng='export LANG=en_US.UTF-8' alias jpn='export LANG=ja_JP.UTF-8'
と書いて、.bash_aliases.erbとして/chef-repo/site-cookbooks/hell/templates/defaultに保存する(この時点ではerbにする意味はないのですが)。
/chef-repo/site-cookbooks/defset/recipes/default.rbに追記する:
template ".bash_aliases" do path "/home/vagrant/.bash_aliases" source ".bash_aliases.erb" mode 0644 end
vim-noxをインストールしてデフォルトエディタにする
vim-noxのインストールのnotifiesからupdate-alternativesを呼び出すようにする(default.rbに追記):
execute "update-alternatives-editor" do command "update-alternatives --set editor /usr/bin/vim.nox" action :nothing end package "vim-nox" do action :install notifies :run, "execute[update-alternatives-editor]", :immediately end
etckeeperとgitとtigのインストール
Chefで完全管理して冪等性が保たれているのにetckeeperもなにもないだろう、という気もしますが、なければないで不安なので、入れます。
vim-noxのときのように、actionに:nothingを指定しておいて、他から呼び出すように書いてもいいのだけれど、どれかが何かのはずみでインストールされてしまうと、他がインストールされなくってめんどくさいので、個別に書きます:
package "etckeeper" do action :install end package "tig" do action :install end package "git" do action :install end
もちろんvim-noxのところでやったように
package "git" do action :install notifies :install, "package[tig]", :immediately end
とやってもよいでしょう(tigのactionは:nothingに)。
/etc/etckeeper/etckeeper.confの編集
.bashrcのときと同じように、ssh mychef3
したコンソールのタブで、
cp /etc/etckeeper/etckeeper.conf /vagrant/etckeeper.conf.erb
でコピーしておく。
このファイルを/chef-repo/site-cookbooks/defset/templates/defaultディレクトリにペーストします。
次の部分を書き換えます:
# The VCS to use. #VCS="hg" VCS="git" #VCS="bzr" #VCS="darcs"
これも、この時点ではerbにする意味はないですが(変数の埋め込みがないので)、念の為にerbファイルにしてtemplateとして扱うのがベターかと思います。
伊藤直也本でもそう述べられています。
template "etckeeper.conf" do path "/etc/etckeeper/etckeeper.conf" source "etckeeper.conf.erb" mode 0644 end
ntpをインストール
package "ntp" do action :install end
ntpはサービスですが、Ubuntuは「インストールしたサービスは、常時動いている。止めたきゃアンインストールせよ」という方針のOSなので、基本的にはサービスのスタートを考えなくてよいとも言えます。
時刻が著しくズレていて同期がとれなくなった場合のことを考えて、設定ファイルを書き換えてもよいかもしれません:
service "ntp" do supports :status => true, :start => true, :stop => true, :restart => true, :reload => true action :nothing end execute "ntpdate" do command "ntpdate ntp.nict.jp" action :nothing notifies :start, "service[ntp]", :immediately end template "ntp.conf" do path "/etc/ntp.conf" source "ntp.conf.erb" mode 0644 notifies :stop, "service[ntp]", :immediately notifies :run, "execute[ntpdate]", :immediately end
この場合もあらかじめ
$ cp /etc/ntp.conf /vagrant/ntp.conf.erb
でコピーをもってきて、次のように書き換えるか:
server ntp.nict.jp #server 0.ubuntu.pool.ntp.org #server 1.ubuntu.pool.ntp.org #server 2.ubuntu.pool.ntp.org #server 3.ubuntu.pool.ntp.org
デフォルトをコメントアウトせずにntp.nict.jpの行を書き加えるだけにするか、どちらでもよいでしょう。
あるいは設定ファイルは書き換えずに、installと同時にstopしてntpdateしてstartする、というだけでもいいかもしれません(ぼくはそうしています)。
パスワード認証の無効化(/etc/ssh/sshd_config)
sshd_configファイルをテンプレート化します。
ssh mychef3
してあるタブで:
$ cp /etc/ssh/sshd_config /vagrant/sshd_config.erb
手元にきたコピーを/templates以下にペースト:
#ポート番号を変数化しておく Port <%= node['sshd_config']['port'] %> #'#PasswordAuthentication yes'の行を書き換える <%= node['sshd_config']['passauth'] %> #'UsePam yes'の行を書き換える <%= node['sshd_config']['usepam'] %>
mychef3.jsonファイルに変数を追記:
"sshd_config":{ "port": 22, "passauth": "PasswordAuthentication no", "usepam": "UsePam no" },
default.rbファイルに追記:
service "ssh" do supports :status => true, :start => true, :stop => true, :restart => true, :reload => true action :nothing end template "sshd_config" do path "/etc/ssh/sshd_config" source "sshd_config.erb" mode 0644 notifies :restart, "service[ssh]", :immediately end
UFW有効化、「OpenSSHへのWorkstationからの許可およびそれ以外からのリミット」ルール作成
UFW(Uncomplicated FireWall)をあつかうのに便利なFirewallリソースが公開されているのですが、この記事を書いている時点で、Travis CIのテストがFailしているので、ひとまず避けておくことにします。
ここではより原始的な方法で。
execute "ufw-allow-openssh" do command 'ufw insert 1 allow from 192.168.33.0/24 to any app OpenSSH' action :nothing end execute "ufw-limit-openssh" do command 'ufw limit OpenSSH' action :nothing notifies :run, "execute[ufw-allow-openssh]", :immediately end execute "ufw-enable" do command 'printf y | ufw enable' notifies :run, "execute[ufw-limit-openssh]", :immediately end
ufw enable
したときに対話的に入力を求められるので(-yオプションがきかない)、printf y
で渡しています。
apache2インストール
UbuntuのAPTで配布されているApache2も、バージョンが2.4系統になったようです。
execute "ufw-allow-apache" do command 'ufw allow "Apache Full"' action :nothing end package "apache2" do action :install notifies :run, "execute[ufw-allow-apache]", :immediately end
Workstationのブラウザからhttp://192.168.33.10(Vagrantfileで設定したプライベートネットワーク)に接続して"Apache2 Ubuntu Default Page"が表示されれば成功です。
以前の簡素な"It works!"ページからだいぶ様変わりしていてびっくりしました。
ドキュメントルートを/vagrantにしたwwwサイトを作ってa2enable、000-defaultをa2disable
まずデフォルトの設定ファイルをコピー(いつのまにか.confという拡張子が付くようになりました):
$ cp /etc/apache2/sites-available/000-default.conf /vagrant/www.conf.erb $ echo "from vagrant" > /vagrant/index.html
2.2ではデフォルトのドキュメントルートが/var/wwwでしたが、設定ファイルを開くとわかるように、2.4では/var/www/htmlになっています(Ubuntu)。
また、Directoryディレクティブが書かれていないので(apache2.confに書かれている)、書き加える必要があります。
www.conf.erbファイルをtemplatesディレクトリにペースト、次のように記述:
DocumentRoot <%= node['wwwconf']['documentroot'] => <Directory <%= node['wwwconf']['directory'] %>> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory>
"wwwconf":{ "documentroot": "/vagrant", "directory": "/vagrant" },
レシピの記述:
service "apache2" do supports :status => true, :start => true, :stop => true, :restart => true, :reload => true action :nothing end execute "a2dissite-default" do command 'a2dissite 000-default' action :nothing notifies :reload, "service[apache2]", :immediately end execute "a2ensite-www" do command 'a2ensite www' action :nothing notifies :run, "execute[a2dissite-default]", :immediately end template "www.conf" do path "/etc/apache2/sites-available/www.conf" source "www.conf.erb" mode 0644 notifies :run, "execute[a2ensite-www]", :immediately end
新しい設定ファイルwwwをensiteすると同時にデフォルトの000-defaultをdissiteするのがポイントです。
mod_sslをa2enmod、apache2リロード
テンプレート用設定ファイルをコピーします:
$ cp /etc/apache2/sites-available/default-ssl.conf /vagrant/www-ssl.conf.erb
書き換えるところはありませんが(DocumentRootを書き換えたければお好きに)、templates/defaultにコピーします。
レシピ:
execute "a2ensite-ssl" do command 'a2ensite www-ssl' action :nothing notifies :reload, "service[apache2]", :immediately end execute "a2enmod-ssl" do command 'a2enmod ssl' action :nothing notifies :run, "execute[a2ensite-ssl]", :immediately end template "www-ssl.conf" do path "/etc/apache2/sites-available/www-ssl.conf" source "www-ssl.conf.erb" mode 0644 notifies :run, "execute[a2ensite-ssl]", :immediately end
service[apache2]はすでに定義してあるものを使います。
WorkstationのChromeでhttps://192.168.33.10にアクセスしてデフォルトページが表示されれば成功です(証明書が自己証明なので警告メッセージが出ます)。
rbenvとruby-build
rubyのReadline6.3に対応してない問題(https://bugs.ruby-lang.org/issues/9578)があって、rbenvからのインストールが難航していたのですが、この問題に対するパッチが適用されたバージョンならインストール(ビルド)可能です。
最近のものだと:
- 2.1.2 (OK)
- 2.1.1 (既知の不具合があるためインストールすべきでない)
- 2.0.0-p481 (OK)
1.9.3系統はたぶん大丈夫かと……(わかんないですが、ぼくの環境では1.9.3-p392がビルドエラー出ませんでした。Bug#9548をみると1.9.3もRequiredになってますが)。
>gem install berkshelf
Windowsだと死ぬほど時間がかかりますが……。
リポジトリトップに"Berksfile"というファイル名のファイルを作ります:
source "https://api.berkshelf.com" cookbook 'ruby_build' cookbook 'rbenv', github: "fnichol/chef-rbenv"
Berkshelfのv0.3系統からBerksfileの書き方も変わったので、注意。
cookbookをインストールします:
>berks vendor
現在のリポジトリにberks-cookbooksというディレクトリがつくられて、そこにインストールされます。
ちなみにv0.2のときみたいにberks install
すると、デフォルトでは~/.berkshelf/cookbooksにインストールされます(設定によると思いますが)。
>berks vendor cookbooks
だとcookbooksにインストールしようと試みますが、すでに存在するディレクトリだと怒られます。なので、必要があれば、cookbooksディレクトリを削除してからやりましょう。
/chef-repo/.chef/knife.rbを修正します:
cookbook_path ["cookbooks", "site-cookbooks", "berks-cookbooks"]
自動的に追加してくれればいいのに……。
mychef3.jsonに追記:
(省略) "rbenv": { "user_installs": [ { "user": "vagrant", "rubies": ["2.1.2"], "global": "2.1.2" } ] }, "run_list":[ "recipe[defset]", "ruby_build", "rbenv::user" ] }
>knife solo cook mychef3
必要な依存パッケージも自動的にじゃんじゃかインストールしていってくれます。超楽チン。
>ssh mychef3 $ ruby -v ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux] $ gem -v 2.2.2 $ rbenv versions system * 2.1.2 (set by /home/vagrant/.rbenv/version)
ふう~大変だった。。。(Readline6.3問題を知らなかったので)
Windows Vagrant ssh問題がなぜか直った話
ここ2~3週間、vagrant ssh
もできなければssh hostname
もできない、という謎現象で苦しんだ。
うまくいきそうになっても、文字化けでどうしようもなかったり。
試したことがあるはずの解決法で、なぜか今日は解決した。
結論をいえば、
C:\Progran Files (x86)\Git\bin
にパスを通したら、すべてうまくいった。
これ、悪戦苦闘してる間に、一度試した方法ではあるはずなのだが……
思い出せる範囲でのプロセス
いろいろ試したけど、思い出せる範囲で記録しておく。
症状の発端
突然、ssh hostname
ができなくなった。
具体的にいうと、~.ssh\configをsshが読みに行ってくれなくなった。
ssh
に-F
オプションをつけてconfigファイルの場所を指定すれば行けたが、簡便のためにconfigファイル作ってるんであって、そんな面倒くさいことをするのは嫌だった。
その直前に行ったこと:
- cwRsyncのFree Editionに入っているファイルをパスの通っているディレクトリにインストール(この中にssh.exeも入っている)
- chefとknife-solo、win32-process、windows-prをgemでインストール
ここで、chefを試している間は問題なし。
> vagrant ssh-config --host hostname >> %HOME%\.ssh\config
も問題なく、hostnameでsshログインできる。
- VirtualBoxとVagrantのアップデート
ここでssh hostname
ができなくなった。
つまりconfigファイルを読みに行ってくれなくなった。
cwRsyncはCygwin版のrsyncとsshとDLLが同梱されている。
おそらくCygwinのディレクトリ解釈がうまく行っていないのだろうと考えた(だとすると、なぜそれ以前はうまくいっていたのかが謎)。
cwRsync同梱のsshがうまいこと~/.ssh/configを読んでくれる方法を探ったけれど、「Cygwin環境下ではどうすればいいか」という情報しか見つからない(cwRsyncにおいてsshはrsyncのための付属品なので……あまり情報がない)。
これで数日費やした。
次に、cwRsyncで入れたssh.exeを削除。
こんどはvagrant ssh
すらできなくなった。
ここで「あれ?いままではなんでできてたんだろう?」という疑問が発生した。
次に、(たぶんここでGit\binにパスを通すのも試したはず)MSYSにあるbinにパスを通した。
文字化けがひどくてダメ。
ゲスト側(Ubuntuサーバ)でLANG
変数にen_US.UTF-8
を設定すれば、まあ、いけるけど、以前はうまくいってたのに、釈然としない。
それに「ログアウト」の文字はどうしても文字化けする(これはMSYSのssh.exeが吐き出してるのかな)。
Gitを新たにインストール、UNIXツール(msysのコマンドのこと)も使えるパターンでインストールしたけど、やはり文字化け。
次に、使ってるコンソールラッパー(Console2を使っていた)がよくないのかな(シェルがWindowsのコマンドプロンプトで、MSYSのsshを使う、という組み合わせが相性が悪いのかな)と考えた。
まずConsole2のシェルをmsysのbashに変えてみた。
文字化けがひどいのは変わらない。
他のものに変えてみよう、というわけで、Console2の後継であるConsoleZに変えた。
Chocolatayを使ってインストール。
変化なし。
minttyに変えてみる。変化なし。
MSYSを新しい(去年出たのかな?)mingw-getのGUIを使って改めてインストールしてみたりしたけど変化なし。
他にもVirtualPCにVistaを入れて、そこにVirtualBoxをインストールして試そうとしたけど、これは32bit OSでは64bit OSを扱えないという壁に阻まれて断念(Vistaのインストールがちゃんと完了するまで3日ぐらいかかった)。
フォントがマズいのかも、と考えていろいろいじったけど変化なし。
で、「MSYSで文字化けするのにGitのssh使って文字化けしないなんてことはないでしょう(だってWindows版GitはmsysGitだもの)」と思いつつ、C:\Program Files (x86)\Git\bin
にパスを通してみる。
なぜか文字化けしない。
は?
どういうことなんでしょう????
.ssh/configも読みに行っている。
どういうことなんでしょう????
そもそもGitのbinにパスを通していない期間に、なぜvagrant ssh
ができていたのかが謎。
ssh hostname
ができたのはcwRsync同梱版を入れたからかな、と思っていたけど、それもconfigファイルを読みに行ってくれていた理由がわからない。
Windowsを窓からぶん投げたくなりました。
↓が出る頃には間に合いそう。
最近調子が悪くなったのでConsole2を改めて。
Vagrantが1.5.3になったり1.5.4になったりしているうちにvagrant ssh
すらできなくなったうえに日本語表示もおかしくなってきて、こういうときは気が滅入ってくるので、基本に立ち返ってConsole2をちゃんとインストールするところから始めたい。
筆者環境
- Windows 7 Ultimate 64bit
手順
「いい感じのconsole.xml」を別名で保存。
Console-2.00b148-Beta_64bit.zipをダウンロード、解凍。
%HOME%\tools\Console2に配置。
日本語IME対応パッチをダウンロード。解凍してできたConsole.exeを先のディレクトリに上書き。
Console.exeをダブルクリック、起動したらクイック起動バーにピン止め。
exit
%HOME%\AppData\Roaming\Consoleに「いい感じのconsole.xml」を上書き。
Console2を起動。
右クリック>Edit>Settings>AppearanceでFontにBitstream Vera Sans Monoをセット。
右クリック>View>Console Windowのタイトルバーを右クリック>プロパティ>フォントでMS ゴシックを選択、OK
で閉じる。
Console Windowのタイトルバーを右クリック>閉じる。
Win+R
>regedit
>HKEY_LOCAL_MACHINE
>SOFTWARE
>Microsoft
>Windows NT
>CurrentVersion
>FontLink
>SystemLink
「編集」>「新規」>「複数行文字列値」
「名前」を「Bitstream Vera Sans Mono」にして、ダブルクリック>「値のデータ」に「meiryoKe_602r1.ttc,MeiryoKe_Console」でOK。
いったんログオフしてから再度ログイン。
Ubuntu14.04のVagrant Base boxをつくる
先月、Ubuntuサーバ12.04 LTSのVagrant Boxをつくる記事を書きましたが14.04 LTSが出たので、あらためてUbuntuサーバーのBoxをつくってみたいと思います。
Chefの記事が書きかけだった気がしますが、次回辺りに続きを書きます。
筆者の環境:
- Vagrant: 1.5.3
- VirtualBox: 4.3.10
- Windows 7 SP1 64bit
ISOをダウンロード
公式サイトからダウンロードしておきます。
このリリースは64bit版のみですね。
564MBなのでCD-ROMにおさまります。
VirtualBoxGuestAdditionsのISOもダウンロードします。
VirtualBox(4.3.10時点)添付のGuestAdditionsはうまくインストールされません。
https://www.virtualbox.org/download/testcase/VBoxGuestAdditions_4.3.11-93070.iso
VirtualBoxにインストール
VirtualBoxを起動。
「新規作成」>「メモリ・HDD」はデフォルト>ドライブはVMDKの可変サイズ。
設定:
- 一般:高度:クリップボードの共有「双方向」、ドラッグ&ドロップ「双方向」
- システム:ポインティングデバイス「PS/2マウス」
- ストレージ:CD/DVDドライブにISOファイル(いまダウンロードしてきたUbuntuのイメージファイル)をマウント
- オーディオ:オーディオを有効化のチェックをはずす
- ネットワーク:NAT
- USB:USBコントローラを有効化のチェックをはずす
「標準インストール」をする(ミニマルインストールではない)。
コンソール
標準インストールしたUbuntu 14.04のコンソールがVirtualBoxで開いている状態で:
$ sudo su # export LANG=en_US.UTF-8 # echo $LANG # aptitude update # echo $? # aptitude dist-upgrade # echo $?
echo $?
で返り値が0なら、直前のコマンドは成功しています。
sudoersの編集
vagrantユーザのsudoはパスワードなしにします:
# visudo %sudo ALL=(ALL:ALL) ALL
と書いてある次の行に追記します:
vagrant ALL=(ALL) NOPASSWD: ALL
Ctrl+X
で編集完了、y
で保存。
終わったらsuからexitします:
# exit $ cd (ホームディレクトリに戻ります)
Guest Additionsのインストール
DKMSをインストールします:
$ export LANG=en_US.UTF-8 $ echo $LANG $ sudo aptitude install dkms $ echo $?
VirtualBoxの「デバイス」>「Guest AdditionsのCDイメージを挿入」を選択します。
VirtualBoxの「デバイス」>「CD/DVDデバイス」>「仮想CD/DVDディスクファイル選択」で、さきにダウンロードしておいたVBoxGuestAdditions_4.3.11-93070.isoを選択します。
ディレクトリをつくって:
$ mkdir /mnt/cdrom
マウントします:
$ sudo mount /dev/cdrom /mnt/cdrom
または:
$ sudo mount /dev/sr0 /mnt/cdrom
スクリプトを実行します:
$ cd /mnt/cdrom $ ls (↓で実行するスクリプトファイルが表示されるのを確認) $ sudo sh ./VBoxLinuxAdditions.run
デスクトップ環境をインストールしていないServerだとWindow Systemのインストールがスキップされますが、無視します。
$ ls /usr/lib
で/opt/VBoxGuestAdditions-4.3.11/lib/VBoxGuestAdditionsへのVBoxGuestAdditionsというディレクトリシンボリックリンクがつくられていればOKです(ISOをダウンロードしなくともシンボリックリンクをつくればOKということでもあります)。
openssh-serverのインストール
$ sudo aptitude install openssh-server $ echo $? $ sudo service ssh status (runningを確認します)
Vagrantで共通して使われるインセキュア・キーを登録します:
$ mkdir .ssh $ cd .ssh $ wget https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub $ mv vagrant.pub authorized_keys $ chmod 600 authorized_keys $ cd .. $ chmod 700 .ssh
ここまでで、たぶん、Vagrant Boxをつくる条件は整っていると思います。
ここ以降は、ホストOS(Windowsとか)のコンソールで作業します。
パッケージング
package --base
に渡す引数は、VMの名称(VirtualBoxのGUIに表示されている名称)を与えます(ここでは"Ubuntu1404")。大文字小文字が区別されます。
vagrant package --base Ubuntu1404
package.boxがカレントディレクトリにつくられます。
テスト
box add
に渡す引数は、そのBoxに自分が付けたい名前を与えます。ここでは"Trusty"とします:
vagrant box add Trusty package.box vagrant box list
これで"Trusty"がリストにあればOKです。
ホームの.vagrant.d/boxes/Trusty/0/virtualboxにmetadata.jsonとともに展開されているのを確認します。
新たに新規プロジェクト用ディレクトリをつくり、そこで立ち上げてみます:
> mkdir mytrusty1 > cd mytrusty1 > vagrant init Trusty > vagrant up
VirtualBoxが立ち上がったままであれば、新たなVMがつくられて「実行中」となっているはずです。
プライベートネットワーク接続を加える
Vagrantfileの次の行のコメントをはずします:
config.vm.network :private_network, ip: "192.168.33.10"
Vagrantfileを再読み込みして、再起動、ログインします:
> vagrant reload > vagrant ssh