Hatena::ブログ(Diary)

naoyaのはてなダイアリー

September 18, 2014

HBFav が iOS 8 でクラッシュする問題について

HBFav をお使いいただきありがとうございます。

現在のバージョン 2.6.1 は、iOS 7 から iOS 8 にアップグレードするとアプリ起動後、記事一覧を読み込んだところでクラッシュしてしまいます。

クラッシュの原因を修正したバージョン 2.7.1 を先週には App Store にアップロードしたのですが、まだ審査待ちでリリースできていません。申し訳ございません。今しばらくお待ち下さい。

September 14, 2014

質問: Go の変数初期化に伴う条件分岐をもっと良い感じに書きたい

Go にはスクリプト言語でいうところの variable = a || b のような構文や三項演算子がないようなので、

var accessKeyId, secretAccessKey string
if config["aws_access_key_id"] == "" {
	accessKeyId = os.Getenv("AWS_ACCESS_KEY_ID")
	secretAccessKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
} else {
	accessKeyId = config["aws_access_key_id"]
	secretAccessKey = config["aws_secret_access_key"]
}

とか

var url string
var err error

if len(os.Args) == 1 {
	url, err = gozo.SendCapture()
} else {
	url, err = gozo.SendFile(os.Args[1])
}

こんな感じのコードを毎回書くことになるんだけど、本当は

accessKeyId = config["aws_access_key_id"] || os.Getenv("AWS_ACCESS_KEY_ID")
secretAccessKey = config["aws_secret_access_key"] || os.Getenv("AWS_SECRET_ACCESS_KEY")

とか

url, err := len(os.Args) == 1 ? gozo.SendCapture() : gozo.SendFile(os.Args[1])

みたいに書けたらいいと思ってますができません。それは諦めるとしてもちっとまともに書きたいんですがみなさんどうしてるんでしょうか。

追記

August 26, 2014

Reverse Proxy がなぜ必要か

フロントエンジニアに知ってもらいたいリバースプロキシの重要性 - RickyNews この記事が目に入って読んでみた。なるほど、昨今は Reverse Proxy は便利な L7 ルーター的なものとして認識されているのだな、と思った。URL の Rewrite や、VirtualHost 云々。確かに Reverse Proxy の便利な側面ではある一方、それらは Nginx などの Reverse Proxy でなければ実装が不可能かと言えばそんなことはないものでもある。

自分は Reverse Proxy はもうすこしサーバー/インフラ的な側面でその役割を捉えている。今更何をというものでもあるが、昼休みがてら時間があるので簡単に書いてみよう。

Reverse Proxy はWebシステム全体のリソース最適化のためのパーツ

Reverse Proxy のインフラ的な視点での役割は「Webシステム全体の系におけるリソース全体最適のための部品」である。なんのこっちゃ・・・ごめん、石投げないで。

改めて Reverse Proxy は

  • Reverse Proxy をフロントエンド
  • Rails でも Plack でも何でもいいのだがそのアプリケーションサーバーをバックエンド

としたときに

  • フロントエンドで裁けるものはフロントエンドで
  • バックエンドでなければ処理できないものだけバックエンドで処理する

ようにし「全体のリソース効率を上げましょう」というためのものである。

マルチプロセスモデルと並行処理性能

最近はいろいろな実装があるので一概には言えないのだが、Webアプリケーションによるバックエンドのアプリケーションサーバーでは、特にスクリプト言語においては、それはマルチプロセスモデルを採用していることがほとんどだ。マルチプロセスモデルでは、1リクエストを1プロセスが担当することになる。従って、1システムで20プロセスが起動していたとすると同時に処理できる並行処理数は最大で20リクエストということになる。

ご存知のようにアプリケーションサーバーはメモリを大量に消費する。メモリ使用量という文脈で「重い」と言える。そのため、32GB とかどんなに潤沢にメモリが確保できる状況でも、せいぜい最大プロセス数は、すなわち並行処理性能は 50 とか 100 とかそのぐらいである。

Web システムが応答しなければいけないのは、例えば /login に来た1リクエストに対してその画面が含む画像、JavaScript、CSS その他大量のアセットを含む。Reverse Proxy を導入していないシステムではこれらのすべてにアプリケーションサーバーが応答しなければならない。ここが問題。本来アプリケーションサーバーは、動的なリクエストを処理するために用意されているものであるにも関わらず、動的でないコンテンツにまで応答しなければならない・・・というので、先にみたように貴重なアプリケーションサーバープロセスが、どうでもいい静的ファイルのリクエストなどにも消費されてしまう、のである。

