elixir in production

38
Elixir in Production Elixir Meetup #1 in Drecom

Upload: tsunenori-oohara

Post on 18-Feb-2017

4.858 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: elixir in production

Elixir in Production

Elixir Meetup #1 in Drecom

Page 2: elixir in production

Agenda• 発表の趣旨• 自己紹介– 最近の Elixir 事情

• 運用システム構成– 利用しているツール / ライブラリ群

• 運用 ( 負荷テスト ) 時のトラブル ( とその地雷処理 )– 事例 1. ログ– 事例 2. コネクションプール– 事例 3. アセット– その他

• まとめ

Page 3: elixir in production

発表の趣旨• 本番運用事例の紹介をする事で– 本番採用事例が増える一助に– 採用ハードルが少しでも低く

• 本番運用時のトラブル対応の紹介をする事で– あるあるの事例と思うので、知見共有する事で同様のトラブル回避の手助けに

 なったら良いなと思います

Page 4: elixir in production

自己紹介• おーはら (@ohrdev)

• 写経 ( 仏教的 )/ 仏像彫り / 寺社仏閣• 広告エンジニア (Drecom)– Erlang/Elixir/Phoenix– Ruby/Rails– (Lisp/Scala)

Page 5: elixir in production

最近の Elixir事情• コミックマーケット C89 で Phoenix 本発売– アランビックの錬金術師– http://hayabusa333.tumblr.com/– BOOTH: https://hayabusa333.booth.pm/items/

186705

• Elixir 1.2 released– https://github.com/elixir-lang/elixir/blob/v1.2.0/

CHANGELOG.md

Page 6: elixir in production

最近の Elixir事情• Programming Elixir 1.2 (Dave Thomas)– https://pragprog.com/book/elixir12/programming

-elixir-1-2

– Β 版• Programming Phoenix– https://pragprog.com/book/phoenix/programmin

g-phoenix

– Β 版

Page 7: elixir in production

運用システム構成• 広告配信 API/ ミニコンテンツ ( ゲーム )– F/W: maru( WAF/API DSL ) / Phoenix( WAF )– DB: Redis( exredis + poolboy ) / Dynamo( ex_aws )– Job: exq( + Sidekiq )– 環境変数 : dotenv– Deploy: exrm / mina / asset_sync( 自製 )– テスト : meck / power_assert– 監視 : sentry( raven-elixir ) / monit / 社内監視 tool– プロビジョニング : ansible( 自製 galaxy-role )– インフラ : AWS( EC2 / AutoScaling / ELB / S3 / CF )

Page 8: elixir in production

利用ツール /ライブラリ• WAF:– maru: https://maru.readme.io/• 特徴 :

– シンプル /Api DSL(grape like)• 出来ない事 :

– セッション管理 /DB コネクション / テンプレート描画– Phoenix: http://www.phoenixframework.org/• 特徴 :

– フルスタック ( 一通り揃ってる )/maru に比べると複雑– アセットコンパイルは brunch.io を採用( node/npm が必要 )

Page 9: elixir in production

利用ツール /ライブラリ• DB:– Redis:• exredis: https://github.com/artemeff/exredis• poolboy: https://github.com/devinus/poolboy

– Dynamodb:• ex_aws: https://github.com/CargoSense/ex_aws

– MySQL/PostgreSQL/Mongodb/etc• 弊社サービスでは採用していないのですが、恐らく ecto: https://github.com/elixir-lang/ecto 一択

Page 10: elixir in production

利用ツール /ライブラリ• Job:– exq: https://github.com/akira/exq (+ sidekiq)– http://qiita.com/ohr486/items/

9db88866786ee8bb89d9

• 環境変数 :– dotenv: https://github.com/avdi/dotenv_elixir

Page 11: elixir in production

利用ツール /ライブラリ• エラー監視 (sentry):– raven-elixir:

https://github.com/vishnevskiy/raven-elixir

Page 12: elixir in production

利用ツール /ライブラリ• 死活監視 / 自動再起動– monit• DB/ ミドルウェア / 外部サービスの監視

