Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Node.js×mongo dbで3年間サービス運用してみた話

14,722 views

Published on

2015/11/12開催の
【ヒカ☆ラボ】Node.js×MongoDBでのサービス運用が1時間で分かる!3年間の運用での失敗談とその対策に加えて、運用のハマりどころやツールついてもお話します!
株式会社サイバーエージェント 橋本 純様の資料です。

Published in: Technology
  • Be the first to comment

Node.js×mongo dbで3年間サービス運用してみた話

  1. 1. Node.js×MongoDBで 3年間サービス運用してみた話 ヒカ☆ラボ 2015/11/12 Atsushi Hashimoto Cyberagent、Inc 1
  2. 2. 自己紹介 ○橋本 純(はしもと あつし) ○32歳 ○サーバサイド エンジニア ○Node.js、Java、Python、PHP 担当したサービス:ピグサバゲー、ピグアイランド、ピグライフ、 ピグワールド、ピグブレイブ(今ココ) 2 ふふふっ。こんにちは、ハッシーです。
  3. 3. アジェンダ ざっくりサービス説明 Node.jsで嵌った MongoDBで嵌った まとめ 3
  4. 4. ざっくりサービス説明 4
  5. 5. 5 ライフ アイランド カフェ ワールド
  6. 6. 6 ブレイブ
  7. 7. サーバー構成 7
  8. 8. 8
  9. 9. フロント WebsocketとFlash 全盛期は、同時接続20万 9
  10. 10. 10 実際の仕様バージョンやモジュールなど ○Node.js v0.8.26 v0.10.29 ○MongoDB 2.2、2.4 ○Npmモジュール express, ws, socket.io, async, request, lodash, moment, ghooks, node-mongodb-native, supertest, sinon, power-assert, istanbul, etc..
  11. 11. ここまでで、ざっくり説明は終了です。 11
  12. 12. Node.jsで嵌った 12
  13. 13. イベントループ系の話 13
  14. 14. イベントループをざっくり説明 14 に行く前に
  15. 15. 15 Node.jsは、イベントループを止めちゃダメ イメージ的には、関数を積み上げて順番に 処理していく感じ。 DBコールやHttpリクエストなど、同期的に処理すると止まっ てしまう処理は、叩くだけにして、戻ったらクロージャを実 行する感じ。(非同期に処理) どこかで止めてしまうと、全てが止まる。。。
  16. 16. console.log使っていると固まるような。。 16 エピソード1
  17. 17. 17 console.log()は、時を止める 使うと標準出力へ同期的に書き出す (サイズが大きいと結構止まる) (本番のコードには残さないように) 通常のログは、ログ出力用のライブラリなど 使ってください。
  18. 18. 高負荷でも無いのに、なぜかHttp リクエストがタイムアウトする。。。 18 エピソード2
  19. 19. 19 重い or 回数の多いfor文のループも時を止める 原因は、アイテム数が多いユーザが特定の行動をすると、 回数の多いfor文の処理となり、時が止まってしまった。 500ループぐらいで、setImmediateを挟んで対応。
  20. 20. 時を止めないように起動時もがんばって 非同期で書いたが、処理が複雑になって しまった。。。 20 エピソード3
  21. 21. 21 初回起動時のみなら、同期的に行っても問題ない コンフィグファイルの読み込み Httpサーバの起動 DB接続 各種セットアップ...などなど 起動時してすぐに、リクエストが飛んでこないなら、 最初は同期的にやったほうが処理がシンプルになる。
  22. 22. コーディング時の失敗の話 22
  23. 23. requireした時点で、処理が動くように書 いたが、バッチ時など動いてほしくない 時も動いてしまった。。。 23 エピソード1
  24. 24. 24 Requireした時点で動く処理は書かないほうがよい 空関数をセットしたりなども出来ないので、 面倒でもセットアップ処理などある場合でも、 使う側で呼んだほうが良いと思います。
  25. 25. エラー時に、try catchの部分で、なぜか callbackが2回呼ばれた。。。 25 エピソード2
  26. 26. 26 try catch finallyを使ったほうがよい。 NGパターンは、callback内でエラーがthrowされると、 catch内のcallbackが呼ばれるため、2回呼ばれる。
  27. 27. 同期的にコーディングしていた部分に、 非同期処理を入れることになったが、修 正が大変だった。。。 27 エピソード3
  28. 28. 28 基本callback形式で書いておいたほうが良い。 メソッドの粒度など、色々とあるかと思いますが、 Private以外の関数や修正が多そうな関数は、 callback形式で書いておいたほうが、無難かと。
  29. 29. スタックオーバーエラーにならなそうな コードで、スタックオーバーエラー。 29 エピソード4
  30. 30. 30 スキップ系は、setImmeiate挟むようにする。
  31. 31. TypeError(ぬるぽ)が起きて、ギフトが 受け取り放題になった。。。 31 エピソード5
  32. 32. 32 ESLintなどのリンター導入とテストの充実を図る TypeErrorは、なんちゃってロールバックも出来なくなるの で、致命的です。。。 無いように実装&テストせねばならないです。
  33. 33. その他 33
  34. 34. 複数CPUを使用したい場合、クラスター モジュールを使う必要がある。 34 エピソード1
  35. 35. 35 でも、クラスターモジュール使ってません。 Node.js v0.4の当時は、複数プロセス立ち上げることが必要 だったので、歴史に引きづられているだけです。 なお、v0.10は、均等に処理が振られないので↑の手法を試 すのも良いかもです。v0.12以上は直ってます。 Node.jsを0.12以上で使うなら素直にクラスターモジュール を使用してください。
  36. 36. Node.jsでバッチ処理を書いたら、メモリ 1.4Gbyte超えた当たりからめっちゃ遅い 36 エピソード2
  37. 37. 37 リミットが1.4Gbyteくらいなので注意です。 node app.js --max-old-space-size=8192 --nouse-idle- notification ↑のような感じで起動すると8Gくらい使えます。 バッチとかでワザとメモリを使う仕様にしないと、あまり上 限に当たらない気もしますが。。。 ググると、起動時パラメータの話とか色々とあると思います。
  38. 38. Websocketを使っているが、処理計測が しづらい。 38 エピソード3
  39. 39. 39 力技で計測処理を入れました。。。 Javaとかと違って、非同期の処理は測りづらいです。結果 Controller層で、処理の終わりで計測メソッドを呼び出すよ うにしました。。。 httpのように、リクエスト&レスポンスが必ずある前提で websocketを使えば、仕込むのは容易だったのですが。。。 賛否あるかと思いますが、僕が次回やるなら仕込めるような 形で実装します。
  40. 40. ここまでで、Node.jsの話は終わりです。 40
  41. 41. MongoDBで嵌った 41
  42. 42. 注意 この資料は、MongoDB2.2系と2.4系で 起こった出来事をありのまま描いた物です。 3系だと起こらないものもあるかもなので、 過度な期待はしないでください。 42
  43. 43. MongoDBについて 43 ■コレクションのシャードキー単 位で、自動で分散してデータ保持 ■Shardはレプリカセット単位
  44. 44. MongoDBについて 44 Shardに直接接続 することもできる が、全データ取得 できないため、通 常は、mogos経 由でアクセス。
  45. 45. シャーディング編 45
  46. 46. サービス公開時に、プライマリシャード に更新が偏ってエラーが出た。 46 エピソード1
  47. 47. 47 予測し得るデータを、予めデータを投入しておく MongoDBは、プライマリシャードというものがあり、空のコ レクションは、まずそこに書き込まれる。 ある程度、量が増えるとchunk移動がおこり、データがレプ リカセットごとに移動する。 先にデータを用意しておけば、chunk移動をあらかじめ起こ すことが出来るので、負荷分散出来る。
  48. 48. コレクションを追加したが、シャーディ ング設定が入ってなかった。。。 48 エピソード2
  49. 49. 49 コンフィグファイルとのチェック機構を追加 プライマリシャードの件でもそうですが、一箇所に更新が固 まると、まずいです。 シャーディング設定を入れないと、データが分散されないの で、注意です。
  50. 50. チャンク移動時に、たまに前のシャード に書き込みが起きる。 50 エピソード3
  51. 51. 51 mongod単位で見ると、_idが複数ある状態に。。。 Chunkの移動が自動で行われると、ロックをかけてから、デ ータ移動という順番ですが、なぜか移動前のシャードに対し てクエリが発行されている場合があります。 全部のクエリがそうでは無いので、自動ではなく手動で chunk移動するか?とか、検討中です。
  52. 52. ローカルではエラーにならなかったが、 ステージング環境でエラーが発生 52 エピソード4
  53. 53. 53 シャーディング環境でないと出ないupdateエラー シャーディング環境だと、シャードキーが無い更新をすると エラーになります。(オプションを指定すれば大丈夫。) ローカル環境とDev環境を、シャーディング設定入れました。 そもそもコードのバグなので、DBアクセス層を工夫すれば、 起こらない問題でもあるかと。
  54. 54. データ編 54
  55. 55. 文字列のデータが入るプロパティに、配 列のデータが入ってる。 55 エピソード1
  56. 56. 56 スキーマレスだけど、スキーマほしいです。 とあるデータは、プロパティが無い状態などもあり、データ の全容が把握しづらかったりします。 RDBMSなら、テーブル定義で分かったりとか出来る部分なの で、スキーマレスでもスキーマがほしい所です。 json-schema入れたり、 Mongoose利用すれば解消します。
  57. 57. マスターデータは、Excelと親和性が良い 物にしたほうが良い。 57 エピソード2
  58. 58. 58 結局マスタの作成はExcelなのです。 管理画面のフォームを作ったは良いが、Excelアップロードし か使われなかったり。。。 エンジニア的には、フォームとExcelアップロード両方作成し たり、jsonとテーブルのインピーダンスミスマッチの解消を するコードを書かなければなりません。。。
  59. 59. ユーザデータの構成は、基本的に1コレ クション、1ドキュメント 59 エピソード3
  60. 60. 60 使い方にもよるかと思いますが、うまくいってます。 各種コレクションごとに、ユーザコードを_idにして、ドキュ メントがあります。 リファレンス式という使い方もあるかと思いますが、特に問 題は出てません。
  61. 61. 行動ログのコレクションをCapped Collectionにしたら耐えられなかった件 61 エピソード4
  62. 62. 62 テラバイト級は、indexすらメモリに入らず。。 元々の、時間ごとに普通のコレクションを作成して、ログを 検索する仕様に戻す。 ↑こちらの仕様だと、データ量が、ある一定量に到達すると namespaceが枯渇してエラーが出ます。なので、サーバーを 切り替える仕組みにして運用していたのですが、それが大変 だったから切り替えたかったのですが。。。
  63. 63. 知らぬ間にオブジェクトサイズが肥大化 しており、パースエラーが発生 63 エピソード5
  64. 64. 64 定期的にドキュメントサイズを計測するようにした 対象のデータは、配列のプロパティだったのですが、不具合 でコードのチェックが働いておらず、無限に追加していく形 になっていた。
  65. 65. バッチ編 65
  66. 66. バッチで一括削除したはずのデータが読 み込まれる 66 エピソード1
  67. 67. 67 mongosにキャッシュが残っていたのが原因 db.adminCommand(‘flushRouterConfig’) ↑か、mongosの再起動で解決する。 現状では、バッチ系を流した後は、上記コマンドを実施して いる。
  68. 68. ローカルでは問題ないのに、本番だとバ ッチの実行が完了しない。 68 エピソード2
  69. 69. 69 更新対象の問題(Hogeでなければ無問題)
  70. 70. その他 70
  71. 71. ObjectIdで、時間の範囲検索をしたい。 71 チップス的な
  72. 72. 72 更新対象の問題(Hogeでなければ無問題) 先頭が4バイトのUnix時間なので、↑の用にやるとできます。 Node.jsからやるなら、 ObjectID.createFromTime(startTime); というメソッドがあるので、↑推奨です。
  73. 73. ここまでで、MongoDBの話は終わりです。 73
  74. 74. まとめ 74
  75. 75. 75 固有の地雷に気をつけてください 今回の発表の内容のようなことが避けられればよいかと。
  76. 76. 76 静的チェック系のモジュールの導入 現状は、ESLintが有力。 現場では、jshintとjscsとgjslintなど使ってます。 移り変わりが激しいですね(‘A`)
  77. 77. 77 自由度が高くスタンダードな実装が無い 設計や実装方針の決めが重要 逆に、うまく設計や実装が出来るとすごく追加・修正がしや すいシステムが出来ると思います。 現場では、MVCで実装してますが、Mが重くなりすぎて、 色々と検討中です。(ただ、分かりやすいです。)
  78. 78. 78 完璧なロールバックはできない トランザクションが無いので、完璧なロールバックは できないです。現場では、エラーが起きた際に、更新前に戻 すクエリを発行したりしています。 お金系処理は、別システムのAPIを叩いています。 RDBMSのような完璧さは無いので、作るシステムによって考 えてほしいです。 マイクロサービスの1つとして使うのはありかと。
  79. 79. 79 Node.js含め、各モジュールの更新頻度が高い アップデート頻度や、モジュール自体のデファクトの移り変 わりが激しいです。 「power-assert」を使うなど、仕様が変わらなそうなものを 使うのも良いと思いますが、基本はモジュールの変更に追従 する方針になると思います。なので、モジュールを変更して も大丈夫なように、テストを充実させておく必要があるとお もいます。
  80. 80. 80 最後に Node.jsの実装は、Javaより楽になっていると思います。 環境構築も、npm iで、一発で出来たりしますし。 MongoDBも複雑な設定をしなくても動いてくれます。 楽ちんではありますが、銀行業務などには向かないので、ケ ースバイケースで使用していくと、幸せになれるのでは?と、 思います。以上です。

×