えいのうにっき

誤字脱字・記述の誤りなどはこちら https://github.com/a-know/blog.a-know.me へお願いします

appengine ja night #18で分かった、Google App Engineの課金の仕組み、節約術・自分用まとめ

**去る11月18日に行われた、

appengine ja night #18。「app engine ja nightって?」というのはこちらあたりでも見てもらうこととして、ぼく個人、これに参加したことはありませんでした(主に地理的要因による)し、今回も参加できたというわけでは無いのですが、appengineのpreview版卒業以降、初のajnということで、「きっと、appengineのリソース節約術が聞けるハズ!」と、心から、その開催を待ち望んでいました。 上述のとおり、ぼくの参加の大きな障害である「地理的要因」は今回のajn #18でも変わらず、ただ一方で“リソース節約術”は何としても聞きたい!ということもあり、今回はTwitter + Ustreamを利用しての“初参加”と相成りました〜。

結果、もちろん直接参加とは勝手が違って色々ともどかしかった部分もあったものの、個人的には非常に勉強になった・得られたものが大きかった、ということで、その「個人的に勉強になったこと・得られたもの」をまだ記憶が新しいうちに、(自分用に)メモしておこうと思います。

TwitterUstreamに齧り付いていられたのは、結局shin1ogawaさんのセッションだけだったりするのですが(松尾さんのはUst無かったしぃ〜))


**@shin1ogawaさん「Google App Engine 倹約のすゝめ」

