静的ウェブサイトの速度改善(サーバ編)


静的ウェブサイトの速度改善(サーバ側)を考えてみる。

■目標
静的ウェブサイトのパフォーマンスを20%くらい?改善させる。

■経緯
昨今、g●●gle先生がSEOやらなんやらで、ウェブサイトの高速化をグイグイ推している雰囲気。
そこでどーゆーことをしたらウェブサイトって早なるんかなー…と思い、とりあえず考えてみよう使って見ようの精神でやってみた。
目標も無く作るのもあんまり面白くないので取り合えずざっくり20%向上という目標を持ってみた。テヘ

と思ったのがこの記事の背景。
ちなみにQiita初投稿。
読みにくかったらごめんなさい。

目次

  • 前提
  • ウェブページの閲覧までの流れ(ざっくり)
  • ブラウザとウェブサーバのやりとり(ざっくり)
  • サーバがリクエストを受けたときの挙動(ざっくり)
  • 遅い原因になっていそうなところ
  • 取り合えず改善しそうな案
  • まとめ

前提

○改善前の環境

  • AWS
  • OS:Amazon Linux 1
  • Web:Apache 2.2系

○改善後の環境

  • AWS
  • OS:CentOS 7
  • Web:Nginx 1.15 php-fpm 5.4

ウェブページの閲覧までの流れ(ざっくり)

↓こんな感じ↓

図 ウェブページ閲覧までのフロー

①. PCからexample.comのIPアドレスを取得するためDNSへ問い合わせ。
②. DNSからexample.comのIPアドレスをPCへ回答。 ここでは例といして192.168.0.10をゲットしたとする。
③. PCから取得したIPアドレス(192.168.0.10)宛にリクエストを送信。
④. 192.168.0.10のウェブサーバがコンテンツを回答

という超絶ざっくりしたウェブページを閲覧するフローがある。

ブラウザとウェブサーバのやりとり(ざっくり)

取り合えず登場人物としては

  • プロトコル(HTTP/1.1)
  • ブラウザ
  • ウェブサーバ (取り合えず今回はApache2.2系)
  • kernel (裏方さん)
  • システムコール (裏方さん)

ぐらい…かな??

ブラウザとウェブサーバのやりとりは"図 ウェブページ閲覧までのフロー"の ③ ~ ④ の部分をもうちょい踏み込んで見てみる。

図 ブラウザとウェブサーバのフロー

取り合えず、ここで言いたいことは

1リクエストにつき、1レスポンスしかできないというHTTP/1.1の仕様がある。

ということです。

サーバがリクエストを受けたときの挙動

さて、次はサーバがレスポンスを受けたときの挙動についてです。

図 ブラウザとウェブサーバのフロー ①のリクエストを受けたときのサーバの挙動について確認しましょう。

↓多分こんな感じ↓ ※間違っていたら指摘お願いします。

図 リクエストを受けたときのウェブサーバの挙動

index.htmlちょーだい!とリクエストが来る。
Apacheからシステムコール(selectかpollかepoll)が投げられる。
カーネルがハードディスクを読みに行ってApacheにレスポンスを返す。
kernelから受け取った情報をブラウザに返す。

と、処理が多い。(間違ってたらごめんちゃい。)

遅い原因になっていそうなところ

上の図をみていると、

  • 1リクエスト、1レスポンスという効率が悪い感じ。
  • サーバがリクエストを受け取ってからレスポンスを返す処理も多い気がする。

多分この辺を改善すればウェブサイトは速くなる気がする。
※データ転送の速度が遅いとかは今回無し。
※他にもあるけど、今回は割愛

取り合えず改善しそうな案

  • HTTP/2の利用
  • ApacheからNginxへ乗り換え

でなんか改善しそうな予感。

HTTP/2とは

ここのサイトがわかりやすくて助かりました。

取り合えず、HTTP/2を使えば"1リクエスト1レスポンス"という効率の悪い感じのやつは改善しそう。

ApacheからNginxへの乗り換え

