VPSからAmazon S3とGoogle Apps for Businessに移行しました

さくらのVPSで運用していたkatahirado.jp(VALUE DOMAINで取得)を、WebサイトはAmazon S3に、メールはGoogle Apps for Businessにそれぞれ移行しました。
Webサイト、メールサーバの他に、redmine、git、Railsのデモアプリが動いていましたが、 git,redminegithubやPivotal Tracker等を使用することにして、既存のデータはとりあえずローカル(MBP)に。
Railsアプリのデモは動かすならHerokuかなと思っていますが、一旦保留としました。
S3とRoute 53,Google Appsでの作業をメモ。

Amazon S3

katahirado.jpはルートドメインでの静的なWebサイトなので、Amazon Route 53を使ってホストする必要がありました。
Amazon S3でのWebサイトですが、基本こちらの通りで。

www.katahirado.jpと、karahido.jpのバケットを作成しました。
なのですが、バケットポリシーを追加する時に、AWS Sample Bucket PolicyのWebページが丁度なくなっていたので、AWS Policy Generatorを利用してバケットポリシーを作成しました。*1

f:id:yuichi_katahira:20140418102545p:plain

Sample Bucket Policiesをクリックするとページがなくなってました。

f:id:yuichi_katahira:20140418102738p:plain

AWS Policy Generatorをクリック。

f:id:yuichi_katahira:20140418102545p:plain

Select Type of PolicyをS3 Bucket Policyにして、Principalを*に。

f:id:yuichi_katahira:20140418103603p:plain

ActionsでGetObjectにチェック。

f:id:yuichi_katahira:20140418103644p:plain

Amazon Resource Name (ARN)のところはご丁寧に formatが乗っているので、それに合わせて入力しました。

arn:aws:s3:::<bucket_name>/<key_name>