-発表時の資料はこちら。 -スライド1枚目 --話の導入でのshin1さんからの質問、「GAE、全く使ったことない方はいる?」→0名。 ---さすが、ajn! -スライド3枚目 --うさぎかわゆす -スライド6枚目 --“リソース消費量が少なければ、無料でも利用できる” ---裏を返せば、リソース節約のためのチューニングの手間を惜しむな!その手間を惜しまなかった者にのみ、gaeの女神は微笑む!・・・ということですよね:) -スライド8枚目 --課金体系の変更 ---今までは「無料or有料」だったのが、新体系では「無料or有料orプレミアム」に。 ----“有料: 使った分→最低$9+それを超えて使った分に” -----まずは無料枠に収められるようチューニングを行い、それが無理だったら今度は、最低利用額である$9に収められるようにチューニングを行う・・・って感じになるんだよね、きっと! -----で、この$9ってのは「$9/month」ではなく、「$2.1/week」??(不明瞭です・・・でも、一ヶ月が30日とすると計算は合いますね) ---今まではCPU使用時間による課金だったのが、インスタンス起動時間による課金に。起動しているインスタンスの数が大きく影響する。 ---加えて、APIの使用回数でも課金。 ----今までではCPU使用時間に含まれていたのが、別途課金対象に。 -スライド15枚目 --CPU Hours と Instance Hoursの両者を考えたときに・・・ --“インスタンスに注目すると” ---“CPU Hoursではなく、インスタンスごとの起動時間+αが課金対象になる” ----“+α=15min。リクエストごとに+15minではなく起動後の最後のリクエストから15min.” ----“インスタンス終了後、15min以内に再び起動した場合は+15minされずに起動時間が継続されていたとして扱われる。” -----15minって結構長いな・・・。場合によっては、めちゃくちゃ不効率なInstance Hoursの使い方になっちゃいそー。 --“APIに注目すると:” ---“CPU Hoursに含むのではなく、その使用回数に応じて別途課金になる。” -スライド17枚目 --そもそも“Frontend Instance”って何物?? ---“App Serverとして起動される論理的なマシンのようなもの。開発者が構築したアプリケーションが読み込まれ、実際の処理を行う。リクエストの忙しさに応じて数が動的に増減する(自動スケールアウト).” -スライド18枚目 --“Google App Engineでは、基本的には1インスタンスで同時にひとつしかリクエストを処理できない!” --“新しいリクエストが到着したがインスタンスが空いてない!どうする!?”・・・次のスライドへ -スライド19枚目 --一つ目の解。“新しいインスタンスを起動=スケールアウトする” ---あらたにもう一つ、Frontend Instanceが起動するイメージ。 -スライド20枚目 --もう一つの解としては、“インスタンスが空くまで、キュー(Pending Request Queue)の中で待機する”! ---すでに起動済みのFrontend Instanceが空くまで、リクエストを待たせておくイメージ。 -スライド21枚目 --“どの空きインスタンス(Idle instance)に処理を任せるかを決める”役割を持つ、“スケジューラ”がある。 ---“空きインスタンスがない場合はインスタンスを新たに起動する(spin upする)か、Pending Request Queueで待つか”を決める。 ----その決定には、“リクエストの平均処理時間と、Pending Request Queue内に溜まったリクエスト数を参考にする。queue内での待ち時間をPending Latencyといいます”。 ---“リクエストが少なくなり、暇になってくるとインスタンスを終了(spin down)する” -スライド22枚目 --そのスケジューラの設定を、「Application Settings -> Performance」で行えるようになっている。 ---“Max Idle Instances: ここで設定した数以上のアイドルインスタンスが存在しても課金対象にならない。” ---“Min Pending Latency: Pending Latencyがここで設定した時間を超えるまではインスタンスが立ち上がらない。” ----“Automatic”にしとくというのは、スケジューラに任せる、ということですね -スライド23枚目 --“デフォルトの設定値は「パフォーマンスを最大限に優先する」という、スケールアウトしやすい=インスタンスが増えやすい=インスタンス時間が大きくなりやすい設定になっている。” --“Max Idle Instances / Min Pending Latencyの設定値を調整することで、インスタンス時間を抑える事ができる。” ---“もちろん、その分スケールアウトしづらくなる=パフォーマンスが犠牲になる。” ---“Min Pending LatencyはMax Idle Instancesに比べて効果が低いのであまり極端な設定をしなくても良い。” -スライド24枚目 --このスライドバーによる設定は、いの一番に行える・一番カンタンなチューニングとも言えますね〜 -スライド25枚目 --Instance数の確認は、Dashboardでも行えるよ! ---“Dashboard画面のInstancesで見ると、アイドルインスタンスが設定よりも多い事があるが、課金されるのはMax IdleInstances=1なら1インスタンス分だけが課金される。” ---“緑色の"Billed"が実際の課金対象となる.” -スライド26枚目 --“スケール設定による節約の事例”。 ---見事にこの設定が功を奏してますね:) ---“誰かがLingrに書きこむと、その内容に応じたアクションを行う”というような、予期できない・外的要因リクエストの局所的な増加に対してのスケールに縛り・上限が設けられる、っていうのは、やっぱり安心だなぁ -スライド27枚前 --もうひとつの切り口として、“インスタンスごとの処理数を増やす”。 -スライド28枚目 --“concurrent requestを利用する” ---“ひとつのインスタンスで同時に複数のリクエストを処理できる。旧課金体系の場合は使ったcpu時間が課金対象なのでインスタンス数を減らすことにはあまり意味が無いが、新課金体系の場合はインスタンス起動時間×インスタンス数が課金対象なので効果が大きい。” ----そんなことができるのか〜! -スライド29枚目 --“concurrent requestの設定方法” ---appengine-web.xmlに“true”でおk! ---ただしもちろん、スレッドセーフに作っている必要はあるよね -スライド31枚目 --“Datastore APIの価格”。 --datastoreの制限が個人的には一番厳しいんだよな〜。 -スライド33枚目 --Datastore Write Opsを減らすことを考える・・・ --そもそも、Datastore Write Opsとは、下記の3種類のOpsで構成される・・・(スライド31枚目) ---Entity Put Ops(エンティティの登録) ---Entity Delete Ops(エンティティの削除) ---Index Write Ops(インデックスの登録) --例えば、“p1='A' p2=1 p3=[1, 2, 3]”なエンティティをひとつ、新規登録する際に発生するWrite Opsの総合計は・・・ ---“Entity Tableへの書き込み”・・・1 Put Ops ---“Index Tableへの書き込み”・・・11 Index Write Ops ----key asc ----p1='A' asc, p1='A' desc ----p2=1 asc, p2=1 desc ----p3=1 asc, p3=1 desc ----p3=2 asc, p3=2 desc ----p3=3 asc, p3=3 desc ---合計なんと、12 Write Ops!!これは怖い!! -スライド34枚目 --上述のエンティティの、p2フィールドだけ変更し上書きした場合・・・ ---“Entity Tableへの書き込み”・・・1 Put Ops ---“Index Tableへの書き込み”・・・2 Index Write Ops ----“p2=1 asc, p2=1 desc” ---更新だけでも、合計3 Write Ops! --感覚的に、なんとなく“更新・登録を行った回数=Write Opsの数”でしょ、って考えちゃってた・・・。 -スライド35枚目 --“Datastore Writes Operationを節約” ---“一回のPUT操作=1 write ops...ではない!”ことを理解する! ----“Batch操作であれば、Entityの件数分必要” ----“Index Writesも含まれる点に注意” ---“必要ないSingle Property Indexを削減する” ----“インデックスを作らない、を明示的に指定する。”! -スライド36枚目 --“Single Property Indexを使わない方法” ---Java ----“entity.setUnindexedProperty('p1', 'A');” ---Slim3 ----“@Attribute(unindexed=true) String p1;” --“フィルタやソートに使っていないプロパティをunindexedにする。” -スライド37枚目 --ただし、“unindexed設定後一回目のPUT”には要注意! ---“unindexedで保存しなおしても、一回目はIndexの削除のためにIndex Writesが発生してしまう。” ---やるなら最初から、だな〜。 -スライド42枚目 --続いて、Datastore Reads Operationsを減らすことを考える・・・ --“クエリを実行して50件のエンティティを取得する”と・・・? ---1 Query Ops + 50 Fetch Ops = 51 Reads Opsの消費!! ----ギャーース ---“キャッシュしても問題ないクエリ結果はキャッシュする”! “memcache APIは無料”!! ----でも、キャッシュしても問題ないデータって、結構限られるきがする。。 --“Keyだけで良い場合はKeyだけ取得する” ---“Keyの取得だけならSmall Opsとなる。” ---“1 Reads Ops + 50 Small Ops” ----shin1さん、これを逆手にとって、keyにデータをブチ込んでしまってみたとかww ----節約のための最終手段としてなら、ライトなデータはこれでいってみる価値はあるかもだけど・・・あんまりやりたくないなぁw -スライド44枚目 --“Backend Instanceにも無料枠がある”。 ---そもそもBackend Instanceとは、ってところなんですが、こちらのエントリの説明がとてもわかりやすかったです。 ----“実行のトリガーはWebアクセスであり、実行ルーチンはサーブレットの形を取るものの、事実上任意のプログラムを無限に実行し続ける(つまりhttp的にreplyを返さないで)ことを可能にする仕掛けだ。さらに、CPUやメモリの量も選ぶことができるし、課金も実際に使用したCPU時間ではなく、インスタンスが起動している時間に対して行われる。” ---“$0.72/dayの無料枠があるので活用しないともったいない!Taskの実行に使うのが便利。” ----一番のロースペックで9時間分の無料枠かぁ・・・。 ----cronでも特定の時間だけbackendにさせることが可能? ---backendについては、もう少し要勉強だな〜 -スライド46枚目 --今セッションのまとめ。 ---“性能が良いアプリケーションを構築する事はこれまで同様...ますますに重要に!”。 ----やるべきことをやらずに“高価いよ!!”と言うなかれ。 -質疑応答 --非常に活発な質疑応答が交わされていました〜。これを整理している方はおられないですかねぇ〜? --その一部は、ハッシュタグ「#ajn18」や、「appengine ja night #18 - Togetter」で追うことができますが! ---“backends instanceの場合も15分単位で起動している” ---“バックエンドのインスタンスのON/OFF観察して規則性は不明” ---“memcacheとインスタンスのライフサイクルは無関係” ---“concurrent request の設定はPython2.5でも有効? →有効ではない” ---“concurrent requestを有効にするとメモリが許す限り1インスタンスで処理できる” ---“cronをfrontend -> backendに移しても無料枠以外は特に変わらない

**他にも、

@tmatsuo さんによるApp Engine 1.6.0解説(資料)があったり、Beer Talkがあったりしたようですが、松尾さんのタイミングでUstが無くなってしまったのと、自分の空腹により(主因)、TLを追っかけることをやめてしまったので、このまとめもここらへんで。

でもほんと今回のは、遠隔参加だったけれども、参加してよかったと心から思えます。今まで「う〜〜・・・。。gaeでWebアプリ動かそうと思ったら、なんかよくわかんないチューニングをしなきゃなんないな・・・」っていう、なんかモヤモヤしてたところの大部分が、一気に晴れた気分。これから作る予定にしてるアプリは今回知ることができたことを最初っから盛り込んで、スムーズにリリースにつなげたいなぁ!

なにはともあれ、皆様、ありがとうございました