nodejsでのシステムフォールトトレランスの向上

nodejsのシステムフォールトトレランス

3年前、nodejsの将来を信じて、この言語をプロジェクトの最も「問題のある」サービスに実装するキャンペーンを開始しました。 すべてがうまくいきました-負荷が下がり、安定性が増していました。 しかし、私が話したいレーキがまだありました。



これは完全なアクションガイドではありません。nodejsのプロである場合は、コメントで推奨事項を追加できます。これについては記事で喜んで参照します。



1. Nodejs-シングルスレッド



コアが4つある場合、起動されたノードは1つだけをロードするため、これは少し異常です。 つまり 4つのカーネルすべてをロードするには、nodejsの4つのインスタンス(コピー)を実行する必要があります。 ここで、これらの4つのインスタンスの前に、負荷を分散するバランサーをインストールする必要があります。 バランサーだけでなく、プロキシサーバー(バランスを取る可能性がある)の方が良い:





2.ノードの前に何を置くべきですか?



ここには、たとえば次のような多くのオプションがあります。

Apacheをインストールしないでください。すべてを実行できますが、関連するリソースの点では最適ではありません。

nginxを選択しました。



3.長時間動作する負荷がかかっているインスタンスが「スローダウン」し始めます



これは、長時間実行されるプロセスの優先度を下げるカーネル機能だけでなく、ノード自体のガベージコレクションの問題の一部であり、Javascriptコードで許可されている問題の一部でもあります。 そして、特定の頻度でインスタンスを再構築することほど良いものは見つかりませんでした。 再起動の頻度(さまざまな理由による)は、1時間に1回から1日に1回です。



サービスの中断がないように、サービスの2つのインスタンス(コピー)を実行する必要があります。 バランサーのインスタンスの再起動中に、インスタンスから負荷を削除する必要があります。

これを行うには、nginxの設定を変更し、インスタンスをリブートする前後にリロードします。



4.負荷がかかっているノードでは、nofile制限(LimitNOFILE)を増やす必要があります



多くのディストリビューションでは、デフォルトで非常に控えめな数字があります。 16000以上のベットをお勧めします(私は131070を持っています)。 これは、 ulimit -n 131070コマンドで 、または/etc/security/limits.conf微調整することで指定できます。



サーバーはsystemd標準で記述されており、制限はLimitNOFILE変数によって設定され、次のようになります(ファイル/usr/lib/systemd/system/nodejs1936.service):

[Unit] Description=Nodejs instance 1936 After=syslog.target network.target [Service] PIDFile=/var/run/nodejs1936.pid Environment=NODE_ENV=production Environment=NODE_APP_INSTANCE=1936 WorkingDirectory=/var/www/myNodejsApp # node_program supervisor ExecStart=/usr/bin/supervisor --harmony -- /var/www/myNodejsApp/index.js -p 1936 User=node Group=node LimitNOFILE=131070 PrivateTmp=true [Install] WantedBy=multi-user.target
      
      







5.ノードインスタンスのメモリは限られているため、この制限はそれほど大きくありません。



Nodejsには、各インスタンスが「食べる」ことができる最大メモリサイズにデフォルトの制限があります。 私はv8でよくある質問を引用しています:

現在、デフォルトでは、v8のメモリ制限は32ビットシステムでは512 MB、64ビットシステムでは1.4 GBです。



ただし、-- max-old-space-sizeキーを使用してこれを変更する方法があります。たとえば、Mでメモリを指定して、たとえば4Gに増やすには、-- max-old-space-size = 4096と書きます。



--stack-size オプションを使用して、スタックサイズに影響を与えることもできます 。たとえば、 --stack-size = 512



メモリを増やすことは、データを処理するためにRAMを最大限に活用するように設計された定期的に開始するプロセスを記述するときに役立ちます。 たとえば、手紙を送るためのスクリプト、ログアナライザーなどです。



6.コードは、接続された大きなキャンディーバーとして作成されます。



「スリーインワン」または「テンインワン」を与えないでください。これは機能する可能性がありますが、緊急事態はプロジェクト全体を圧倒します。 それどころか-すべてを3、5、10個の独立したサービスに分割します。 サービスが単純で簡単であればあるほど、その動作は安定します。 1つのサービスの「出発」は、プロジェクト全体ではなく機能の一部の出発につながります。



