ラベル Amazon AWS の投稿を表示しています。 すべての投稿を表示
ラベル Amazon AWS の投稿を表示しています。 すべての投稿を表示

2011年12月31日土曜日

URLRewrite+CloudFrontでパフォーマンスを取り戻す

年末ですね。年末だからこそ大量のアクセスが発生することもありますね。コンシューマー向けのものだと、平時に比べれば多かったりしますよね。爆発的なアクセスになることもありましょう。おめーさん、容赦無いね!

特にHTTPS。コレはもうホントしんどいですよね。SSLアクセラレータとかSSLオフロードっていうか、そういう前段でさばくように構成して、実処理はHTTPのみにしとかないとHTTPSの処理に尋常じゃないCPU使用率を持っていかれたりしちゃいましょう。そーなると、本来の処理にCPUが割り当てられない。SSLハンドシェーク。悪魔のようです。でも、そんな構成すぐに取れない!そんな時の選択肢としてCDN。え?なんで?だってアクセスの総量が減ればその分処理量も減じゃない!コードチューニングより効果が高いこともあります。パフォーマンス20%アップのコードチューニングより200%アップ(することもある)のCDN。

お手軽なのはAmazon CloudFront。

  • カスタムオリジンで既存サーバーをそのまま利用することで、オリジンサーバーにコンテンツを事前にアップロードしなくてもいい
  • URLRewrite2.0から導入されてるOutboundRulesでのHTML書き換えで、CDNを指すようにしてしまうことで、レンダリングコードに(ほとんど)手を加えずにCDNが使えるようになる
  • CNAMEでカスタムドメインでもそれなりに(HTTPSのカスタムドメインはダメだけど)
  • コンテンツへのHTTPSアクセス負荷をCloudFrontに肩代わりさせれる(コレ!!)
  • 使ったぶんだけ課金は、いまさらですけどやっぱり素敵ポイント

もちろんIISね。Apacheもなんか書き換えありましたよね。mod_ext_filter?IIS以外はよく知らないです。

ココはひとつ、mvcPhotos(覚えてますか?)に実験台として登場してもらいましょう。

cf2

http://mvcphotos.takepara.com

まだ見れる状態になったままでした。そろそろ閉鎖しないと...。

http://mvcphotos.codeplex.com/

CloudFrontの登録とか、そういうのはいろんな人が書いてるので、その辺は省略。

cf3

こんな感じですね。コレといって何のへんてつもない設定。カスタムオリジンとProtocol PolicyをHttp Only、CNAMEでカスタムドメイン(HTTPSの時は割り当てられたドメインをそのまま使う)。

あとは、Web.configにoutboundRulesを追加(ExpressWebでURLRewrite使えると書かれてるけど、バージョンが見当たらなかったから不安だったけど、ちゃんと2.0以降が入ってる模様)。

    <rewrite>
      <outboundRules>
        <rule name="CloudFrontContents" preCondition="html" enabled="true">
          <match filterByTags="A,Img" pattern="^/Photos/Image/(.*)"/>
          <action type="Rewrite" value="//cdn.mvcphotos.takepara.com/Photos/Image/{R:1}"/>
        </rule>
        <preConditions>
          <preCondition name="html">
            <add input="{RESPONSE_CONTENT_TYPE}" pattern="text/html"/>
            <add input="{REQUEST_URI}" pattern="/mobile" negate="true"/>
          </preCondition>
        </preConditions>
      </outboundRules>
    </rewrite>

これだけ。これで、/mobile以外の時に/Photos/Imageへのアクセス(a hrefとimg src)すべてCDNへ変更します(詳しくはCreating Outbound Rules for URL Rewrite Module : URL Rewrite Module 2 : URL Rewrite Module : The Official Microsoft IIS Site )。/mobileを除外してるのには理由があります。mvcPhotosはHTTPSでのアクセスが無いので意味ないんですが、実際は有りましょう。その際、ケータイからのアクセスだとCloudFrontで使ってるワイルドカード証明書が残念なコトになります。まだまだ正常に処理できないですよね。

cf

なので、もともと素材の少ないケータイアクセスの場合(/mobile配下へのアクセス)は、CDNを利用しないようにして、コレまで通りのアクセスにしときます。PCやスマフォでのHTTPアクセスなら処理が負担にならない、って言う場合はconditionsを以下のように追加してHTTPSの時だけCDN参照するようにするのがいいでしょう。

        <rule name="CloudFrontContents" preCondition="html" enabled="true">
          <match filterByTags="A,Img" pattern="^/Photos/Image/(.*)"/>
          <action type="Rewrite" value="//cdn.mvcphotos.takepara.com/Photos/Image/{R:1}"/>
          <conditions>
            <add input="{HTTPS}" pattern="^on$"/>
          </conditions>
        </rule>

