工場長のブログ

日々思ったことを書いてます。

innodbのチューニングとEC2のIO

ここでは、EC2の上でinnodbをチューニングして使うという観点でTIPSをまとめてみた。
RDS便利だから使おうぜってのは今回の話のスコープには含みません。
あと、innodbについて、割りとちゃんと調べてみたのは初めてだったりするので、間違ってる点など見つけたらぜひご指摘くださいませ。

innodb関連
バッファプール

    • ワーキングセットを乗せておくオンメモリのバッファ領域。読み書き共にこの領域を経由して実施される。
      • 参照時はバッファプール上でデータを探して、なければテーブルスペースから取得する。(そのデータはバッファプール上に格納される)
      • 書き込み時はリクエストを受け付けてワーキングセットを更新し、ログの書き込みへ移行する。
    • 設定はinnodb_buffer_pool_size
    • 監視はSHOW ENGINE INNODB STATUSか、mysqladmin extended-statusで取得できる「Free buffers」を見る。
    • 監視のポイントはFree buffersが枯渇(=ワーキングセットがページング)していないようにすること。

ログ

    • innodbでは、ログバッファに書いてからログの実ファイルが更新される。ログの更新が完了した時点で更新リクエストはコミット完了となる。(その先のテーブルスペースへの書き込みは非同期)
    • 実ファイルの更新時にデフォルトでは同期的にfsyncが走るようになっている。(ログの消失を防ぐため)
      • この設定を外すととても高速になる。が、ログの完全性が弱くなる(ACID属性が失われる)ので基本的に推奨されない。
        • 設定項目はinnodb_flush_log_at_trx_commit
    • ログの容量が大きいと、突然の大量の書き込みに対してログがバッファプール的に働いてくれるので、ディスクIOが平準化することができる。(ただし、大きすぎるとクラッシュ時の再起動時に時間が多くかかるようなので、ただ大きくすればいいわけではない)
      • 設定項目は以下の2つ
        1. innodb_log_file_size : ログファイルのサイズ
        2. innodb_log_files_in_group : ログファイルの個数
      • MySQL5.5まではログの容量が合計で4GBまでしか設定できない。5.6.3以降は512GBまで設定可能。
    • クエリ的にはログ書き込みが終了した時点でコミット完了が返る。
    • 監視項目としてはmysqladmin extended-statusで取れるdirtiy page(ログにだけ書き込まれて、テーブルスペースへの書き込みが終わっていないデータ)。これが線形に増えて行かないように監視する。これが増えてしまう場合には更新クエリの量を減らすかディスクのIOを改善するか検討する。
      • SHOW ENGINE INNODB STATUSでLog sequence numberからLast checkpoint atを引いた値を監視するのでもOK.

テーブルスペース

    • いわゆる実データが乗っているディスク領域。ログの書き込みとは別のディスクにしておくとベター。
    • ログの書き込み完了後、非同期に更新処理が走るので、理論的にはログが溢れてしまわない程度のIO速度があればOK。
    • ダブルライトバッファという仕組みがあり、これを使ってオールオアナッシングの書き込みを実現している。具体的には下記のようなフローとなる。
      1. ダブルライトバッファにデータを書き込む
      2. これに失敗したら書き込みは失敗とする(ナッシング)
      3. 次に実ファイルを書き込む
      4. ダブルライトバッファと比較して差異があれば書き込みは失敗とする(ナッシング)
      5. 一致していたら書き込みOK(オール)
      • zfsのようにfs側でオールオアナッシングな書き込みを提供してくれている環境の場合はオフってOK。設定項目はskip_innodb_doublewrite。

ここまで書いておいてアレですが、上記のフローとポイントを図にしてみた。
f:id:imai-factory:20121227210818p:plain

レプリケーション

  1. これだけでだいぶ長くなりそうなのでまた今度。。
  2. 今回ひとつだけ書いておきたいのは、参照専用マシンと更新専用マシンではワーキングセットの内容がぜんぜん異なったものになるはずなので、innodb的にもMasterとSlaveを分けると無駄なメモリを使わなくて済むようになると思う。

