YeomanでフロントエンドとREST APIサーバーを同時に開発する方法

先月のHTML5など勉強会で、Yeoman超入門を発表したときに、Yeomanはフロントエンド開発専用にlocalhostサーバー立ち上げるからサーバーサイドとの同時開発はちょっと工夫がいるよね〜みたいな話題があって、参加されてたnode.jsに詳しい方からhttp-proxyつかってapiの部分リダイレクトかけたらいいよみたいな方法を教えてもらった。

なるほどそれは便利だなと思って実際書いてみたら手軽に使える感じにできたので書いておきます。ちなみに今週水曜日にGoogle Developers Liveに出演してYeomanのことを喋らせていただく機会に恵まれたので、その時の参照にも使えるかと思って。(ライブのスライドはこちら

 

Yeomanは $ grunt server で開発用のWebサーバーを起動することができる。デフォルトで http://localhost:9000/ 以降ファイルをセーブするたびに自動的に画面をリロードしてくれたりしてデバッグやDesigning in the Browserな点ですごく便利。

フロントエンドのみを開発する場合はこれで十分なんだけど、JSONベースのREST APIサーバーも同時に開発している際はちょっと問題がある。フロントエンド開発用サーバーがlocalhost:9000を専有しているからREST APIサーバーを同じオリジンで立てることができない。

つまり別のオリジン例えば localhost:3000 とかで立てないといけないんだけど、そうすると同一生成元ポリシーの関係でフロントエンド側からJSONを取得したりすることができなくなる。フロントエンドとAPIサーバーを同時進行で開発というのはよくあるし、End-to-Endテストしたい場合どうすんの?とか、ちょっと問題になってくるわけです。

こういうケースの場合、上記2つのサーバーとは別にプロキシサーバーを立てて、フロントエンドからAPIサーバーへのリクエストのみを別オリジンのAPIサーバーに渡してしまおうというのが今回のお話です。まあ上記の理屈がよくわからなくても実際やってみるとなんとなく感覚がわかると思います。

以下はYeomanが使える環境が整っている前提。

まず準備から。APIサーバーをいちいち書くのがめんどくさいので今回はnode.jsのeasymockモジュールを使います。REST APIサーバーのモックアップが超簡単に立てられるやつ。これをグローバルインストールします。

$ npm install -g easymock

次にyomanでフロントエンドのスケルトンを作成します。
仮に名前を test としましょう。

$ mkdir /YOUR PROJECT DIR/test
$ cd /YOUR PROJECT DIR/test
$ yo webapp
Out of the box I include HTML5 Boilerplate, jQuery and Modernizr.
Would you like to include Twitter Bootstrap for Sass? (Y/n) y
Would you like to include RequireJS (for AMD support)? (Y/n) y

プロキシーサーバー用のnode.jsモジュール http-proxy をインストールします。

$ npm install http-proxy

次にこのディレクトリにサーバー用のディレクトリを作成します。

$ mkdir -p server/api-server/api/items

easymock用のコンフィグファイルを各ディレクトリに配置します。
まず、以下のコードを server/api-server/api/config.json という名前で配置します。
server/api-server/api/config.json

次に以下のコードを server/api-server/api/items/_get.json という名前で配置します。

ここまでできたら、server/api-serverディレクトリに移動してAPIサーバーを起動してみましょう。

$ cd server/api-server
$ easymock
Server running on http://localhost:3000
Server running on http://localhost:3000/_documentation/

サーバーが起動するのでWebブラウザで http://localhost:3000/api/items/ にアクセスしてみます。

[
  {
    "id": 1,
    "user": "john",
    "message": "hello"
  },
  {
    "id": 2,
    "user": "bob",
    "message": "Hi!"
  },
  {
    "id": 3,
    "user": "mike",
    "message": "good bye."
  }
]    

というJSONが返ってくれば正常に起動しています。違う場合はディレクトリの配置、ファイル名などが違っている場合があるので確認しましょう。

このAPIサーバーはそのまま起動しておいて、別のターミナルウィンドウを開き、プロジェクトのディレクトリに移動します。

$ cd /YOUR PROJECT DIR/test

app/index.htmlのdiv.containerブロックを以下のように変更します。

次に app/scripts/app.coffee をapp.coffeeという名前で配置します。

フロントエンド開発用のサーバーを起ち上げます。

$ grunt server

Webブラウザが自動的に開いて以下の様な画面が表示されます。

f:id:bathtimefish:20130422120903p:plain

Webブラウザとwebappのサーバーをそのままにして、また別のターミナルウィンドウを開いてプロジェクト中のserverディレクトリに移動します。

 $ cd /YOUR PROJECT DIR/test/server

このserverディレクトリに proxyServer.coffee という名前で以下のコードを配置します。
proxyServer.coffee

proxyServer.coffeeを起動します。

$coffee proxyServer.coffee
Starting Server at http://localhost:8000/

localhost:8000 でプロキシサーバーが起動しました。
この状態で、Webブラウザで開いているwebapp画面のURLを http://localhost:8000/ に変更してみましょう。以下のような表示になるはずです。

f:id:bathtimefish:20130422122005p:plain

最初にwebappを起動した時とちがって、リストが表示されました。これは http://localhost:8000/api/items から取得したデータです。データの取得は app.coffeeの jQuery.getJSON で行なっています。

proxyServer.coffeeで、/api のリクエストのみを http://localhost:3000 へ再リクエストしています。これによってsame originリクエストとなりajaxリクエストに対して正常にJSONが返ってくることになりました。

ちなみに、app.coffee で $.getJSON('http://localhost:3000/api/items/', ... とやってポート3000のAPIサーバーに直接リクエストを送ってみると、、、

f:id:bathtimefish:20130422124134p:plain

というふうにcross originは許可されてないよと言われてデータが正常に取得できません。この場合はChrome先生がおっしゃってるようにAPIサーバでAccess-Control-Allow-OriginをHTTPヘッダに追加してレスポンスしてやる必要がありますが、開発するアプリケーションのフロントエンドとAPIサーバーが同一オリジンならばそもそもこの設定は不要です。

下手にやると脆弱性につながる危険性もあるので、上記のようにプロキシをかませる方法で開発を進めたほうがより良いんじゃないかなと思います。これだと設定も何も変えなくてもフロントエンドとAPIサーバーを本番環境にアップできるのでいいですね。

そうそう、このセットアップ以降はYeomanでのフロントエンド開発は localhost:8000 で行う。grunt で起動したポートが違うけどファイルのセーブとか監視してくれんの?と思うけど大丈夫。しっかり監視してリロードしてくれます。

ただし一度落として $ grunt server で再起動した場合は localhost:9000 で上がってくるのでもう一度手動で localhost:8000 に変更してやる必要があります。お忘れなく。

追記 2013.4.23
http://bit.ly/13r1xj5
このプロキシ使う方法を教えていただいた @kamiyam さんがもっと便利な方法を公開してくださいました。途中までやり方はいっしょだけど要のproxyの管理がgrunt taskになっていてよりスマートになってます。こちらのほうがオススメです!

AWS OpsWorksでLayerをカスタマイズしたらAmazon Linuxでエラーが出たお話

前回AWS OpsWorksでLayerをカスタマイズしてLAMP All in Oneなインスタンスを起ち上げてみた。そのためにいろいろやっててわかった現時点でのハマりどころを書いておく。前回の冒頭に書いたAmazon Linuxを選択肢なかったという話だ。

 

現時点ではAmazon Linuxでこの設定をすると、デプロイ時にエラーが発生するのを確認している。ためしにdb-master2インスタンスをAmazon Linuxで追加し起動してみると下図のようにエラーが発生する。

f:id:bathtimefish:20130405174643p:plain

 

failure logを参照してみると、どうもphp設定後のapacheの再起動でコケてるっぽい。

f:id:bathtimefish:20130405175000p:plain


mod_php5をデプロイしたあと、apache2 をrestartしようとしてエラーになっている。ん?Amazon LinuxRedhat系だからapache2じゃなくてhttpdじゃね?


該当するrecipeはどこかと思ってOpsworksのcookbookを眺めてみたら、どうもapache2::mod_php5が該当するっぽかった。ソースをみると んーなるほど、これだとRedhat系の場合でもservice[apache2]で処理されるな。

f:id:bathtimefish:20130405180541p:plain

まあこれくらいだとOpsworks/cookbooksをforkして直せばいいし。
まあまだベータ版だし、このへんはすぐ更新されそうなところに思える。ボクはdebian派だからubuntuがまともだったらまあいいやw


ただ、recipeもまだ更新中っぽいから現行のrecipeを組み合わせるといろいろホコリが出てきそうな感じはわかった。こういうところをどうケアしていくのか、OpsWorksの今後の動きが興味深いところであります。

AWS OpsWorks のLayerをカスタマイズしてLAMP All in Oneサーバーを起動してみる

前回次の日くらいに続き書くとか言っておきながら春の日差しでぼんやりしててかくの忘れてました。思い出したのでぼちぼち書きます。

前回書いたように現時点ではAWS Opsworksには Apache + MySQL + PHP が1インスタンスで起動できるLayerが用意されていない。ので、ちょっとLayerをいじってそういう構成のLayerを作成しインスタンスを起動するところまでやってみる。

まず、Stackの作成。OpsWorksのダッシュボードから Add Stackボタンをクリックして作成する。

f:id:bathtimefish:20130405153326p:plain

 

名前は適当にtestとする。なんでもいい。Default operating systemはUbuntu12.4 LTSを選択する。Amazon Linuxにしたいところだけど現時点でうまくいかなかったので。理由は次回に書きます。


さらに、Region, Default Availability Zoneはお好みで。大事なのはDefault SSH Keyで自分が使っているAWSのSSH Keyを選択すること。これを設定しとかないとインスタンスを起動した後にsshでログインできなくなる。

選択したら右下のAdd StackボタンをクリックしてStackを作成する。

f:id:bathtimefish:20130405153710p:plain

こんな感じでtestという名前のStackができる。次に画面左側メニューのLayersをクリックする。

f:id:bathtimefish:20130405154437p:plain

 

Layerの管理画面になるので、Add a layerボタンをクリックする。

f:id:bathtimefish:20130405154743p:plain

Layer typeはMySQLを選択する。このMySQLレイヤーをベースにApache + PHPを組み込むのが良さそうだ。なぜならMySQLレイヤーでは画面のようにレイヤーの設定でroot passwordが設定可能だから。recipeで設定するよりはこっちのほうが便利だろう。

Layer typeをMySQLにし、rootパスワードはまあそれっぽいものが入ってるのでそのままにして右下のAdd layerボタンをクリックする。

f:id:bathtimefish:20130405155309p:plain

こんな感じでMySQLのlayerができる。これにapacheとかの設定を組み込んでいく。どうやるかというと、PHP App ServerというLayer typeのrecipe設定の一部をこのMySQL layerに追加するという方法をとる。こういうことができるのがクラウドマネージメントツールの面白いところだ。

まずはMySQL layerの行の右側、action項目のeditボタンをクリックする。

f:id:bathtimefish:20130405155936p:plain

 

layerの編集画面になる。まずはBuilt-in Chef recipesのところにあるshowボタンをクリックしてみよう。

f:id:bathtimefish:20130405160250p:plain

 

このレイヤーで定義されているrecipeの一覧が表示される。それぞれSetupからShutdownまでのカテゴリに分けられていて、インスタンスを起動してMySQLサーバーをデプロイするためのrecipeが定義されている。ここにPHP App Serverで定義されているrecipeの一部を追加してLAMP All in oneなレイヤーを作る。

まず、recipeを追加する前に他に必要な設定をやっておく。
この下段にSecurity Groupsの項目がある。ここのadditional security groupsからAWS-OpsWork-PHP-App-Serverを選択し、リストボックスの右側の+ボタンをクリックする。

f:id:bathtimefish:20130405161056p:plain

 

すると下図のような感じでSecurty Groupが追加される。
これでインスタンスが起動したあと80などWebサーバーへのアクセスに必要なポートが開くようになる。

f:id:bathtimefish:20130405161257p:plain

さて、次にいよいよrecipeの追加だ。まずはCustom Chef recipesの欄のconfigure cookbooksボタンをクリックする。

f:id:bathtimefish:20130405162056p:plain

Stackの編集画面になるので、まずUse Custom Chef cookbooksのスイッチをYesにする。次に現れるRepository URL欄に git://github.com/aws/opsworks-cookbooks.git を入力する。これはAWS OpsWorksのcookbookのリポジトリだ。入力したら右下のSaveボタンをクリックしてStackの設定を保存する。

f:id:bathtimefish:20130405162817p:plain

保存できたら、右側メニューのLayersをクリックしてLayerの管理画面にもどる。そしてMySQLレイヤーのeditボタンをクリックしてレイヤーの編集画面に移動する。
するとCustom Chef recipesの欄が下図のようにrecipeが追加できるようになっているはずだ。これでAWS Opsworks cookbooksのrepipeがこのレイヤーに追加できるようになったわけだ。

f:id:bathtimefish:20130405163243p:plain

SetupからShutdownのそれぞれのカテゴリにrecipeを追加していく。
recipeはそれぞれのカテゴリのテキストボックスにrecipeを入力して+ボタンをクリックすることで登録できる。recipeはひとつずつ+で追加していく。それぞれ以下のrecipeを入力する。

Setup

mod_php5_apache2

Configure

mod_php5_apache2::php, php::configure

Deploy

deploy::php

Undeploy

deploy::php-undeploy

Shutdown

apache2::stop

f:id:bathtimefish:20130405164831p:plain

上記のように入力したらページ右下のSaveボタンをクリックしてレイヤーの設定を保存する。

エラーなく保存できたら、Custom Chef recipesは下図のように登録されるはずである。エラーになった場合はrecipe名をタイポしている可能性があるので間違いなく登録し直す。

f:id:bathtimefish:20130405165220p:plain

ここまでできたら、右側メニューのInstancesボタンをクリックしてインスタンスの管理画面にいく。MySQLレイヤーのインスタンスはまだひとつも登録されていないので、Add an Instanceボタンをクリックしてインスタンスを登録しよう。

f:id:bathtimefish:20130405165543p:plain


インスタンスの設定はとりあえずそのままでいい。右下のAdd Instanceボタンをクリックして登録する。

インスタンスのHost NameはStackのHostname themeにしたがって適当な名前をつけてくれる。インスタンスサイズはデフォルトでHigh CPU Mediumになっているのが商売上手な気もするけどw

実際にこのインスタンスを起動してそれぞれのアプリケーションがデプロイされるまでには結構な時間がかかるので、smallあたりでさらに起動時間が伸びるよりはいいか。という判断で。まあテストですぐ止めるしいいか。

f:id:bathtimefish:20130405170254p:plain

 

これで起動準備がすべて完了した。下図のようになると、いよいよ起動である。

f:id:bathtimefish:20130405170529p:plain



db-master-1欄のstartボタンをクリックしてインスタンスを起動する。

f:id:bathtimefish:20130405170631p:plain


起動完了までにはけっこう時間がかかるので気長に待とう。
ここでOpsWorksはOSをブートし、Apacheなど必要なソフトウェアをインストール・設定し、サービスを立ち上げるといった一連の動作を自動で行う。
起動状況はStatus欄で逐次確認できる。

f:id:bathtimefish:20130405171152p:plain

 

しばらくすると、ステータスがonlineとなる。
これで起動完了だ。db-master1欄のPublic IPにインスタンスに割り当てられたIPアドレスが表示される。stopボタン右側のsshボタンをクリックすると、sshでのアクセス方法が表示される。sshで接続して、インスタンスの状態を確認してみよう。

f:id:bathtimefish:20130405171555p:plain

 

とりあえずsshでログインしてps打ったりしてapache2、mysqlphpなどのプロセスが走っているか確認する。ボクはついでに /var/www/info.php を作成して、おなじみphpinfo() を書いてブラウザでアクセスすると正常に表示された。pdo-mysqlもちゃんとインストールされている。すぐにphpアプリケーション開発ができる環境が整ったわけだ。

f:id:bathtimefish:20130405172547p:plain

 

このサーバーのデプロイ設定は上記で設定したレイヤーに全て定義されているので、インスタンスを増やせばいくらでも同じ設定のサーバーを起動できるのである。スケールアウトにはもちろん有効だけど、単体運用ベースでも障害復旧やテスト環境の即時起ち上げなどでメリットがありそうだ。自分がよく使うサーバー環境を作っておくといろいろと楽ができそうである。

さて、起動のテストはこれで終わりである。終了前には必ずstopボタンで起動しているインスタンスを終了させておこう。無駄にお金がかかることになるので。気になる人はstop実行後インスタンスがterminateされたかどうかをEC2の画面で確認しておくといいだろう。OpsWorksはまだベータ版なので念のため確認しといたほうが無難だ。

f:id:bathtimefish:20130405181413p:plain


あと、冒頭に書いたAmazon Linuxを選択するとうまくいかなかった話は次回に書きます。

AWS OpsWorks をようやくさわってみた。

AWS OpsWorks。発表されたときに「ついにきたかー」と思って近々さわってみようと思っていたんだけど全然やれてなくて、でもまあぼちぼち使ってみないとなーと思ってちょっとさわってみた。

最初に感想を書いておくと、いまのところRightScaleなどに比べて実用に耐えるものじゃないなと感じた。まだベータ版だし仕方ないかな。chefでできることは全部できる感じだけどいろいろ足りない機能が多い。まあ既にRightScaleを知っているからそう思うのかもしれないけど。今後の機能追加に期待したいところです。


今はそんな感じだけど、いつか実用できるレベルになるはずなので知っとく価値はある。そんなわけで主にLayer周りの使い方を知っとこうと思った。いまんとこその辺とAWSなchefのrecipeがわかっとけばまあいいかなーと。LayerはRSで言うところのServer Templateにあたる層かな。

 

OpsworksのStackとかLayerが何なのか?はウェブ上にたくさん記事があるのでまあわかった。現時点では選択できるLayerもまだまだ少ない。けどなんかRails Serverとかnode.js Serverとかイマドキにツボなものが用意されている。なんかわかってるなーな感じ。

そんなかんじでダラダラしながらLayerを眺めてみて気づいたんだけど、PHP App ServerとMySQLという2つのLayerがあるんだけど、LAMPオールインワンな感じのLayerがなかった。


これそのまんま使ってLAMP構成しようと思ったらPHP App ServerとMySQLインスタンス1つずつ作ってそれぞれ起動しないといけないじゃん。計2つのインスタンス課金されるじゃん。と。


んーこれじゃあ小規模なLAMPサーバー自動デプロイしたいってできねーやん。OpsWorksというサービスからして1DB Server + n App Serverっていう構成のほうがニーズ高いでしょうがという意見はわかります。でも人情的にLAMPオールインワンくらい用意してくれても良さそうじゃね?と。


そう思ったのでちょっとLayerを編集してPHP MySQL AapacheというLAMPを1インスタンスにまとめてやろうと思ってやったらできた。ちょっとbeta版くさいハマりかたをしたけどカンタンでした。ついでにLayer周りのいい勉強になりました。機能がシンプルでGUIがわかりやすい分RSより敷居は低いかな。


で、やり方は以下のとおり。
。。。といきたいところだけど、今からちょっと外出しないといけないので次回書きます。今日の夜か明日くらい。二日酔いでなければ。

第6回HTML5など勉強会 大阪 で Yeoman RIAビルドツール超入門 を発表してきました。

先日久々に開催した第6回 HTML5など勉強会で「Yeoman RIAビルドツール超入門」という発表をさせていただきました。


けっこういま仕事でがっつり使っているツールなので、もうちょっと踏み込んだ内容をやろうかなと当初思っていたのだけど、スライドにもあるように直前に1.0beta出てるのしって泡食ったというのと、関西ではまだあんまり知られてなさそうな感じがしたので、まずは基本的なことを紹介してみんなに興味持ってもらったらいいなーという気持ちで超入門にしてみました。

そしたらyeoman使ってる人もいて、後の雑談タイムで知らない使い方も教えてくれたりしてすごく勉強になった。後片付けとかでバタバタしてて頭回ってなかったけど後でもっとちゃんと話しとけばよかった。でもまあまたどこかでお会いできるかもしれないし、そんときはちゃんとお話したいもんだ。Gruntもすごく詳しそうだったし。

そうそうGruntも0.4になって感じ変わったし。grunt-cliとかなりやがったし。どっかでまた掘り下げねばなるまい。

スライドはRIAビルドツールという題名にしているので、Webアプリケーション開発者のためのツールというイメージになるかもしれないけど、Webデザイナーにも有用なツールだと思いますyeoman。

レスポンシブなWebサイトのスケルトン(ひな形)を一瞬で作ってくれるし、CSSの圧縮とかも自動でやってくれるだけでもいい感じだと思うんです。別にJavaScriptやSassを使わくたって、それだけでも十分に楽できると思う。yeomanを構成するツールたちの独立性がけっこう高いので、自分が使いたいところだけ使うということもやりやすくなってるし。自分の制作フローの一部に組み込みやすいと思います。

これもスライドに書いたけど、もっと知りたいという声があったらyeoman勉強会みたいのを企画してもいいなと思ってるくらいです。ハンズオンのほうがいいかな?こういう制作現場で使える的なハンズオンはやる気が出るのでやりたいですねー。

試行錯誤しながらCSSを書いていたらIE10で表示がおもしろいことになった

仕事でサイトをつくっていて、こんなふうな仕切り線みたいの(何て言うんだろう?)をCSSだけで書いてやろうといろいろ試行錯誤していた。

f:id:bathtimefish:20130316222659p:plain

そしたら途中でIE10だけ表示が面白いことになった。
どんなふうになったかというと、

まずこんな感じのHTMLを書いて

こんなCSSを書いてみた。

制作でメインなfirefoxでは良い感じに表示されるんだけどWebkitではタイトルの文字と三角の部分が微妙にずれてしまう。そっかーインライン要素だから高さの判定が微妙になるのかなと、ブロック要素にしたほうがいいかなとか思った。

ふとIE10ではどんな表示になるかな?とおもって見てみた。するとなんだか面白いことにw以下、スクリーンショットを撮って解説してみよう。
確認したバージョンはIE 10.0.9200.16484 for Windows8。Windows7版では確認してない。

まず普通に表示した。とりあえずボロボロに崩れた。

f:id:bathtimefish:20130316223720p:plain


次に、下にスクロールして仕切り線部分を上に逃してみる。

f:id:bathtimefish:20130316223919p:plain

ほんでまた上にスクロールを戻してみると、、、

f:id:bathtimefish:20130316223955p:plain

 

あれ?なんか三角の部分の大きさが変わってる。しかもバラバラの大きさにw
ちなみにスクロールを繰り返して隠したり表示したりするとその度に三角の部分の大きさが変わっていく。なかなか面白い現象だ。サンプルはこちら

この現象はFirefoxWebkit系では起きない。

なんなんだろうか?バグといっていいものなのかどうか判断がつかない。
たぶん画面の更新ごとにしている計算に関係があるんだろうけど。
まあこの書き方だといろんなブラウザで崩れるからどっちみち使えないんだけどIE10だけダイナミックだったので気になった次第。

 

ちなみにもう少しねばったあげく、だいたい安定したものができた。やっぱタイトルと三角の部分をブロック要素にしてheightを指定するとちゃんとなった。

まあまともに表示する。もうちょっとスッキリ書けるような気がするけど、まあ今はこれでいいや。サンプルはこちら