ただ今回、mvcPhotosのちょっと残念だったところと、Cloud Frontの制限が丁度マッチして、ちょびっとだけViewとスクリプトの変更がありました。

画像を返す部分をPhotosControllerにやらせてるんですが、そのパラメータをRoutingじゃなくてQueryStringで渡してたんです。が、Cloud FrontはQueryString無視します。なので、そこだけ変更してQueryStringでのサイズと変換方法の指定をRoutingパラメータにしました。

/Photos/Image/1?size=100&type=fit

↑こうだったものを↓こう。

/Photos/Image/1/100/fit

そのためにRoute登録。

    routes.MapRoute(
        "PhotoImage",
        "Photos/Image/{id}/{size}/{type}",
        new {controller = "Photos", action = "Image"});

コードいじってるじゃん!さーせん。でも、普通はいじる必要ないはず。あっても、ViewやScriptだけで済むはずです。サービスとして公開するならURLの設計もちゃんとするはずなので。今回の微調整もRouting以外はViewだけ。View内にknockoutで使うHtml Template埋め込んでるし、パス関連は全てそのテンプレートに展開してるから、OutboundRulesの対象になる。

これで、mvcPhotosにアクセスしてみましょう。

cf4 cf7

クリックして拡大すると見えると思いますが、/Photos/Image配下はCDNへ。その他の要求(JavaScript/CSS/Ajax)は自サーバーに行ってますね。もちろんホントはJavaScriptやCSSも持っていくのがいいでしょう。

cf5 cf6

↑こちらは/mobile配下。今度は同じ/Photos/Image配下のものもCDNに行かず、自サーバー参照のままです。

なんてお手軽。

実際HTTPSを利用してるサイトでBLOBコンテンツのダウンロードが大量に発生している場合、この方法でサーバーへのコネクションを1/10とかに抑えられることになって、ウハウハ。もちろんお金はかかりますけど。それでもサーバー増強やSSLアクセラレーションする機器の購入に比べれば、安いし経費で落とせます!HTTPSでのカスタムドメインが使えないのは気に入らない、って言うことがあるなら他の方法をとってくださいってことになります(ARRでSSLオフロードとかね、お金貰えればなんでもいいでしょう)。

とりあえず、今回のような方法でCDNを利用する時に、こういうふうにページ(View部)作るといいかもと、思ったことを書きだしておきます。

  • 素材を意味ごとに決まったフォルダに分けておく
    → パターンを増やすとその分の処理にCPU使っちゃう
  • スクリプトでスクリプトをロードしないほうがいいかも
    → スクリプト内のパスを書き換えるのが面倒
  • スクリプト内に極力パスを書かない
    → HTMLの属性を使う。DOMにデータを持たせとく。か、クライアントサイドのHTML Templateで。
  • 同じくCSSの中でimportしないほうがいいかも
    → 相対パスなら許容可能
  • HTML内のsrc/hrefはルートからの絶対パスで書いておく
    → 相対パスだとパターンがぶれるし、思わぬ参照先に。どうしても相対ならCSSにする
  • HTML内でスタイル指定しない
    → パスの書き換え問題。同じ理由でタグ要素にstyle属性も困る。

トリッキーなことしないで、メンテナンス性を考慮したページの作り方をしてれば、CDNへの振り向けはすんなり行きやすいという感じですね。

目指せ Ultra-Fast ASP.NET!

2011年12月17日土曜日

MongoDBを本格利用するために

MongoDB使ってますか?RavenDBのほうがいいですか?それともCassandraですか?いやいや、NoSQLなんて使わない派ですか?RDBよりも使い勝手のいいところがイロイロあると思うので、積極的につかてみてはどうでしょう。

先日MongoDBのSessionStateStoreに手を加えて、高速化してみたわけですが、そもそもMongoDBをどこに構築しておきましょう。自社内サーバー?DC内サーバー?いろいろ悩ましいところだと思いますが、ココは思い切ってAWS EC2に構築するのはどうでしょう。最近AzureでもOpennessでMongoDBが取り上げられてましたね。それもいいですけど、今回はEC2です。

おまたせしました。楽しいMongoDBの時間デス(1/5):企業のIT・経営・ビジネスをつなぐ情報サイト EnterpriseZine (EZ)