Reverse Proxy で得意なものは得意な人が応答するようにする

そこで Reverse Proxy を入れて、(ネットワーク的に) アプリケーションサーバーの手前に配置する。そして静的ファイルなど、アプリケーションサーバーを介さずに応答できるものは Reverse Proxy が直接クライアントに応答し、アプリケーションサーバーでなければ応答できないリクエストのみアプリケーションサーバーに転送する。

Reverse Proxy は、(アプリケーションサーバーがマルチプロセスモデルであるのと対照的に) 昨今の場合 Nginx のようにシングルプロセス・イベントドリブンなアーキテクチャの実装のものを用いる。このアーキテクチャは、どんなに大量のリクエストが来てもプロセス数は増やさずにコンテキストスイッチングのみ (CPU 的な意味のコンテキストスイッチングではなく、イベントの意味での。具体的には select や epoll に類するシステムコール) で並行処理性能を稼ぐところが利点。そのためメモリ使用量の文脈では「軽い」。

この「重い」アプリケーションサーバーと「軽い」Reverse Proxy を組み合わせてそれぞれ自分が得意なものだけ担当することで、システム全体の系でみたときにリソース効率を全体最適させましょう・・・というのがインフラ視点で Reverse Proxy を導入したい一番の理由である。

Reverse Proxy はネットワーク的に遅いクライアントや、KeepAlive リクエスト、あるいは大きなファイルをアップロードしてくるクライアントなど「あまり仕事はしないけどネットワーク接続の維持は要求される」されるような側面でも役に立つ。それらは Reverse Proxy 側で処理しておき、アプリケーションサーバーには本当に必要なときだけにしかリクエストを転送しない・・・ たとえば遅いクライアントのHTTPリクエストがすべて着信してから転送、KeepAlive はフロントだけ有効にしておきバックエンドは応答が終わったらそく切断、アップロードファイルは終わるまでフロント側でバッファリング・・・などである。

この役割分担が、更に得意なものは得意なところで、というリソース全体最適化に寄与する。

四方山

この辺が Reverse Proxy に求められる役割の主軸で、L7 的な便利機能・・・ URL の Rewrite とかフロントエンド側でのアクセス制御というのはあくまで副次的な要素に過ぎない・・・というのがインフラ視点での認識ではある。まあ、フロントエンドエンジニアリングからしたらその視点が逆転する、というのはわからなくもない。

ちなみに最近は Amazon の CloudFront など CDN を比較的カジュアルに使えるようになったので、静的ファイルの配信に関しては CDN だけで賄ってしまうという力業を使う例も増えてる気もする。個人的には、その副次的な用途の魅力も大きいので、CDN に使うにしても Rerverse Proxy あったほうがいいよとは思うけど。

・・・書いてるうちにだんだんドヤ顔になってきてしまった。すみません。自分の知識がある程度正しいという思い込みを前提に、きちんとこの辺の背景事情を続く世代に継承していけたらとも思うところです。

ちなみに、この辺は『サーバー/インフラを支える技術』という(とっても)素晴らしい本に詳しく書かれているのでこちらを読むと良いと思う。

August 21, 2014

インフラの継続的デリバリー

事前に断っておくがここでいう「インフラ」はレイヤ的には OS より上の話。

少し前に GitHub 時代のデプロイ戦略 - naoyaのはてなダイアリー で、GitHub を介したデプロイを実践しているということを紹介した。普段の開発を Pull Request ベースでやっているので、デプロイもまた Pull Request を契機に実行させると色々捗る、という話。

このプラクティスの対象領域をインフラにまで拡大してみました、というのが今回の話。

DNS レコードを Pull Request を merge した契機に自動で更新

AWS を利用している場合、ドメインの管理も Amazon Route 53 を使うといろいろと都合がいい。

Route 53 での DNS レコードの更新はこれまでブラウザから操作していた。これだと誰がいつ作業したかわからないし履歴もトラックしづらい。また変更確定前に事前レビューするのにも向いてない。

そこで roadworker を使う。roadworker は Route 53 の DNS レコード設定を ruby の DSL で書けるようにするツール。裏では Route 53 と API で通信する。

roadworker を使うと、以下のようにレコードを Ruby のファイルで書ける。(GitHub から引用)