– supervisor(OTP)• 言語の一部 (OTP) として提供される• きちんとチューニング / 設定しておくと安心できる

– 半年の本番運用で 2 回程お世話になった– パラメータの設定不備でプロセスが死亡する事象が発生したが、自動的にプロセスを監視 / 再起動して、サービスを落す事なく復旧 / 修正まで凌げた

Page 13: elixir in production

利用ツール /ライブラリ• プロビジョニング :– ansible + galaxy-role: https

://galaxy.ansible.com/detail#/role/2930– Erlang/Elixir/Phoenix のバージョン up の頻度はかなり早いので、自動化しておくべき– Erlang と Elixir のバージョンの相性がある為、両方のバージョンを指定できると捗る

Page 14: elixir in production

運用 (負荷テスト )時のトラブル• Elixir の開発 / 本番運用を約 1 年程やってきて、地雷はそれなりに踏み抜いてきました• 今回は特に– 開発時には発見し辛く、負荷テスト / 本番運用時に現れる– サービス規模 / トラフィックがある程度大きくなるまで現れない といった事例をピックアップしています

Page 15: elixir in production

事例 1.ログ• 事象 : 大したリクエスト数でもないのに、

iowait が高まり LA / CPU 使用率が上昇

Page 16: elixir in production

事例 1.ログ• 検証 :– サンプル http://github.com/falood/

maru_examples– ab で負荷をかけて計測 (echo_server)

$ ab -n 10000000 -c 2 http://xxx.xxx.xxx.xxx:8800/

$ vmstat -a 1

Page 17: elixir in production

事例 1.ログ• 検証 :– vmstat の結果 : bo が異常値

$ vmstat -a 1procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu----- r b swpd free inact active si so bi bo in cs us sy id wa st 2 0 0 6807288 45796 562724 0 0 21 1242 11712 5338 37 25 30 8 0 1 1 0 6807156 45796 562988 0 0 0 4528 28259 5784 15 15 51 20 0 1 1 0 6806908 45800 563184 0 0 0 4484 28252 5717 15 16 50 20 0 0 1 0 6806660 45796 563384 0 0 0 4548 28171 5843 15 16 50 19 0 2 0 0 6806412 45796 563584 0 0 0 4556 28003 5825 15 15 51 20 0 1 1 0 6806288 45796 563780 0 0 0 4484 28262 5787 15 16 50 20 0 1 1 0 6806040 45796 563984 0 0 0 4488 28604 5868 14 17 50 19 0 2 1 0 6805792 45796 564180 0 0 0 4472 28473 5888 13 18 50 19 0 1 1 0 6805668 45800 564384 0 0 0 4480 28193 5772 15 16 50 19 0 1 1 0 6805420 45796 564580 0 0 0 4472 28356 5746 14 17 50 20 0 2 0 0 6805420 45796 564784 0 0 0 4492 28445 5757 14 16 50 19 0 1 1 0 6805172 45796 564980 0 0 0 4504 28393 5787 15 16 50 20 0 1 1 0 6804924 45796 565184 0 0 0 4456 28411 5671 13 17 50 20 0 2 0 0 6804676 45796 565380 0 0 0 4488 28169 5786 14 15 51 20 0 1 1 0 6804428 45800 565576 0 0 0 4504 28053 5761 13 17 50 20 0

Page 18: elixir in production

事例 1.ログ• 検証 :– logger の backend がデフォルト (console) だと、

O_SYNC モードでログファイルが open されるhttps://github.com/erlang/otp/blob/maint/erts/etc/common/run_erl_common.c

– naoya さんの記事 (Linux I/O のお話 write 編 )http://d.hatena.ne.jp/naoya/20070523/1179938637

– 『アプリケーションが SYNC モードでファイルを開いていたり、明示的に fsync() してたりするとそこで wait が発生するのはいわずもがな、です。』

Page 19: elixir in production

事例 1.ログ• 事象 : 大したリクエスト数でもないのに、