MongoDBも2になってからはジャーナルが標準だし、ReplicaSetとShardingもあるし、アクセス認証もかけれるしで、良い感じですよね!

がしかし!つい最近までは致命的な問題があったんです。認証使えるはずなのに、Sharding環境で認証をかけてるとC# Official Drivderが例外おきて接続できなくて、実質認証を使えないという問題。DC内ならいいけど、クラウドに持って行こうと思うと、これが非常に都合が悪い。パフォーマンスを考慮すると大変都合が悪かったです。

そもそもは、なんでMongoDBを運用環境に利用しようとしたか、ですけど、そんな話はどーでもいいですね。ちなみになんで運用環境に持って行こうと考えたかというと、SQLServerのミラーリングのコストが馬鹿にならなくなってきたから。あとログを1箇所に集約したかったっていう理由も大きい。イベントログめんどくね?フォワーディングするにしても、操作しにくいし。DBのマシン性能が低いっていう問題があったんだけど、そこに投資しないと解決出来ないってわけでも気がしたんですよね。違うアプローチで解決出来そうだとゴーストがささやいた。もちろん投資すれば一発解決。大人の解決方法です。でも、なんかNoSQLで解決できる気がしたんすよ。そういうことあるでしょ?

TraceListner into MongoDB
MongoDBにASP.NETのSessionを格納する
MongoDBにASP.NETのSessionを格納する - 完結編  

ASP.NETのセッションデータと、トレースログをSQLServer以外に保持したい。そのために必要なパーツは用意して、パフォーマンスとか可用性とか拡張性とか検証して行けそうだと感じたら、どこに構築しましょうか、ってことになりますよね。DC内に構築するのがネットワーク的にも近いし、効率いいのはそうなんですけど、んじゃサーバーどこから調達するのさー、ってなるし、よく分からないものに投資してくれろ、とは言いづらいしそもそも投資するならまずDBだろ、みたいな事になって行ったり来たり。むふー。とりあえずは、仮想マシン用意してその中にMongoDBの構築を行うっていうところでスモールスタートするわけですが、それも時限式。余裕ない。こうなったら、本格的にクラウドで構築するしかねー!MongoHQっていう選択肢も無いわけじゃないけど、いっその事、AWS EC2にがっつり構築するのがいいんじゃないかと、一人プロジェクトスタート。クラウド使ってるっていうとなんかカッコイイ気がするじゃない。

イロイロ試した結果、ReplicaSetとShardingでmongodインスタンスを6個(rsを3台ずつ2シャード)、mongosインスタンス4個、コンフィグインスタンス3個。MongoDBだけでまずは13個。それとDNSが2台。コンフィグ2台とDNS2台は相乗りにして全13台。DNSはAWS内部での名前解決に使う目的と、既存ドメインのサブドメインで外向きの名前解決用。既存のDNSの管理者権限なんて欲しくないし、弄りたくもないので、既存DNSにはゾーン委譲だけしてもらうようにして、AWS用のサブドメインを用意しました。その方が自分で好きなように制御出来るもんね!なぜmongosが4個なのかって?スモールスタートなら2個でいいじゃん!と思うところですが理由は別。

予算の関係もあって、すべてマイクロインスタンスで構築。なので、DC内からのアクセスでVPCは除外。VPNが簡単でよかったんだけど、そもそもDCにVPNルータを設置する余裕すらない。スペースと電源ね。大丈夫だとは思うけど、EC2がマイクロじゃないと予算が辛い。だってこの時点ではまだ自腹。そんなに稼いでませんから!

で、まぁ、今のMongoDBのバージョン(2.0.1)じゃShardingで認証が出来ないってところにたどり着いてガックリしてたわけです。