AWS関連

  1. EBSについて
      • Provisioned IOPSはブロックサイズ16KBで計算されている。そのため1000IOPSをプロビジョンした場合、ブロックサイズ16KB以下なら1000IOPS出るが、32KBのブロックサイズだと約500IOPSになる。
      • PIOPSは指定したIOPSを安定して発揮してくれるが、バーストはしない点に注意。
        • 性能を高めるよりも、性能を担保するイメージで利用するのがベター。
      • MySQLのデフォルトのページサイズは16KBなのでそのまま計算すればOK.
  2. インスタンスストアについて
    • EC2にはEBS以外に内蔵ディスクとしてインスタンスストアと呼ばれるブロックデバイスが付いている。
    • 現在USのリージョンで利用できるhi1などのインスタンスではこのインスタンスストアがSSDになっていたりするのでIOを求めるならこれを利用する。
    • インスタンスストアの注意点としては、インスタンスを落とすとデータがなくなること。
    • EBSとインスタンスストアの、一般的な特性の違いとしては、前者はランダムアクセスに強く、後者はシーケンシャルアクセスに強い。
  3. RDSについて
    • チューニングの話のスコープからは外してたけど、ついでにRDSの特徴も。
      • ストレージは多めに確保したほうがIOが出やすい。
      • Provisioned IOPSを利用する場合、EC2と違ってIOレートはストレージ容量の10倍に自動的に設定される。(変更不可)

まとめ

  1. ワーキングセットがページングしないようにinnodb_buffer_pool_sizeを設定しよう。監視すべきはfree_buffers。
  2. ログについてはdirty pagesが線形に増えないか監視。線形に増えるようなら更新クエリにディスクIOが追いついていない。
  3. ログのサイズは大きいほうが更新性能は上がるが、大きすぎるとクラッシュ時に復旧に時間がかかる。
  4. ログとテーブルスペースは別ディスクにするとベター。
  5. PIOPSは性能を高めるのではなく安定させるソリューション。
  6. RDS使うならディスク容量は大きめがベター。


参考にしたページ・資料など

CloudFormationとChefを使って拡張Stampパターン

こんばんは!
今回はCDP Advent Calendarへの投稿です。
今日はStampパターンの拡張の話について書いてみます。

きっかけは「MongoDBのクラスタがAMIくらいお手軽に試せたらいろいろ触ってみるんだけどなぁ」という声を頂いたこと。

はじめに
今回やってみるのは、タイトルの通りだけどCloud FormationとChefを使ってMongoDBのクラスタをデプロイしてみるということ。いろんなサードパーティーベンダーさんが提供している彼らの製品が乗っているAMIのごとく、さっとクラスタをデプロイして、ベンチして、終わったら捨てる、みたいなことができることを目指します。

まず、Cloud Design Patternには、Stampパターンというのがあって、AMIという仮想マシンのイメージを使って同じ構成のサーバーを、あたかもスタンプを押すように複製するデザインパターンです。

更に、CloudFormationでシステムスタックをデプロイするStack Deploymentパターンというのもあって、今回の話はこれと似ています。というか技術的には変わりはありません。

じゃあ何が違うのかというと
f:id:imai-factory:20121222200235p:plain
Stack Deploymentパターンでは、こんなかんじで、どちらかというとLBがあって、WEBサーバーがあって、DBがあって、みたいな一連のシステムスタックをデプロイの対象としている感じ(あくまでイメージであって、厳密な定義はありません)。今回はこういうのではなくて、イメージ的には
f:id:imai-factory:20121222200916p:plain
こんなかんじで、一つのアプリケーションのクラスタ(MongoDBとかMySQL Cluserとか)をAMIみたいに簡単に取り扱いたいということ。もっというとこんな感じ
f:id:imai-factory:20121222210825p:plain
外からみたら一台に見えるところを目指します。ということは、複数の役割(マスタやスレーブなど)のサーバーから成り立つクラスタであっても、例えばベンチ&チューニングをする際に、いちいちそれぞれのサーバーにログインしてコンフィグ修正して再起動して・・・みたいなことはしたくないよね。APIのエンドポイントも設定のチューニングも一台のサーバーで完結するようにしたい。

