いまさらGAE/Goに入門してみる
最近周囲がGAEづいていて、やったこともなかったので入門してみた。 今の状況的にはやっぱりGoかなと思うので、GAE/Goで。
GCPに登録
まずは登録
https://console.cloud.google.com
初めての人は、60日間有効な$300クレジットがあるのでそれで遊ぶどいいよ (2016/05/21) 登録時にクレカの登録を求められるけど、無料期間が終わっていきなり請求されるわけではないらしい。
プロジェクトを作る
何故か最初からAPI Project
ができていたがApp Engineがほしいので、新しいプロジェクトを作る。特に選択肢なくせずにGAEのプロジェクトができた。
プロジェクト名はご自由に。
AppEngineのページに行くと、言語ごとのチュートリアルも読むことができる。 すごく良く出来てて基本的な使い方を理解できるので、正直やっとくといいよ。
自分はGoのチュートリアルをやった。 チュートリアルを実行すると、リポジトリにサンプルアプリが配置されるので、実行テストにはそれを使いまわすと楽。
App Engine SDK のセットアップ
ちょっと前のドキュメントだとGoogle Platform SDK の gcloudコマンドが統合管理するよ!って感じだったけど、どうやらGAEのSDKはそこからstandaloneになった様子。
https://cloud.google.com/appengine/downloads
ここからpackageを取ってくる。
% curl -O https://storage.googleapis.com/appengine-sdks/featured/go_appengine_sdk_darwin_amd64-1.9.37.zip % unzip go_appengine_sdk_darwin_amd64-1.9.37.zip
.*shrc
的なものにPATHを通す。
export PATH="/path/to/go_appengine:$PATH"
.*shrc
を読みなおしてgoapp
コマンドにパスが通っている事が確認できればOK。
ローカルでサンプルを動作させてみる
この辺は作りたいものを作ってく感じだけど、とりあえずまずGAEの操作を覚えるのでサンプルのまま。
gae-sample ├── app.yaml └── hello └── hello.go
package hello import ( "fmt" "net/http" ) func init() { http.HandleFunc("/", handler) } func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World!") }
version: 1 runtime: go api_version: go1 handlers: - url: /.* script: _go_app
上記を作成し、対象ディレクトリをgoapp serve
する。
% goapp serve gae-sample INFO 2016-05-21 04:45:56,681 devappserver2.py:769] Skipping SDK update check. WARNING 2016-05-21 04:45:56,885 simple_search_stub.py:1146] Could not read search indexes from /var/folders/r8/gnn5k3_j4qq1rjmd3k3x1b4r0000gn/T/appengine.None.yudoufu/search_indexes INFO 2016-05-21 04:45:56,891 api_server.py:205] Starting API server at: http://localhost:58354 INFO 2016-05-21 04:45:56,897 dispatcher.py:197] Starting module "default" running at: http://localhost:8080 INFO 2016-05-21 04:45:56,901 admin_server.py:116] Starting admin server at: http://localhost:8000
これでローカルにアプリが起動するので、http://localhost:8080/
で動作をチェックできたら完了。
http://localhost:8000/
でアプリケーションの状況もチェックできる。
余談だが、最初yamlを書く際にGCP上のサンプルを真似してversion: 1
無しで作ったら、GAEにdeployする際に A version or backend parameter is required.
と怒られてしまった。
その辺、参考にしたサンプル次第で色々エラーに見舞われると思うのでご注意をば。
GAE上に配置する
今のサンプルをGAE上に配置してみる。
% goapp deploy -application [project id] gae-sample
project id
は、作成したGAEプロジェクトのダッシュボード左上あたりに書かれてるので、それを引っ張ってくる。
実行すると、ブラウザ上で認証を求められるので許可すると、デプロイされる。
完了したらhttp://[project id].appspot.com
にアクセスすると、動作が確認できる。
これでひとまず、GAEアプリケーションを開発してく準備が整った感じ。
fluent-plugin-elasticsearch(elasticsearch-ruby)で負荷分散してる場合のリトライ設定
結論的にはマニュアル以上の情報はないです。個人的メモに近い感じ。
この辺は気軽にテスト/確認しづらいレイヤだと思ってるので、具体的な実装を知らないと怖かったので調べてみました。
背景
世にある記事だと、elasticsearchサーバのローカルにfluentd receiverを置いて、そこからesに投入する形式が多く見られた。
が、それだとreceiverが生きててesプロセスだけが死んだ場合にデータの投入が部分的に止まるよねってことで、送信元のfluentdからesに直接接続して投入することにした。
その場合fluent-plugin-elasticsearch
が分散処理を行うので、その部分のfailure/retryの扱いやパラメータを調査した。
ポイント
先に、設定項目とかのまとめだけ。
fluent-plugin-elasticsearch
は内部的にelasticsearch-ruby
(elasticsearch-transport
)を利用していて、そこが接続管理をしている。設定を検討した方がいい
elatissearch-ruby
の項目は以下reload_connections
reload_on_failure
- 接続失敗した場合にサーバリストを更新するか否か。
- これは
true
にするのが良さそうだが、サービスの監視体制等次第かも。正常にクラスタから外れないまま壊れたサーバが出るとどうなるだろう。。。 reload_connections
の長さとのバランスにもよる。
retry_on_failure
- サーバがunreachableだった場合のretry可否と試行回数。
true
指定だとdefaultの3
が使われる。 fluent-plugin-elasticsearch
では5
で固定。まぁそんもん感。
- サーバがunreachableだった場合のretry可否と試行回数。
retry_on_status
- サーバエラーが返ってきた時にretryするかどうか。
- クラスタのうちの1台だけが異常になった場合とか想定すると、
true
したい。- ...だが、これは
fluent-plugin-elasticsearch
では指定できない。
- ...だが、これは
環境次第だがWARNのログも閾値設けて監視の対象とした方が安全そう
- ここを見ないとLANポート・ケーブル故障とかネットワーク異常系の場合に気づきにくそう
reload_connections
をしていれば無停止でクラスタ構成変更に対応できるが、confの書き換えは忘れず行う必要があるので手順漏れがないよう注意以下項目は、関連するがdefaultのままでもたぶん支障はない
resurrect_after
- default
60
- default
resurrect_timeout
fluent-plugin-elasticsearch
にはない
sniffer_timeout
fluent-plugin-elasticsearch
にはない
以下詳細
まぁほぼソースコードの解説なので。。。
アクセスエラー処理の流れ
対象となるソースコードはこのあたりにまとまっている。
とくにBase#perform_request
が中心。
- 指定された選択方式(RRとか)で接続先サーバを決め、リクエストを送る
- retry対応が走るのは以下のパターンだけで、それ以外は問答無用で死ぬ
unreachableだった場合(Timeout / ConnectionFailed)
- 対象サーバは死亡判定を受ける
reload_on_failure
パラメータがあれば、接続不能なサーバにあたった時点で即座にサーバリストのリロードが走るretry_on_failure
パラメータが設定されていると、改めてretry- max回数を超えるまでは失敗してもWARNしか吐かないので、WARNログをチェックしたほうが良さそう
[#{e.class}] Attempt #{tries} connecting to #{connection.host.inspect}
ServerErrorだった場合
retry_on_status
パラメータがあるなら、無条件でretry- エラーを返したサーバに接続ペナルティはない(dead判定もfailure判定もされない)ので、調子悪いサーバが対象から外れることはない。
- WARNで出る
[#{e.class}] Attempt #{tries} to get response from #{url}
のログをちゃんと監視しておくと良いのかもしれない。 - max失敗するとFatal。default回数は3。
retry_on_failure
パラメータに数字を渡すと変えられる。
- パラメータがないならそのまま失敗する!!
なお、fluent-plugin-elasticsearch
にはretry_on_status
がないので、この処理は通らない。
死亡判定と復帰判定
サーバの死亡判定はunreachable時、復帰の判定は接続先サーバの選定時に走る。
死亡判定
unreachableになると一旦死亡判定される
- 死亡判定をされると、メモリ上に死亡フラグと失敗回数インクリメント、死亡時刻が記録される
reload_connections
パラメータがセットされてると、指定リクエスト回数毎にサーバリストのリロードが走る
- メモリがクリアされると死亡判定は消えちゃうよ
- 死亡判定されると一旦接続対象から外されて、復帰判定を待つ
復帰判定
unreachableになったサーバにも再度接続のチャンスを与えるために、一定時間経過の後で復帰判定が行われる
resurrect_after
パラメータが関与- 最後のアクセスから
resurrect_after
秒後に復活チェック(default: 60sec) - 死亡判定を受けたConnectionが、死亡時刻から
resurrect_timeout x (2 ** 失敗回数-1)
経っていたら復帰させる
復帰のあと、接続に成功した場合
復帰後にちゃんと接続に成功したら、正常に戻ったと判定されて失敗回数がクリアされる
対象サーバリストのリロード
- 実際にクラスタにnode情報を取りに行って、対象サーバのリストを更新する
- その際のtimeoutは別途
sniffer_timeout
で決められる(default: 1)
- その際のtimeoutは別途
- この機能を使うことで、設定ファイルのhostsリストを編集・リロードすることなく、クラスタ群の追加削除に対応できる
- ただし情報はメモリ上にロードされるだけなので、設定ファイルの更新は併せて行う必要がある
だいたいこんな感じ。そんじゃーね。
Kyobashi.swift #1 参加したメモ
http://kyobashi-swift.connpass.com/event/23712/
第1回めだったので、雰囲気も含めてメモメモ
まとめ
- おにくおいしいです。 リクルートマーケティングパートナーズ さん++
- 会場が大変にしゃれおつ!
- drinkup的に、お酒飲みながら。ゆるふわ。
- みんな軽いLTな感じで、ハードルの上がっている昨今では発表しやすいかも。
以下、聞きながらとったメモ。スライドは上のイベントページを参照のこと
既存プロジェクトにSwiftLintを導入した話
- Githubが公開してるSwiftのコーディングスタイルガイドに沿っているかチェックしてくれる
導入するのは簡単
今回はチームでいかに導入したかの話
- チームで合意形成、まずはdisableにしてから徐々に。
- auto-correctがそこそこやってくれる
ExtraView
StoryboardのViewControllerの外側に作れるViewのこと
- View単体で作れる
たとえば: SectionHeader
- コードで作ったり
- xibで作ったり
- やりづらい
- せっかくならすべての要素を1つのStoryboardで管理したい
- 整理がついてイメージが作りやすいので、めっちゃ便利
ReSwift
- Fluxフレームワーク
- 一方向のデータフロー
- Reduxにインスパイアされた
OSSから学ぶSwift実践テクニック
- Alamofire
- URLStringConvertible
- String風に見えるけどじつは違う
- NSURLでもStringでもなんでも渡せる型
- extensionをつかってProtocol毎にグルーピングして書くとわかりやすい
- メソッドチェーンでリクエストを使いやすく
- 大量のオプションでも見やすく書ける
- URLStringConvertible
Nearby Messages API
NSError
- Swift 2.0から導入された新しいエラーハンドリング方式
- Exceptiionてきなもの
NSErrorに存在した詳細なエラー情報がない
定義は空っぽ
FriendlyErrorType
- NSErrorとの共存
DI in Swift
Swiftは静的言語でも動的言語でもない、という話
swift-evolutionのMLを読んでいたら面白いメールがあったので個人的にメモメモ。 https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001948.html
Protocol Extensionのmethod dispatchがわかりにくいからdynamicにすべきだ、という提案から徐々に話がそれてSwiftはdynamicかstaticか、いやこうあるべきだどーのこーのと話が揉めてきたところ、Swift作者のChris Lattnerが降臨してSwiftの設計思想についての話を書いていた。
前半は各言語のパラダイムや思想について話していて、その上で後半でSwiftがどういう立ち位置を目指しているのかを説明してる。
スレの流れも含めて要約すると、こんな感じの事言ってるっぽい。
- Swiftは動作的にはdynamicにもstaticにも属しているわけではなく、予測可能なパフォーマンスとなるモデルにしている。
- = JIT任せで何でもdynamic、ってのは目指してない。
両者イイトコどりの
"opportunitistic" language
(日和見的言語)だとしている。言語的には、基本的にダイナミックだが、それをstatic compilerを使ってJITなしに高いパフォーマンスを実現するように作っている。
- たとえばnon-publicなクラスをfinalとして解釈することによって、dynamic dispatchのお世話にならずに高速に動作させている
- memo: Whole Module Optimization とかにも絡む話をしているんだと思わるる
旧来のstaticかdynamicかという話はSwiftでは重要ではなくて、本当に実現したいモデルはProgrammer model
個人的メモなんで、あんまり正確な書き方になってない。。。(;´▽`A``英語弱いから誤解してるかもだけど、こんな話みたい。間違ってたら誰か突っ込んでw
なお、個人的には元々の提案の方に興味があって見てたんだが、明確な結論は出てないようだった。 話的には、Swift3でそこら辺のdispatchの関係が「もっと理解しやすくなる」のかな。
負荷テストツールGatlingを触ってみた
負荷ツールとしてGatlingのことを少し前から耳にする機会が増えたので、利用してみることにした。
色々既出だとは思うが、公式のQuickstartに従って試してみたのでメモ。
Gatlingとは
JMaterと似た感じのツールではあるが、
- ハイパフォーマンス
- 見やすいレポートHTML
- developerフレンドリーなシナリオファイル
というのをウリとして謳っている。
たぶん、3項目とも対JMater(重い・レポート見づらい・XMLのシナリオつらい)を意識したメリットだろうなー。
なお、シナリオファイルは。。。
Gatling simulation scripts are written in Scala, but don’t panic!
わろた。
というわけで、触ってみる
Install
JavaとGalingをインストールする。Gatlingはunzipするだけ。
% brew cask install java % wget https://repo1.maven.org/maven2/io/gatling/highcharts/gatling-charts-highcharts-bundle/2.1.6/gatling-charts-highcharts-bundle-2.1.6-bundle.zip % unzip gatling-charts-highcharts-bundle-2.1.6-bundle.zip
シナリオの作成
テストシナリオ
まず最初に、テストシナリオを決める。チュートリアルに従って以下のとおり。
測定対象とするのは http://computer-database.herokuapp.com/ というページ
- これは、いろんなPC等コンピュータのモデル情報を登録するCGM的なサイト
- Play framework製!
- 自分のローカルで動かしてもいい https://github.com/playframework/playframework/tree/2.2.x/samples/scala/computer-database
リアルなユーザーのシナリオとして、以下の様な想定でテストする
- ユーザーがアプリケーションにアクセス
-
mackbook
で検索 - 関連するモデルを開く
- homeページに戻る
- これを繰り返す
- ユーザーが新しいモデルを登録する
Gatling Recorderによるシナリオの記録
シナリオの作成には、Gatling Recorderを使う。
Gatling Recorderは、proxyを通してブラウザアクセスして記録を取ことで、Scalaで書かれたシナリオファイルを自動生成してくれるツール。
もちろん、自分でScalaを書いてシナリオ作りをしても良いが、今回は手軽にできるproxy式の手法をとる。
起動と設定
Gatling Recorderを起動する。
% cd gatling-charts-highcharts-bundle-2.1.6 % bin/recorder.sh
ツールが起動したら、初期設定として以下のように項目を埋める。
この状態で、WebプロキシとしてGatling Recorderを設定する。
ブラウザアクセスでシナリオを作る
Start
を押すと設定に従ってproxyが起動するので、ブラウザでアクセスしながら記録を取る。
シナリオ作成時のTipsとして、変なpluginなどの影響がないようにsecret windowでやるとよさ気。 また、Gatling Recorderにはログの間にTAGを差し込む機能があり、シナリオの記録が綺麗になるのでそれも活用するといい。
ということで、以下の流れで操作を記録する。
- Recorder側で
Search
タグを追加 - ブラウザでhttp://computer-database.herokuapp.com/ のトップに移動
mackbook
で検索Macbook Pro
をBrowse
タグを追加- http://computer-database.herokuapp.com/ のトップに戻る
- 何回か
Next
ボタンで遷移 - いくつかページを開いてみる
Edit
タグを追加- http://computer-database.herokuapp.com/ のトップに戻る
Add new computer
を押す- (チュートリアルではもっとあるけど、ここまでにした)
ここまで実行し終わったら、Stop & Save
を押すと、user-files/simulations/computerdatabase/BasicSimulation.scala
にシナリオファイルが保存される。
内容は以下のようになった。
package computerdatabase import scala.concurrent.duration._ import io.gatling.core.Predef._ import io.gatling.http.Predef._ import io.gatling.jdbc.Predef._ class BasicSimulation extends Simulation { val httpProtocol = http .baseURL("http://computer-database.herokuapp.com") .inferHtmlResources(BlackList(""".*\.css""", """.*\.js""", """.*\.ico"""), WhiteList()) .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8") .acceptEncodingHeader("gzip, deflate, sdch") .acceptLanguageHeader("ja,en-US;q=0.8,en;q=0.6") .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.39 Safari/537.36") val uri1 = "http://computer-database.herokuapp.com" val scn = scenario("BasicSimulation") // Search .exec(http("request_0") .get("/")) .pause(5) .exec(http("request_1") .get("/computers?f=macbook")) .pause(2) .exec(http("request_2") .get("/computers/6")) .pause(10) // Browse .exec(http("request_3") .get("/")) .pause(2) .exec(http("request_4") .get("/computers?p=1") .resources(http("request_5") .get(uri1 + "/computers?p=2"), http("request_6") .get(uri1 + "/computers?p=3"))) .pause(2) .exec(http("request_7") .get("/computers/72")) .pause(1) .exec(http("request_8") .get("/")) .pause(1) .exec(http("request_9") .get("/computers?p=1") .resources(http("request_10") .get(uri1 + "/computers?p=2"))) .pause(3) .exec(http("request_11") .get("/computers/70")) .pause(10) // Edit .exec(http("request_12") .get("/")) .pause(1) .exec(http("request_13") .get("/computers/new")) setUp(scn.inject(atOnceUsers(1))).protocols(httpProtocol) }
以上でシナリオの作成は完了。
テストの実行
今のシナリオを元に、Gatlingを起動する
% bin/gatling.sh
しばし待つと、実行候補となるシナリオの一覧が出てくるので、対象を選ぶ。(今回は0) なお、BasicSimulation以外の候補は、元々含まれているサンプル。
その他の質問もとりあえずEnterして実行する。
Choose a simulation number: [0] computerdatabase.BasicSimulation [1] computerdatabase.advanced.AdvancedSimulationStep01 [2] computerdatabase.advanced.AdvancedSimulationStep02 [3] computerdatabase.advanced.AdvancedSimulationStep03 [4] computerdatabase.advanced.AdvancedSimulationStep04 [5] computerdatabase.advanced.AdvancedSimulationStep05 0 Select simulation id (default is 'basicsimulation'). Accepted characters are a-z, A-Z, 0-9, - and _ Select run description (optional) Simulation computerdatabase.BasicSimulation started...
するとテストが実行され、最終的に結果が出力される
================================================================================ ---- Global Information -------------------------------------------------------- > request count 18 (OK=18 KO=0 ) > min response time 191 (OK=191 KO=- ) > max response time 414 (OK=414 KO=- ) > mean response time 217 (OK=217 KO=- ) > std deviation 64 (OK=64 KO=- ) > response time 50th percentile 195 (OK=195 KO=- ) > response time 75th percentile 197 (OK=197 KO=- ) > mean requests/sec 0.441 (OK=0.441 KO=- ) ---- Response Time Distribution ------------------------------------------------ > t < 800 ms 18 (100%) > 800 ms < t < 1200 ms 0 ( 0%) > t > 1200 ms 0 ( 0%) > failed 0 ( 0%) ================================================================================ Reports generated in 0s. Please open the following file: results/basicsimulation-1434181826508/index.html
結果の確認
results/basicsimulation-1434181826508/index.html
にレポートが出力されたようなので、見てみる
% open results/basicsimulation-1434181826508/index.html
思った以上にリッチなレポートが出力されていて、全体のrpsの状況や成功失敗のグラフ、各リクエストごとの詳細な情報、パーセンタイル値などが出た。
今回のテストは動作チェック程度なので大したグラフになっていないが、これはまじめに負荷テストやるとすごく良さそう。 機会があったら、プロダクト側で使ってみたい。
Embulkではてなブログの記事をparseしてElasticsearchに入れてみる
Embulk良さそうだね、と言いつついじったことなかったので。 先日ちょっと近所で話題に上がったので、仕事中に息抜きでやってみた。on mac
先にまとめ
- input / parser / execute / outputと、細かく分けてpluginになっており、一般的な形式ならほぼ組み合わせていける
- 異常に楽
- 一旦input側を作ってstdoutに出す -> outputを作る(もしくは逆順)、とステップを分割して作っていけるので、普通に作るよりハマりにくそう
- sampleのinputデータも自動生成してくれるので、outputを先に作っても楽だったと思う(今回はやらなかったけど)
- pluginを取り扱うのに
embulk gem
コマンドとか用意されてて地味に便利 - 関係ないけどbrewすげぇ
Embulkのinstall
手元のMacでの実験なのでこんな感じ
% brew cask install java % brew install embulk
RSSを取得してSTDOUTに出力させる
一気にやるとわからなくなるので、とりあえずRSSから取得してstdoutに出してみる
参考にするのは以下のあたり
- http://qiita.com/takumakanari/items/8f6efe9c115411f25547
- https://github.com/takumakanari/embulk-parser-xml
Pluginをinstall
% embulk gem install embulk-input-http % embulk gem install embulk-parser-xml
config.ymlの作成
input
にhttp
、parser
としてxml
を使う
in: type: http url: http://yudoufu.hatenablog.jp/rss params: ~ parser: type: xml root: rss/channel/item schema: - { name: title, type: string } - { name: link, type: string } - { name: pubDate, type: string } method: get out: type: stdout
- とりあえず標準出力に出す
- schemaは、descriptionを含めちゃうと
実行
% embulk run config.yml 2015-06-09 16:12:06.611 +0900: Embulk v0.6.5 2015-06-09 16:12:08.400 +0900 [INFO] (transaction): {done: 0 / 1, running: 0} 2015-06-09 16:12:08.681 +0900 [INFO] (task-0000): GET "http://yudoufu.hatenablog.jp/rss" Norikra meetup #2 に参加してきた,http://yudoufu.hatenablog.jp/entry/2015/06/03/235131,Wed, 03 Jun 2015 23:51:31 +0900 AWS Summit 2015 - Day2 行ってきたまとめ,http://yudoufu.hatenablog.jp/entry/2015/06/03/232347,Wed, 03 Jun 2015 23:23:47 +0900 AWS Summit 2015 - Day2 - 新サービス解説セッション EFS と ML,http://yudoufu.hatenablog.jp/entry/2015/06/03/223904,Wed, 03 Jun 2015 22:39:04 +0900 AWS Summit 2015 - Day2 - クラウドを活用したIoT/M2Mソリューション,http://yudoufu.hatenablog.jp/entry/2015/06/03/223701,Wed, 03 Jun 2015 22:37:01 +0900 AWS Summit 2015 - Day2 - AWS セキュアデザイン(IAM) Deep Dive,http://yudoufu.hatenablog.jp/entry/2015/06/03/223637,Wed, 03 Jun 2015 22:36:37 +0900 AWS Summit 2015 - Day2 - AWS System Operation Deep Dive,http://yudoufu.hatenablog.jp/entry/2015/06/03/223605,Wed, 03 Jun 2015 22:36:05 +0900 AWS Summit 2015 - Day2 - ネットワークDeep Dive,http://yudoufu.hatenablog.jp/entry/2015/06/03/223523,Wed, 03 Jun 2015 22:35:23 +0900 2015-06-09 16:12:09.064 +0900 [INFO] (transaction): {done: 1 / 1, running: 0} 2015-06-09 16:12:09.079 +0900 [INFO] (main): Committed. 2015-06-09 16:12:09.080 +0900 [INFO] (main): Next config diff: {"in":{},"out":{}}
- ひとまず目的のデータは出た
Elasticsearchに入れてみる
以下の記事やREADMEを参考に、書いてみる
- http://swfz.hatenablog.com/entry/2015/04/25/184339
- https://github.com/muga/embulk-output-elasticsearch
ElasticsearchのInstall
- https://www.elastic.co/guide/en/elasticsearch/guide/current/_installing_elasticsearch.html
- unzipすればいいらしい
- 今回は実験なので、brewでinstall
% brew install elasticesearch
- brewで入れると
cluster_name
がelasticsearch_username
になってるので、必要なら適当に変える
% vi /usr/local/opt/elasticsearch/config/elasticsearch.yml cluster.name: elasticsearch_yudoufu
% elasticsearch --config=/usr/local/opt/elasticsearch/config/elasticsearch.yml
- とりあえずこれで、
http://127.0.0.1:9200/
にnodeが立つ 9300
がノード間通信のportで、こっちを使って接続するっぽい
Embulk pluginを入れる
% embulk gem install embulk-output-elasticsearch
config.ymlのoutputを作る
- out部分を修正
- 事前に
index
を作っておく必要は特にないので、cluster_name
とnodes
の情報だけ合わせておく
- 事前に
out: type: elasticsearch cluster_name: elasticsearch_yudoufu nodes: - { host: 127.0.0.1, port: 9300 } index: embulk_yudoufulog_rss index_type: embulk
実行
% embulk run config.yml 2015-06-09 17:05:01.632 +0900: Embulk v0.6.5 2015-06-09 17:05:03.559 +0900 [INFO] (transaction): [Dominus] loaded [], sites [] 2015-06-09 17:05:04.391 +0900 [INFO] (transaction): {done: 0 / 1, running: 0} 2015-06-09 17:05:04.400 +0900 [INFO] (task-0000): [Siena Blaze] loaded [], sites [] 2015-06-09 17:05:04.686 +0900 [INFO] (task-0000): GET "http://yudoufu.hatenablog.jp/rss" 2015-06-09 17:05:05.137 +0900 [INFO] (task-0000): Execute 7 bulk actions 2015-06-09 17:05:05.735 +0900 [INFO] (elasticsearch[Siena Blaze][transport_client_worker][T#5]{New I/O worker #22}): 7 bulk actions succeeded 2015-06-09 17:05:05.745 +0900 [INFO] (transaction): {done: 1 / 1, running: 0} 2015-06-09 17:05:05.761 +0900 [INFO] (main): Committed. 2015-06-09 17:05:05.762 +0900 [INFO] (main): Next config diff: {"in":{},"out":{}}
データの確認
- データ入ったのを確認!!\(^o^)/
% curl -X GET http://127.0.0.1:9200/embulk_yudoufulog_rss/embulk/_search\?q\=AWS\&pretty { "took" : 10, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 }, "hits" : { "total" : 6, "max_score" : 0.26516503, "hits" : [ { "_index" : "embulk_yudoufulog_rss", "_type" : "embulk", "_id" : "AU3XWfGno1wqzcGPiVrl", "_score" : 0.26516503, "_source":{"title":"AWS Summit 2015 - Day2 - AWS System Operation Deep Dive","link":"http://yudoufu.hatenablog.jp/entry/2015/06/03/223605","pubDate":"Wed, 03 Jun 2015 22:36:05 +0900"} }, { "_index" : "embulk_yudoufulog_rss", "_type" : "embulk", "_id" : "AU3XWfGno1wqzcGPiVrh", "_score" : 0.111475274, "_source":{"title":"AWS Summit 2015 - Day2 行ってきたまとめ","link":"http://yudoufu.hatenablog.jp/entry/2015/06/03/232347","pubDate":"Wed, 03 Jun 2015 23:23:47 +0900"} }, { "_index" : "embulk_yudoufulog_rss", "_type" : "embulk", "_id" : "AU3XWfGno1wqzcGPiVrm", "_score" : 0.111475274, "_source":{"title":"AWS Summit 2015 - Day2 - ネットワークDeep Dive","link":"http://yudoufu.hatenablog.jp/entry/2015/06/03/223523","pubDate":"Wed, 03 Jun 2015 22:35:23 +0900"} }, { "_index" : "embulk_yudoufulog_rss", "_type" : "embulk", "_id" : "AU3XWfGno1wqzcGPiVrk", "_score" : 0.081366636, "_source":{"title":"AWS Summit 2015 - Day2 - AWS セキュアデザイン(IAM) Deep Dive","link":"http://yudoufu.hatenablog.jp/entry/2015/06/03/223637","pubDate":"Wed, 03 Jun 2015 22:36:37 +0900"} }, { "_index" : "embulk_yudoufulog_rss", "_type" : "embulk", "_id" : "AU3XWfGno1wqzcGPiVrj", "_score" : 0.057534903, "_source":{"title":"AWS Summit 2015 - Day2 - クラウドを活用したIoT/M2Mソリューション","link":"http://yudoufu.hatenablog.jp/entry/2015/06/03/223701","pubDate":"Wed, 03 Jun 2015 22:37:01 +0900"} }, { "_index" : "embulk_yudoufulog_rss", "_type" : "embulk", "_id" : "AU3XWfGno1wqzcGPiVri", "_score" : 0.057534903, "_source":{"title":"AWS Summit 2015 - Day2 - 新サービス解説セッション EFS と ML","link":"http://yudoufu.hatenablog.jp/entry/2015/06/03/223904","pubDate":"Wed, 03 Jun 2015 22:39:04 +0900"} } ] } }
最終的なconfig.yml
備忘録としてまとめておく
in: type: http url: http://yudoufu.hatenablog.jp/rss params: ~ parser: type: xml root: rss/channel/item schema: - { name: title, type: string } - { name: link, type: string } - { name: pubDate, type: string } method: get out: type: elasticsearch cluster_name: elasticsearch_yudoufu nodes: - { host: 127.0.0.1, port: 9300 } index: embulk_yudoufulog_rss index_type: embulk
Norikra meetup #2 に参加してきた
Norikraをつかいたいなーという気持ちを主張するためにとりあえずmeetupに参加してきた。
AWS Summitからのはしごだったのでさすがに疲れた。。。
メルカリでのNorikraの活用、Mackerelを添えて
- kazeburoさん
Mercari
いかに早くスムーズにサイクルを回すか
Zabb....?
- いいことはいっぱい
- 煩雑だしめんどくさい
DevとOpsで情報を共有
- MetricsをDevと共有
これを何とかするのに、fluentd経由でkibana / bigquery / tdなどでやっている
何かあった時にすぐ知りたい
Norikra + mackerel
mackerel
Norikra にSQLを投入するとmackerelにグラフを作られるようにしてみた
- そこにalert用の閾値設定
メルカリでのNorikra構成
Norikraが落ちる問題
- GCの途中で落ちてるっぽい
- 正直分からない
設定とクエリ
Basic count()
percentile
- アクセスのレスポンスタイムのパーセンタイルを取りたい
- 全件やるとクエリが重そうだったので、1分間の最初の五万件だけを対象に絞った
- percentilesのプラグインが返してくるのがオブジェクトの形になってしまうので、これをフラットにするためにもう一つplugin
- レスポンスタイムの監視は、ほとんどの速いレスポンスに引きづられるので平均は意味が無い
- なので、パーセンタイルや中央値などを使って監視するのが正しい
Q: kibanaと比べてどう?
- データを一杯入れると重いし、グラフを多人数で見るのが重たい
- リアルタイムにエラーログを見たい、とかはkibana使ってる
Q: メモリとかGCオプション
- memory 64Gのマシンで、max 40Gで使ってる
- メモリが少ないと圧縮、とかのオプションは外してる
- フルGCで泊まる時間はNorikraでは問題ないので、そんなに気にしてない
Q: クエリのタイムウィンドウはどれくらいで運用してる?
- 1分ぐらい
- 特別重くかかるものの時だけは5分ぐらい
Q: 冗長化はしてる?
- してない
Norikra to realtime log analytics
harukasan
昔はsshでログ取ってくるとかhogehoge
- 今はfluentd
- そのままTDやHDFSやElasticsearchとか
さらにそれをCloudforecastとか、kibanaとかtableauとか
fluentdがあるおかげで、ここを通せばだいたいどうとでもなる
- でも、そこから先の解析はまだよしなにやってくれるわけじゃない
解析の方式
- Batch processing
- daily / monthly...
- PVとか
- Ad-hoc analysis
- Kibata & Elasticsearch
- BI Tool: Tablau
Offline Analysis
- Excel....!
Batch processは重すぎる
- 5分毎の・・・1分ごとの。。。というのを知りたいときには困る
- エラー時の情報をすぐに通知して欲しい
こういうのに向いてる方式
Stream Processing
- データはずっと流れていて、そこにtime windowを切る
- そのデータに対して処理をする
- Norikra
Norikra
- schema less
- SQL like query
fluentdから送る
- fluent-plugin-norikra
target_map_tag
にしておくと便利
- fluent-plugin-norikra
じゃあその結果を他に流すか?
- fluentd -> Norikra -> fluentd
- sweep でタグを使って投げる
Norikra Deployment
Norikra / GrowthforecastあたりはSPOFで困ってる
Hardware
- Norikraにはメモリいっぱい(8GB以上
JVM 1.7
- jruby でbuild
daemonize はsupervisord
Q: Norikra自身の監視は?
- 特にしてない
Q: どのくらいの数のクエリ入れてますか?
- 5~6こ
- そんなに多くはならない
Q: time windowどのくらいにしてる?
- 1分にしてる
- でかくするとすごいメモリを食う
NorikraでWebサービスを守る
- fujiwaraさん
- HTTP status code / response timeを書いたり
fluentd-plugin-(datacounter|numeric-monitor)を置き換えた
r3.xlarge ( 4core 30GBのメモリ )
- メモリは10Gから多い時で20Gいくかどうか
クエリ数はちょっと多い
- VirtualHost毎にカウントしてる
- なんだかんだ40クエリぐらいかけている
スパマーが来た
- Web版を出したら、APIとかが見えるので、POSTをしてきたりされた
- APIにPOSTメソッドにモリモリ送ってきものをクエリで抽出
- twitter経由でのログインも抽出
- さらに10分に何回、1時間に何回、という閾値も設定
これで引っかかったものをfluentdの自作プラグインでフックして、memcachedに投げ込んでspam-reportが発行される
まとめ
- 簡単にクエリをカスタマイズできて、自作plugin書くと色々できる
Gunosy AdのNorikra事例
-
- Adでターゲティング広告みたいなことしてる
- ユーザーの好みと外れてるものはできるだけ早く外したい
- イケてる広告画像をできるだけ早く洗い出したい
環境
c4.large 1台あたり
- 1000req/sec
- 2000~2500log/sec
元々はRedshift+独自の集計
- だるい
ログ量多くてkibana4に生ログ突っ込むと死ぬ
- Speak streaming 使ってもしょうがない
そこでNorikra
最終段のところでKibana4につっこむ
OpsWorksでポチポチサーバ構築して多段構成を何とかしてる
- クエリ情報は全部custom jsonにくくりだしてる
監視
datadogはdashbordにiframe埋め込めて便利
Norikra Recent update
(スライド見つからなかった)
tagomorisさん
1年間で約17リリース
Suspend Queries
- なんかの理由でちょっと止めたい
- 登録はするけど一時的に止める、ということが可能に
- suspendしたクエリはstats fileに吐き出されないので注意
Nullable fields
- 同じようなデータなんだけど複数の場所から送られてくる
- 新しいパラメータには存在するけど、古い方にはない。。。けど集計にはどっちも含めたい!という場合
- NULLABLE()で囲むと、 項目がnullのイベントも抽出される
Listener
- クエリのグループを指定するできる
- LOOPBACK(target)
- STDOUT() -> 結果が出たら、メモリプールに行かずにNorikraのログに吐かれる
- クエリのグループを指定するできる
Listener plugin
- Listernerを自分で作れる
- これでfluentd-pluginがなくても色々できる!
- sync listener vs async listener
Dynamic plugin reloading
- SIGHUPで読み込まれるようになった
- restartなしでよくなった
その他にも色々