[#SERVER-3929] Arbitrary replica set authentication "need to login" errors. - MongoDB

しょうがないよね。できないものは出来ないと諦めて、IP制限で接続制限かけることにしました。アクセス経路はDCからAWSなので暗号化はしておきたい。でもVPCは厳しい。そうなるとIPSecかSSL tunnelか。ELBにSSL tunnel出来る機能があるので、それがいいなと思ってたんだけど、ELBはIP制限かけられない。インスタンスに届いた時のIPはELBのIPだし、SSL tunnelだとHTTPなわけでもないからX-Forworded-Forなんか無いし。そうなるとIPSecでマシン間接続の暗号化かってことになって、IPSecの設定の勉強から。認証できれば一気に解決なのにー、と思いつつできないものはしょうがない。

なので結局選択肢はなくIPSec。初めてIP セキュリティポリシー使いました。ウィザードの使い方をちゃんと理解すれば大丈夫だった。あとネゴシエーションのためにUDP500,4500開けるのを忘れずに。この話長いから省略。

ちなみにMongoDBにアクセスするすべてのマシンにIPSecを登録するのは大変なので、Delegateを利用してポートフォワード。コレはコレで懐かしい感じですね。NIFTYの時以来。

DeleGate Home Page (www.delegate.org)

やっと、経路がセキュアになって、IP制限をかけれるようにもなったので、EC2で構築したMongoDBへのパフォーマンステスト。ただ残念ながら、IPSecしないのに比べて随分遅い。4:3くらいの比率で遅い。暗号化のコストが発生するからmongosがヒーヒー言ってる...。

先ほどmongos4台用意したといったのには意味があって、外向き(IPSecで外部から接続する場合)と内向き(AWS内のMonogDB以外のインスタンスからの接続用、暗号化しなくていいもんね)でそれぞれ構築しないとパフォーマンスしんどいからです。なので、外向きはIPSec対応mongos、内向きは素のmongos。それぞれ2台ずつ用意して計4台。

IPSecを使ったら遅いのはCPUがパワー不足なのも原因だからインスタンス大きいの使うしかないのかなー、と思いつつもそれでもSQLServerに入れるよりは早いし、まぁ、いいかと。いつの日か先のMongoDBの不具合が改修された暁には再度設定しなおそうと思い、まずはこれで開始。

いろいろ大変だね。OpenSSL使って証明書を作ったり、Delegateでゲートウェイ用意したり、IPSecの接続環境作ったり、そもそもMongoDBの環境作ることも含め、やること山盛り。それらのHowTo書こうと思うと大変面倒なので全てまとめて省略!勉強になったよ。MongoDBでReplicaSet+Shardingでの認証のやり方(ReplicaSetで個別にPRIMARYにユーザー登録して、mongos起動後configサーバーにもユーザー登録、最後にauthとkeyFile指定してサービス化。ちなみにそこまではコンソール起動でちょこちょこ調整しながら作業しました)がわかりにくかった。これまでのバージョンでうまくいってない部分だったみたいだからやむなし。インフラ屋って大変ね。

OpenSSL: The Open Source toolkit for SSL/TLS

今年ももう十分やりきっただろ。と、思った矢先にMongoDB2.0.2リリースのお知らせ。

Downloads – MongoDB

数日前にRC2が出たときには今年はもうないな、と思ってたのに。ウズウズ。ワクワク。ちなみにRCで先の不具合が解消されてたのは確認済みだったので、リリースをひたすら待つだけだったんですけど、まさかこのタイミングで出てくるとはねー。

こうなるとELBを利用してSSL tunnel(ELBではSecureTCPっていう名前)を利用する環境を用意してしまおうじゃないかとなりましょう。ReplicaSet+Shardingで認証かけれれば、後は経路の問題だけだし、何よりELBで暗号化復号化してくれればmongosが通信に専念すればいい状態になるし、更にはELBがラウンドロビンでmongosを選んでくれるから、パフォーマンスは大幅向上。1.25倍(2倍近くさばける時もある)くらい早くなりました。ELBだとアクセスが増えると受け口を自動でスケールしてくれるので、負荷がもっと高くなると、5分くらいで自動でよしなにしてくれるらしい。5分だっけ?そこはいいか。そうするとIPSec用mongosは用なし。さらにEIPでグローバルも不要。DNSにELBのCNAMEを登録すればいいだけになって更にお買い得。

運用の目処もたち、Reserved Instanceで見積もったり(超間違えたけど)して、最終的にやっと会社が払ってくれるようになって一安心。DBもWebもある程度はAWSに持っていっちゃう予定だけど、最終形態はDCとクラウドのハイブリッドが良い感じに思えますが、一般的にはどうなんだろ。

今はまだテスト環境のセッションストアと、トレースログの取得だけにしか使ってないけど、徐々に全てのトラフィックをDC内のMongoDBからAWSのMongoDBに変更していく予定です。どーなるかなー。楽しみだなー。

せっかくなのでPPT用の素材を使って今回の構成図を書いてみたよ。

MongoDB 2.0.1でIPSecの時

スライド1

MongoDB 2.0.2でELBの時

スライド2

あは。センスないー。素材があってもコレじゃ...。なんとなくでも伝わるといいな。mongosからReplicaSetをShardingとして参照するように設定してるのをうまく表現できませんでした...。

ところで、CloudWatchって数値高めに出てない?実際ログインして直接パフォーマンス見てみると、CloudWatchで表示されるほど高くないんだけどなー。なんだろ。そんなもの?

2009年4月1日水曜日

EC2のWindows上で日本語Webアプリケーションを動かす

ここ最近、ずっとAmazon AWSでの環境構築をしてて、運用環境の変更を行ってました。クラシックASPや、ASP.NET 1.1、2.0で動かしているものをEC2に移行してしまおうという魂胆です。ハードウェアも古くなりすぎてて、にっちもさっちも行かない感じだったのもあり、いっそのことクラウドへ。Azureにいければ一番良かったけど、用途も合わないし、そもそもがまだ無理だしね。

Amazon EC2でWindowsサーバーを動かすだけなら、前に一度チャレンジしてるし今ならいろんな所でエントリもされてるから検索すれば沢山見つかると思います。もちろん日本語で。実際の運用環境を作ってみるにあたりデプロイ含めて少し。自分が忘れないためのメモエントリです。作業はElasticfoxとS3 Organizer、AWS Management Consoleですべて完結。自動化するなら"Amazon Web Services Developer Community : C# Library for Amazon EC2"を使ってコード書いて対応。今回は2台しかインスタンス立ち上げないので使わないです。

まずはどのインスタンスを使うか。x86(32ビット)かx64(64ビット)が大きな分かれ目。x86ならStandard Smallか、High-CPU Mediumの2択。x64ならStandard Large/Extra Largeか、High-CPU Extra Largeの3択。それぞれのVMスペックは"Amazon EC2 Instance Types"に全部書いてるの詳細はそちらで確認。それぞれのインスタンスにSQL Server 2005か同Expressが入ってるイメージがあるけど、ぶっちゃけStandard Smallでは使い物にならない。SQL Serverを使うならx64しか選べないけど、同Expressを使うならx86でもいいので、High-CPU Mediumが一番安く現実的な選択肢。そうなるとSQLServer AgentがないのでWindowsのタスク機能を使ってバックアップとることになるので、ちょっと作業が増える。それよりなによりライセンス的に大丈夫なのかがよく分からない。ウェブアプリケーションのストレージとしてExpressっていいんでしたっけ?Datacenter Editionだとなんかライセンス違うのかな。ちなみにAuthentication Serviceは今回使ってないのでよく分かりません。インスタンスの料金については"Amazon Elastic Compute Cloud (EC2) Running Microsoft Windows Server and SQL Server"ここで。なにげにSQLServerを使いたいなら最低$1.10必要だから1日$26.4が最低価格になるんだよね。1アプリケーション1DBサーバーなんて贅沢な使い方をしようとすると、それだけで月に$800くらいかかっちゃうから気をつけないとね。

AMIも最初は名前見ただけじゃ何がインストールされるのかよく分からなかったけど、何度もやってると普通の名前に思えてくるから恐ろしい。

ec2_1

OwnerがAmazonになってるWindows AMIはこれで全部。Authenticationの有無(WinAuthと付いてるか付いてないか)の組み合わせが5種類なので全10種類のみ。”i386”が32ビットで”x84-64”が64ビット。SQLServer入り(SqlSvrStd)が2種類で、同Express(SqlSvrExp)入りが4種類。どっちも入って無いのが4種類。
...分かりやすいじゃないか。

これらとインスタンスタイプの組み合わせだけなので、慣れればなんてことないね。

で、日本語化。当然、すべて英語版なのでそのままだと日本語が表示されない(インスタンス上で)ばかりか、各種書式フォーマット(ロケール)も変えないと、アプリケーションで変な表示になってしまう。内部的にも文字列から復元出来なかったりするところが出るかもしてないし。

と、言うわけで"Amazon Web Services Developer Community : Configuring Windows Components on Amazon EC2"に書いてるスナップショットIDをもとにしたVolumeを作成してドライブを割り当てておいてから、コントロールパネルの"Regional and Language Options"で設定を変えること。

ec2_2

ec2_3

ec2_4

それと、Time Zoneも変えた方がいいかも。

とりあえずこれで、サーバー上で日本語が使えるようになるし、書式化フォーマットも見慣れた物になるけど、ASP.NETアプリケーションでの書式が変。ここの設定が反映されてない。おかしいな~、と思って調べて見たら"globalization 要素 (ASP.NET 設定スキーマ)"でカルチャー設定するのが正しい対応でした。てへ。

DBも言語や照合順序の設定が日本語向けのものじゃないから、その辺の設定も忘れずに。だいたいこんな感じでOKなんじゃないでしょうか(ちょいちょいコントロールパネルの設定が英語に戻ってるのは、なにがきっかけなんだろう)。