何をするのか
ということで、今回はこういうものを作ってみようと思いました。
#ちなみに実装が間に合ってなくて、今回はお話だけです。。。。orz
f:id:imai-factory:20121222230716p:plain
箇条書きで説明するとこんな感じ

■起動Procedure

  1. CloudFormationでManagement Hostを起動。ここで受け取る入力は以下のとおり。
    • Instance Type, Shard数, ShardごとのReplica set内のホスト数, DBファイルの格納パス
  2. Management HostがCloud Formationを使ってconfig serverとmongosを起動。
    • このときInstance TypeとDBのディレクトリを渡してそのとおりの設定でサーバーを起動する。
    • また、サーバーのRole(config, mongos)をuserdataで渡す。
    • 立ち上がったサーバー群はgitとchef-soloをインストールしたあと、githubからchefのcookbookを取ってきて、渡されたroleにしたがって自分を構成する
  3. Management Hostが今後はShard nodeをCloud Formationで起動
    • だいたい上と同じ操作が実施される。
    • ひとつのCloudFormationでは、ひとつのShardだけを作成する。指定されたShard数の分、この操作は繰り返される。
    • Replica setで指定された台数を起動するのには、Auto Scaling Groupを利用する。
  4. 指定されたShard数のぶん、3を繰り返す

■その他備考

  • AMIはすべて素の状態で利用。chefとgit、それからuserdataだけで自己構成される。
  • すべてのホストは起動時にgitとchef-soloがインストールされる
  • まだ実装までちゃんと考えてないけど、github側のアップデートと同時にchef-soloを使って各種設定ファイルなどをデプロイしなおせるようにしたい。
  • これもまだ実装はちゃんと考えてないけど、Management Hostのterminateにhookして、子供のCloudFormation Stackを削除するようにしたい。

そうすると・・・
という感じ。これがちゃんと動くと、以下の様な機能が実現されます

  • 台数とハードウェアスペックを指定して簡単にMongoDBのクラスタを起動する
  • 検証などして、終わったらスタックをまるっと捨てる

EMRでHBaseを動かすみたいなイメージですね!
こうなると、以下の様なことができるわけですね!

  1. 来週からクリスマスとお正月でサイトのアクセス数が激増する!!!時間がない!!!!
  2. 拡張Stampパターンを作って複数の条件でMongoDBのクラスタをサクサクつくって、どれくらいの規模のクラスタつくればいいか検証!
  3. 規模感がわかったら検証用のクラスタはぜんぶ綺麗にすてちゃう。
  4. で、最後に本番用のクラスタをつくって本番に備える
  5. 割りと複雑な検証がサクサクやれたのでなんとか本番間に合ったぜ!

まとめ
・・・と、そんなにうまい話になったらいいですが、せっかくサーバーの調達が簡単なクラウド環境があるのでもっとカジュアルにクラスタアプリケーション動かしてみようぜというお話でした。ただし、CloudFormationとChefを使う以上、ある程度の柔軟性は犠牲にしています。なので、どちらかというと本番向きではなく、あくまで機能や性能検証、もしくは開発環境向けかなーと思っています。Chefは本番でも使えると思いますが、おそらく本番はより柔軟な設定ができるようなChefの組み方が必要になると思います。

まあとにかく、まだ実装できてないので、冬休みの宿題で作って見ることにします!
#どこかの会社も、まずはプレスリリースから出すっていうしね。
#MySQL Clusterとか、他のクラスタアプリケーションへの対応もそのあと考えます。

海外のDSPが日本に進出してきているという話