hosted_zone "winebarrel.jp." do
  rrset "winebarrel.jp.", "A" do
    ttl 300
    resource_records(
      "127.0.0.1",
      "127.0.0.2"
    )
  end

  rrset "winebarrel.jp.", "MX" do
    ttl 300
    resource_records(
      "10 mx.winebarrel.jp",
      "10 mx2.winebarrel.jp"
    )
  end
end

こうして「DNSレコードの現在の状態」がコードになったので、あとはいつも通り Git と GitHub のフローに乗せることができる。ファイルの更新には Pull Request を使い、コードレビューする。万歳。

更に、Route 53 への設定の適用すなわち roadworker の実行は CircleCI に任せる。Pull Request が master に merge されたら自動で roadwork コマンドを実行するようにする。テストフェーズで dry run するようにすれば、万が一 Ruby コードが壊れていてもそれを検知できる。

dependencies:
  pre:
    - bundle install:
        timeout: 600

test:
  post:
    - bundle exec roadwork --dry-run --apply

deployment:
  master:
    branch: master
    commands:
      - bundle exec roadwork --apply

実行を CircleCI に任せれば (Git にファイルの更新履歴が残るだけでなく) 実行履歴のログも残せるし、ステータスを Slack に通知するのも簡単。

DNS レコードを書き換える、というと「えーと、どうやるんだっけ? インフラ担当お願い」ということになりがちだが、これで、前例に倣って Pull Request を投げて merge するだけですべてが実行される、ということになった。

以上は最近 KAIZEN platform Inc. に入社した glidenote さんのお仕事である。グッジョブ

Chef の適用も CircleCI 経由で、そして ChatOps

アプリケーションのデプロイを Pull Request をマージするタイミングで実行するという考え方を DNS 変更にも適用したのが先の例。同様に「もう Chef でのインフラの変更も全部同じようにやっちゃえよ」ということで mdoi がやったった。

(ここでは簡易的に master に merge したらと書いているけど、実際にはデプロイ用のブランチが別にあったりとちょっと設計は異なる)

Chef のクックブックのサーバーへの適用には knife-solo を使っているが、それを CircleCI に実行させる。CircleCI には ssh の秘密鍵をセキュアに渡せる仕組みがあるので、認証周りの心配も特に必要ない。CircleCI に実行を任せることで、毎回の実行ログをそのまま残せるのは先の DNS の例に同じくで、とても良い。

インフラに実際にコードを適用するのは、クックブック変更 Pull Request の merge 契機だとさすがにアグレッシブすぎるので、冒頭でも紹介した以前の記事に同じく、デプロイ用の Pull Request を使う方法を採用している。インフラに最新のクックブックを適用したいな、と思ったら deployment/qa-proxy など、デプロイ用のブランチに Pull Request を作って merge する。すると CircleCI が動き出して対象システムに Chef 適用を始める。

ここまで来たら、例によって ChatOps。

インフラへの変更適用もチャットからやってしまえばよい。これで、複数の担当者が同時にインフラに適用する、なんてことも自然と防ぐことができる。

インフラを自動で更新なんていうとえらくリスクの高いことをやっているように聞こえるが、実際には手元で knife solo していたのを CircleCI に実行させているだけであり、このプロセスに移行することによるリスクの増化はない。

「Merge pull request」ボタンに作用を集約させる

こうして、システム構成の変更などの作用を PullRequest + CircleCI によるオペレーションに集約させる、というのはなかなか良いプラクティスである。

単に Pull Request を送って変更しましょうね、だけでも十分に恩恵はあるのだけど、実際に何かしらの変更実行のトリガが「Merge pull Request ボタンを押す」というボタンに紐づけられると意識しなくても自然と Pull Request を送っておこうという考え方になる。DNS に変更を加えたい → Pull Request をマージしたら (※ 実際には master に変更があったら) 変更処理が自動で行われる → じゃ、プルリク作るか、という具合。

こうして結果的に、何をやるにも緑のボタンを押すだけ、すなわちワンクリップデプロイの仕組みが構築できた。Infrastructure as Code を推し進めて、インフラも CI して、ワンクリックデプロイ ─ 継続的デリバリーの実践である。

August 15, 2014

BigQuery と Google の Big Data Stack 2.0

先日、有志で集まって「BigQuery Analytics」という書籍の読書会をやった。その名の通り Google BigQuery について書かれた洋書。

BigQuery を最近仕事で使い始めたのだが、BigQuery が開発された背景とかアーキテクチャーとかあまり調べもせずに使い始めたので今更ながらその辺のインプットを増やして以降と思った次第。