iowait が高まり LA / CPU 使用率が上昇• 原因 :logger のバックエンドがデフォルトの console だった(為、ログファイルを

O_SYNC モードで開いていた)ので、毎秒ディスクへの同期とフラッシュが走っていた

Page 20: elixir in production

事例 1.ログ• 対応– 本番環境の logger の backend を console から変更– ※ Elixir の Logger の backend は公式には consoleしか提供されていない

• https://github.com/elixir-lang/elixir/tree/master/lib/logger/lib/logger/backends

• https://github.com/onkel-dirtus/logger_file_backend• https://github.com/basho/lager

– logger_file_backend はカスタマイズに難ありだったので backend を自前実装した

Page 21: elixir in production

事例 1.ログ• 教訓 :– 本番環境のログの backend は必ず fileベースのものに変更すること– 負荷テスト時のサーバー状況のチェックは必ず実施する事(あたりまえですが)

Page 22: elixir in production

事例 2.コネクションプール• 事象 : リクエストを大量に長時間なげ続けると、 ElasticCache(Redis) のコネクション数があふれる

Page 23: elixir in production

事例 2.コネクションプール• 状況 :–各種 DB ドライバのコネクション管理事情• PostgreSQL/MySQL/MSSQL/SQLite3/MongoDB

– ecto: https://github.com/elixir-lang/ecto– コネクション管理 ( 内部的には poolboy で実装 ) を含む

• Redis– exredis: https://github.com/artemeff/exredis– クライアントのみ、コネクション管理は含まれない

• Dynamodb– ex_aws: https://github.com/CargoSense/ex_aws– APIベースなのでコネクション管理なし– Read/Write Capacity Unit の設定で担保

Page 24: elixir in production

事例 2.コネクションプール• 状況 :– ( コネクション管理をしていなかったので ) リクエスト毎に Redis のコネクションを確立していた– ElasticCache(Redis) のコネクション• コネクションタイムアウト : デフォルト 5分• コネクション上限数 :2万

– 開発時は、コネクション数自体が少なく、上限にいくまでにタイムアウトで切断されていたので気がつかなかった、高負荷をかけて初めて発覚

Page 25: elixir in production

事例 2.コネクションプール• 事象 : リクエストを大量に長時間なげ続けると、 ElasticCache(Redis) のコネクション数があふれる• 原因 :Redis のコネクション管理をしていなかった / するドライバを使っていなかった

Page 26: elixir in production

事例 2.コネクションプール• 対応 :– コネクション管理を含む Redis ドライバを使用• https://github.com/quarkgames/exredis_pool• https://github.com/le0pard/redis_pool• 問題点 : コネクションプールの設定が貧弱 /十分にチューニングできない

– poolboy でコネクションプールを自前実装• 参考 : たのしい poolboy(@hagiyat さん )• http://qiita.com/hagiyat/items/

a28683d01223bfc204d9

Page 27: elixir in production