昨日、アメリカのアドエクスチェンジの人と飲んでいろいろ気づいたことがあるので書いておく。発端は、話の中で「最近海外のDSPが日本に進出してきる。実際、ヨーロッパのクライアントがDSP経由で日本のインプレッションを買いたいって話もよく聞くよ」みたいなことを聞いたところから。

先週のad:tech Tokyoでも、たしかに海外のDSPの人たちを結構な頻度で目にした。あと、その逆に日本のDSPが海外に進出していくという話を最近よく聞く。

後者については、日本のいろいろなサービス、特にゲームプロバイダーが海外に進出しているのでその人たちに対してマーケットを提供するためだよなーくらいにしか思ってなくて、正直ちゃんと考えてなかった。

で、それはそれで間違ってはないんだけど、DSPが海外に出ていくと、海外に出ていった日本のクライアントだけじゃなくて、日本にいるクライアントも海外のインプレッションを買い付けることができるようになる。で、それは逆も真で海外のクライアントも日本のインプレッションを買い付けることができるようになってくる。こうなってくると、海外の広告費が日本の市場に入ってくる/日本の広告費が海外に出ていく状況が生まれてくるわけですね。しかもクライアントはDSPからインプレッションを直接買い付けられるので商流も若干変わってくる。

自分たちで買い付けの運用したり、海外のインプレッションを買い付けるなんて、クライアントみんながみんなやるわけではないけけれど、お金を払う側にとって選択肢が広がるのはいい話ですね。RTBはアドエクスチェンジみたいにスター型で真ん中に管理をする人がいるのではなくて、DSPとSSPが自律的にメッシュ状につながっていくので、そのあたりのトレンドも市場の理論でできあがっていくのが面白い。

一方、DSPはBidを受けたからといって必ず売上になるわけではないし、海外のインプレッションを買い付けるには、地理的に近いところにシステム投資をする必要があるので、海外進出ってそうそう簡単な話ではないと思う。そのあたり、AWS使うと海外進出も比較的簡単だし、初期投資も圧縮できるよっていう話もありつつ、DSPやSSPの人たちがAWSの各リージョンに集まると、Ad版のIXみたいになって面白いんじゃないかなぁ、とぼんやり考えてみた。

CloudFront+S3でCORSしようとしてハマった。

