はじめに
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利用の場合
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.netやQiita記事を参照ください。
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にあげたのですが、その時に全然改善しなかったのは
他のボトルネックが大きすぎて、見えづらかったのかも。と今になって思いました。
最後に、今回あげた改善例も種類は違えどキャッシュの改善。
やっぱりキャッシュ戦略大事ですね!!