これは少し記載。
Nginxは静的コンテンツを扱うウェブサイトに向いているという話をよく聞く。
なぜかというと早くなるから。 (エェェェェ…

Nginxの特徴として、Nginxを起動すると1プロセスしか立ち上がらない。
Apacheとは違い、Nginxは1プロセスが全てのリクエストを捌くため、プロセスをフォークするオーバーヘッドが無い。
Apacheはリクエストのたびにプロセスをフォークするためオーバーヘッドが大きくなるため、Nginxに比べてレスポンスが悪いといわれている。

Nginxがなぜ1プロセスで大量のリクエストを捌けるのというと、イベント駆動型を採用しているから。
イベント駆動型が何かというとリクエストを受けたとことをトリガーとしてプロセス内で処理を開始するイメージ。
Apacheはマルチ○○○型で頑張っている。(いくつもプロセスとかが立ち上がって頑張っているイメージ。)

また、Nginxはepollというシステムコールを利用している模様。(Apacheも使っている?ような気がする。)
このepollというシステムコールが優秀らしく、
図 リクエストを受けたときのウェブサーバの挙動 がいい感じに処理されてkernelからの待ち時間が効率よく使われる(らしい。)

かなり雑に書いてしまったのでNginxの軽いまとめ

  • Nginxは起動しても1プロセスしか立ち上がらない。そのためプロセスを起動するオーバーヘッドが少ない。
  • イベント駆動型を採用
  • epollというイケてるシステムコールを使っている。(らしい)

ということでNginx + HTTP/2でサーバ作ってみた。

・Centos7
・AWS
・Nginx + php-fpm
・HTTP/2

  • わぁぁぁぁぁぁっとNginxをインストール
  • Nginxだけやとphpが動かなかったのでphp-fpmをぶわぁぁぁぁっとインストールしていい感じに設定
  • HTTP/2をおりゃぁぁぁぁっと設定

以下詳細

Nginxはリポジトリを追加しないといけないためリポジトリを追加

cat << EOL > /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1
EOL

続いてNginx インストール

yum install nginx php-fpm

Nginx の設定ファイルをいい感じに設定。
基本デフォルトでもいいけど、バージョン隠す設定とcssとかを圧縮する設定を入れてとく。

# cat /etc/nginx/nginx.conf

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    server_tokens off;    # ←これがバージョン隠す設定

    sendfile        on;

    keepalive_timeout  65;

    gzip  on;   # この辺が圧縮の設定
    gzip_types  text/plain
                text/xml
                text/css
                text/js
                image/svg+xml
                application/xml
                application/xhtml+xml
                application/rss+xml
                application/atom_xml
                application/javascript
                application/x-javascript
                application/x-httpd-php
                application/x-font-ttf
                application/x-font-opentype ;

    include /etc/nginx/conf.d/*.conf;

}

各バーチャルホストの設定は/etc/nginx/conf.d/default.confをコピって修正。

$ cp -a /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/example.conf

適当にVirtualhostの設定をする。

# vim /etc/nginx/conf.d/example.conf

server {
    listen 443 ssl http2;   # ← http2と書けばHTTP/2で通信される(ブラウザが対応してれば。)
                            #   ブラウザが対応していない場合は、HTTP/1.1で通信されるため表示影響はなし。

    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    access_log  /var/log/site/example/ssl_access.log main;
    error_log  /var/log/site/example/ssl_error.log ;

    expires 7d;

    location / {
        root   /var/www/example;
        index  index.php;
    }

    # ↓ここら辺がphp-fpmに投げる設定
    location ~\.(php|html)$ {
        root           /usr/local/example;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

}

続いてphp-fpmの設定
userとgroupがapacheになっているのでそこを修正

# grep apache /etc/php-fpm.d/www.conf
; RPM: apache Choosed to be able to access some dir as httpd
user = apache
group = apache
# cp -a /etc/php-fpm.d/www.conf /etc/php-fpm.d/www.conf_org
# sed -i s/apache/nginx/g /etc/php-fpm.d/www.conf

# grep nginx /etc/php-fpm.d/www.conf
; RPM: nginx Choosed to be able to access some dir as httpd
user = nginx
group = nginx

書き換わっているからOK

ここまで来たらnginxとphp-fpmを起動

# nginx -t 
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

# systemctl start nginx php-fpm

※自動起動もお忘れなく
# systemctl enable nginx php-fpm

取り合えずアクセスできる状態になったので、abコマンドとh2loadコマンドでベンチマークを取ってみる。
※abコマンドはHTTP/1.1用
※h2loadコマンドはHTTP/2用

取り合えず、10同時接続、トータルで100リクエストを投げてみる。

■普通のApache構成
# ab -c 10 -n 100 https://【apacheのドメイン】/
・・・
Requests per second:    510.58 [#/sec] (mean)
・・・

続いてNginx + php-fpm + HTTP/2のほうに叩いてみる

# h2load -c 10 -n 100 https://【Nginxのドメイン】/
・・・
finished in 78.91ms, 1267.31 req/s, 319.18KB/s
※ちょっとabコマンドと出力内容違うけど、気にしない。
・・・

上の結果をまとめると

Apache: 510.58 [req/s]
Nginx :1267.31 [req/s]

1秒間に処理できるリクエスト数が倍以上に…!!

Nginx + php-fpm + HTTP/2 構成に夢ありすぎた。

サイトの評価も調べてみる。
■モバイル版サイト
※計測サイト:Test my Site

2秒早くなった!!!

■PC版サイト
※計測サイト:GTmetrix

めちゃくちゃ早くなった!!!!
※見るところとしてはPageSpeed Score と Fully Loaded Time が多分重要。

Page Speed Insight っていうサイトもあるけど、見かたがよくわからんから割愛

まとめ

  • サーバ側で速度改善をしようとすると

    • 1リクエスト1レスポンスをどう早くするか
    • サーバ側のディスクからの読み込み処理をどう効率よくするか
    • 今回は書いていないが、キャッシュさせるのでもいいと思う。
    • CDNを使うって言うのもいい感じ
  • Nginx と Apache の比較

    • 静的コンテンツならNginx、動的はApacheのほうがよさげ。
    • NginxでもWordpressを爆速にできるらしい。今度やってみよう。
    • 大量アクセス大量リクエストがあるならNginxが向いている
    • ウェブサーバとしてはApacheのほうが機能が豊富のなためいろいろできる。
  • HTTP/2について

    • 見た感じまだそこまでHTTP/2で実装されているサイトは少なそうな雰囲気
    • HTTP/2の恩恵を受けられるサイトは大量のリクエストが発生するサイト。
    • もともとリクエスト数が少ないサイトはあまり恩恵が受けられない。
    • 常時HTTPS化が必須。

速度改善においてインフラだけで成果を出すことは難しいので、サイトを作っている開発の方と協力していくことが大事。
速度がよくてもコンテンツがイケてないと、SEOは上がらないし、
コンテンツがよくても速度がゴミだったら、SEOは上がりません。

一緒に頑張りましょうo(○`・д・´○)ノ ヵ゛ン'`゛ルゾ-!!!

ながながとグダグダな内容でしたが、最後まで読んでいただいた方、ありがとうございました。

参考にさせていただいたサイト

とてもとても助かりました。ありがとうございます。