事例 2.コネクションプール• 教訓 :– Redis を使う際は、コネクション管理に注意、必ずコネクション管理機構を実装する事– poolboy は良実装( Erlang の良いお手本コード)– 負荷テスト時のサーバー状況のチェック( ry

Page 28: elixir in production

事例 3.アセット• 事象 :AWS のデータ転送量の費用が増大• 原因 :EC2 上に deploy した phoenix アプリのアセット ( 画像 /css/js/font/etc) の転送量だった

Page 29: elixir in production

事例 3.アセット• Phoenix のアセットの参照 :– digest: 静的ファイルを圧縮して manifest を作成• phoenix.digest (mix コマンド )• manifest.json に、圧縮前後のファイルパスの mapping を

JSON形式で出力する– cache_static_manifest != true (config.exs)• 静的ファイルを priv/static 以下から参照

– cache_static_manifest == true (config.exs)• manifest.json をパースして、 ets(Erlang の In-memory ストレージ ) にマッピング情報をストア• 静的ファイル参照時に、圧縮後のファイルを参照させる

Page 30: elixir in production

事例 3.アセット• 改善前の手順– 1. アセットの参照方法を manifest に

– 2. 圧縮ファイルと manifest.json を作成• MIX_ENV=prod mix phoenix.digest

– 3. MIX_ENV=prod でアプリを起動– 4. 2. のマッピング先 (圧縮後ファイル ) が参照

# config/prod.exsconfig :my_app, MyApp.Endpoint, http: [port: xxxx], url: [host: “xxxx”], cache_static_manifest: “priv/static/manifest.json”, server: true

Page 31: elixir in production

事例 3.アセット• アセット参照の実体 (static_path/2)– Phoenix.Route.Helpers # static_path/2• https://github.com/phoenixframework/phoenix/blob/

master/lib/phoenix/router/helpers.ex

– MyApp.Web # web ( web/web.ex )• import MyApp.Router.Helpers

– static_path/2 が import されて利用可能に– View/css/javascript # static_path/2• static_path/2 でアセットを参照

Page 32: elixir in production

事例 3.アセット• アセット参照の実体 (priv/static参照 )– http://xxx.xxx.xxx.xxx:4000/images/hoge.png• priv/static/images/hoge.png が参照される

– http://xxx.xxx.xxx.xxx:4000/css/var.css• priv/static/css/var.css が参照される

Page 33: elixir in production

事例 3.アセット• 対応 :– Phoenix には asset_sync 相当のライブラリが

( 自分が調べた限り採用できそうなのものが )存在しなかった– Rails の asset_sync 相当のライブラリを自前実装• 対象ファイルのアセットファイルを s3 に upload• static_path/2 を、 s3/cloudfront を参照しにいく様に差換える• アセットファイルをバージョン管理 (sync/cleanup)

Page 34: elixir in production

事例 3.アセット• 対応 :– asset_sync(phoenix 版 ) は、もう少し本番運用した後、 hex に公開予定–仮リポジトリ /参考実装(テスト / ドキュメント /残ン実装対応中):

• https://github.com/ohr486/asset_sync–注意 :

• 2016/1 現在、 ex_aws の s3 は東京リージョンから一部機能が利用できません• https://github.com/CargoSense/ex_aws/pull/101 の修正が hex に publish されるまでパッチを当てるなりして下さい

Page 35: elixir in production

事例 3.アセット• 教訓 :– インフラコストはちゃんと監視する事– Phoenix on AWS で画像を扱う際は、転送量に注意、必要に応じて CloudFrontへ– 負荷テスト時のサーバー状況のチェック (ry

Page 36: elixir in production

その他• 地雷処理の為にやった事 / 必要だった事– ドキュメント (英語 ) を読む• 日本語の情報はまだまだ不足

–実装コード (Elixir/Erlang) を読む• Erlang の参照ライブラリを読むケースはそれなりにある( Erlang のライブラリを wrap しただけの

Elixir ライブラリ)– awesome-elixir/awesome-erlang のチェック• 他の実装ライブラリや、似た様な機能を探す際に便利、定期的に追いかけておくと何かと捗る

Page 37: elixir in production

その他• 地雷処理の為にやった事 / 必要だった事– 地雷処理できる人を増やす活動• Erlang が読めるように

– Elixir独自の機能はありますが、やはりベースは Erlang です– Erlang を Ruby の Syntax で記述している感じ

• OTP の概念を理解できるように– OTP の理解無しに Elixir のアプリ運用はできない(と感じた)

• 関数型言語を理解する– 社内勉強会(すごい E 本 / コップ本読書会)– Erlang に自信が無い場合は、時雨堂さんにコンサルをお願いすれば良いんじゃないでしょうか

Page 38: elixir in production

まとめ• 弊社にて運用している Elixir のサービスについての紹介をしました• 本番運用時に発生したトラブルとその対応を紹介しました• (完璧では無いですが )十分に本番で運用できています• まだまだ足りない機能 / ライブラリがあるので、もっと充実してきて欲しいです ( 自分ももっと公開していきます )