Webアプリ性能をnscd使って改善した話


はじめに

Webサイトにおけるパフォーマンス(サイト表示速度)を改善するポイントはいくつもあります。
以下一例です。

  • フロントエンドの改善
    • リクエスト回数を減らす
    • 通信量を減らす
    • レンダリング速度の最適化
    • CDN活用
  • サーバサイド
    • (Memcached/Redis等)キャッシュ活用
    • アプリロジック改善
      • n+1問題回避やらいろいろ
    • APサーバと静的コンテンツ配信サーバ分離
    • 開発言語バージョンアップ
    • DBチューニング
      • インデックス見直し
      • SQLチューニング

弊社サービスでは、これまでも上記に取り組み、少しずつ改善はしていました。
それもそろそろ今のアーキテクチャでは頭打ちかな?という状況でしたが、最近実施して意外にも
大きく改善したことがあったのでご紹介します。

環境構成

  • インフラは全てAWS
  • APサーバには、apache2.4 + php7.2 + opcache
  • Cacheサーバには、ElastiCache(セッション用、キャッシュ用の2つ)
  • DBサーバには、RDS for PostgreSQL

よくある構成ですね。^^;

主題:nscdでDNSキャッシュ

弊社の環境では、Route53のPrivateDNSでRDS/ElastiCacheエンドポイントに別名のホスト名を
割り当ててそれをAPサーバから使うことで、リソース切り替えを容易にしています。(疎結合!)
この方式の弊害としては、PrivateDNSだとALIASレコードが使えないため、CNAMEレコードを設定する必要が
あり、これにより名前解決に余計に1回クエリが飛んでしまうというもの。

PrivateDNSでもALIASレコードは使えるようです(コメント欄参照)

また、phpは(apache)プロセス内でDNSクエリ結果をキャッシュしないようで、毎回DNSクエリを投げます。
このRDS/ElastiCacheの名前解決処理により、遅延が発生している可能性を疑いました。

そこで、nscdを用いてDNSクエリ結果をキャッシュを試してみました。
nscd(name service cache daemon)とは、その名の通り、DNSやNISなどの一般的なネームサービスの
内部DBを保持し、passwd、group、hostsファイルなどの情報をキャッシュするサービスです。

nscd導入手順

$ sudo yum install nscd
$ sudo vi /etc/nscd.conf  # 後述のnscd.confの内容にする
$ sudo systemctl start nscd
$ sudo systemctl enable nscd
$ sudo apachectl graceful # Apache利用の場合
/etc/nscd.conf
    logfile             /var/log/nscd.log
#   threads             4
#   max-threads         32
    server-user         nscd
#   stat-user           somebody
    debug-level         0
#   reload-count        5
    paranoia            no
#   restart-interval    3600

    enable-cache            hosts       yes
    positive-time-to-live   hosts       60
    negative-time-to-live   hosts       5
    check-files             hosts       yes
    shared                  hosts       yes

    enable-cache        passwd      no
    enable-cache        group       no
    enable-cache        services    no
    enable-cache        netgroup    no

効果

結果として、 約30% サイトの平均レスポンスタイムが改善しました。
(当社比。もちろん、SDKや各種エンドポイントの利用頻度によって効果は違います)
DNSクエリをtcpdumpで確認してみると、RDS/ElastiCacheだけではなくS3やCloudFrontなど他の
AWSリソースにもリクエストを投げており、それらSDKを利用したアクセスにも効果がありました。

注意点

DNSキャッシュしている期間は、フェイルオーバー遅延などが起こるので、許容できる範囲でttlを設定する必要があります。

おまけの改善:リリース回数が増えると性能劣化する問題

次に、nscdでレスポンスが速くなったことで リリース後にレスポンスが遅くなる という問題が顕在化しました。
(nscdの改善に比べればおまけみたいなものですが)

原因

OPcacheのCache Fullが発生し、キャッシュにのりきらなくなったことが原因でした。
※OPcacheについては、php.netQiita記事を参照ください。
Opcache Control Panelを使って性能劣化時のOPcacheキャッシュ利用状況を取得してみました。
取得した結果(抜粋)が以下です。

Cache Fullが1になってますね。^^;

弊社では、php実装したWebアプリは、
デプロイサーバでコード取得→composer install→APサーバ配布→シンボリックリンク切り替え
という流れでアトミックにデプロイしています。よくある方法ですね。
最近リリース頻度が増えてきており、1日に数回リリースすることがあります。(多分2桁はいかないけど)

OPcacheは、一度キャッシュしたデータは放置すると消えません。
キャッシュをクリアするには再起動するかopcache_reset()を実行する必要があります。
なので、リリースを繰り返した結果キャッシュサイズ上限を超えると、毎回コンパイルが走り、性能が劣化することになります。

対策

  • Apache再起動してキャッシュクリア
  • 上限超えないように共有メモリサイズ(opcache.memory_consumption)を増やす
  • 変更なしファイルは同じタイムスタンプでデプロイする(不要なキャッシュを回避)

などなど、いろいろやり方はあると思いますが、
今回は簡単にできるhttpd再起動(apachectl graceful)の定期実行でキャッシュクリアするようにしました。

効果

一部サーバのレスポンスタイムの推移です。後半は性能劣化が起きなくなっています\(^o^)/

まとめ

頭打ちと思っていても、まだまだ改善できる余地はあるのだなぁ(スキル不足w)と実感。
ちなみに今回の改善をする前に、php5.6 -> php7にあげたのですが、その時に全然改善しなかったのは
他のボトルネックが大きすぎて、見えづらかったのかも。と今になって思いました。

最後に、今回あげた改善例も種類は違えどキャッシュの改善。
やっぱりキャッシュ戦略大事ですね!!