それで、読書会の第1回目は書籍の中でも Overview に相当するところを中心に読み合わせていった。それだけでもなかなかに面白かったので少しブログにでも書いてみようかなと思う。

BigQuery の話そのものも面白いが、個人的には Google のインフラが書籍『Google を支える技術』で解説されたものが "Big Data Stack 1.0" だとして、BigQuery は Big Data Stack 2.0 の上に構築されており、更に Google 内部ではとっくに Big Data Stack 3.0 に移行してる (しつつある?) みたいな話に強く興味を持った。(まあ、3.0 は論文にも何にも発表されてないので、そういうニュアンスのみしか嗅ぎ取れないのではあるが)

Google BigQuery

Google BigQuery は、一言でいうと「超でかいデータをSQLで数秒で解析できるクラウドサービス」。

f:id:naoya:20140815185813p:image

例えば fluentd なんかで毎日ログを送りつけておいて、溜まった 数百GB とか数TB、あるいは数PB とかになったデータに SQL を投げるとちゃんと結果が返ってくる、そういうもの。いわゆるビッグデータ基盤である。裏側では当然分散処理が行われていて、Google の持つ大量のコンピュータとディスク、それから高速なネットワークによってそれが処理されている。

もっと知りたいという方は hadoop - Googleの虎の子「BigQuery」をFluentdユーザーが使わない理由がなくなった理由 #gcpja - Qiita あたりを参照すべし。

なぜ大きなデータを秒単位で処理できるかというと、それがまさに BigQuery Analytics の本題なのだがざっくりは

  • SQL は高度に並列化できる言語 (基本、シーケンシャルに処理するしね)
  • RDBMS のようにインデックスを作るわけではなく、基本フルスキャンする
  • そのフルスキャンに伴う I/O を分散処理するために大量の HDD を用意して、それを大量のサーバーに接続してクラスタリング (もちろん、スケールアウトの発想で)
  • データをそのクラスタ内でカラムナー形式で保持しつつ分散させて保存しておき、SQL が来たらそれを並列処理化して I/O を散らす

みたいな感じである。より詳しくは・・・とそこが書籍の本題でありその理解は今後の読書会を通じて行われる予定、といって誤魔化す。

こういうアーキテクチャになってるので、BigQuery に対するクエリのレスポンス速度はデータサイズに比例しない。例えば 100GB のデータに対して 3sec かかったクエリが、対象が 1TB になっても 5sec で収まるとか、そういう特性を持っている。

Google BigQuery の製品ポジション / MPP の盛り上がり

この、SQL を並列分散処理してビッグデータを数秒で解析しましょう、という製品は何も BigQuery だけではない。

OSS の実装であれば Facebook が開発して OSS になった Presto、それから Cloudera Impala などがある。この辺は、Hadoop クラスタにアドオンする形で利用すると、Hadoop クラスタに溜め込んだデータを SQL 並列処理できるようになってる。クラウドサービスでいえば (自分は詳しくないし、結構特性には違うらしいが) Amazon Redshift なども同様のカテゴリのサービスのようだ。それから、以前にこのブログでも紹介した TreasureData はもともと Hive + Hadoop を基礎にしたサービスだったがその後 Presto をアドオンすることで同カテゴリのサービスにバージョンアップしている。

この辺の話は Hadoop Conference Japan 2014 での tagomoris さんの発表スライド Batch processing and Stream processing by SQL が詳しい。

資料の内容を軽くサマリする。

SQL ベースのビッグデータ解析基盤は大きく分類すると

  • Large Batch
  • Short Batch
  • Stream Processing

の3に分類される。それぞれ

  • Large Batch : 安定して巨大なデータをバッチ処理できるが、実行時オーバーヘッド大きい (そのためちょいちょいクエリを変えては投げる目的 ・・・ アドホッック クエリには向いてない)
  • Short Batch : Large Batch の安定性と規模性を多少犠牲にしつつ、実行時オーバーヘッドが数秒 (つまりアドホック クエリに向いている)
  • Stream Processing : ストリームに流れるデータをリアルタイム処理。バッチではない

というもの。対応する代表的な実装は

  • Large Batch ・・・ Hive + Hadoop
  • Short Batch ・・・ Presto / Impala etc.
  • Stream Processing ・・・ Twitter Storm / Norikra

などとなってる。この Short Batch のユースケースに含まれる実装は昨今 MPP (Massively Parallel Processing) 系クエリエンジンと呼ばれていて、ビッグデータ界隈では今もっともホットなトピック・・・であると、Hadoop Conference に出てみて自分はそう感じた。

