2020/05/29

nginxのproxy_cacheを設定して、Nuxt.jsのサイトを高速化しました

nginxnuxtjs

概要

本サイトのクライアント側のアプリ(htmlのレンダリングやインタラクティブな処理)にはNuxt.jsを利用しています。
リリース当初は(めんどくさくて)やっていなかったのですが、やろうやろうと思っていたNginxのproxy_cacheの導入を行いました。

まずは、ローカルのdocker-composeでテストをして、問題ないことを確認しつつ、本番のnginxの設定を修正して適用しました。

ただ、ローカルではうまくいったものの、本番ではどうしてもうまくいかなったので、そちらの調査と解決も行いました。

nginxの設定ファイルは基本的には nginx をリバースプロキシとして使う - NuxtJS こちらの記事を踏襲しました。

実施事項

ローカルでの動作確認

以下のような構成で試しました。

├── docker-compose.yml
└── nginx
    ├── Dockerfile
    └── sites-available
        └── portfolio-web.conf

nginx/Dockerfile

  • nginxのバージョンは本番環境のものに合わせました “nginx -V” で確認できます。
  • また、docker上では、site-availableの設定ファイルを conf.d/default.conf へ上書きします
    • 通常、site-availableをシンボリックファイルをsites-enabledに作成して、読み込ませるのがbest practiceではありますが、Dockerは1プロセス1サイトが基本なので、こちらの構成がシンプルでみやすいです
FROM nginx:1.10.3-alpine
RUN apk update; \
    apk add curl bash
COPY sites-available/portfolio-web.conf /etc/nginx/conf.d/default.conf
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]

nginx/sites-available/portfolio-web.conf

proxy_cache_path  /var/cache/nginx/portfolio-web levels=2:2 keys_zone=portfolio-web:1m max_size=1g inactive=5m use_temp_path=off;
proxy_temp_path   /var/cache/nginx/portfolio-web/cache;

upstream portfolio_web {
  server web:3000; # docker-composeで定義した service名 port番号と合わせる
}

server {
  listen 0.0.0.0:80;
  listen [::]:80;
  server_name _;
  access_log /dev/stdout;
  error_log /dev/stderr;

  location / {
    proxy_pass                          http://portfolio_web;
    proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto  $scheme;
    proxy_set_header X-Real-IP          $remote_addr;
    proxy_set_header Host               $host;
    proxy_redirect                      off;
    proxy_read_timeout                  1m;
    proxy_connect_timeout               1m;

    # cache settings
    proxy_ignore_headers      Cache-Control;          # nuxt appで付与された Cache-Controlを無視する
    add_header X-Cache-Status $upstream_cache_status; # キャッシュが有効かどうかをレスポンスヘッダーに付与する
    proxy_cache               portfolio-web;
    proxy_cache_key           $uri$is_args$args;
    proxy_cache_valid         200 201 300 301 302  5m;
    proxy_cache_valid         404                  1m;
    proxy_cache_lock          on;
  }
}

docker-compose.yml

環境変数の定義など、説明の都合上省略してます

version: "3"

services:
  nginx:
    image: portfolio-nginx:latest
    ports:
      - "8080:80"
    depends_on:
      - web
  web:
    image: portfolio-web:latest
    ports:
      - "3000:3000"
    environment:
      - BASE_URL=http://web:3000
      - API_URL=http://api:8000/api/v1
    depends_on:
      - api
  api:
    image: portfolio-api:latest
    ports:
      - "8000:8000"
    environment:
      - POSTGRES_USER=portfolio
      - POSTGRES_PASSWORD=password
      - POSTGRES_SERVER=db
      - POSTGRES_DB=portfolio_dev
    depends_on:
      - db
  db:
    image: postgres:12.2-alpine
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=portfolio
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=portfolio_dev
      - PGDATA=/var/lib/postgresql/data/pgdata
    volumes:
      - ./.docker/pgdata:/var/lib/postgresql/data

上記のような構成で、起動し、nginxのコンテナにログイン(docker exec -it bash)します。
それで、 以下のコマンドで、アクセスし、“X-Cache-Status” headerの値をみます。

curl -v localhost

2回目のアクセスで、“X-Cache-Status"の値が、“MISS"から"HIT"に変われば成功です。
また、”/var/cache/nginx/portfolio-web” 配下に実際にcacheされたデータが作成されているのがわかります。

本番環境での適用

本番環境でもローカルでテストしたものと似たような構成に変更して、nginxを再起動してみました。

ただ、何度アクセスしても キャッシュ用のディレクトリ “/var/cache/nginx/portfolio-web” はできているのに
キャッシュファイル自体が作成されませんでした。

いろいろ原因を考え、

proxy_ignore_headers                X-Accel-Redirect X-Accel-Expires Cache-Control Expires Set-Cookie;

を追加したり、

“proxy_cache_bypass” や、"proxy_no_cache"を追加・削除してみたりして試したのですが・・一向にうまくいきません。

何かエラーログに記載されているかもしれないと思い、エラーログをみてみました。
すると以下のようなログが出ていました。

# tail -n 50 /var/log/nginx/error.log
2020/05/29 03:47:05 [emerg] 9585#9585: bind() to [::]:80 failed (98: Address already in use)
2020/05/29 03:47:05 [emerg] 9585#9585: bind() to 0.0.0.0:443 failed (98: Address already in use)
2020/05/29 03:47:05 [emerg] 9585#9585: bind() to [::]:443 failed (98: Address already in use)
2020/05/29 03:47:05 [emerg] 9585#9585: still could not bind()

どうやら、nginxがうまく起動できていないのが原因でした。

systemctl経由とsupervisorctl経由の2つ同時で起動しており、古いプロセスがずっと生きていました。
なので、新しい設定ファイルを適用したnginxのプロセスが起動直後にエラーとなってしまい、以下のようなことが起きていました。

  • cacheのディレクトリは作成されたけれど、cacheが適用された設定では起動できていなかった
  • ブラウザからのアクセスでは、cacheが有効になっていない古いプロセスが応答していたため、普通にアクセスできてしまっていた

教訓

まあ当たり前の話なんですが・・「変なことが起きたら真っ先にエラーログを確認しましょう。」
これにつきますね。
ただ、解決するためにいろいろ調べたので、時間はかかりましたが、nginxのcache周りに少し詳しくなりました。

以上になります。