分解を使用して、複雑なサービスをいくつかの単純なサービスに分割します。 RESTプロトコルを使用してサービス間で対話します。 これは、プロジェクトの成長のための技術的基盤になります。 「膨潤した」サービスは、アプリケーションアーキテクチャを変更することなく、常に生産性の高いサーバーに簡単に再配置できます。



コインの裏側もあり、サービス自体を単純化します。サービス間の接続を複雑にします。また、アプリケーションに数十または数百のサービスがある場合、問題が発生する可能性があります。 これにより、システムのフォールトトレランスが向上しますが、開発の複雑さ、プロジェクトの展開、開発者がプロ​​ジェクトに参加するためのしきい値も増加します。



7.フォールドノードプロセスを再起動できるソフトウェアを使用する



コードがどれだけ正しく書かれていても、とにかくその時が来ます。 それが落ちたとき、あなたは予見しなかったエラーの後。 この標準の問題を解決するために、ノードのプロセスを監視し、必要に応じてそれをオーバーロードする多くのcliユーティリティが作成されています。





スーパーバイザーを使用します。



Systemdサービス設定を使用して、同じ機能を整理できます(最近記事がありました)。



8.ノードの新しいリリースを独自に収集します



ディストリビューションでノードが更新されるのを待ちません。 非常に機能しなくなります。 特に複雑なことは何もないので、Linuxディストリビューション用のパッケージを自分でビルドする方法を学ぶことをお勧めします。



ノードの最新バージョンは、Fedoraディストリビューションのrpmパッケージの形式で、最新リリースの1〜3日後に収集されます 。 短いテストの後、すべての本番サービスを新しいノードに移行します。



node_modulesを再構築することを忘れないでください。ノードのバージョン(またはメジャーバージョンまたはマイナーバージョン)を変更する場合、モジュールで問題が発生する可能性があります。そのコードの一部はpで記述されています。



9. ECMAScript 2015(ES6)の使用を恐れないでください



現在、運用サーバーにnodejs 5.0.0がインストールされており、1年前は0.11.6でした。



4.2.2に戻り、arr2配列の要素をarr1配列の最後に追加するには、次のように記述する必要がありました。

 var arr1 = [0, 1, 2]; var arr2 = [3, 4, 5]; // Append all items from arr2 onto arr1 Array.prototype.push.apply(arr1, arr2);
      
      





5.0.0ではこのようにできます

 var arr1 = [0, 1, 2]; var arr2 = [3, 4, 5]; arr1.push(...arr2);
      
      





「これは安定性にどのように影響しますか?」という疑問が生じます。 ジェネレーター、クラス、約束...未来が来ましたこれがコードをより理解しやすく、予測可能にし、コードをより理解しやすくするほど、コードの異常に気づき、それを排除しやすくなります。 ECMAScript 2015(ES6)を使用することで、CallBack hellを完全に取り除き、コードの量を大幅に削減することができました。



10.テスト(まあ、少なくともコードの一部)



今すぐあなたの指先にあるものを私に投げつけようと急がないでください。 はい、私も開発の初期段階でのテストのファンではありませんが、そのような記事を読んだら、すでに製品の安定化の段階にいますか? :)



すべてをテストするわけではありません。 通常、「外部」サービスとそのサービス自体の重要な機能に問題がないことを確信できる簡単なテストを作成します。 例えば、mysql、mongodb、外部サービスと同様に、memcachedまたはredisのキーを作成、編集、削除します。



多くの場合、「MySQLをバックフィックスで監視しているのに、なぜサービス自体から何かを確認する必要があるのか​​」という誘惑があります。 私の練習では、サービスがすべてOKのように見える状況がありましたが、MySQLではすべてがOKですが、サービスとMySQLの間の接続に問題があります。たとえば、管理者が誤ってiptablesに誤ったルールを追加した、サーバーとスイッチ間のレースが曲がった、またはそれらはフックし、うまく機能せず、ネットワーク経由の送信中にエラーが発生します。あまりにも「スマート」なMySQLベースのカーネルは、サービスがSYNフラッドを生成し、パケットのドロップを開始するなどと判断しました。



概して、フレームワークなしでテストすることが可能であり、単にいくつかの値(0または1)をzhiksまたはいくつかの数値に返し、許容限度外の値を受け取ると、問題に関するSMSを送信します。



フレームワークが必要な場合は、次をご覧ください。





これは私が言いたかったことすべてではありませんが、記事には十分です。 次のシリーズでは、ノード、仮想化レーキ、nodejsインスタンスからnginx番目の応答を正しくキャッシュする方法のニーズに合わせたカーネルチューニングを検討します。



All Articles