S3がCORS(Cross Origin Resource Sharing)をサポート開始(http://aws.typepad.com/aws_japan/2012/09/amazon-s3-cross-origin-resource-sharing-support-1.html)したので、それをCloudFront経由で使ってみようと思ってハマったのでメモ。

CloudFront経由でS3に配置したファイルをCORS利用しようとしたらうまく動いたり動かなかったり。その原因を追ってみたところ、CORS自体の仕様とかいろいろ勉強になったのでスライドにしてみましたというお話。

詳細はスライドを見て欲しいのだけれども、S3がCORSの仕様(http://www.w3.org/TR/cors/)に厳密に実装されているのでしようがないという話で、ひとまずのワークアラウンドとしてはEC2を使いましょうという話。

しかし、S3でもうまく動くケースがあって、そのパターンがまだ特定できてなくて謎。このへんのディスカッションはうまく動く前提で話が進んでる。。
https://forums.aws.amazon.com/thread.jspa?messageID=377513

Elastic BeanstalkでFuelPHPを動かしてみた。

2012/12時点で、Elastic BeanstalkのPHPコンテナが新しくなっているので、この記事通りに動かないかもしれません。



PHPを書く機会があったのでElastic Beanstalkを使ってみた。
gitからpushでデプロイできるので便利ですね。

使い方のイメージは下記の感じ。(内容はほぼ下記のとおり)
http://docs.amazonwebservices.com/ja_jp/elasticbeanstalk/latest/dg/create_deploy_PHP_eb.sdlc.html

必要なもの

手順

1. ディレクトリ構成を確認

repo/
  public/
    index.php
  fuel/
    app/
      bootstrap.php
      classes/
      ...
    core/
      bootstrap.php
      classes/
      ...
  ...

2. repo直下でgit initする。

$ cd repo
$ git init

3. 次にElastic Beanstalkのツールでリポジトリをinitする。

$ eb init
  • 途中でいろいろ聞かれます。AccessKeyとSecretKey以外はすべて選択肢が提示されますのでとても簡単です。
    • AWSのAccessKey
    • AWSのSecretKey
    • 今回利用するリージョン
    • ElasticBeanstalk上で識別するためのアプリケーション名
    • ElasticBeanstalk上で識別するための環境名
    • 今回アプリケーションサーバーに利用するEC2のインスタンスタイプ

4. 環境を作成してアプリケーションを開始する。

$ eb start
  • このコマンドが完了した時点でEC2やELBが立ち上がってくる。アプリケーションに関してはAWSで用意しているPHPのサンプルアプリケーションがデプロイされた状態。

5. FuelPHPのアプリをデプロイする

git aws.push
  • これで手もとのリポジトリのコードがEC2にデプロイされる。

6. 動作確認!

  • あとは環境が立ち上がってきてmanagement console上からこんなふうに見えるようになっていればOK!なんだだけど、この状態だと残念ながらそうはならない。緑ではなく赤い四角が見えてる状態。いくつかapacheのconfigの修正が必要。

f:id:imai-factory:20121010200111j:plain

7. EC2にログインしてhttpd.confを修正

  • Elastic Beanstalkでは、'/var/www/html'にgitからpushされたコードをデプロイしてくれるんだけど、FuelPHPでは'リポジトリルート/public'がDocumentRootになるので、ここが問題になっている。
  • 修正の方法としては、立ち上がっているEC2にログインしてhttpd.confを修正するこ。まずはElastic Beanstalkの環境の設定画面から下記の用にログイン用のKey Pairを登録(Existing Key Pairのところ)。これはすでに自分の持っているSecurity Key Pairの名前を入力すればOK。

f:id:imai-factory:20121010200635j:plain

  • Apply Changesするとサーバーが再起動してくるので、再起動が終わったらEC2にログイン。再起動の状況やEC2のIPアドレスなどはEC2の管理画面から確認可能です。
  • ログインしたら"/etc/httpd/sites/application(中身はこのアプリケーション用のVirtualHostの設定)というファイルのDocumentRootを/var/www/htmlから/var/www/html/publicに修正
<VirtualHost *:80>
    #DocumentRoot /var/www/html 旧設定
    DocumentRoot /var/www/html/public
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" proxy
    SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded
    CustomLog "/var/log/httpd/application-access_log" combined env=!forwarded
    CustomLog "/var/log/httpd/application-access_log" proxy env=forwarded
    ErrorLog /var/log/httpd/application-error_log

    #<Directory "/var/www/html"> 旧設定
    <Directory "/var/www/html/public">
        Options Indexes FollowSymLinks
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
  • これでapacheを再起動してやれば無事に立ち上がってきます。

8. AMIを登録しておく

  • 上記で編集したhttpd.confの設定を永続化するためにEC2のイメージ(AMI)を作成しておく。やりかたはEC2の管理画面からインスタンスを右クリックして"Create Image(EBS AMI)"をすればOK.
  • 作成されたAMIのIDをElastic Beanstalkの設定画面から、起動イメージ(Custom AMI ID)のところに設定してやれば次回以降はこのイメージからマシンが立ち上がってくる。

f:id:imai-factory:20121010200635j:plain


あとは開発環境で開発したコードをgit aws.pushしてやればそのたびにコードをデプロイして環境を再起動してくれます!

注意点

  • 注意すべき点として、EC2がロードバランサーからのヘルスチェックに200を返せる状態にしておかないとロードバランサー側でバランス対象から自動で外されてしまうこと。同じく環境設定画面の"Load Balancer"のタブの中でヘルスチェックの設定ができるのでここを適宜設定する感じで。

f:id:imai-factory:20121010202054j:plain

まとめ

  • Elastic BeanstalkはAWSをPaasっぽく使えるようにしてくれるラッパーっぽいもの。
  • デフォルトでは、コードは/var/www/htmlにデプロイされて、apacheのDocumentRootがそこを向いてる環境が立ち上がってくるので、好みにあわせてサーバーの設定をいじって、再度AMIにかためておく。

という感じ。

EC2スポットインスタンスを固定IP付きで立ち上げる

EC2のAPIでは、オンデマンドインスタンス(リザーブドインスタンスも)は固定IP付きで立ち上げることができるが、スポットインスタンスはそういうAPIはない。

もともとスポットインスタンスは起動していることが保証されているわけではないので固定IPを付けて運用するようなケースはあまりないと思うが、そういうケースに出くわしたので考えてみた。ちなみに今回はVPCの中で動かすのが想定ケース。

ステップの概要としては

  1. 欲しいIPをつけたENIを作る
  2. スポットインスタンスAPIでリクエストする。このときにAPIのパラメータとしてuser-dataでアタッチしたいENIのIDを渡す
  3. 立ち上がってきたスポットインスタンスはuser-dataで渡されたENIをcloud-initのuser-scriptやinitスクリプトでアタッチする

pythonで書くとこんな感じ。
今回はcloud-initのuser-scriptを使って実装しました。

import json
import optparse
import urllib2
import sys

import boto.ec2

# parse args
parser = optparse.OptionParser()
parser.add_option('-e', '--eni_id', dest='eni_id')
parser.add_option('-r', '--iam_role', dest='iam_role')
parser.add_option('-t', '--target_iam_role', dest='target_iam_role')
parser.add_option('-a', '--iam_arn', dest='iam_arn')
parser.add_option('-p', '--price', dest='price')
parser.add_option('-k', '--key', dest='key')
parser.add_option('-s', '--sg_id', dest='sg_id')
parser.add_option('-i', '--ami', dest='ami')
parser.add_option('-n', '--subnet', dest='subnet')
options, reminder = parser.parse_args()

# create a ec2connection instance
response = urllib2.urlopen('http://169.254.169.254/latest/meta-data/iam/security-credentials/' + options.iam_role)
credential = json.loads(response.read())
ec2connection = boto.ec2.connect_to_region(
	'ap-northeast-1',
	aws_access_key_id=credential["AccessKeyId"],
	aws_secret_access_key=credential["SecretAccessKey"],
	security_token=credential["Token"]
)

# make a spot_instance request
user_data = "#!/bin/bash -ex \n python /usr/local/staticip_to_spotinstance_in_vpc/client.py -e" + options.eni_id + " -t " + options.target_iam_role
request_spot_instance = ec2connection.request_spot_instances(
	options.price,
	options.ami,
	user_data=user_data,
	key_name=options.key, 
	subnet_id=options.subnet,
	security_group_ids=[options.sg_id],
	instance_profile_arn=options.iam_arn
)

print request_spot_instance
  • そんで立ち上がってくるスポットインスタンス側には/usr/localの下にこんなの仕込んでおいて、起動後に実行される。パラメータとしてアタッチするENIのID(とこのサーバーのIAM ROLE)が渡ってくる。
import json
import optparse
import sys
import urllib2

import boto.ec2

# parse args
parser = optparse.OptionParser()
parser.add_option('-e', '--eni_id', dest='eni_id')
parser.add_option('-r', '--iam_role', dest='iam_role')
parser.add_option('-t', '--target_iam_role', dest='target_iam_role')
options, reminder = parser.parse_args()

# create ec2connection instance
response =
urllib2.urlopen('http://169.254.169.254/latest/meta-data/iam/security-credentials/'
+ options.target_iam_role)
credential = json.loads(response.read())
ec2connection = boto.ec2.connect_to_region(
	'ap-northeast-1',
	aws_access_key_id=credential["AccessKeyId"],
	aws_secret_access_key=credential["SecretAccessKey"],
	security_token=credential["Token"]
)

# attach eni
response_instance_id = urllib2.urlopen('http://169.254.169.254/latest/meta-data/instance-id')
instance_id = response_instance_id.read()
print ec2connection.attach_network_interface(options.eni_id, instance_id, 2)


ENIをアタッチするところで時々APIがタイムアウトするのを見かけたので、成功するまで何度かループしたほうが良かったかも。

あと、今回はSTS(Security Token Service)というのをAPIの認証に使ってみた。
IAMのROLEをインスタンスに割り振っておいて、そのROLEに対する、期限付きのACCESS_KEY, SECRET_KEY, TOKENがもらえてそれでAPIの認証ができるという仕組み。pythonのbotoからの使い方はいまいちちゃんとわからなかったのだけれども、EC2のメタデータサービスから下記の用にしてこれらのデータが取れることを確認

$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/IAM_ROME_NAME

レスポンスはこんなJSON

{
  "Code" : "Success",
  "LastUpdated" : "2012-10-08T05:20:14Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "期限付きACCESS_KEY",
  "SecretAccessKey" : "期限付きSECRET_KEY",
  "Token" : "期限付きTOKEN",
  "Expiration" : "2012-10-08T11:50:33Z"
}

AMIやコード、設定ファイルの中にCredential系の情報を埋め込まなくて済むのでいいですね。

VPC内にEC2を立ち上げようとしてcloud-initのpackage_setupでハマる

VPC内にamiからインスタンスを立ち上げようとしたら何故か立ち上がらない。
management consoleから当該インスタンスの"Get System Log"してみると下記な感じで止まってた。
cloud-initのpackage_setupのところでリポジトリを探しに行って、通信ができなくて止まってる感じ。

cloud-init:  package-setuphttp://packages.ap-northeast-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: [Errno 12] Timeout on http://packages.ap-northeast-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: (28, 'connect() timed out!')
Trying other mirror.
http://packages.ap-southeast-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: [Errno 12] Timeout on http://packages.ap-southeast-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: (28, 'connect() timed out!')
Trying other mirror.
http://packages.eu-west-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: [Errno 12] Timeout on http://packages.eu-west-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: (28, 'connect() timed out!')
Trying other mirror.
http://packages.sa-east-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: [Errno 12] Timeout on http://packages.sa-east-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: (28, 'connect() timed out!')
Trying other mirror.
http://packages.us-east-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: [Errno 12] Timeout on http://packages.us-east-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: (28, 'connect() timed out!')
Trying other mirror.
http://packages.us-west-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: [Errno 12] Timeout on http://packages.us-west-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: (28, 'connect() timed out!')
Trying other mirror.
http://packages.us-west-2.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: [Errno 12] Timeout on http://packages.us-west-2.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/repomd.xml: (28, 'connect() timed out!')
Trying other mirror.
http://packages.ap-northeast-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/filelists.sqlite.bz2: [Errno 12] Timeout on http://packages.ap-northeast-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/filelists.sqlite.bz2: (28, 'connect() timed out!')
Trying other mirror.
http://packages.ap-southeast-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/filelists.sqlite.bz2: [Errno 12] Timeout on http://packages.ap-southeast-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/filelists.sqlite.bz2: (28, 'connect() timed out!')
Trying other mirror.
http://packages.eu-west-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/filelists.sqlite.bz2: [Errno 12] Timeout on http://packages.eu-west-1.amazonaws.com/2012.03/main/24ff18fc5683/x86_64/repodata/filelists.sqlite.bz2: (28, 'connect() timed out!')
Trying other mirror.

/etc/sysconfig/cloud-initのpackage_setupの項目をnoにしたら解決した。

CONFIG_SSH=yes
CONFIG_MOUNTS=yes
PACKAGE_SETUP=no
RUNCMD=yes
RUN_USER_SCRIPTS=yes
CONFIG_LOCALE=yes

しかしこの問題、初回起動時には起きないし、大元のインスタンスでは再起動しても起きない。
amiにしてそこから立ち上げると発生する。なんでだろ。
(調べろよって話ですが。)