そして、Google の BigQuery は元々 Google 内部で開発された Dremel というクエリエンジンが基になっている。その Dremel がまさにこの MPP 系クエリエンジンに相当する類で、BigQuery はそれにストレージを加えて公開サービスにしたものだと現時点では理解している。(BigQuery は背後にそびえるクラスタ群が超大規模なので、Short Batch に分類されるとはいえ Large Batch も包含するようなサービスではある。)

OSS 系の流れからいくと、Hadooop + Hive で SQL でビッグデータを解析するというソリューションが盛り上がったが、MapReduce はタスク起動時のオーバーヘッドが大きくアドホッククエリの分析には向いてない、そこをカバーするために Presto や Impala が出てきてみたいな流れだと思う。

Big Data Stack 2.0

それで、冒頭の Big Data Stack 2.0 である。BigQuery は Google の Big Data Stack 2.0 の上に構築されている、らしい。書籍によれば。

そもそも Big Data Stack 1.0 とは、GFS, MapReduce, BigTable などの「Google を支える技術」のことである。MapReduce などが論文で発表されたのが 2004 年とかだった。あれから 10 年経った結果、Google 内部は書籍を読む限りは Big Data Stack 2.0 にまで進化している 。(そして現在進行系では 3.0 に至っているようなこともチラホラ見える。)

その Big Data Stack 2.0 の主要コンポーネントは以下である。説明文は読書会で hakobera さんがサマリしてくれたテキストから引用する。

  • Colossus
    • GFS の後継 (の分散ファイルシステム?)。詳細は未発表。
  • Megastore
    • Paxos アルゴリズムにより、複数データセンターでの一貫性のある Read/Write を実現した NoSQL DB。Bigtable 上に構築されている。
  • Spanner
    • Megastore + データに地域制約(どのデータセンターに所属するか)を付与することができる。
  • FlumeJava
    • MapReduce のパイプライン処理を簡単に書けるようにしたフレームワーク
  • Dremel
    • 分散 SQL クエリエンジン。BigQuery の核となるアーキテクチャ。ストレージに依存しない。

これらの実装は概ね Big Data Stack 1.0 の上に構築されているらしい。そしてこれも hakobera さんテキストからの引用なのだが

  • Big Data Stack 1.0 は、Datacenter を1つのサーバとして扱う技術群
  • Big Data Stack 2.0 は、複数 Datacenter を1つのサーバとして扱う技術群

という風に (乱暴には) まとめられる。こうして世界に分散しているデータセンターを、プログラマからみた場合は 1つのサーバーとして扱えるような形で抽象化したのが Google の Big Data Stack 2.0 だそうだ。

ご存知のように Big Data Stack 1.0 をリファレンスにでてきた OSS が Hadoop や HDFS、HBase 等々だったように、この Big Data Stack 2.0 を参考にしたと思われる OSS 実装も当然出てきていて、それらが Apache Crunch や Presto、Impala ・・・というのが昨今の状況。Google が論文などでそのあらましを発表するころには、Google 内部は次の世代の実装に移行しているというのが過去のパターンなので、そこから Google 内部はもはや Big Data Stack 3.0 なのでは? と推測される ─ といった具合である。

自分はこの分野を見てまわるのはそれこそ Google を支える技術から初期の Hadoop ぐらい、Big Data Stack 1.0 の頃で止まっていたので、それからしばらく時間が経ってこんな状況になっていたとは、改めて Google はすごい企業だという感想を抱くにいたったのは当然のこと、とても面白く読めた。ずっとこの分野を追っていた人にとっては何を今更という話なのかもしれないが、面白さあまってブログにまとめるイマココである。

ま、人の会社の話なんだけどね。Google の威を借る naoya。

8/30 の YAPC::Asia では、BigQuery Analytics をもう少し読み進めた後のサマリと、実際に BigQuery をプロダクションで利用してみてのユースケースや感想などを含めて発表できたらと思っている。

急ぎで書いたので誤字脱字や乱暴な解説がいつも以上に多いと思うが、ご勘弁を。

余談

ちなみにこの辺をみて『Google を支える技術II』の出版が待たれる! と声を大にして言おうとおもったら『サーバー/インフラを支える技術』もなんとかしろ、という神の声が聞こえてきた。編集さん、アジェンダなかなか書かなくてごめんなさい・・・。