arn:aws:s3:::www.katahirado.jp/*

f:id:yuichi_katahira:20140418103750p:plain

Add StatementしてGenerate Policyして表示されたものをコピーして貼り付ければOK

f:id:yuichi_katahira:20140418103848p:plain

f:id:yuichi_katahira:20140418103902p:plain

Amazon Route 53

Amazon Route 53でDNSの設定をします。ここいらを参考に。

Create Hosted Zoneして出来たNS,SOA以外にkatahirado.jpのA,www.katahirado.jpのCNAMEを追加。
MXレコードはGoogle Apps for Businessがまだ処理し終わっていないので、まだ登録しません。
というか出来ません。
VALUE DOMAINの管理コンソールにいってNSのValueをネームサーバとして登録。

なお、メール等のダウンタイムを極力なくすという意味ではAmazon Route 53→Google Apps for Business→S3とかの流れの方が良かった気がします。
ちなみにGoogle Apps for Businessでの処理で若干手間取って、30分ぐらいメールサーバー的にアレになってたかと。

Google Apps for Business

次、Google Apps for Businessを独自ドメインで設定。
以下が大変丁寧。

Google Apps for Businessのドメイン所有権の確認、MXレコード登録の流れで、Google AppsAmazonを行ったり来たりして若干混乱したので流れを整理。

Google Apps for Businessの無料トライアル申し込み

ドメイン所有権の確認(Google Apps for Business)

Google Appsの管理コンソールからTXTレコードに設定する値を取得する

Amazon Route53のコンソールで取得したTXTレコードを登録

ドメインの所有権確認終了(Google Apps for Business)

Google Appsの管理コンソールからMXレコードに設定する値を取得する
http://angelndxp.wordpress.com/2012/05/27/amazon-route53%E3%82%92google-apps%E3%81%AEdns%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%A8%E3%81%97%E3%81%A6%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B-%EF%BD%9E%E7%8B%AC%E8%87%AA%E3%83%89%E3%83%A1%E3%82%A4/

Amazon Route53のコンソールでMXレコードの設定を行う
http://angelndxp.wordpress.com/2012/05/28/amazon-route53%E3%82%92google-apps%E3%81%AEdns%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%A8%E3%81%97%E3%81%A6%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B-%EF%BD%9E%E3%83%A1%E3%83%BC%E3%83%AB%E8%BB%A2%E9%80%81/

MXレコードの設定反映完了(Google Apps for Business)

感想

若干手間取りましたが、がっちりサーバを構築するのに比べるとだいぶお手軽。
クラウドもずいぶん環境整ったなとしみじみ。
サーバ運用を自宅サーバVPSで長いこと続けてきましたが、もうクラウドでいいですね。
追記: 料金はさくらのVPSが年間一括払いだったので、月924円(4/18現在)。
Google Apps for Businessが年払いで一人利用なので、月500円、AWSは運用してみないとわかりませんが、Route 53は1ゾーンで、S3も大したアクセスでもないので両方で多分200円以内、合わせてもVPSより安くなるかなと想定しています。

*1:一時的な問題だったようで作業翌日には復活していました

WebViewに動的にJSを読み込みした後の評価(実行)タイミングではまった

11/20日追記:SafariChromeでも試して見たところ、JSでbodyに複数appendChildすると順番は一定しない事を確認。そうだったのか。
http://blog.setunai.net/20120317/javascript%E3%81%AE%E5%AE%9F%E8%A1%8C%E9%A0%86%E5%BA%8F%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/



備忘録。


WebViewは、JSファイルを並列に読み込む。
で、動的に4つくらいJSファイルをbodyの末尾に読み込んでいるが、コードが追加順番に依存しているので、追加した順番に評価してほしいがどうも一定しない。



LABjsRequireJS等を使うのが良いのだろうが、手っ取り早く、Railsライクに全部のJavaScriptファイルを順番に結合したファイルを作成、読み込むことで解決した。


ただこれをやると開発時に面倒くさくなるので、各JSファイルをちょっと手直しして結合する書き捨てスクリプトRubyで書いた。
こういった処理は、GoogleのClosure Compilerや、RailsのAsset Pipelineなどを利用するのが標準的か。
しかし今回の場合は、Objective-C側から呼び出している為JS側では呼び出されていないメソッドや、Objective-C側から動的に挿入されているはずのネームスペースのメソッドをJS側から呼ぶといった、変則的なコードが散らばっていたりするという事情があるので、自前の結合スクリプトを用意した。圧縮も勝手にされるとちと困るので。


参考
http://kawa.at.webry.info/200911/article_7.html

ファイルのアイコンを取得する

一つ前のエントリーでは端折っていたが、ファイルのアイコンを取得するのが目的で、pathをあれこれやってあれってなったのであんなエントリーを残した。

せっかくなのでアイコン取得もメモ。


pathをStringでアイコン取得する場合。

//_fullPath はこんなのが来てる。 @"file://localhost/directory/index.html";
NSString *filePath =		
[[_fullPath stringByReplacingOccurrencesOfString:@"file:/localhost" withString:@""]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return [[NSWorkspace sharedWorkspace] iconForFile:filePath];

とかやる。前のエントリーのは、このケースでいうとfile://localhostを置換しようとしているのに、実際にはここに渡って来る前の文字列生成処理で、file:/localhostが渡ってきてた&decodeも抜けてたという話。


NSURLでアイコンを取得

//_fullPath はこんなのが来てる。 @"file://localhost/directory/index.html";
NSURL *fileUrl = [NSURL URLWithString:_fullPath];
NSImage *image=nil;
[fileUrl getResourceValue:&image forKey:NSURLEffectiveIconKey error:NULL];
return image;

file:/localhost状態の文字列をNSURLにして、アイコン取得できなくて、StringでもNSURLでもアイコンが取得出来ないなあと調べたら、file:/localhostになっちゃってるじゃないかというのが、前回のエントリの真相でした。

file://localhost/...を使っているのは、アプリがWebViewメインのものなので、このほうが都合がいいため。

file://localhost/directory/といったURL文字列にstringByAppendingPathComponent:を使うと・・・

やっちゃ駄目って書いてあるからやるほうが悪いんですが・・


stringByAppendingPathComponent:のリファレンスには下記のように書いてあります。

Note that this method only works with file paths (not, for example, string representations of URLs).

file pathsでだけ動くと書いてありますが、表題のようにfile://localhost/....となっている文字列で実行してみると・・・

NSString *baseURLString = @"file://localhost/directory/";
NSString *fullPath = [baseURLString stringByAppendingPathComponent:@"index.html"];
NSLog(@"%@",fullPath);
// file:/localhost/directory/index.html

file:/localhost/... というように、file:の後の//が/に削られてしまいます。
stringByAppendingPathComponent:は使用せず、stringByAppendingString:を使うとか、

NSString *baseURLString = @"file://localhost/directory/";
NSString *fullPath = [baseURLString stringByAppendingString:@"index.html"];
NSLog(@"%@",fullPath);
// file://localhost/directory/index.html

ただ、stringByAppendingString:を使った場合だと、当然ながらpathを考慮されているわけではないです。
例えばpathComponentsを格納した配列から取り出して、個別に連結するといった場合には"/"を自前処理する必要があります。
文字列に%20であるとかが含まれれば、stringByReplacingPercentEscapesUsingEncoding:も必要です。

NSURLのインスタンスにして処理したほうがいいでしょう。

NSString *baseURLString = @"file://localhost/directory/";
NSURL *baseURL = [NSURL URLWithString:baseURLString];
NSString *fullPath = [[baseURL URLByAppendingPathComponent:@"index.html"] absoluteString];
NSLog(@"%@",fullPath);
// file://localhost/directory/index.html

WebViewでsetFrameをアニメーションさせようとしたら記述量が多かった

NSAnimatablePropertyContainer プロトコルのanimatorを使えば下記のような感じに書ける↓
https://developer.apple.com/library/mac/#samplecode/BasicCocoaAnimations/Listings/MainWindowController_m.html


しかしWebViewでanimatorメソッドを使ってみたら、エラーでproxyオブジェクトがselectorを呼べなかった。
ので、下記サイトのようにNSViewAnimationを明示的に生成して処理した。
http://xcatsan.blogspot.jp/2008/12/window_28.html

animatorメソッド使用で、setFrame:display:とか呼ぶ場合は、暗黙的に処理してくれるので、変更前のNSRectとかいらないが、NSViewAnimationでsetFrameをアニメーションさせる場合は、変更前のNSRect,変更後のNSRectをNSValueに変換したやつを用意して,NSViewAnimationStartFrameKey,NSViewAnimationEndFrameKeyをKeyにもつNSDictionaryを生成する必要がある。
記述量がいきなり増えますね。

jQuery の複数バージョンを同時使用する

http://stacktrace.jp/jquery/with_other_lib.html#other_version


noConflictの引数にtrue渡せばいいと。

<html>
<head>
    <script type="text/javascript" src="jquery-1.2.6.js"></script>
    <script type="text/javascript" src="jquery-1.4.2.js"></script>
    <script type="text/javascript">
        // $ 関数および jQuery関数の上書きを元に戻します。
        var $j = jQuery.noConflict(true);

        // $ は jQuery ver1.2.6を参照します。
        alert($.fn.jquery);      // => 1.2.6

        // jQuery は jQuery ver1.2.6を参照します。
        alert(jQuery.fn.jquery); // => 1.2.6

        // $j は jQuery ver1.4.2を参照します。
        alert($j.fn.jquery);     // => 1.4.2
    </script>
</head>
<body></body>
</html>

noConflictって他のライブラリとの併用だけでなく、jQueryの複数バージョンもOKだったんですね。
1.8.2で実装を確認してみました。

//............................................
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,

// Map over the $ in case of overwrite
_$ = window.$,
//......................................
jQuery.extend({
	noConflict: function( deep ) {
		if ( window.$ === jQuery ) {
			window.$ = _$;
		}

		if ( deep && window.jQuery === jQuery ) {
			window.jQuery = _jQuery;
		}

		return jQuery;
	},
//.........................
// Expose jQuery to the global object
window.jQuery = window.$ = jQuery;
//........................

true渡すと window.$だけじゃなく、window.jQueryも _jQueryに保持されている古いjQueryオブジェクトを参照するようにしてますね。